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 $maxloop = 20;
966 $loopnb = 0; // Protection against infinite loop
967 while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/i', $out, $reg) && ($loopnb < $maxloop)) { // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
968 $loopnb++;
969 $newout = '';
970
971 if ($reg[1] == 'DAY') {
972 $tmp = dol_getdate(dol_now(), true);
973 $newout = $tmp['mday'];
974 } elseif ($reg[1] == 'MONTH') {
975 $tmp = dol_getdate(dol_now(), true);
976 $newout = $tmp['mon'];
977 } elseif ($reg[1] == 'YEAR') {
978 $tmp = dol_getdate(dol_now(), true);
979 $newout = $tmp['year'];
980 } elseif ($reg[1] == 'PREVIOUS_DAY') {
981 $tmp = dol_getdate(dol_now(), true);
982 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
983 $newout = $tmp2['day'];
984 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
985 $tmp = dol_getdate(dol_now(), true);
986 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
987 $newout = $tmp2['month'];
988 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
989 $tmp = dol_getdate(dol_now(), true);
990 $newout = ($tmp['year'] - 1);
991 } elseif ($reg[1] == 'NEXT_DAY') {
992 $tmp = dol_getdate(dol_now(), true);
993 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
994 $newout = $tmp2['day'];
995 } elseif ($reg[1] == 'NEXT_MONTH') {
996 $tmp = dol_getdate(dol_now(), true);
997 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
998 $newout = $tmp2['month'];
999 } elseif ($reg[1] == 'NEXT_YEAR') {
1000 $tmp = dol_getdate(dol_now(), true);
1001 $newout = ($tmp['year'] + 1);
1002 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1003 $newout = $mysoc->country_id;
1004 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1005 $newout = $user->id;
1006 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1007 $newout = $user->fk_user;
1008 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1009 $newout = $conf->entity;
1010 } elseif ($reg[1] == 'ID') {
1011 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1012 } else {
1013 $newout = ''; // Key not found, we replace with empty string
1014 }
1015 //var_dump('__'.$reg[1].'__ -> '.$newout);
1016 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1017 }
1018 }
1019
1020 // Check type of variable and make sanitization according to this
1021 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1022 if (!is_array($out) || empty($out)) {
1023 $out = array();
1024 } else {
1025 $tmparray = explode(':', $check);
1026 if (!empty($tmparray[1])) {
1027 $tmpcheck = $tmparray[1];
1028 } else {
1029 $tmpcheck = 'alphanohtml';
1030 }
1031 foreach ($out as $outkey => $outval) {
1032 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1033 }
1034 }
1035 } else {
1036 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1037 // we use the < or > to make a search on a numeric value to do higher or lower so we can add a space to break html tags
1038 if (strpos($paramname, 'search_') === 0) {
1039 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1040 }
1041
1042 // @phan-suppress-next-line UnknownSanitizeType
1043 $out = sanitizeVal($out, $check, $filter, $options);
1044 }
1045
1046 // Sanitizing for special parameters.
1047 // Note: There is no reason to allow the backtopage/backtopageforcancel/backtopagejs, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1048 if (preg_match('/backtopage/', $paramname) || $paramname == 'backtolist' || $paramname == 'backtourl') {
1049 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1050 $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retrieve it after other replacements.
1051 do {
1052 $oldstringtoclean = $out;
1053 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1054 $out = preg_replace(array('/^[^\?]*%/'), '', $out); // We remove any % chars before the ?. Example in url: '/product/stock/card.php?action=create&backtopage=%2Fdolibarr_dev%2Fhtdocs%2Fpro%25duct%2Fcard.php%3Fid%3Dabc'
1055 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1056 } while ($oldstringtoclean != $out);
1057 }
1058
1059 // Code for search criteria persistence.
1060 // Save data into session if key start with 'search_'
1061 if (empty($method) || $method == 3 || $method == 4) {
1062 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1063 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1064
1065 // We save search key only if $out not empty that means:
1066 // - posted value not empty, or
1067 // - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
1068
1069 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1070 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1071 }
1072 }
1073 }
1074
1075 return $out;
1076}
1077
1087function GETPOSTINT($paramname, $method = 0)
1088{
1089 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1090}
1091
1092
1101function GETPOSTFLOAT($paramname, $rounding = '')
1102{
1103 // price2num() is used to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
1104 return (float) price2num(GETPOST($paramname), $rounding, 2);
1105}
1106
1107
1118function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1119{
1120 return sanitizeVal($out, $check, $filter, $options);
1121}
1122
1132function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1133{
1134 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1135 // Check is done after replacement
1136 switch ($check) {
1137 case 'none':
1138 break;
1139 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1140 if (!is_numeric($out)) {
1141 $out = '';
1142 }
1143 break;
1144 case 'intcomma':
1145 if (is_array($out)) {
1146 $out = implode(',', $out);
1147 }
1148 if (preg_match('/[^0-9,-]+/i', $out)) {
1149 $out = '';
1150 }
1151 break;
1152 case 'san_alpha':
1153 $out = filter_var($out, FILTER_SANITIZE_STRING);
1154 break;
1155 case 'email':
1156 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1157 break;
1158 case 'aZ':
1159 if (!is_array($out)) {
1160 $out = trim($out);
1161 if (preg_match('/[^a-z]+/i', $out)) {
1162 $out = '';
1163 }
1164 }
1165 break;
1166 case 'aZ09':
1167 if (!is_array($out)) {
1168 $out = trim($out);
1169 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1170 $out = '';
1171 }
1172 }
1173 break;
1174 case 'aZ09arobase': // great to sanitize $objecttype parameter
1175 if (!is_array($out)) {
1176 $out = trim($out);
1177 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1178 $out = '';
1179 }
1180 }
1181 break;
1182 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1183 if (!is_array($out)) {
1184 $out = trim($out);
1185 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1186 $out = '';
1187 }
1188 }
1189 break;
1190 case 'alpha': // No html and no ../ and "
1191 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1192 if (!is_array($out)) {
1193 $out = trim($out);
1194 do {
1195 $oldstringtoclean = $out;
1196 // Remove html tags
1197 $out = dol_string_nohtmltag($out, 0);
1198 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1199 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1200 // Remove also other dangerous string sequences
1201 // '../' or '..\' is dangerous because it allows dir transversals
1202 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1203 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1204 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1205 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1206 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1207 } while ($oldstringtoclean != $out);
1208 // keep lines feed
1209 }
1210 break;
1211 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email>". Less secured than 'alphanohtml'
1212 if (!is_array($out)) {
1213 $out = trim($out);
1214 do {
1215 $oldstringtoclean = $out;
1216 // Decode html entities
1217 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1218 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1219 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1220 // Remove also other dangerous string sequences
1221 // '../' or '..\' is dangerous because it allows dir transversals
1222 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1223 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1224 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1225 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1226 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1227 } while ($oldstringtoclean != $out);
1228 }
1229 break;
1230 case 'nohtml': // No html
1231 $out = dol_string_nohtmltag($out, 0);
1232 break;
1233 case 'restricthtmlnolink':
1234 case 'restricthtml': // Recommended for most html textarea
1235 case 'restricthtmlallowclass':
1236 case 'restricthtmlallowunvalid':
1237 $out = dol_htmlwithnojs($out, 1, $check);
1238 break;
1239 case 'custom':
1240 if (!empty($out)) {
1241 if (empty($filter)) {
1242 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1243 }
1244 if (is_null($options)) {
1245 $options = 0;
1246 }
1247 $out = filter_var($out, $filter, $options);
1248 }
1249 break;
1250 default:
1251 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1252 $out = GETPOST($out, 'alphanohtml');
1253 break;
1254 }
1255
1256 return $out;
1257}
1258
1259
1260if (!function_exists('dol_getprefix')) {
1271 function dol_getprefix($mode = '')
1272 {
1273 // If prefix is for email (we need to have $conf already loaded for this case)
1274 if ($mode == 'email') {
1275 global $conf;
1276
1277 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1278 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1279 return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1280 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1281 return $_SERVER["SERVER_NAME"];
1282 }
1283 }
1284
1285 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1286 if (!empty($conf->file->instance_unique_id)) {
1287 return sha1('dolibarr'.$conf->file->instance_unique_id);
1288 }
1289
1290 // For backward compatibility when instance_unique_id is not set
1291 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1292 }
1293
1294 // If prefix is for session (no need to have $conf loaded)
1295 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1296 $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
1297
1298 // The recommended value (may be not defined for old versions)
1299 if (!empty($tmp_instance_unique_id)) {
1300 return sha1('dolibarr'.$tmp_instance_unique_id);
1301 }
1302
1303 // For backward compatibility when instance_unique_id is not set
1304 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1305 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1306 } else {
1307 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1308 }
1309 }
1310}
1311
1322function dol_include_once($relpath, $classname = '')
1323{
1324 global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var']
1325
1326 $fullpath = dol_buildpath($relpath);
1327
1328 if (!file_exists($fullpath)) {
1329 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1330 return false;
1331 }
1332
1333 if (!empty($classname) && !class_exists($classname)) {
1334 return include $fullpath;
1335 } else {
1336 return include_once $fullpath;
1337 }
1338}
1339
1340
1354function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1355{
1356 global $conf;
1357
1358 $path = preg_replace('/^\//', '', $path);
1359
1360 if (empty($type)) { // For a filesystem path
1361 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1362 if (is_array($conf->file->dol_document_root)) {
1363 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1364 if ($key == 'main') {
1365 continue;
1366 }
1367 // if (@file_exists($dirroot.'/'.$path)) {
1368 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1369 $res = $dirroot.'/'.$path;
1370 return $res;
1371 }
1372 }
1373 }
1374 if ($returnemptyifnotfound) {
1375 // Not found into alternate dir
1376 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1377 return '';
1378 }
1379 }
1380 } else {
1381 // For an url path
1382 // We try to get local path of file on filesystem from url
1383 // Note that trying to know if a file on disk exist by forging path on disk from url
1384 // works only for some web server and some setup. This is bugged when
1385 // using proxy, rewriting, virtual path, etc...
1386 $res = '';
1387 if ($type == 1) {
1388 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1389 }
1390 if ($type == 2) {
1391 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1392 }
1393 if ($type == 3) {
1394 $res = DOL_URL_ROOT.'/'.$path;
1395 }
1396
1397 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1398 if ($key == 'main') {
1399 if ($type == 3) {
1400 /*global $dolibarr_main_url_root;*/
1401
1402 // Define $urlwithroot
1403 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1404 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1405 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1406
1407 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1408 }
1409 continue;
1410 }
1411 $regs = array();
1412 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1413 if (!empty($regs[1])) {
1414 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1415 //if (file_exists($dirroot.'/'.$regs[1])) {
1416 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1417 if ($type == 1) {
1418 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1419 } elseif ($type == 2) {
1420 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1421 } elseif ($type == 3) {
1422 /*global $dolibarr_main_url_root;*/
1423
1424 // Define $urlwithroot
1425 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1426 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1427 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1428
1429 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).$conf->file->dol_url_root[$key].'/'.$path; // Test on start with http is for old conf syntax
1430 }
1431 break;
1432 }
1433 }
1434 }
1435 }
1436
1437 return $res;
1438}
1439
1450function dol_get_object_properties($obj, $properties = [])
1451{
1452 // Get real properties using get_object_vars() if $properties is empty
1453 if (empty($properties)) {
1454 return get_object_vars($obj);
1455 }
1456
1457 $existingProperties = [];
1458 $realProperties = get_object_vars($obj);
1459
1460 // Get the real or magic property values
1461 foreach ($properties as $property) {
1462 if (array_key_exists($property, $realProperties)) {
1463 // Real property, add the value
1464 $existingProperties[$property] = $obj->{$property};
1465 } elseif (property_exists($obj, $property)) {
1466 // Magic property
1467 $existingProperties[$property] = $obj->{$property};
1468 }
1469 }
1470
1471 return $existingProperties;
1472}
1473
1474
1489function dol_clone($object, $native = 0)
1490{
1491 if ($native == 0) {
1492 // deprecated method, use the method with native = 2 instead
1493 $tmpsavdb = null;
1494 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1495 $tmpsavdb = $object->db;
1496 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1497 }
1498
1499 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1500
1501 if (!empty($tmpsavdb)) {
1502 $object->db = $tmpsavdb;
1503 }
1504 } elseif ($native == 2) {
1505 // recommended method to have a full isolated cloned object
1506 $myclone = new stdClass();
1507 $tmparray = get_object_vars($object); // return only public properties
1508
1509 if (is_array($tmparray)) {
1510 foreach ($tmparray as $propertykey => $propertyval) {
1511 if (is_scalar($propertyval) || is_array($propertyval)) {
1512 $myclone->$propertykey = $propertyval;
1513 }
1514 }
1515 }
1516 } else {
1517 $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (referring to the same target/variable)
1518 }
1519
1520 return $myclone;
1521}
1522
1532function dol_size($size, $type = '')
1533{
1534 global $conf;
1535 if (empty($conf->dol_optimize_smallscreen)) {
1536 return $size;
1537 }
1538 if ($type == 'width' && $size > 250) {
1539 return 250;
1540 } else {
1541 return 10;
1542 }
1543}
1544
1545
1557function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1558{
1559 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1560 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1561 // Char '/' and '\' are file delimiters.
1562 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1563 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1564 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1565 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1566 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1567 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1568 $tmp = str_replace('..', '', $tmp);
1569 return $tmp;
1570}
1571
1572
1584function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1585{
1586 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1587 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1588 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1589 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1590 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1591 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1592 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1593 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1594 $tmp = str_replace('..', '', $tmp);
1595 return $tmp;
1596}
1597
1605function dol_sanitizeUrl($stringtoclean, $type = 1)
1606{
1607 // We clean string because some hacks try to obfuscate evil strings by inserting non printable chars. Example: 'java(ascci09)scr(ascii00)ipt' is processed like 'javascript' (whatever is place of evil ascii char)
1608 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1609 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1610 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1611 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1612
1613 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1614 if ($type == 1) {
1615 // removing : should disable links to external url like http:aaa)
1616 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1617 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1618 }
1619
1620 do {
1621 $oldstringtoclean = $stringtoclean;
1622 // removing '&colon' should disable links to external url like http:aaa)
1623 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1624 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1625 } while ($oldstringtoclean != $stringtoclean);
1626
1627 if ($type == 1) {
1628 // removing '//' should disable links to external url like //aaa or http//)
1629 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1630 }
1631
1632 return $stringtoclean;
1633}
1634
1641function dol_sanitizeEmail($stringtoclean)
1642{
1643 do {
1644 $oldstringtoclean = $stringtoclean;
1645 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1646 } while ($oldstringtoclean != $stringtoclean);
1647
1648 return $stringtoclean;
1649}
1650
1659function dol_string_unaccent($str)
1660{
1661 global $conf;
1662
1663 if (is_null($str)) {
1664 return '';
1665 }
1666
1667 if (utf8_check($str)) {
1668 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1669 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1670 return $transliterator->transliterate($str);
1671 }
1672 // See http://www.utf8-chartable.de/
1673 $string = rawurlencode($str);
1674 $replacements = array(
1675 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1676 '%C3%87' => 'C',
1677 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1678 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1679 '%C3%91' => 'N',
1680 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1681 '%C5%A0' => 'S',
1682 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1683 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1684 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1685 '%C3%A7' => 'c',
1686 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1687 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1688 '%C3%B1' => 'n',
1689 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1690 '%C5%A1' => 's',
1691 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1692 '%C3%BD' => 'y', '%C3%BF' => 'y'
1693 );
1694 $string = strtr($string, $replacements);
1695 return rawurldecode($string);
1696 } else {
1697 // See http://www.ascii-code.com/
1698 $string = strtr(
1699 $str,
1700 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1701 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1702 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1703 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1704 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1705 \xF9\xFA\xFB\xFC\xFD\xFF",
1706 "AAAAAAC
1707 EEEEIIIIDN
1708 OOOOOUUUY
1709 aaaaaaceeee
1710 iiiidnooooo
1711 uuuuyy"
1712 );
1713 $string = strtr($string, array("\xC4" => "Ae", "\xC6" => "AE", "\xD6" => "Oe", "\xDC" => "Ue", "\xDE" => "TH", "\xDF" => "ss", "\xE4" => "ae", "\xE6" => "ae", "\xF6" => "oe", "\xFC" => "ue", "\xFE" => "th"));
1714 return $string;
1715 }
1716}
1717
1731function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1732{
1733 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1734 if (empty($keepspaces)) {
1735 $forbidden_chars_to_replace[] = " ";
1736 }
1737 $forbidden_chars_to_remove = array();
1738 //$forbidden_chars_to_remove=array("(",")");
1739
1740 if (is_array($badcharstoreplace)) {
1741 $forbidden_chars_to_replace = $badcharstoreplace;
1742 }
1743 if (is_array($badcharstoremove)) {
1744 $forbidden_chars_to_remove = $badcharstoremove;
1745 }
1746
1747 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1748 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1749}
1750
1751
1765function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1766{
1767 if ($removetabcrlf) {
1768 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1769 } else {
1770 return preg_replace('/[\x00-\x08\x11-\x12\x14-\x1F\x7F]/u', '', $str); // /u operator should make UTF8 valid characters being ignored so are not included into the replace
1771 }
1772}
1773
1782function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1783{
1784 if (is_null($stringtoescape)) {
1785 return '';
1786 }
1787
1788 // escape quotes and backslashes, newlines, etc.
1789 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1790 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1791 if (empty($noescapebackslashn)) {
1792 $substitjs["\n"] = '\\n';
1793 $substitjs['\\'] = '\\\\';
1794 }
1795 if (empty($mode)) {
1796 $substitjs["'"] = "\\'";
1797 $substitjs['"'] = "\\'";
1798 } elseif ($mode == 1) {
1799 $substitjs["'"] = "\\'";
1800 } elseif ($mode == 2) {
1801 $substitjs['"'] = '\\"';
1802 } elseif ($mode == 3) {
1803 $substitjs["'"] = "\\'";
1804 $substitjs['"'] = "\\\"";
1805 }
1806 return strtr($stringtoescape, $substitjs);
1807}
1808
1818function dol_escape_uri($stringtoescape)
1819{
1820 return rawurlencode($stringtoescape);
1821}
1822
1829function dol_escape_json($stringtoescape)
1830{
1831 return str_replace('"', '\"', $stringtoescape);
1832}
1833
1841function dol_escape_php($stringtoescape, $stringforquotes = 2)
1842{
1843 if (is_null($stringtoescape)) {
1844 return '';
1845 }
1846
1847 if ($stringforquotes == 2) {
1848 return str_replace('"', "'", $stringtoescape);
1849 } elseif ($stringforquotes == 1) {
1850 // We remove the \ char.
1851 // If we allow the \ char, we can have $stringtoescape =
1852 // abc\';phpcodedanger; so the escapement will become
1853 // abc\\';phpcodedanger; and injecting this into
1854 // $a='...' will give $ac='abc\\';phpcodedanger;
1855 $stringtoescape = str_replace('\\', '', $stringtoescape);
1856 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1857 }
1858
1859 return 'Bad parameter for stringforquotes in dol_escape_php';
1860}
1861
1868function dol_escape_all($stringtoescape)
1869{
1870 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1871}
1872
1879function dol_escape_xml($stringtoescape)
1880{
1881 return $stringtoescape;
1882}
1883
1891function dolPrintLabel($s)
1892{
1893 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1894}
1895
1905function dolPrintHTML($s, $allowiframe = 0)
1906{
1907 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1908}
1909
1917function dolPrintHTMLForAttribute($s)
1918{
1919 // The dol_htmlentitiesbr will convert simple text into html
1920 // The dol_escape_htmltag will escape html chars.
1921 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
1922}
1923
1932function dolPrintHTMLForTextArea($s, $allowiframe = 0)
1933{
1934 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
1935}
1936
1943function dolPrintPassword($s)
1944{
1945 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
1946}
1947
1948
1965function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1966{
1967 if ($noescapetags == 'common') {
1968 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
1969 // Add also html5 tags
1970 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
1971 }
1972 if ($cleanalsojavascript) {
1973 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1974 }
1975
1976 // escape quotes and backslashes, newlines, etc.
1977 if ($escapeonlyhtmltags) {
1978 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1979 } else {
1980 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
1981 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
1982 }
1983 if (!$keepb) {
1984 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
1985 }
1986 if (!$keepn) {
1987 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1988 } elseif ($keepn == -1) {
1989 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
1990 }
1991
1992 if ($escapeonlyhtmltags) {
1993 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1994 } else {
1995 // Escape tags to keep
1996 $tmparrayoftags = array();
1997 if ($noescapetags) {
1998 $tmparrayoftags = explode(',', $noescapetags);
1999 }
2000 if (count($tmparrayoftags)) {
2001 $reg = array();
2002 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2003
2004 foreach ($tmparrayoftags as $tagtoreplace) {
2005 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2006 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2007 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2008
2009 // For case of tag with attribute
2010 do {
2011 $tmpold = $tmp;
2012
2013 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2014 // We want to pprotect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() lafter
2015 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2016 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes); // TODO Try to remove this
2017 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2018 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2019 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2020 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2021 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2022 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2023
2024 // TODO Test the replacement by using a memory array for attributes to restore them
2025 // TODO Test a tag like '<a href="https://mydomain.com/aaa%20bbb">abc</a>'
2026 //$tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2027 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2028 }
2029 // TODO This may be already in previous case ? Try to remove this.
2030 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)(\s+)\/>/', $tmp, $reg)) {
2031 // We want to protect the attribute part ... in '<xxx ... />' to avoid transformation by htmlentities() lafter
2032 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must not have [ ] inside the attribute string
2033 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2034 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2035 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2036 //$tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2037 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].$reg[3].'/>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2038 }
2039
2040 $diff = strcmp($tmpold, $tmp);
2041 } while ($diff);
2042 }
2043 }
2044
2045 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2046
2047 //print $result;
2048
2049 if (count($tmparrayoftags)) {
2050 foreach ($tmparrayoftags as $tagtoreplace) {
2051 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2052 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2053 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2054 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2055 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2056 }
2057
2058 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2059 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2060 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2061 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2062 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2063 }
2064
2065 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2066
2067 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2068
2069 return $result;
2070 }
2071}
2072
2080function dol_strtolower($string, $encoding = "UTF-8")
2081{
2082 if (function_exists('mb_strtolower')) {
2083 return mb_strtolower($string, $encoding);
2084 } else {
2085 return strtolower($string);
2086 }
2087}
2088
2097function dol_strtoupper($string, $encoding = "UTF-8")
2098{
2099 if (function_exists('mb_strtoupper')) {
2100 return mb_strtoupper($string, $encoding);
2101 } else {
2102 return strtoupper($string);
2103 }
2104}
2105
2114function dol_ucfirst($string, $encoding = "UTF-8")
2115{
2116 if (function_exists('mb_substr')) {
2117 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2118 } else {
2119 return ucfirst($string);
2120 }
2121}
2122
2131function dol_ucwords($string, $encoding = "UTF-8")
2132{
2133 if (function_exists('mb_convert_case')) {
2134 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2135 } else {
2136 return ucwords($string);
2137 }
2138}
2139
2162function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2163{
2164 global $conf, $user, $debugbar;
2165
2166 // If syslog module enabled
2167 if (!isModEnabled('syslog')) {
2168 return;
2169 }
2170
2171 // Check if we are into execution of code of a website
2172 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2173 global $website, $websitekey;
2174 if (is_object($website) && !empty($website->ref)) {
2175 $suffixinfilename .= '_website_'.$website->ref;
2176 } elseif (!empty($websitekey)) {
2177 $suffixinfilename .= '_website_'.$websitekey;
2178 }
2179 }
2180
2181 // Check if we have a forced suffix
2182 if (defined('USESUFFIXINLOG')) {
2183 $suffixinfilename .= constant('USESUFFIXINLOG');
2184 }
2185
2186 if ($ident < 0) {
2187 foreach ($conf->loghandlers as $loghandlerinstance) {
2188 $loghandlerinstance->setIdent($ident);
2189 }
2190 }
2191
2192 if (!empty($message)) {
2193 // Test log level
2194 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2195 $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');
2196
2197 if (!array_key_exists($level, $logLevels)) {
2198 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2199 $level = $logLevels[LOG_ERR];
2200 }
2201 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2202 return;
2203 }
2204
2205 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2206 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2207 }
2208
2209 // If adding log inside HTML page is required
2210 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2211 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2212 $ospid = sprintf("%7s", dol_trunc(getmypid(), 7, 'right', 'UTF-8', 1));
2213 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2214
2215 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2216 }
2217
2218 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2219 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2220 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2221 print "\n\n<!-- Log start\n";
2222 print dol_escape_htmltag($message)."\n";
2223 print "Log end -->\n";
2224 }
2225
2226 $data = array(
2227 'message' => $message,
2228 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2229 'level' => $level,
2230 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2231 'ip' => false,
2232 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2233 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2234 );
2235
2236 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2237 if (!empty($remoteip)) {
2238 $data['ip'] = $remoteip;
2239 // This is when server run behind a reverse proxy
2240 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2241 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2242 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2243 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2244 }
2245 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2246 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2247 $data['ip'] = $_SERVER['SERVER_ADDR'];
2248 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2249 // 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).
2250 $data['ip'] = $_SERVER['COMPUTERNAME'];
2251 } else {
2252 $data['ip'] = '???';
2253 }
2254
2255 if (!empty($_SERVER['USERNAME'])) {
2256 // 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).
2257 $data['osuser'] = $_SERVER['USERNAME'];
2258 } elseif (!empty($_SERVER['LOGNAME'])) {
2259 // 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).
2260 $data['osuser'] = $_SERVER['LOGNAME'];
2261 }
2262
2263 // Loop on each log handler and send output
2264 foreach ($conf->loghandlers as $loghandlerinstance) {
2265 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2266 continue;
2267 }
2268 $loghandlerinstance->export($data, $suffixinfilename);
2269 }
2270 unset($data);
2271 }
2272
2273 if ($ident > 0) {
2274 foreach ($conf->loghandlers as $loghandlerinstance) {
2275 $loghandlerinstance->setIdent($ident);
2276 }
2277 }
2278}
2279
2291function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2292{
2293 global $langs, $db;
2294
2295 $form = new Form($db);
2296
2297 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2298 if (empty($templatenameforexport)) {
2299 $templatenameforexport = 'website_'.$website->ref;
2300 }
2301
2302 $out = '';
2303 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2304
2305 // for generate popup
2306 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2307 $out .= 'jQuery(document).ready(function () {';
2308 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2309 $out .= ' var dialogHtml = \'';
2310
2311 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2312 $dialogcontent .= ' <div style="margin-top: 20px;">';
2313 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2314 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2315 $dialogcontent .= ' </div>';
2316 $dialogcontent .= ' <br>';
2317 $dialogcontent .= ' <div style="margin-top: 20px;">';
2318 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2319 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2320 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2321 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2322 $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>';
2323 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2324 $dialogcontent .= ' </form>';
2325 $dialogcontent .= ' </div>';
2326 $dialogcontent .= ' </div>';
2327
2328 $out .= dol_escape_js($dialogcontent);
2329
2330 $out .= '\';';
2331
2332
2333 // Add the content of the dialog to the body of the page
2334 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2335 $out .= ' if ($dialog.length > 0) {
2336 $dialog.remove();
2337 }
2338 jQuery("body").append(dialogHtml);';
2339
2340 // Configuration of popup
2341 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2342 $out .= ' autoOpen: false,';
2343 $out .= ' modal: true,';
2344 $out .= ' height: 290,';
2345 $out .= ' width: "40%",';
2346 $out .= ' title: "' . dol_escape_js($label) . '",';
2347 $out .= ' });';
2348
2349 // Simulate a click on the original "submit" input to export the site.
2350 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2351 $out .= ' console.log("Clic on exportsite.");';
2352 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2353 $out .= ' console.log("element founded:", target.length > 0);';
2354 $out .= ' if (target.length > 0) { target.click(); }';
2355 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2356 $out .= ' });';
2357
2358 // open popup
2359 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2360 $out .= ' return false;';
2361 $out .= ' });';
2362 $out .= '});';
2363 $out .= '</script>';
2364
2365 return $out;
2366}
2367
2368
2385function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2386{
2387 global $conf;
2388
2389 if (strpos($url, '?') > 0) {
2390 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2391 } else {
2392 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2393 }
2394
2395 $out = '';
2396
2397 $backtopagejsfieldsid = '';
2398 $backtopagejsfieldslabel = '';
2399 if ($backtopagejsfields) {
2400 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2401 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2402 $backtopagejsfields = $name.":".$backtopagejsfields;
2403 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2404 } else {
2405 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2406 }
2407 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2408 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2409 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2410 }
2411
2412 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2413 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2414 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2415 if (empty($conf->use_javascript_ajax)) {
2416 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2417 } elseif ($jsonopen) {
2418 $out .= ' href="#" onclick="'.$jsonopen.'"';
2419 } else {
2420 $out .= ' href="#"';
2421 }
2422 $out .= '>'.$buttonstring.'</a>';
2423
2424 if (!empty($conf->use_javascript_ajax)) {
2425 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2426 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2427 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2428 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2429 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2430
2431 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2432 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2433 jQuery(document).ready(function () {
2434 jQuery(".button_'.$name.'").click(function () {
2435 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2436 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2437 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2438 $tmpdialog.dialog({
2439 autoOpen: false,
2440 modal: true,
2441 height: (window.innerHeight - 150),
2442 width: \'80%\',
2443 title: \''.dol_escape_js($label).'\',
2444 open: function (event, ui) {
2445 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2446 },
2447 close: function (event, ui) {
2448 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2449 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2450 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2451 if (returnedid != "" && returnedid != "div for returned id") {
2452 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2453 }
2454 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2455 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2456 }
2457 }
2458 });
2459
2460 $tmpdialog.dialog(\'open\');
2461 return false;
2462 });
2463 });
2464 </script>';
2465 }
2466 return $out;
2467}
2468
2485function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2486{
2487 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2488}
2489
2506function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2507{
2508 global $conf, $langs, $hookmanager;
2509
2510 // Show title
2511 $showtitle = 1;
2512 if (!empty($conf->dol_optimize_smallscreen)) {
2513 $showtitle = 0;
2514 }
2515
2516 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2517
2518 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2519 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2520 }
2521
2522 // Show right part
2523 if ($morehtmlright) {
2524 $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.
2525 }
2526
2527 // Show title
2528 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2529 $limittitle = 30;
2530 $out .= '<a class="tabTitle">';
2531 if ($picto) {
2532 $noprefix = $pictoisfullpath;
2533 if (strpos($picto, 'fontawesome_') !== false) {
2534 $noprefix = 1;
2535 }
2536 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2537 }
2538 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2539 $out .= '</a>';
2540 }
2541
2542 // Show tabs
2543
2544 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2545 $maxkey = -1;
2546 if (is_array($links) && !empty($links)) {
2547 $keys = array_keys($links);
2548 if (count($keys)) {
2549 $maxkey = max($keys);
2550 }
2551 }
2552
2553 // Show tabs
2554 // if =0 we don't use the feature
2555 if (empty($limittoshow)) {
2556 $limittoshow = (!getDolGlobalString('MAIN_MAXTABS_IN_CARD') ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2557 }
2558 if (!empty($conf->dol_optimize_smallscreen)) {
2559 $limittoshow = 2;
2560 }
2561
2562 $displaytab = 0;
2563 $nbintab = 0;
2564 $popuptab = 0;
2565 $outmore = '';
2566 for ($i = 0; $i <= $maxkey; $i++) {
2567 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2568 // If active tab is already present
2569 if ($i >= $limittoshow) {
2570 $limittoshow--;
2571 }
2572 }
2573 }
2574
2575 for ($i = 0; $i <= $maxkey; $i++) {
2576 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2577 $isactive = true;
2578 } else {
2579 $isactive = false;
2580 }
2581
2582 if ($i < $limittoshow || $isactive) {
2583 // Output entry with a visible tab
2584 $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])).' -->';
2585
2586 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2587 if (!empty($links[$i][0])) {
2588 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2589 } else {
2590 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2591 }
2592 } elseif (!empty($links[$i][1])) {
2593 //print "x $i $active ".$links[$i][2]." z";
2594 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2595 if (!empty($links[$i][0])) {
2596 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2597 $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).'">';
2598 }
2599 $out .= $links[$i][1];
2600 if (!empty($links[$i][0])) {
2601 $out .= '</a>'."\n";
2602 }
2603 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2604 $out .= '</div>';
2605 }
2606
2607 $out .= '</div>';
2608 } else {
2609 // Add entry into the combo popup with the other tabs
2610 if (!$popuptab) {
2611 $popuptab = 1;
2612 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2613 }
2614 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2615 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2616 if (!empty($links[$i][0])) {
2617 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2618 } else {
2619 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2620 }
2621 } elseif (!empty($links[$i][1])) {
2622 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2623 $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.
2624 $outmore .= '</a>'."\n";
2625 }
2626 $outmore .= '</div>';
2627
2628 $nbintab++;
2629 }
2630 $displaytab = $i;
2631 }
2632 if ($popuptab) {
2633 $outmore .= '</div>';
2634 }
2635
2636 if ($popuptab) { // If there is some tabs not shown
2637 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2638 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2639 $widthofpopup = 200;
2640
2641 $tabsname = $moretabssuffix;
2642 if (empty($tabsname)) {
2643 $tabsname = str_replace("@", "", $picto);
2644 }
2645 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2646 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2647 $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".
2648 }
2649 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2650 $out .= $outmore;
2651 $out .= '</div>';
2652 $out .= '<div></div>';
2653 $out .= "</div>\n";
2654
2655 $out .= '<script nonce="'.getNonce().'">';
2656 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2657 var x = this.offsetLeft, y = this.offsetTop;
2658 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2659 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2660 $('#moretabsList".$tabsname."').css('".$right."','8px');
2661 }
2662 $('#moretabsList".$tabsname."').css('".$left."','auto');
2663 });
2664 ";
2665 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2666 $out .= "</script>";
2667 }
2668
2669 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2670 $out .= "</div>\n";
2671 }
2672
2673 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2674 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom')));
2675 $out .= '">'."\n";
2676 }
2677 if (!empty($dragdropfile)) {
2678 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2679 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2680 }
2681 $parameters = array('tabname' => $active, 'out' => $out);
2682 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2683 if ($reshook > 0) {
2684 $out = $hookmanager->resPrint;
2685 }
2686
2687 return $out;
2688}
2689
2697function dol_fiche_end($notab = 0)
2698{
2699 print dol_get_fiche_end($notab);
2700}
2701
2708function dol_get_fiche_end($notab = 0)
2709{
2710 if (!$notab || $notab == -1) {
2711 return "\n</div>\n";
2712 } else {
2713 return '';
2714 }
2715}
2716
2736function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2737{
2738 global $conf, $form, $user, $langs, $hookmanager, $action;
2739
2740 $error = 0;
2741
2742 $maxvisiblephotos = 1;
2743 $showimage = 1;
2744 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2745 // @phan-suppress-next-line PhanUndeclaredMethod
2746 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2747 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2748 $showbarcode = 0;
2749 }
2750 $modulepart = 'unknown';
2751
2752 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2753 $modulepart = $object->element;
2754 } elseif ($object->element == 'member') {
2755 $modulepart = 'memberphoto';
2756 } elseif ($object->element == 'user') {
2757 $modulepart = 'userphoto';
2758 }
2759
2760 if (class_exists("Imagick")) {
2761 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2762 $modulepart = $object->element;
2763 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2764 $modulepart = 'ficheinter';
2765 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2766 $modulepart = 'contract';
2767 } elseif ($object->element == 'order_supplier') {
2768 $modulepart = 'supplier_order';
2769 } elseif ($object->element == 'invoice_supplier') {
2770 $modulepart = 'supplier_invoice';
2771 }
2772 }
2773
2774 if ($object->element == 'product') {
2776 '@phan-var-force Product $object';
2777 $width = 80;
2778 $cssclass = 'photowithmargin photoref';
2779 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2780 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2781 if ($conf->browser->layout == 'phone') {
2782 $maxvisiblephotos = 1;
2783 }
2784 if ($showimage) {
2785 $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>';
2786 } else {
2787 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2788 $nophoto = '';
2789 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2790 } else { // Show no photo link
2791 $nophoto = '/public/theme/common/nophoto.png';
2792 $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>';
2793 }
2794 }
2795 } elseif ($object->element == 'category') {
2797 '@phan-var-force Categorie $object';
2798 $width = 80;
2799 $cssclass = 'photowithmargin photoref';
2800 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2801 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2802 if ($conf->browser->layout == 'phone') {
2803 $maxvisiblephotos = 1;
2804 }
2805 if ($showimage) {
2806 $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>';
2807 } else {
2808 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2809 $nophoto = '';
2810 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2811 } else { // Show no photo link
2812 $nophoto = '/public/theme/common/nophoto.png';
2813 $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>';
2814 }
2815 }
2816 } elseif ($object->element == 'bom') {
2818 '@phan-var-force Bom $object';
2819 $width = 80;
2820 $cssclass = 'photowithmargin photoref';
2821 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2822 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2823 if ($conf->browser->layout == 'phone') {
2824 $maxvisiblephotos = 1;
2825 }
2826 if ($showimage) {
2827 $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>';
2828 } else {
2829 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2830 $nophoto = '';
2831 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2832 } else { // Show no photo link
2833 $nophoto = '/public/theme/common/nophoto.png';
2834 $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>';
2835 }
2836 }
2837 } elseif ($object->element == 'ticket') {
2838 $width = 80;
2839 $cssclass = 'photoref';
2841 '@phan-var-force Ticket $object';
2842 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2843 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2844 if ($conf->browser->layout == 'phone') {
2845 $maxvisiblephotos = 1;
2846 }
2847
2848 if ($showimage) {
2849 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2850 if ($object->nbphoto > 0) {
2851 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2852 } else {
2853 $showimage = 0;
2854 }
2855 }
2856 if (!$showimage) {
2857 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2858 $nophoto = '';
2859 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2860 } else { // Show no photo link
2861 $nophoto = img_picto('No photo', 'object_ticket');
2862 $morehtmlleft .= '<!-- No photo to show -->';
2863 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2864 $morehtmlleft .= $nophoto;
2865 $morehtmlleft .= '</div></div>';
2866 }
2867 }
2868 } else {
2869 if ($showimage) {
2870 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2871 $phototoshow = '';
2872 // Check if a preview file is available
2873 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2874 $objectref = dol_sanitizeFileName($object->ref);
2875 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2876 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2877 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2878 $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
2879 } else {
2880 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2881 }
2882 if (empty($subdir)) {
2883 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2884 }
2885
2886 $filepath = $dir_output.$subdir."/";
2887
2888 $filepdf = $filepath.$objectref.".pdf";
2889 $relativepath = $subdir.'/'.$objectref.'.pdf';
2890
2891 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2892 $fileimage = $filepdf.'_preview.png';
2893 $relativepathimage = $relativepath.'_preview.png';
2894
2895 $pdfexists = file_exists($filepdf);
2896
2897 // If PDF file exists
2898 if ($pdfexists) {
2899 // Conversion du PDF en image png si fichier png non existent
2900 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2901 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2902 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2903 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2904 if ($ret < 0) {
2905 $error++;
2906 }
2907 }
2908 }
2909 }
2910
2911 if ($pdfexists && !$error) {
2912 $heightforphotref = 80;
2913 if (!empty($conf->dol_optimize_smallscreen)) {
2914 $heightforphotref = 60;
2915 }
2916 // If the preview file is found
2917 if (file_exists($fileimage)) {
2918 $phototoshow = '<div class="photoref">';
2919 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2920 $phototoshow .= '</div>';
2921 }
2922 }
2923 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
2924 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2925 }
2926
2927 if ($phototoshow) {
2928 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2929 $morehtmlleft .= $phototoshow;
2930 $morehtmlleft .= '</div>';
2931 }
2932 }
2933
2934 if (empty($phototoshow)) { // Show No photo link (picto of object)
2935 if ($object->element == 'action') {
2936 $width = 80;
2937 $cssclass = 'photorefcenter';
2938 $nophoto = img_picto('No photo', 'title_agenda');
2939 } else {
2940 $width = 14;
2941 $cssclass = 'photorefcenter';
2942 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
2943 $prefix = 'object_';
2944 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
2945 $picto = 'project'; // instead of projectpub
2946 }
2947 if (strpos($picto, 'fontawesome_') !== false) {
2948 $prefix = '';
2949 }
2950 $nophoto = img_picto('No photo', $prefix.$picto);
2951 }
2952 $morehtmlleft .= '<!-- No photo to show -->';
2953 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2954 $morehtmlleft .= $nophoto;
2955 $morehtmlleft .= '</div></div>';
2956 }
2957 }
2958 }
2959
2960 // Show barcode
2961 if ($showbarcode) {
2962 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2963 }
2964
2965 if ($object->element == 'societe') {
2966 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2967 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2968 } else {
2969 $morehtmlstatus .= $object->getLibStatut(6);
2970 }
2971 } elseif ($object->element == 'product') {
2972 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2973 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2974 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2975 } else {
2976 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2977 }
2978 $morehtmlstatus .= ' &nbsp; ';
2979 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2980 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2981 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2982 } else {
2983 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2984 }
2985 } elseif (in_array($object->element, array('salary'))) {
2986 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
2987 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2988 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
2989 }
2990 $morehtmlstatus .= $tmptxt;
2991 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
2992 $totalallpayments = $object->getSommePaiement(0);
2993 $totalallpayments += $object->getSumCreditNotesUsed(0);
2994 $totalallpayments += $object->getSumDepositsUsed(0);
2995 $tmptxt = $object->getLibStatut(6, $totalallpayments);
2996 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2997 $tmptxt = $object->getLibStatut(5, $totalallpayments);
2998 }
2999 $morehtmlstatus .= $tmptxt;
3000 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3001 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3002 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3003 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3004 }
3005 $morehtmlstatus .= $tmptxt;
3006 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3007 if ($object->statut == 0) {
3008 $morehtmlstatus .= $object->getLibStatut(5);
3009 } else {
3010 $morehtmlstatus .= $object->getLibStatut(4);
3011 }
3012 } elseif ($object->element == 'facturerec') {
3013 '@phan-var-force FactureRec $object';
3014 if ($object->frequency == 0) {
3015 $morehtmlstatus .= $object->getLibStatut(2);
3016 } else {
3017 $morehtmlstatus .= $object->getLibStatut(5);
3018 }
3019 } elseif ($object->element == 'project_task') {
3020 $object->fk_statut = 1;
3021 if ($object->progress > 0) {
3022 $object->fk_statut = 2;
3023 }
3024 if ($object->progress >= 100) {
3025 $object->fk_statut = 3;
3026 }
3027 $tmptxt = $object->getLibStatut(5);
3028 $morehtmlstatus .= $tmptxt; // No status on task
3029 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3030 $tmptxt = $object->getLibStatut(6);
3031 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3032 $tmptxt = $object->getLibStatut(5);
3033 }
3034 $morehtmlstatus .= $tmptxt;
3035 }
3036
3037 // Add if object was dispatched "into accountancy"
3038 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3039 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3040 if (method_exists($object, 'getVentilExportCompta')) {
3041 $accounted = $object->getVentilExportCompta();
3042 $langs->load("accountancy");
3043 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3044 }
3045 }
3046
3047 // Add alias for thirdparty
3048 if (!empty($object->name_alias)) {
3049 '@phan-var-force Societe $object';
3050 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3051 }
3052
3053 // Add label
3054 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3055 if (!empty($object->label)) {
3056 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3057 }
3058 }
3059 // Show address and email
3060 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3061 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3062 if ($moreaddress) {
3063 $morehtmlref .= '<div class="refidno refaddress">';
3064 $morehtmlref .= $moreaddress;
3065 $morehtmlref .= '</div>';
3066 }
3067 }
3068 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)) {
3069 $morehtmlref .= '<div style="clear: both;"></div>';
3070 $morehtmlref .= '<div class="refidno opacitymedium">';
3071 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3072 $morehtmlref .= '</div>';
3073 }
3074
3075 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3076 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3077 if ($reshook < 0) {
3078 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3079 } elseif (empty($reshook)) {
3080 $morehtmlref .= $hookmanager->resPrint;
3081 } elseif ($reshook > 0) {
3082 $morehtmlref = $hookmanager->resPrint;
3083 }
3084
3085 // $morehtml is the right part (link "Back to list")
3086 // $morehtmlleft is the picto or photo of banner
3087 // $morehtmlstatus is part under the status
3088 // $morehtmlright is part of htmlright
3089
3090 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3091 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3092 print '</div>';
3093 print '<div class="underrefbanner clearboth"></div>';
3094}
3095
3105function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3106{
3107 global $langs;
3108 $ret = '';
3109 if ($fieldrequired) {
3110 $ret .= '<span class="fieldrequired">';
3111 }
3112 $ret .= '<label for="'.$fieldkey.'">';
3113 $ret .= $langs->trans($langkey);
3114 $ret .= '</label>';
3115 if ($fieldrequired) {
3116 $ret .= '</span>';
3117 }
3118 return $ret;
3119}
3120
3128function dol_bc($var, $moreclass = '')
3129{
3130 global $bc;
3131 $ret = ' '.$bc[$var];
3132 if ($moreclass) {
3133 $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
3134 }
3135 return $ret;
3136}
3137
3151function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3152{
3153 global $langs, $hookmanager;
3154
3155 $ret = '';
3156 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3157
3158 // See format of addresses on https://en.wikipedia.org/wiki/Address
3159 // Address
3160 if (empty($mode)) {
3161 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3162 }
3163 // Zip/Town/State
3164 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3165 // US: title firstname name \n address lines \n town, state, zip \n country
3166 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3167 $ret .= (($ret && $town) ? $sep : '').$town;
3168
3169 if (!empty($object->state)) {
3170 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3171 }
3172 if (!empty($object->zip)) {
3173 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3174 }
3175 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3176 // UK: title firstname name \n address lines \n town state \n zip \n country
3177 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3178 $ret .= ($ret ? $sep : '').$town;
3179 if (!empty($object->state)) {
3180 $ret .= ($ret ? ", " : '').$object->state;
3181 }
3182 if (!empty($object->zip)) {
3183 $ret .= ($ret ? $sep : '').$object->zip;
3184 }
3185 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3186 // ES: title firstname name \n address lines \n zip town \n state \n country
3187 $ret .= ($ret ? $sep : '').$object->zip;
3188 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3189 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3190 if (!empty($object->state)) {
3191 $ret .= $sep.$object->state;
3192 }
3193 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3194 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3195 // See https://www.sljfaq.org/afaq/addresses.html
3196 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3197 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3198 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3199 // IT: title firstname name\n address lines \n zip town state_code \n country
3200 $ret .= ($ret ? $sep : '').$object->zip;
3201 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3202 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3203 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3204 } else {
3205 // Other: title firstname name \n address lines \n zip town[, state] \n country
3206 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3207 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3208 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3209 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3210 $ret .= ($ret ? ", " : '').$object->state;
3211 }
3212 }
3213
3214 if (!is_object($outputlangs)) {
3215 $outputlangs = $langs;
3216 }
3217 if ($withcountry) {
3218 $langs->load("dict");
3219 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3220 }
3221 if ($hookmanager) {
3222 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3223 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3224 if ($reshook > 0) {
3225 $ret = '';
3226 }
3227 $ret .= $hookmanager->resPrint;
3228 }
3229
3230 return $ret;
3231}
3232
3233
3234
3244function dol_strftime($fmt, $ts = false, $is_gmt = false)
3245{
3246 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3247 return dol_print_date($ts, $fmt, $is_gmt);
3248 } else {
3249 return 'Error date outside supported range';
3250 }
3251}
3252
3274function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3275{
3276 global $conf, $langs;
3277
3278 // If date undefined or "", we return ""
3279 if (dol_strlen($time) == 0) {
3280 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3281 }
3282
3283 if ($tzoutput === 'auto') {
3284 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3285 }
3286
3287 // Clean parameters
3288 $to_gmt = false;
3289 $offsettz = $offsetdst = 0;
3290 if ($tzoutput) {
3291 $to_gmt = true; // For backward compatibility
3292 if (is_string($tzoutput)) {
3293 if ($tzoutput == 'tzserver') {
3294 $to_gmt = false;
3295 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3296 // @phan-suppress-next-line PhanPluginRedundantAssignment
3297 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3298 // @phan-suppress-next-line PhanPluginRedundantAssignment
3299 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3300 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3301 $to_gmt = true;
3302 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3303
3304 if (class_exists('DateTimeZone')) {
3305 $user_date_tz = new DateTimeZone($offsettzstring);
3306 $user_dt = new DateTime();
3307 $user_dt->setTimezone($user_date_tz);
3308 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3309 $offsettz = $user_dt->getOffset(); // should include dst ?
3310 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3311 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3312 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3313 }
3314 }
3315 }
3316 }
3317 if (!is_object($outputlangs)) {
3318 $outputlangs = $langs;
3319 }
3320 if (!$format) {
3321 $format = 'daytextshort';
3322 }
3323
3324 // Do we have to reduce the length of date (year on 2 chars) to save space.
3325 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3326 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3327 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3328 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3329 if ($formatwithoutreduce != $format) {
3330 $format = $formatwithoutreduce;
3331 $reduceformat = 1;
3332 } // so format 'dayreduceformat' is processed like day
3333
3334 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3335 // TODO Add format daysmallyear and dayhoursmallyear
3336 if ($format == 'day') {
3337 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3338 } elseif ($format == 'hour') {
3339 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3340 } elseif ($format == 'hourduration') {
3341 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3342 } elseif ($format == 'daytext') {
3343 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3344 } elseif ($format == 'daytextshort') {
3345 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3346 } elseif ($format == 'dayhour') {
3347 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3348 } elseif ($format == 'dayhoursec') {
3349 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3350 } elseif ($format == 'dayhourtext') {
3351 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3352 } elseif ($format == 'dayhourtextshort') {
3353 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3354 } elseif ($format == 'dayhourlog') {
3355 // Format not sensitive to language
3356 $format = '%Y%m%d%H%M%S';
3357 } elseif ($format == 'dayhourlogsmall') {
3358 // Format not sensitive to language
3359 $format = '%y%m%d%H%M';
3360 } elseif ($format == 'dayhourldap') {
3361 $format = '%Y%m%d%H%M%SZ';
3362 } elseif ($format == 'dayhourxcard') {
3363 $format = '%Y%m%dT%H%M%SZ';
3364 } elseif ($format == 'dayxcard') {
3365 $format = '%Y%m%d';
3366 } elseif ($format == 'dayrfc') {
3367 $format = '%Y-%m-%d'; // DATE_RFC3339
3368 } elseif ($format == 'dayhourrfc') {
3369 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3370 } elseif ($format == 'standard') {
3371 $format = '%Y-%m-%d %H:%M:%S';
3372 }
3373
3374 if ($reduceformat) {
3375 $format = str_replace('%Y', '%y', $format);
3376 $format = str_replace('yyyy', 'yy', $format);
3377 }
3378
3379 // Clean format
3380 if (preg_match('/%b/i', $format)) { // There is some text to translate
3381 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3382 $format = str_replace('%b', '__b__', $format);
3383 $format = str_replace('%B', '__B__', $format);
3384 }
3385 if (preg_match('/%a/i', $format)) { // There is some text to translate
3386 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3387 $format = str_replace('%a', '__a__', $format);
3388 $format = str_replace('%A', '__A__', $format);
3389 }
3390
3391 // Analyze date
3392 $reg = array();
3393 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
3394 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"]));
3395 return '';
3396 } 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
3397 // This part of code should not be used anymore.
3398 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);
3399 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3400 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3401 $syear = (!empty($reg[1]) ? $reg[1] : '');
3402 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3403 $sday = (!empty($reg[3]) ? $reg[3] : '');
3404 $shour = (!empty($reg[4]) ? $reg[4] : '');
3405 $smin = (!empty($reg[5]) ? $reg[5] : '');
3406 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3407
3408 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
3409
3410 if ($to_gmt) {
3411 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3412 } else {
3413 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3414 }
3415 $dtts = new DateTime();
3416 $dtts->setTimestamp($time);
3417 $dtts->setTimezone($tzo);
3418 $newformat = str_replace(
3419 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3420 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3421 $format
3422 );
3423 $ret = $dtts->format($newformat);
3424 $ret = str_replace(
3425 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3426 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3427 $ret
3428 );
3429 } else {
3430 // Date is a timestamps
3431 if ($time < 100000000000) { // Protection against bad date values
3432 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3433
3434 if ($to_gmt) {
3435 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3436 } else {
3437 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3438 }
3439 $dtts = new DateTime();
3440 $dtts->setTimestamp($timetouse);
3441 $dtts->setTimezone($tzo);
3442 $newformat = str_replace(
3443 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3444 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3445 $format
3446 );
3447 $ret = $dtts->format($newformat);
3448 $ret = str_replace(
3449 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3450 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3451 $ret
3452 );
3453 //var_dump($ret);exit;
3454 } else {
3455 $ret = 'Bad value '.$time.' for date';
3456 }
3457 }
3458
3459 if (preg_match('/__b__/i', $format)) {
3460 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3461
3462 if ($to_gmt) {
3463 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3464 } else {
3465 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3466 }
3467 $dtts = new DateTime();
3468 $dtts->setTimestamp($timetouse);
3469 $dtts->setTimezone($tzo);
3470 $month = (int) $dtts->format("m");
3471 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3472 if ($encodetooutput) {
3473 $monthtext = $outputlangs->transnoentities('Month'.$month);
3474 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3475 } else {
3476 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3477 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3478 }
3479 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3480 $ret = str_replace('__b__', $monthtextshort, $ret);
3481 $ret = str_replace('__B__', $monthtext, $ret);
3482 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3483 //return $ret;
3484 }
3485 if (preg_match('/__a__/i', $format)) {
3486 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3487 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3488
3489 if ($to_gmt) {
3490 $tzo = new DateTimeZone('UTC');
3491 } else {
3492 $tzo = new DateTimeZone(date_default_timezone_get());
3493 }
3494 $dtts = new DateTime();
3495 $dtts->setTimestamp($timetouse);
3496 $dtts->setTimezone($tzo);
3497 $w = $dtts->format("w");
3498 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3499
3500 $ret = str_replace('__A__', $dayweek, $ret);
3501 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3502 }
3503
3504 return $ret;
3505}
3506
3507
3528function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3529{
3530 if ($timestamp === '') {
3531 return array();
3532 }
3533
3534 $datetimeobj = new DateTime();
3535 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3536 if ($forcetimezone) {
3537 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3538 }
3539 $arrayinfo = array(
3540 'year' => ((int) date_format($datetimeobj, 'Y')),
3541 'mon' => ((int) date_format($datetimeobj, 'm')),
3542 'mday' => ((int) date_format($datetimeobj, 'd')),
3543 'wday' => ((int) date_format($datetimeobj, 'w')),
3544 'yday' => ((int) date_format($datetimeobj, 'z')),
3545 'hours' => ((int) date_format($datetimeobj, 'H')),
3546 'minutes' => ((int) date_format($datetimeobj, 'i')),
3547 'seconds' => ((int) date_format($datetimeobj, 's')),
3548 '0' => $timestamp
3549 );
3550
3551 return $arrayinfo;
3552}
3553
3575function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3576{
3577 global $conf;
3578 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3579
3580 if ($gm === 'auto') {
3581 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3582 }
3583 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3584
3585 // Clean parameters
3586 if ($hour == -1 || empty($hour)) {
3587 $hour = 0;
3588 }
3589 if ($minute == -1 || empty($minute)) {
3590 $minute = 0;
3591 }
3592 if ($second == -1 || empty($second)) {
3593 $second = 0;
3594 }
3595
3596 // Check parameters
3597 if ($check) {
3598 if (!$month || !$day) {
3599 return '';
3600 }
3601 if ($day > 31) {
3602 return '';
3603 }
3604 if ($month > 12) {
3605 return '';
3606 }
3607 if ($hour < 0 || $hour > 24) {
3608 return '';
3609 }
3610 if ($minute < 0 || $minute > 60) {
3611 return '';
3612 }
3613 if ($second < 0 || $second > 60) {
3614 return '';
3615 }
3616 }
3617
3618 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3619 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3620 $localtz = new DateTimeZone($default_timezone);
3621 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3622 // We use dol_tz_string first because it is more reliable.
3623 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3624 try {
3625 $localtz = new DateTimeZone($default_timezone);
3626 } catch (Exception $e) {
3627 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3628 $default_timezone = @date_default_timezone_get();
3629 }
3630 } elseif (strrpos($gm, "tz,") !== false) {
3631 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3632 try {
3633 $localtz = new DateTimeZone($timezone);
3634 } catch (Exception $e) {
3635 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3636 }
3637 }
3638
3639 if (empty($localtz)) {
3640 $localtz = new DateTimeZone('UTC');
3641 }
3642 //var_dump($localtz);
3643 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3644 $dt = new DateTime('now', $localtz);
3645 $dt->setDate((int) $year, (int) $month, (int) $day);
3646 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3647 $date = $dt->getTimestamp(); // should include daylight saving time
3648 //var_dump($date);
3649 return $date;
3650}
3651
3652
3663function dol_now($mode = 'auto')
3664{
3665 $ret = 0;
3666
3667 if ($mode === 'auto') {
3668 $mode = 'gmt';
3669 }
3670
3671 if ($mode == 'gmt') {
3672 $ret = time(); // Time for now at greenwich.
3673 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3674 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3675 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3676 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3677 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3678 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3679 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3680 // $ret=dol_now('gmt')+($tzsecond*3600);
3681 //}
3682 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3683 // Time for now with user timezone added
3684 //print 'time: '.time();
3685 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3686 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3687 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3688 }
3689
3690 return $ret;
3691}
3692
3693
3702function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3703{
3704 global $conf, $langs;
3705 $level = 1024;
3706
3707 if (!empty($conf->dol_optimize_smallscreen)) {
3708 $shortunit = 1;
3709 }
3710
3711 // Set value text
3712 if (empty($shortvalue) || $size < ($level * 10)) {
3713 $ret = $size;
3714 $textunitshort = $langs->trans("b");
3715 $textunitlong = $langs->trans("Bytes");
3716 } else {
3717 $ret = round($size / $level, 0);
3718 $textunitshort = $langs->trans("Kb");
3719 $textunitlong = $langs->trans("KiloBytes");
3720 }
3721 // Use long or short text unit
3722 if (empty($shortunit)) {
3723 $ret .= ' '.$textunitlong;
3724 } else {
3725 $ret .= ' '.$textunitshort;
3726 }
3727
3728 return $ret;
3729}
3730
3741function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3742{
3743 global $langs;
3744
3745 if (empty($url)) {
3746 return '';
3747 }
3748
3749 $linkstart = '<a href="';
3750 if (!preg_match('/^http/i', $url)) {
3751 $linkstart .= 'http://';
3752 }
3753 $linkstart .= $url;
3754 $linkstart .= '"';
3755 if ($target) {
3756 $linkstart .= ' target="'.$target.'"';
3757 }
3758 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3759 $linkstart .= '>';
3760
3761 $link = '';
3762 if (!preg_match('/^http/i', $url)) {
3763 $link .= 'http://';
3764 }
3765 $link .= dol_trunc($url, $max);
3766
3767 $linkend = '</a>';
3768
3769 if ($morecss == 'float') { // deprecated
3770 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3771 } else {
3772 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3773 }
3774}
3775
3788function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3789{
3790 global $user, $langs, $hookmanager;
3791
3792 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3793 //$showinvalid = 1; $email = 'rrrrr';
3794
3795 $newemail = dol_escape_htmltag($email);
3796
3797 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3798 $withpicto = 0;
3799 }
3800
3801 if (empty($email)) {
3802 return '&nbsp;';
3803 }
3804
3805 if (!empty($addlink)) {
3806 $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="';
3807 if (!preg_match('/^mailto:/i', $email)) {
3808 $newemail .= 'mailto:';
3809 }
3810 $newemail .= $email;
3811 $newemail .= '">';
3812
3813 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3814
3815 $newemail .= dol_trunc($email, $max);
3816 $newemail .= '</a>';
3817 if ($showinvalid && !isValidEmail($email)) {
3818 $langs->load("errors");
3819 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3820 }
3821
3822 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3823 $type = 'AC_EMAIL';
3824 $linktoaddaction = '';
3825 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3826 $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>';
3827 }
3828 if ($linktoaddaction) {
3829 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3830 }
3831 }
3832 } else {
3833 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3834
3835 if ($showinvalid && !isValidEmail($email)) {
3836 $langs->load("errors");
3837 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3838 }
3839 }
3840
3841 //$rep = '<div class="nospan" style="margin-right: 10px">';
3842 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3843 //$rep .= '</div>';
3844 $rep = $newemail;
3845
3846 if ($hookmanager) {
3847 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3848
3849 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3850 if ($reshook > 0) {
3851 $rep = '';
3852 }
3853 $rep .= $hookmanager->resPrint;
3854 }
3855
3856 return $rep;
3857}
3858
3864function getArrayOfSocialNetworks()
3865{
3866 global $conf, $db;
3867
3868 $socialnetworks = array();
3869 // Enable caching of array
3870 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3871 $cachekey = 'socialnetworks_' . $conf->entity;
3872 $dataretrieved = dol_getcache($cachekey);
3873 if (!is_null($dataretrieved)) {
3874 $socialnetworks = $dataretrieved;
3875 } else {
3876 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3877 $sql .= " WHERE entity=".$conf->entity;
3878 $resql = $db->query($sql);
3879 if ($resql) {
3880 while ($obj = $db->fetch_object($resql)) {
3881 $socialnetworks[$obj->code] = array(
3882 'rowid' => $obj->rowid,
3883 'label' => $obj->label,
3884 'url' => $obj->url,
3885 'icon' => $obj->icon,
3886 'active' => $obj->active,
3887 );
3888 }
3889 }
3890 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3891 }
3892 return $socialnetworks;
3893}
3894
3905function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3906{
3907 global $user, $langs;
3908
3909 $htmllink = $value;
3910
3911 if (empty($value)) {
3912 return '&nbsp;';
3913 }
3914
3915 if (!empty($type)) {
3916 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3917 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3918 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3919 if ($type == 'skype') {
3920 $htmllink .= dol_escape_htmltag($value);
3921 $htmllink .= '&nbsp; <a href="skype:';
3922 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3923 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3924 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3925 $htmllink .= '</a><a href="skype:';
3926 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3927 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3928 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3929 $htmllink .= '</a>';
3930 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3931 $addlink = 'AC_SKYPE';
3932 $link = '';
3933 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
3934 $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>';
3935 }
3936 $htmllink .= ($link ? ' '.$link : '');
3937 }
3938 } else {
3939 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
3940 if (getDolGlobalString($networkconstname)) {
3941 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
3942 if (preg_match('/^https?:\/\//i', $link)) {
3943 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3944 } else {
3945 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3946 }
3947 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
3948 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3949 if ($tmpvirginurl) {
3950 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3951 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3952
3953 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3954 if ($tmpvirginurl3) {
3955 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3956 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3957 }
3958
3959 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3960 if ($tmpvirginurl2) {
3961 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3962 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3963 }
3964 }
3965 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3966 if (preg_match('/^https?:\/\//i', $link)) {
3967 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3968 } else {
3969 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3970 }
3971 } else {
3972 $htmllink .= dol_escape_htmltag($value);
3973 }
3974 }
3975 $htmllink .= '</div>';
3976 } else {
3977 $langs->load("errors");
3978 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3979 }
3980 return $htmllink;
3981}
3982
3992function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
3993{
3994 global $mysoc;
3995
3996 if (empty($profID) || empty($profIDtype)) {
3997 return '';
3998 }
3999 if (empty($countrycode)) {
4000 $countrycode = $mysoc->country_code;
4001 }
4002 $newProfID = $profID;
4003 $id = substr($profIDtype, -1);
4004 $ret = '';
4005 if (strtoupper($countrycode) == 'FR') {
4006 // France
4007 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4008
4009 if ($id == 1 && dol_strlen($newProfID) == 9) {
4010 // SIREN (ex: 123 123 123)
4011 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4012 }
4013 if ($id == 2 && dol_strlen($newProfID) == 14) {
4014 // SIRET (ex: 123 123 123 12345)
4015 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4016 }
4017 if ($id == 3 && dol_strlen($newProfID) == 5) {
4018 // NAF/APE (ex: 69.20Z)
4019 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4020 }
4021 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4022 // TVA intracommunautaire (ex: FR12 123 123 123)
4023 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4024 }
4025 }
4026 if (!empty($addcpButton)) {
4027 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4028 } else {
4029 $ret = $newProfID;
4030 }
4031 return $ret;
4032}
4033
4049function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = '')
4050{
4051 global $conf, $user, $langs, $mysoc, $hookmanager;
4052
4053 // Clean phone parameter
4054 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4055 if (empty($phone)) {
4056 return '';
4057 }
4058 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4059 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4060 }
4061 if (empty($countrycode) && is_object($mysoc)) {
4062 $countrycode = $mysoc->country_code;
4063 }
4064
4065 // Short format for small screens
4066 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4067 $separ = '';
4068 }
4069
4070 $newphone = $phone;
4071 $newphonewa = $phone;
4072 if (strtoupper($countrycode) == "FR") {
4073 // France
4074 if (dol_strlen($phone) == 10) {
4075 $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);
4076 } elseif (dol_strlen($phone) == 7) {
4077 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4078 } elseif (dol_strlen($phone) == 9) {
4079 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4080 } elseif (dol_strlen($phone) == 11) {
4081 $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);
4082 } elseif (dol_strlen($phone) == 12) {
4083 $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);
4084 } elseif (dol_strlen($phone) == 13) {
4085 $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);
4086 }
4087 } elseif (strtoupper($countrycode) == "CA") {
4088 if (dol_strlen($phone) == 10) {
4089 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4090 }
4091 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4092 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4093 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4094 }
4095 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4096 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4097 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4098 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4099 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4100 }
4101 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4102 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4103 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4104 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4105 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4106 }
4107 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4108 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4109 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4110 }
4111 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4112 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4113 $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);
4114 }
4115 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4116 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4117 $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);
4118 }
4119 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4120 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4121 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4122 }
4123 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4124 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4125 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4126 }
4127 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4128 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4129 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4130 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4131 $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);
4132 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4133 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4134 }
4135 } elseif (strtoupper($countrycode) == "ML") {//Mali
4136 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4137 $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);
4138 }
4139 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4140 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4141 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4142 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4143 $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);
4144 }
4145 } elseif (strtoupper($countrycode) == "MU") {
4146 //Maurice
4147 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4148 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4149 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4150 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4151 }
4152 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4153 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4154 $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);
4155 }
4156 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4157 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4158 $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);
4159 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4160 $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);
4161 }
4162 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4163 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4164 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4165 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4166 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4167 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4168 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4169 }
4170 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4171 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4172 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4173 }
4174 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4175 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4176 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4177 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4178 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4179 }
4180 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4181 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4182 $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);
4183 }
4184 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4185 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4186 $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);
4187 }
4188 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4189 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4190 $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);
4191 }
4192 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4193 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4194 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4195 }
4196 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4197 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4198 $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);
4199 }
4200 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4201 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4202 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4203 }
4204 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4205 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4206 $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);
4207 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4208 $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);
4209 }
4210 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4211 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4212 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4213 }
4214 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4215 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4216 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2).$separ.substr($newphone, 11, 2);
4217 }
4218 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4219 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4220 $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);
4221 }
4222 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4223 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4224 $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);
4225 }
4226 } elseif (strtoupper($countrycode) == "IT") {//Italie
4227 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4228 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4229 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4230 $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);
4231 }
4232 } elseif (strtoupper($countrycode) == "AU") {
4233 //Australie
4234 if (dol_strlen($phone) == 12) {
4235 //ex: +61_A_BCDE_FGHI
4236 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4237 }
4238 } elseif (strtoupper($countrycode) == "LU") {
4239 // Luxembourg
4240 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4241 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4242 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4243 $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);
4244 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4245 $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);
4246 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4247 $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);
4248 }
4249 } elseif (strtoupper($countrycode) == "PE") {
4250 // Peru
4251 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4252 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4253 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4254 $newphonewa = '+51'.$newphone;
4255 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4256 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4257 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4258 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4259 $newphonewa = $newphone;
4260 $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);
4261 }
4262 }
4263
4264 $newphoneastart = $newphoneaend = '';
4265 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4266 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
4267 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4268 $newphoneaend .= '</a>';
4269 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4270 if (empty($user->clicktodial_loaded)) {
4271 $user->fetch_clicktodial();
4272 }
4273
4274 // Define urlmask
4275 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4276 if (!empty($user->clicktodial_url)) {
4277 $urlmask = $user->clicktodial_url;
4278 }
4279
4280 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4281 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4282 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4283 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4284 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4285 // Those lines are for substitution
4286 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4287 '__PHONETO__' => urlencode($phone),
4288 '__LOGIN__' => $clicktodial_login,
4289 '__PASS__' => $clicktodial_password);
4290 $url = make_substitutions($url, $substitarray);
4291 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4292 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4293 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4294 $newphoneaend = '</a>';
4295 } else {
4296 // Old method
4297 $newphoneastart = '<a href="'.$url.'"';
4298 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4299 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4300 }
4301 $newphoneastart .= '>';
4302 $newphoneaend .= '</a>';
4303 }
4304 }
4305
4306 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4307 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4308 $type = 'AC_TEL';
4309 $addlinktoagenda = '';
4310 if ($addlink == 'AC_FAX') {
4311 $type = 'AC_FAX';
4312 }
4313 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4314 $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>';
4315 }
4316 if ($addlinktoagenda) {
4317 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4318 }
4319 }
4320 }
4321
4322 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4323 // Link to Whatsapp
4324 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4325 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4326 }
4327
4328 if (empty($titlealt)) {
4329 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4330 }
4331 $rep = '';
4332
4333 if ($hookmanager) {
4334 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4335 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4336 $rep .= $hookmanager->resPrint;
4337 }
4338 if (empty($reshook)) {
4339 $picto = '';
4340 if ($withpicto) {
4341 if ($withpicto == 'fax') {
4342 $picto = 'phoning_fax';
4343 } elseif ($withpicto == 'phone') {
4344 $picto = 'phoning';
4345 } elseif ($withpicto == 'mobile') {
4346 $picto = 'phoning_mobile';
4347 } else {
4348 $picto = '';
4349 }
4350 }
4351 if ($adddivfloat == 1) {
4352 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">';
4353 } elseif (empty($adddivfloat)) {
4354 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').' style="margin-right: 10px;">';
4355 }
4356
4357 $rep .= $newphoneastart;
4358 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4359 if ($separ != 'hidenum') {
4360 $rep .= ($withpicto ? ' ' : '').$newphone;
4361 }
4362 $rep .= $newphoneaend;
4363
4364 if ($adddivfloat == 1) {
4365 $rep .= '</div>';
4366 } elseif (empty($adddivfloat)) {
4367 $rep .= '</span>';
4368 }
4369 }
4370
4371 return $rep;
4372}
4373
4381function dol_print_ip($ip, $mode = 0)
4382{
4383 global $langs;
4384
4385 $ret = '';
4386
4387 if (empty($mode)) {
4388 $ret .= $ip;
4389 }
4390
4391 if ($mode != 2) {
4392 $countrycode = dolGetCountryCodeFromIp($ip);
4393 if ($countrycode) { // If success, countrycode is us, fr, ...
4394 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4395 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4396 } else {
4397 $ret .= ' ('.$countrycode.')';
4398 }
4399 } else {
4400 // Nothing
4401 }
4402 }
4403
4404 return $ret;
4405}
4406
4415function getUserRemoteIP()
4416{
4417 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4418 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4419 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4420 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4421 } else {
4422 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4423 }
4424 } else {
4425 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4426 }
4427 } else {
4428 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4429 }
4430 return $ip;
4431}
4432
4441function isHTTPS()
4442{
4443 $isSecure = false;
4444 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4445 $isSecure = true;
4446 } 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') {
4447 $isSecure = true;
4448 }
4449 return $isSecure;
4450}
4451
4458function dolGetCountryCodeFromIp($ip)
4459{
4460 global $conf;
4461
4462 $countrycode = '';
4463
4464 if (!empty($conf->geoipmaxmind->enabled)) {
4465 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4466 //$ip='24.24.24.24';
4467 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4468 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4469 $geoip = new DolGeoIP('country', $datafile);
4470 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4471 $countrycode = $geoip->getCountryCodeFromIP($ip);
4472 }
4473
4474 return $countrycode;
4475}
4476
4477
4484function dol_user_country()
4485{
4486 global $conf, $langs, $user;
4487
4488 //$ret=$user->xxx;
4489 $ret = '';
4490 if (!empty($conf->geoipmaxmind->enabled)) {
4491 $ip = getUserRemoteIP();
4492 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4493 //$ip='24.24.24.24';
4494 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4495 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4496 $geoip = new DolGeoIP('country', $datafile);
4497 $countrycode = $geoip->getCountryCodeFromIP($ip);
4498 $ret = $countrycode;
4499 }
4500 return $ret;
4501}
4502
4515function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4516{
4517 global $conf, $user, $langs, $hookmanager;
4518
4519 $out = '';
4520
4521 if ($address) {
4522 if ($hookmanager) {
4523 $parameters = array('element' => $element, 'id' => $id);
4524 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4525 $out .= $hookmanager->resPrint;
4526 }
4527 if (empty($reshook)) {
4528 if (empty($charfornl)) {
4529 $out .= nl2br($address);
4530 } else {
4531 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4532 }
4533
4534 // TODO Remove this block, we can add this using the hook now
4535 $showgmap = $showomap = 0;
4536 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4537 $showgmap = 1;
4538 }
4539 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4540 $showgmap = 1;
4541 }
4542 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4543 $showgmap = 1;
4544 }
4545 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4546 $showgmap = 1;
4547 }
4548 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4549 $showomap = 1;
4550 }
4551 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4552 $showomap = 1;
4553 }
4554 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4555 $showomap = 1;
4556 }
4557 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4558 $showomap = 1;
4559 }
4560 if ($showgmap) {
4561 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4562 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4563 }
4564 if ($showomap) {
4565 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4566 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4567 }
4568 }
4569 }
4570 if ($noprint) {
4571 return $out;
4572 } else {
4573 print $out;
4574 }
4575}
4576
4577
4587function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4588{
4589 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4590 return true;
4591 }
4592 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4593 return true;
4594 }
4595 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4596 return true;
4597 }
4598
4599 return false;
4600}
4601
4611function isValidMXRecord($domain)
4612{
4613 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4614 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4615 return 0;
4616 }
4617 if (function_exists('getmxrr')) {
4618 $mxhosts = array();
4619 $weight = array();
4620 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4621 if (count($mxhosts) > 1) {
4622 return 1;
4623 }
4624 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4625 return 1;
4626 }
4627
4628 return 0;
4629 }
4630 }
4631
4632 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4633 return -1;
4634}
4635
4643function isValidPhone($phone)
4644{
4645 return true;
4646}
4647
4648
4658function dolGetFirstLetters($s, $nbofchar = 1)
4659{
4660 $ret = '';
4661 $tmparray = explode(' ', $s);
4662 foreach ($tmparray as $tmps) {
4663 $ret .= dol_substr($tmps, 0, $nbofchar);
4664 }
4665
4666 return $ret;
4667}
4668
4669
4677function dol_strlen($string, $stringencoding = 'UTF-8')
4678{
4679 if (is_null($string)) {
4680 return 0;
4681 }
4682
4683 if (function_exists('mb_strlen')) {
4684 return mb_strlen($string, $stringencoding);
4685 } else {
4686 return strlen($string);
4687 }
4688}
4689
4700function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4701{
4702 global $langs;
4703
4704 if (empty($stringencoding)) {
4705 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4706 }
4707
4708 $ret = '';
4709 if (empty($trunconbytes)) {
4710 if (function_exists('mb_substr')) {
4711 $ret = mb_substr($string, $start, $length, $stringencoding);
4712 } else {
4713 $ret = substr($string, $start, $length);
4714 }
4715 } else {
4716 if (function_exists('mb_strcut')) {
4717 $ret = mb_strcut($string, $start, $length, $stringencoding);
4718 } else {
4719 $ret = substr($string, $start, $length);
4720 }
4721 }
4722 return $ret;
4723}
4724
4725
4739function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4740{
4741 global $conf;
4742
4743 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4744 return $string;
4745 }
4746
4747 if (empty($stringencoding)) {
4748 $stringencoding = 'UTF-8';
4749 }
4750 // reduce for small screen
4751 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4752 $size = round($size / 3);
4753 }
4754
4755 // We go always here
4756 if ($trunc == 'right') {
4757 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4758 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4759 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4760 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4761 } else {
4762 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4763 return $string;
4764 }
4765 } elseif ($trunc == 'middle') {
4766 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4767 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4768 $size1 = round($size / 2);
4769 $size2 = round($size / 2);
4770 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4771 } else {
4772 return $string;
4773 }
4774 } elseif ($trunc == 'left') {
4775 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4776 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4777 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4778 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4779 } else {
4780 return $string;
4781 }
4782 } elseif ($trunc == 'wrap') {
4783 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4784 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4785 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4786 } else {
4787 return $string;
4788 }
4789 } else {
4790 return 'BadParam3CallingDolTrunc';
4791 }
4792}
4793
4801function getPictoForType($key, $morecss = '')
4802{
4803 // Set array with type -> picto
4804 $type2picto = array(
4805 'varchar' => 'font',
4806 'text' => 'font',
4807 'html' => 'code',
4808 'int' => 'sort-numeric-down',
4809 'double' => 'sort-numeric-down',
4810 'price' => 'currency',
4811 'pricecy' => 'multicurrency',
4812 'password' => 'key',
4813 'boolean' => 'check-square',
4814 'date' => 'calendar',
4815 'datetime' => 'calendar',
4816 'phone' => 'phone',
4817 'mail' => 'email',
4818 'url' => 'url',
4819 'ip' => 'country',
4820 'select' => 'list',
4821 'sellist' => 'list',
4822 'radio' => 'check-circle',
4823 'checkbox' => 'list',
4824 'chkbxlst' => 'list',
4825 'link' => 'link',
4826 'icon' => "question",
4827 'point' => "country",
4828 'multipts' => 'country',
4829 'linestrg' => "country",
4830 'polygon' => "country",
4831 'separate' => 'minus'
4832 );
4833
4834 if (!empty($type2picto[$key])) {
4835 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4836 }
4837
4838 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4839}
4840
4841
4863function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4864{
4865 global $conf;
4866
4867 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4868 $url = DOL_URL_ROOT;
4869 $theme = isset($conf->theme) ? $conf->theme : null;
4870 $path = 'theme/'.$theme;
4871 if (empty($picto)) {
4872 $picto = 'generic';
4873 }
4874
4875 // Define fullpathpicto to use into src
4876 if ($pictoisfullpath) {
4877 // Clean parameters
4878 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4879 $picto .= '.png';
4880 }
4881 $fullpathpicto = $picto;
4882 $reg = array();
4883 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4884 $morecss .= ($morecss ? ' ' : '').$reg[1];
4885 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4886 }
4887 } else {
4888 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
4889 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4890 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4891
4892 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
4893 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
4894 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4895 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4896
4897 // Compatibility with old fontawesome versions
4898 if ($pictowithouttext == 'file-o') {
4899 $pictowithouttext = 'file';
4900 }
4901
4902 $pictowithouttextarray = explode('_', $pictowithouttext);
4903 $marginleftonlyshort = 0;
4904
4905 if (!empty($pictowithouttextarray[1])) {
4906 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4907 $fakey = 'fa-'.$pictowithouttextarray[0];
4908 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
4909 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4910 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4911 } else {
4912 $fakey = 'fa-'.$pictowithouttext;
4913 $faprefix = 'fas';
4914 $facolor = '';
4915 $fasize = '';
4916 }
4917
4918 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4919 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4920 $morestyle = '';
4921 $reg = array();
4922 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4923 $morecss .= ($morecss ? ' ' : '').$reg[1];
4924 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4925 }
4926 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4927 $morestyle = $reg[1];
4928 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4929 }
4930 $moreatt = trim($moreatt);
4931
4932 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4933 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4934 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4935 $enabledisablehtml .= $titlealt;
4936 }*/
4937 $enabledisablehtml .= '</span>';
4938
4939 return $enabledisablehtml;
4940 }
4941
4942 if (empty($srconly) && in_array($pictowithouttext, array(
4943 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4944 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
4945 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
4946 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4947 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4948 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
4949 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
4950 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
4951 'commercial', 'companies',
4952 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4953 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4954 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
4955 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4956 'hands-helping', 'help', 'holiday',
4957 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4958 'key', 'knowledgemanagement',
4959 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4960 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4961 'off', 'on', 'order',
4962 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4963 'stock', 'resize', 'service', 'stats',
4964 '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',
4965 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4966 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4967 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4968 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4969 'technic', 'ticket',
4970 'error', 'warning',
4971 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4972 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4973 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
4974 'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4975 'conferenceorbooth', 'eventorganization',
4976 'stamp', 'signature',
4977 'webportal'
4978 ))) {
4979 $fakey = $pictowithouttext;
4980 $facolor = '';
4981 $fasize = '';
4982 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
4983 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'))) {
4984 $fa = 'far';
4985 }
4986 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4987 $fa = 'fab';
4988 }
4989
4990 $arrayconvpictotofa = array(
4991 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
4992 'asset' => 'money-check-alt', 'autofill' => 'fill',
4993 'bank_account' => 'university',
4994 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
4995 'bookcal' => 'calendar-check',
4996 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
4997 'bom' => 'shapes',
4998 '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',
4999 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5000 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5001 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5002 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5003 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5004 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5005 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5006 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5007 'generic' => 'file', 'holiday' => 'umbrella-beach',
5008 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5009 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5010 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5011 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5012 'sign-out' => 'sign-out-alt',
5013 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
5014 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5015 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5016 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5017 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5018 'other' => 'square',
5019 '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',
5020 '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',
5021 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5022 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5023 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5024 'service' => 'concierge-bell',
5025 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5026 'status' => 'stop-circle',
5027 'stripe' => 'stripe-s', 'supplier' => 'building',
5028 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5029 'title_agenda' => 'calendar-alt',
5030 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5031 'jabber' => 'comment-o',
5032 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5033 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5034 'webportal' => 'door-open'
5035 );
5036 if ($conf->currency == 'EUR') {
5037 $arrayconvpictotofa['currency'] = 'euro-sign';
5038 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5039 } else {
5040 $arrayconvpictotofa['currency'] = 'dollar-sign';
5041 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5042 }
5043 if ($pictowithouttext == 'off') {
5044 $fakey = 'fa-square';
5045 $fasize = '1.3em';
5046 } elseif ($pictowithouttext == 'on') {
5047 $fakey = 'fa-check-square';
5048 $fasize = '1.3em';
5049 } elseif ($pictowithouttext == 'listlight') {
5050 $fakey = 'fa-download';
5051 $marginleftonlyshort = 1;
5052 } elseif ($pictowithouttext == 'printer') {
5053 $fakey = 'fa-print';
5054 $fasize = '1.2em';
5055 } elseif ($pictowithouttext == 'note') {
5056 $fakey = 'fa-sticky-note';
5057 $marginleftonlyshort = 1;
5058 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5059 $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');
5060 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5061 if (preg_match('/selected/', $pictowithouttext)) {
5062 $facolor = '#888';
5063 }
5064 $marginleftonlyshort = 1;
5065 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5066 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5067 } else {
5068 $fakey = 'fa-'.$pictowithouttext;
5069 }
5070
5071 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5072 $morecss .= ' em092';
5073 }
5074 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5075 $morecss .= ' em088';
5076 }
5077 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5078 $morecss .= ' em080';
5079 }
5080
5081 // Define $marginleftonlyshort
5082 $arrayconvpictotomarginleftonly = array(
5083 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5084 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
5085 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5086 );
5087 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
5088 $marginleftonlyshort = 0;
5089 }
5090
5091 // Add CSS
5092 $arrayconvpictotomorcess = array(
5093 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5094 'bank_account' => 'infobox-bank_account',
5095 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5096 'bookcal' => 'infobox-action',
5097 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5098 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5099 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5100 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5101 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5102 'incoterm' => 'infobox-supplier_proposal',
5103 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5104 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5105 'order' => 'infobox-commande',
5106 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5107 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5108 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5109 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5110 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5111 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5112 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5113 'resource' => 'infobox-action',
5114 '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',
5115 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5116 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5117 'vat' => 'infobox-bank_account',
5118 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5119 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5120 );
5121 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5122 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5123 }
5124
5125 // Define $color
5126 $arrayconvpictotocolor = array(
5127 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5128 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5129 'dynamicprice' => '#a69944',
5130 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5131 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5132 'lock' => '#ddd', 'lot' => '#a69944',
5133 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5134 'other' => '#ddd', 'world' => '#986c6a',
5135 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5136 //'shipment'=>'#a69944',
5137 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5138 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5139 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5140 'website' => '#304', 'workstation' => '#a69944'
5141 );
5142 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5143 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5144 }
5145
5146 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5147 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5148 $morestyle = '';
5149 $reg = array();
5150 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5151 $morecss .= ($morecss ? ' ' : '').$reg[1];
5152 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5153 }
5154 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5155 $morestyle = $reg[1];
5156 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5157 }
5158 $moreatt = trim($moreatt);
5159
5160 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5161 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5162 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5163 $enabledisablehtml .= $titlealt;
5164 }*/
5165 $enabledisablehtml .= '</span>';
5166
5167 return $enabledisablehtml;
5168 }
5169
5170 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5171 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5172 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5173 $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
5174 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5175 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5176 }
5177
5178 // If we ask an image into $url/$mymodule/img (instead of default path)
5179 $regs = array();
5180 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5181 $picto = $regs[1];
5182 $path = $regs[2]; // $path is $mymodule
5183 }
5184
5185 // Clean parameters
5186 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5187 $picto .= '.png';
5188 }
5189 // If alt path are defined, define url where img file is, according to physical path
5190 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5191 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5192 if ($type == 'main') {
5193 continue;
5194 }
5195 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5196 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5197 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5198 break;
5199 }
5200 }
5201
5202 // $url is '' or '/custom', $path is current theme or
5203 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5204 }
5205
5206 if ($srconly) {
5207 return $fullpathpicto;
5208 }
5209
5210 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5211 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
5212}
5213
5227function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5228{
5229 if (strpos($picto, '^') === 0) {
5230 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5231 } else {
5232 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5233 }
5234}
5235
5247function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5248{
5249 global $conf;
5250
5251 if (is_numeric($picto)) {
5252 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5253 //$picto = $leveltopicto[$picto];
5254 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5255 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5256 $picto .= '.png';
5257 }
5258
5259 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5260
5261 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5262}
5263
5275function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5276{
5277 global $conf;
5278
5279 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5280 $picto .= '.png';
5281 }
5282
5283 if ($pictoisfullpath) {
5284 $path = $picto;
5285 } else {
5286 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5287
5288 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5289 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5290
5291 if (file_exists($themepath)) {
5292 $path = $themepath;
5293 }
5294 }
5295 }
5296
5297 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5298}
5299
5313function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5314{
5315 global $langs;
5316
5317 if (empty($titlealt) || $titlealt == 'default') {
5318 if ($numaction == '-1' || $numaction == 'ST_NO') {
5319 $numaction = -1;
5320 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5321 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5322 $numaction = 0;
5323 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5324 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5325 $numaction = 1;
5326 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5327 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5328 $numaction = 2;
5329 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5330 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5331 $numaction = 3;
5332 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5333 } else {
5334 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5335 $numaction = 0;
5336 }
5337 }
5338 if (!is_numeric($numaction)) {
5339 $numaction = 0;
5340 }
5341
5342 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5343}
5344
5352function img_pdf($titlealt = 'default', $size = 3)
5353{
5354 global $langs;
5355
5356 if ($titlealt == 'default') {
5357 $titlealt = $langs->trans('Show');
5358 }
5359
5360 return img_picto($titlealt, 'pdf'.$size.'.png');
5361}
5362
5370function img_edit_add($titlealt = 'default', $other = '')
5371{
5372 global $langs;
5373
5374 if ($titlealt == 'default') {
5375 $titlealt = $langs->trans('Add');
5376 }
5377
5378 return img_picto($titlealt, 'edit_add.png', $other);
5379}
5387function img_edit_remove($titlealt = 'default', $other = '')
5388{
5389 global $langs;
5390
5391 if ($titlealt == 'default') {
5392 $titlealt = $langs->trans('Remove');
5393 }
5394
5395 return img_picto($titlealt, 'edit_remove.png', $other);
5396}
5397
5406function img_edit($titlealt = 'default', $float = 0, $other = '')
5407{
5408 global $langs;
5409
5410 if ($titlealt == 'default') {
5411 $titlealt = $langs->trans('Modify');
5412 }
5413
5414 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5415}
5416
5425function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5426{
5427 global $langs;
5428
5429 if ($titlealt == 'default') {
5430 $titlealt = $langs->trans('View');
5431 }
5432
5433 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5434
5435 return img_picto($titlealt, 'eye', $moreatt);
5436}
5437
5446function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5447{
5448 global $langs;
5449
5450 if ($titlealt == 'default') {
5451 $titlealt = $langs->trans('Delete');
5452 }
5453
5454 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5455}
5456
5464function img_printer($titlealt = "default", $other = '')
5465{
5466 global $langs;
5467 if ($titlealt == "default") {
5468 $titlealt = $langs->trans("Print");
5469 }
5470 return img_picto($titlealt, 'printer.png', $other);
5471}
5472
5480function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5481{
5482 global $langs;
5483
5484 if ($titlealt == 'default') {
5485 $titlealt = $langs->trans('Split');
5486 }
5487
5488 return img_picto($titlealt, 'split.png', $other);
5489}
5490
5498function img_help($usehelpcursor = 1, $usealttitle = 1)
5499{
5500 global $langs;
5501
5502 if ($usealttitle) {
5503 if (is_string($usealttitle)) {
5504 $usealttitle = dol_escape_htmltag($usealttitle);
5505 } else {
5506 $usealttitle = $langs->trans('Info');
5507 }
5508 }
5509
5510 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5511}
5512
5519function img_info($titlealt = 'default')
5520{
5521 global $langs;
5522
5523 if ($titlealt == 'default') {
5524 $titlealt = $langs->trans('Informations');
5525 }
5526
5527 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5528}
5529
5538function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5539{
5540 global $langs;
5541
5542 if ($titlealt == 'default') {
5543 $titlealt = $langs->trans('Warning');
5544 }
5545
5546 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5547 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5548}
5549
5556function img_error($titlealt = 'default')
5557{
5558 global $langs;
5559
5560 if ($titlealt == 'default') {
5561 $titlealt = $langs->trans('Error');
5562 }
5563
5564 return img_picto($titlealt, 'error.png');
5565}
5566
5574function img_next($titlealt = 'default', $moreatt = '')
5575{
5576 global $langs;
5577
5578 if ($titlealt == 'default') {
5579 $titlealt = $langs->trans('Next');
5580 }
5581
5582 //return img_picto($titlealt, 'next.png', $moreatt);
5583 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5584}
5585
5593function img_previous($titlealt = 'default', $moreatt = '')
5594{
5595 global $langs;
5596
5597 if ($titlealt == 'default') {
5598 $titlealt = $langs->trans('Previous');
5599 }
5600
5601 //return img_picto($titlealt, 'previous.png', $moreatt);
5602 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5603}
5604
5613function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5614{
5615 global $langs;
5616
5617 if ($titlealt == 'default') {
5618 $titlealt = $langs->trans('Down');
5619 }
5620
5621 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5622}
5623
5632function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5633{
5634 global $langs;
5635
5636 if ($titlealt == 'default') {
5637 $titlealt = $langs->trans('Up');
5638 }
5639
5640 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5641}
5642
5651function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5652{
5653 global $langs;
5654
5655 if ($titlealt == 'default') {
5656 $titlealt = $langs->trans('Left');
5657 }
5658
5659 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5660}
5661
5670function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5671{
5672 global $langs;
5673
5674 if ($titlealt == 'default') {
5675 $titlealt = $langs->trans('Right');
5676 }
5677
5678 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5679}
5680
5688function img_allow($allow, $titlealt = 'default')
5689{
5690 global $langs;
5691
5692 if ($titlealt == 'default') {
5693 $titlealt = $langs->trans('Active');
5694 }
5695
5696 if ($allow == 1) {
5697 return img_picto($titlealt, 'tick.png');
5698 }
5699
5700 return '-';
5701}
5702
5710function img_credit_card($brand, $morecss = null)
5711{
5712 if (is_null($morecss)) {
5713 $morecss = 'fa-2x';
5714 }
5715
5716 if ($brand == 'visa' || $brand == 'Visa') {
5717 $brand = 'cc-visa';
5718 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5719 $brand = 'cc-mastercard';
5720 } elseif ($brand == 'amex' || $brand == 'American Express') {
5721 $brand = 'cc-amex';
5722 } elseif ($brand == 'discover' || $brand == 'Discover') {
5723 $brand = 'cc-discover';
5724 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5725 $brand = 'cc-jcb';
5726 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5727 $brand = 'cc-diners-club';
5728 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5729 $brand = 'credit-card';
5730 }
5731
5732 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5733}
5734
5743function img_mime($file, $titlealt = '', $morecss = '')
5744{
5745 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5746
5747 $mimetype = dol_mimetype($file, '', 1);
5748 $mimeimg = dol_mimetype($file, '', 2);
5749 $mimefa = dol_mimetype($file, '', 4);
5750
5751 if (empty($titlealt)) {
5752 $titlealt = 'Mime type: '.$mimetype;
5753 }
5754
5755 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5756 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5757}
5758
5759
5767function img_search($titlealt = 'default', $other = '')
5768{
5769 global $langs;
5770
5771 if ($titlealt == 'default') {
5772 $titlealt = $langs->trans('Search');
5773 }
5774
5775 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5776
5777 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5778 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5779
5780 return $input;
5781}
5782
5790function img_searchclear($titlealt = 'default', $other = '')
5791{
5792 global $langs;
5793
5794 if ($titlealt == 'default') {
5795 $titlealt = $langs->trans('Search');
5796 }
5797
5798 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5799
5800 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5801 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5802
5803 return $input;
5804}
5805
5818function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5819{
5820 global $conf, $langs;
5821
5822 if ($infoonimgalt) {
5823 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5824 } else {
5825 if (empty($conf->use_javascript_ajax)) {
5826 $textfordropdown = '';
5827 }
5828
5829 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5830 $fa = 'info-circle';
5831 if ($picto == 'warning') {
5832 $fa = 'exclamation-triangle';
5833 }
5834 $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> ';
5835 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5836 $result .= ($nodiv ? '' : '</div>');
5837
5838 if ($textfordropdown) {
5839 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5840 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5841 jQuery(document).ready(function() {
5842 jQuery(".'.$class.'text").click(function() {
5843 console.log("toggle text");
5844 jQuery(".'.$class.'").toggle();
5845 });
5846 });
5847 </script>';
5848
5849 $result = $tmpresult.$result;
5850 }
5851 }
5852
5853 return $result;
5854}
5855
5856
5868function dol_print_error($db = null, $error = '', $errors = null)
5869{
5870 global $conf, $langs, $user, $argv;
5871 global $dolibarr_main_prod;
5872
5873 $out = '';
5874 $syslog = '';
5875
5876 // If error occurs before the $lang object was loaded
5877 if (!$langs) {
5878 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5879 $langs = new Translate('', $conf);
5880 $langs->load("main");
5881 }
5882
5883 // Load translation files required by the error messages
5884 $langs->loadLangs(array('main', 'errors'));
5885
5886 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5887 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5888 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5889 $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";
5890 }
5891 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5892
5893 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5894 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5895 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5896 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5897 }
5898 if ($user instanceof User) {
5899 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
5900 }
5901 if (function_exists("phpversion")) {
5902 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5903 }
5904 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5905 if (function_exists("php_uname")) {
5906 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5907 }
5908 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5909 $out .= "<br>\n";
5910 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5911 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5912 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5913 $out .= "<br>\n";
5914 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5915 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5916 } else { // Mode CLI
5917 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5918 $syslog .= "pid=".dol_getmypid();
5919 }
5920
5921 if (!empty($conf->modules)) {
5922 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
5923 }
5924
5925 if (is_object($db)) {
5926 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5927 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5928 $lastqueryerror = $db->lastqueryerror();
5929 if (!utf8_check($lastqueryerror)) {
5930 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5931 }
5932 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5933 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5934 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5935 $out .= "<br>\n";
5936 } else { // Mode CLI
5937 // No dol_escape_htmltag for output, we are in CLI mode
5938 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5939 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5940 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5941 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5942 }
5943 $syslog .= ", sql=".$db->lastquery();
5944 $syslog .= ", db_error=".$db->lasterror();
5945 }
5946
5947 if ($error || $errors) {
5948 // Merge all into $errors array
5949 if (is_array($error) && is_array($errors)) {
5950 $errors = array_merge($error, $errors);
5951 } elseif (is_array($error)) { // deprecated, use second parameters
5952 $errors = $error;
5953 } elseif (is_array($errors) && !empty($error)) {
5954 $errors = array_merge(array($error), $errors);
5955 } elseif (!empty($error)) {
5956 $errors = array_merge(array($error), array($errors));
5957 }
5958
5959 $langs->load("errors");
5960
5961 foreach ($errors as $msg) {
5962 if (empty($msg)) {
5963 continue;
5964 }
5965 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5966 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5967 } else { // Mode CLI
5968 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5969 }
5970 $syslog .= ", msg=".$msg;
5971 }
5972 }
5973 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5974 xdebug_print_function_stack();
5975 $out .= '<b>XDebug information:</b>'."<br>\n";
5976 $out .= 'File: '.xdebug_call_file()."<br>\n";
5977 $out .= 'Line: '.xdebug_call_line()."<br>\n";
5978 $out .= 'Function: '.xdebug_call_function()."<br>\n";
5979 $out .= "<br>\n";
5980 }
5981
5982 // Return a http header with error code if possible
5983 if (!headers_sent()) {
5984 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5985 top_httphead();
5986 }
5987 //http_response_code(500); // If we use 500, message is not output with some command line tools
5988 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
5989 }
5990
5991 if (empty($dolibarr_main_prod)) {
5992 print $out;
5993 } else {
5994 if (empty($langs->defaultlang)) {
5995 $langs->setDefaultLang();
5996 }
5997 $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.
5998 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5999 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";
6000 print $langs->trans("DolibarrHasDetectedError").'. ';
6001 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6002 if (!defined("MAIN_CORE_ERROR")) {
6003 define("MAIN_CORE_ERROR", 1);
6004 }
6005 }
6006
6007 dol_syslog("Error ".$syslog, LOG_ERR);
6008}
6009
6020function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6021{
6022 global $langs;
6023
6024 if (empty($email)) {
6025 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6026 }
6027
6028 $langs->load("errors");
6029 $now = dol_now();
6030
6031 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6032 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6033 if ($errormessage) {
6034 print '<br><br>'.$errormessage;
6035 }
6036 if (is_array($errormessages) && count($errormessages)) {
6037 foreach ($errormessages as $mesgtoshow) {
6038 print '<br><br>'.$mesgtoshow;
6039 }
6040 }
6041 print '</div></div>';
6042}
6043
6060function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6061{
6062 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6063}
6064
6083function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6084{
6085 global $langs, $form;
6086 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6087
6088 if ($moreattrib == 'class="right"') {
6089 $prefix .= 'right '; // For backward compatibility
6090 }
6091
6092 $sortorder = strtoupper($sortorder);
6093 $out = '';
6094 $sortimg = '';
6095
6096 $tag = 'th';
6097 if ($thead == 2) {
6098 $tag = 'div';
6099 }
6100
6101 $tmpsortfield = explode(',', $sortfield);
6102 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6103 $tmpfield = explode(',', $field);
6104 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6105
6106 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6107 $prefix = 'wrapcolumntitle '.$prefix;
6108 }
6109
6110 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6111 // If field is used as sort criteria we use a specific css class liste_titre_sel
6112 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6113 $liste_titre = 'liste_titre';
6114 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6115 $liste_titre = 'liste_titre_sel';
6116 }
6117
6118 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6119 //$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)).'"' : '');
6120 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6121 $tagstart .= '>';
6122
6123 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6124 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6125 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6126 $options = preg_replace('/&+/i', '&', $options);
6127 if (!preg_match('/^&/', $options)) {
6128 $options = '&'.$options;
6129 }
6130
6131 $sortordertouseinlink = '';
6132 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6133 if (preg_match('/^DESC/i', $sortorder)) {
6134 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6135 } else { // We reverse the var $sortordertouseinlink
6136 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6137 }
6138 } else { // We are on field that is the first current sorting criteria
6139 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6140 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6141 } else {
6142 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6143 }
6144 }
6145 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6146 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6147 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6148 $out .= '>';
6149 }
6150 if ($tooltip) {
6151 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6152 if (preg_match('/:\w+$/', $tooltip)) {
6153 $tmptooltip = explode(':', $tooltip);
6154 } else {
6155 $tmptooltip = array($tooltip);
6156 }
6157 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6158 } else {
6159 $out .= $langs->trans($name);
6160 }
6161
6162 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6163 $out .= '</a>';
6164 }
6165
6166 if (empty($thead) && $field) { // If this is a sort field
6167 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6168 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6169 $options = preg_replace('/&+/i', '&', $options);
6170 if (!preg_match('/^&/', $options)) {
6171 $options = '&'.$options;
6172 }
6173
6174 if (!$sortorder || ($field1 != $sortfield1)) {
6175 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6176 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6177 } else {
6178 if (preg_match('/^DESC/', $sortorder)) {
6179 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6180 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6181 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6182 }
6183 if (preg_match('/^ASC/', $sortorder)) {
6184 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6185 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6186 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6187 }
6188 }
6189 }
6190
6191 $tagend = '</'.$tag.'>';
6192
6193 $out = $tagstart.$sortimg.$out.$tagend;
6194
6195 return $out;
6196}
6197
6206function print_titre($title)
6207{
6208 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6209
6210 print '<div class="titre">'.$title.'</div>';
6211}
6212
6224function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6225{
6226 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6227}
6228
6242function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6243{
6244 $return = '';
6245
6246 if ($picto == 'setup') {
6247 $picto = 'generic';
6248 }
6249
6250 $return .= "\n";
6251 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
6252 $return .= '<tr class="titre">';
6253 if ($picto) {
6254 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6255 }
6256 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6257 $return .= '<div class="titre inline-block">';
6258 $return .= $title; // $title is already HTML sanitized content
6259 $return .= '</div>';
6260 $return .= '</td>';
6261 if (dol_strlen($morehtmlcenter)) {
6262 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6263 }
6264 if (dol_strlen($morehtmlright)) {
6265 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6266 }
6267 $return .= '</tr></table>'."\n";
6268
6269 return $return;
6270}
6271
6295function 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 = '')
6296{
6297 global $conf;
6298
6299 $savlimit = $limit;
6300 $savtotalnboflines = $totalnboflines;
6301 if (is_numeric($totalnboflines)) {
6302 $totalnboflines = abs($totalnboflines);
6303 }
6304
6305 $page = (int) $page;
6306
6307 if ($picto == 'setup') {
6308 $picto = 'title_setup.png';
6309 }
6310 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6311 $picto = 'title.gif';
6312 }
6313 if ($limit < 0) {
6314 $limit = $conf->liste_limit;
6315 }
6316
6317 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6318 $nextpage = 1;
6319 } else {
6320 $nextpage = 0;
6321 }
6322 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
6323
6324 print "\n";
6325 print "<!-- Begin title -->\n";
6326 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
6327
6328 // Left
6329
6330 if ($picto && $title) {
6331 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
6332 }
6333
6334 print '<td class="nobordernopadding valignmiddle col-title">';
6335 print '<div class="titre inline-block">';
6336 print $title; // $title may contains HTML
6337 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
6338 print '<span class="opacitymedium colorblack paddingleft totalnboflines">('.$totalnboflines.')</span>';
6339 }
6340 print '</div></td>';
6341
6342 // Center
6343 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6344 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6345 }
6346
6347 // Right
6348 print '<td class="nobordernopadding valignmiddle right col-right">';
6349 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6350 if ($sortfield) {
6351 $options .= "&sortfield=".urlencode($sortfield);
6352 }
6353 if ($sortorder) {
6354 $options .= "&sortorder=".urlencode($sortorder);
6355 }
6356 // Show navigation bar
6357 $pagelist = '';
6358 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6359 if ($totalnboflines) { // If we know total nb of lines
6360 // Define nb of extra page links before and after selected page + ... + first or last
6361 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6362
6363 if ($limit > 0) {
6364 $nbpages = ceil($totalnboflines / $limit);
6365 } else {
6366 $nbpages = 1;
6367 }
6368 $cpt = ($page - $maxnbofpage);
6369 if ($cpt < 0) {
6370 $cpt = 0;
6371 }
6372
6373 if ($cpt >= 1) {
6374 if (empty($pagenavastextinput)) {
6375 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6376 if ($cpt > 2) {
6377 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6378 } elseif ($cpt == 2) {
6379 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6380 }
6381 }
6382 }
6383
6384 do {
6385 if ($pagenavastextinput) {
6386 if ($cpt == $page) {
6387 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6388 $pagelist .= '/';
6389 }
6390 } else {
6391 if ($cpt == $page) {
6392 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6393 } else {
6394 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6395 }
6396 }
6397 $cpt++;
6398 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6399
6400 if (empty($pagenavastextinput)) {
6401 if ($cpt < $nbpages) {
6402 if ($cpt < $nbpages - 2) {
6403 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6404 } elseif ($cpt == $nbpages - 2) {
6405 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6406 }
6407 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6408 }
6409 } else {
6410 //var_dump($page.' '.$cpt.' '.$nbpages);
6411 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6412 }
6413 } else {
6414 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6415 }
6416 }
6417
6418 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6419 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
6420 }
6421
6422 // js to autoselect page field on focus
6423 if ($pagenavastextinput) {
6424 print ajax_autoselect('.pageplusone');
6425 }
6426
6427 print '</td>';
6428 print '</tr>';
6429
6430 print '</table>'."\n";
6431
6432 // Center
6433 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6434 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6435 }
6436
6437 print "<!-- End title -->\n\n";
6438}
6439
6456function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
6457{
6458 global $conf, $langs;
6459
6460 print '<div class="pagination"><ul>';
6461 if ($beforearrows) {
6462 print '<li class="paginationbeforearrows">';
6463 print $beforearrows;
6464 print '</li>';
6465 }
6466
6467 if (empty($hidenavigation)) {
6468 if ((int) $limit > 0 && empty($hideselectlimit)) {
6469 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6470 $pagesizechoices .= ',5000:5000,10000:10000';
6471 //$pagesizechoices .= ',20000:20000'; // Memory trouble on browsers
6472 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6473 //$pagesizechoices .= ',2:2';
6474 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6475 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6476 }
6477
6478 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6479 print '<li class="pagination">';
6480 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.'">';
6481 print '<datalist id="limitlist">';
6482 } else {
6483 print '<li class="paginationcombolimit valignmiddle">';
6484 print '<select id="limit" class="flat selectlimit nopadding maxwidth75 center" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6485 }
6486 $tmpchoice = explode(',', $pagesizechoices);
6487 $tmpkey = $limit.':'.$limit;
6488 if (!in_array($tmpkey, $tmpchoice)) {
6489 $tmpchoice[] = $tmpkey;
6490 }
6491 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6492 if (!in_array($tmpkey, $tmpchoice)) {
6493 $tmpchoice[] = $tmpkey;
6494 }
6495 asort($tmpchoice, SORT_NUMERIC);
6496 foreach ($tmpchoice as $val) {
6497 $selected = '';
6498 $tmp = explode(':', $val);
6499 $key = $tmp[0];
6500 $val = $tmp[1];
6501 if ($key != '' && $val != '') {
6502 if ((int) $key == (int) $limit) {
6503 $selected = ' selected="selected"';
6504 }
6505 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6506 }
6507 }
6508 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6509 print '</datalist>';
6510 } else {
6511 print '</select>';
6512 print ajax_combobox("limit", array(), 0, 0, 'resolve', -1, 'limit');
6513 //print ajax_combobox("limit");
6514 }
6515
6516 if ($conf->use_javascript_ajax) {
6517 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6518 <script>
6519 jQuery(document).ready(function () {
6520 jQuery(".selectlimit").change(function() {
6521 console.log("Change limit. Send submit");
6522 $(this).parents(\'form:first\').submit();
6523 });
6524 });
6525 </script>
6526 ';
6527 }
6528 print '</li>';
6529 }
6530 if ($page > 0) {
6531 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>';
6532 }
6533 if ($betweenarrows) {
6534 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6535 print $betweenarrows;
6536 print '<!--</div>-->';
6537 }
6538 if ($nextpage > 0) {
6539 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>';
6540 }
6541 if ($afterarrows) {
6542 print '<li class="paginationafterarrows">';
6543 print $afterarrows;
6544 print '</li>';
6545 }
6546 }
6547 print '</ul></div>'."\n";
6548}
6549
6550
6562function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6563{
6564 $morelabel = '';
6565
6566 if (preg_match('/%/', $rate)) {
6567 $rate = str_replace('%', '', $rate);
6568 $addpercent = true;
6569 }
6570 $reg = array();
6571 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6572 $morelabel = ' ('.$reg[1].')';
6573 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6574 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6575 }
6576 if (preg_match('/\*/', $rate)) {
6577 $rate = str_replace('*', '', $rate);
6578 $info_bits |= 1;
6579 }
6580
6581 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6582 if (!preg_match('/\//', $rate)) {
6583 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6584 } else {
6585 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6586 $ret = $rate.($addpercent ? '%' : '');
6587 }
6588 if (($info_bits & 1) && $usestarfornpr >= 0) {
6589 $ret .= ' *';
6590 }
6591 $ret .= $morelabel;
6592 return $ret;
6593}
6594
6595
6611function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6612{
6613 global $langs, $conf;
6614
6615 // Clean parameters
6616 if (empty($amount)) {
6617 $amount = 0; // To have a numeric value if amount not defined or = ''
6618 }
6619 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6620 if ($rounding == -1) {
6621 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6622 }
6623 $nbdecimal = $rounding;
6624
6625 if ($outlangs === 'none') {
6626 // Use international separators
6627 $dec = '.';
6628 $thousand = '';
6629 } else {
6630 // Output separators by default (french)
6631 $dec = ',';
6632 $thousand = ' ';
6633
6634 // If $outlangs not forced, we use use language
6635 if (!($outlangs instanceof Translate)) {
6636 $outlangs = $langs;
6637 }
6638
6639 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6640 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6641 }
6642 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6643 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6644 }
6645 if ($thousand == 'None') {
6646 $thousand = '';
6647 } elseif ($thousand == 'Space') {
6648 $thousand = ' ';
6649 }
6650 }
6651 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6652
6653 //print "amount=".$amount."-";
6654 $amount = str_replace(',', '.', $amount); // should be useless
6655 //print $amount."-";
6656 $data = explode('.', $amount);
6657 $decpart = isset($data[1]) ? $data[1] : '';
6658 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6659 //print "decpart=".$decpart."<br>";
6660 $end = '';
6661
6662 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6663 if (dol_strlen($decpart) > $nbdecimal) {
6664 $nbdecimal = dol_strlen($decpart);
6665 }
6666
6667 // If nbdecimal is higher than max to show
6668 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6669 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6670 $nbdecimal = $nbdecimalmaxshown;
6671 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6672 // If output is truncated, we show ...
6673 $end = '...';
6674 }
6675 }
6676
6677 // If force rounding
6678 if ((string) $forcerounding != '-1') {
6679 if ($forcerounding === 'MU') {
6680 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6681 } elseif ($forcerounding === 'MT') {
6682 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6683 } elseif ($forcerounding >= 0) {
6684 $nbdecimal = $forcerounding;
6685 }
6686 }
6687
6688 // Format number
6689 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6690 if ($form) {
6691 $output = preg_replace('/\s/', '&nbsp;', $output);
6692 $output = preg_replace('/\'/', '&#039;', $output);
6693 }
6694 // Add symbol of currency if requested
6695 $cursymbolbefore = $cursymbolafter = '';
6696 if ($currency_code && is_object($outlangs)) {
6697 if ($currency_code == 'auto') {
6698 $currency_code = $conf->currency;
6699 }
6700
6701 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6702 $listoflanguagesbefore = array('nl_NL');
6703 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6704 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6705 } else {
6706 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6707 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6708 }
6709 }
6710 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6711
6712 return $output;
6713}
6714
6739function price2num($amount, $rounding = '', $option = 0)
6740{
6741 global $langs, $conf;
6742
6743 // Clean parameters
6744 if (is_null($amount)) {
6745 $amount = '';
6746 }
6747
6748 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6749 // Numbers must be '1234.56'
6750 // Decimal delimiter for PHP and database SQL requests must be '.'
6751 $dec = ',';
6752 $thousand = ' ';
6753 if (is_null($langs)) { // $langs is not defined, we use english values.
6754 $dec = '.';
6755 $thousand = ',';
6756 } else {
6757 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6758 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6759 }
6760 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6761 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6762 }
6763 }
6764 if ($thousand == 'None') {
6765 $thousand = '';
6766 } elseif ($thousand == 'Space') {
6767 $thousand = ' ';
6768 }
6769 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6770
6771 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6772 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6773 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6774 if (!is_numeric($amount)) {
6775 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6776 }
6777
6778 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
6779 $amount = str_replace($thousand, '', $amount);
6780 }
6781
6782 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6783 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6784 // So if number was already a good number, it is converted into local Dolibarr setup.
6785 if (is_numeric($amount)) {
6786 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6787 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6788 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6789 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6790 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6791 }
6792 //print "QQ".$amount."<br>\n";
6793
6794 // Now make replace (the main goal of function)
6795 if ($thousand != ',' && $thousand != '.') {
6796 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6797 }
6798
6799 $amount = str_replace(' ', '', $amount); // To avoid spaces
6800 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6801 $amount = str_replace($dec, '.', $amount);
6802
6803 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6804 }
6805 //print ' XX'.$amount.' '.$rounding;
6806
6807 // Now, $amount is a real PHP float number. We make a rounding if required.
6808 if ($rounding) {
6809 $nbofdectoround = '';
6810 if ($rounding == 'MU') {
6811 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6812 } elseif ($rounding == 'MT') {
6813 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6814 } elseif ($rounding == 'MS') {
6815 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6816 } elseif ($rounding == 'CU') {
6817 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6818 } elseif ($rounding == 'CT') {
6819 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6820 } elseif (is_numeric($rounding)) {
6821 $nbofdectoround = (int) $rounding;
6822 }
6823
6824 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6825 if (dol_strlen($nbofdectoround)) {
6826 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6827 } else {
6828 return 'ErrorBadParameterProvidedToFunction';
6829 }
6830 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6831
6832 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6833 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6834 if (is_numeric($amount)) {
6835 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6836 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6837 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6838 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6839 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6840 }
6841 //print "TT".$amount.'<br>';
6842
6843 // Always make replace because each math function (like round) replace
6844 // with local values and we want a number that has a SQL string format x.y
6845 if ($thousand != ',' && $thousand != '.') {
6846 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6847 }
6848
6849 $amount = str_replace(' ', '', $amount); // To avoid spaces
6850 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6851 $amount = str_replace($dec, '.', $amount);
6852
6853 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6854 }
6855
6856 return $amount;
6857}
6858
6871function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6872{
6873 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6874
6875 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6876 $dimension *= 1000000;
6877 $unit -= 6;
6878 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6879 $dimension *= 1000;
6880 $unit -= 3;
6881 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6882 $dimension /= 1000000;
6883 $unit += 6;
6884 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6885 $dimension /= 1000;
6886 $unit += 3;
6887 }
6888 // Special case when we want output unit into pound or ounce
6889 /* TODO
6890 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6891 {
6892 $dimension = // convert dimension from standard unit into ounce or pound
6893 $unit = $forceunitoutput;
6894 }
6895 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6896 {
6897 $dimension = // convert dimension from standard unit into ounce or pound
6898 $unit = $forceunitoutput;
6899 }*/
6900
6901 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6902 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6903 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6904
6905 return $ret;
6906}
6907
6908
6921function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
6922{
6923 global $db, $conf, $mysoc;
6924
6925 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6926 $thirdparty_seller = $mysoc;
6927 }
6928
6929 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);
6930
6931 $vatratecleaned = $vatrate;
6932 $reg = array();
6933 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
6934 $vatratecleaned = trim($reg[1]);
6935 $vatratecode = $reg[2];
6936 }
6937
6938 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6939 {
6940 return 0;
6941 }*/
6942
6943 // Some test to guess with no need to make database access
6944 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6945 if ($local == 1) {
6946 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6947 return 0;
6948 }
6949 if ($thirdparty_seller->id == $mysoc->id) {
6950 if (!$thirdparty_buyer->localtax1_assuj) {
6951 return 0;
6952 }
6953 } else {
6954 if (!$thirdparty_seller->localtax1_assuj) {
6955 return 0;
6956 }
6957 }
6958 }
6959
6960 if ($local == 2) {
6961 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6962 if (!$mysoc->localtax2_assuj) {
6963 return 0; // If main vat is 0, IRPF may be different than 0.
6964 }
6965 if ($thirdparty_seller->id == $mysoc->id) {
6966 if (!$thirdparty_buyer->localtax2_assuj) {
6967 return 0;
6968 }
6969 } else {
6970 if (!$thirdparty_seller->localtax2_assuj) {
6971 return 0;
6972 }
6973 }
6974 }
6975 } else {
6976 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6977 return 0;
6978 }
6979 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6980 return 0;
6981 }
6982 }
6983
6984 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6985 if (in_array($mysoc->country_code, array('ES'))) {
6986 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6987 }
6988
6989 // Search local taxes
6990 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
6991 if ($local == 1) {
6992 if ($thirdparty_seller != $mysoc) {
6993 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6994 return $thirdparty_seller->localtax1_value;
6995 }
6996 } else { // i am the seller
6997 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6998 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6999 }
7000 }
7001 }
7002 if ($local == 2) {
7003 if ($thirdparty_seller != $mysoc) {
7004 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7005 // TODO We should also return value defined on thirdparty only if defined
7006 return $thirdparty_seller->localtax2_value;
7007 }
7008 } else { // i am the seller
7009 if (in_array($mysoc->country_code, array('ES'))) {
7010 return $thirdparty_buyer->localtax2_value;
7011 } else {
7012 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
7013 }
7014 }
7015 }
7016 }
7017
7018 // By default, search value of local tax on line of common tax
7019 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7020 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7021 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7022 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7023 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7024 if (!empty($vatratecode)) {
7025 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7026 } else {
7027 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7028 }
7029
7030 $resql = $db->query($sql);
7031
7032 if ($resql) {
7033 $obj = $db->fetch_object($resql);
7034 if ($obj) {
7035 if ($local == 1) {
7036 return $obj->localtax1;
7037 } elseif ($local == 2) {
7038 return $obj->localtax2;
7039 }
7040 }
7041 }
7042
7043 return 0;
7044}
7045
7046
7055function isOnlyOneLocalTax($local)
7056{
7057 $tax = get_localtax_by_third($local);
7058
7059 $valors = explode(":", $tax);
7060
7061 if (count($valors) > 1) {
7062 return false;
7063 } else {
7064 return true;
7065 }
7066}
7067
7074function get_localtax_by_third($local)
7075{
7076 global $db, $mysoc;
7077
7078 $sql = " SELECT t.localtax".$local." as localtax";
7079 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7080 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7081 $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";
7082 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7083 $sql .= " AND t.localtax".$local."_type <> '0'";
7084 $sql .= " ORDER BY t.rowid DESC";
7085
7086 $resql = $db->query($sql);
7087 if ($resql) {
7088 $obj = $db->fetch_object($resql);
7089 if ($obj) {
7090 return $obj->localtax;
7091 } else {
7092 return '0';
7093 }
7094 }
7095
7096 return 'Error';
7097}
7098
7099
7111function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7112{
7113 global $db;
7114
7115 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7116
7117 // Search local taxes
7118 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7119 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7120 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7121 if ($firstparamisid) {
7122 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7123 } else {
7124 $vatratecleaned = $vatrate;
7125 $vatratecode = '';
7126 $reg = array();
7127 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7128 $vatratecleaned = $reg[1];
7129 $vatratecode = $reg[2];
7130 }
7131
7132 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7133 /*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 ??
7134 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7135 $sql .= " WHERE t.fk_pays = c.rowid";
7136 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7137 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7138 } else {
7139 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7140 }
7141 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7142 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7143 if ($vatratecode) {
7144 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7145 }
7146 }
7147
7148 $resql = $db->query($sql);
7149 if ($resql) {
7150 $obj = $db->fetch_object($resql);
7151 if ($obj) {
7152 return array(
7153 'rowid' => $obj->rowid,
7154 'code' => $obj->code,
7155 'rate' => $obj->rate,
7156 'localtax1' => $obj->localtax1,
7157 'localtax1_type' => $obj->localtax1_type,
7158 'localtax2' => $obj->localtax2,
7159 'localtax2_type' => $obj->localtax2_type,
7160 'npr' => $obj->npr,
7161 'accountancy_code_sell' => $obj->accountancy_code_sell,
7162 'accountancy_code_buy' => $obj->accountancy_code_buy
7163 );
7164 } else {
7165 return array();
7166 }
7167 } else {
7168 dol_print_error($db);
7169 }
7170
7171 return array();
7172}
7173
7190function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7191{
7192 global $db, $mysoc;
7193
7194 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7195
7196 // Search local taxes
7197 $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";
7198 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7199 if ($firstparamisid) {
7200 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7201 } else {
7202 $vatratecleaned = $vatrate;
7203 $vatratecode = '';
7204 $reg = array();
7205 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7206 $vatratecleaned = $reg[1];
7207 $vatratecode = $reg[2];
7208 }
7209
7210 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7211 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7212 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7213 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7214 } else {
7215 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7216 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7217 }
7218 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7219 if ($vatratecode) {
7220 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7221 }
7222 }
7223
7224 $resql = $db->query($sql);
7225 if ($resql) {
7226 $obj = $db->fetch_object($resql);
7227
7228 if ($obj) {
7229 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7230
7231 if ($local == 1) {
7232 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7233 } elseif ($local == 2) {
7234 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7235 } else {
7236 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);
7237 }
7238 }
7239 }
7240
7241 return array();
7242}
7243
7254function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7255{
7256 global $db, $mysoc;
7257
7258 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7259
7260 $ret = 0;
7261 $found = 0;
7262
7263 if ($idprod > 0) {
7264 // Load product
7265 $product = new Product($db);
7266 $product->fetch($idprod);
7267
7268 if (($mysoc->country_code == $thirdpartytouse->country_code)
7269 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7270 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7271 ) {
7272 // If country of thirdparty to consider is ours
7273 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7274 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7275 if ($result > 0) {
7276 $ret = $product->vatrate_supplier;
7277 if ($product->default_vat_code_supplier) {
7278 $ret .= ' ('.$product->default_vat_code_supplier.')';
7279 }
7280 $found = 1;
7281 }
7282 }
7283 if (!$found) {
7284 $ret = $product->tva_tx; // Default sales vat of product
7285 if ($product->default_vat_code) {
7286 $ret .= ' ('.$product->default_vat_code.')';
7287 }
7288 $found = 1;
7289 }
7290 } else {
7291 // TODO Read default product vat according to product and an other countrycode.
7292 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7293 }
7294 }
7295
7296 if (!$found) {
7297 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7298 // 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).
7299 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7300 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7301 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7302 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7303 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7304 $sql .= $db->plimit(1);
7305
7306 $resql = $db->query($sql);
7307 if ($resql) {
7308 $obj = $db->fetch_object($resql);
7309 if ($obj) {
7310 $ret = $obj->vat_rate;
7311 if ($obj->default_vat_code) {
7312 $ret .= ' ('.$obj->default_vat_code.')';
7313 }
7314 }
7315 $db->free($resql);
7316 } else {
7317 dol_print_error($db);
7318 }
7319 } else {
7320 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7321 // '1.23'
7322 // or '1.23 (CODE)'
7323 $defaulttx = '';
7324 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7325 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7326 }
7327 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7328 $defaultcode = $reg[1];
7329 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7330 }*/
7331
7332 $ret = $defaulttx;
7333 }
7334 }
7335
7336 dol_syslog("get_product_vat_for_country: ret=".$ret);
7337 return $ret;
7338}
7339
7349function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7350{
7351 global $db, $mysoc;
7352
7353 if (!class_exists('Product')) {
7354 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7355 }
7356
7357 $ret = 0;
7358 $found = 0;
7359
7360 if ($idprod > 0) {
7361 // Load product
7362 $product = new Product($db);
7363 $result = $product->fetch($idprod);
7364
7365 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7366 /* Not defined yet, so we don't use this
7367 if ($local==1) $ret=$product->localtax1_tx;
7368 elseif ($local==2) $ret=$product->localtax2_tx;
7369 $found=1;
7370 */
7371 } else {
7372 // TODO Read default product vat according to product and another countrycode.
7373 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7374 }
7375 }
7376
7377 if (!$found) {
7378 // If vat of product for the country not found or not defined, we return higher vat of country.
7379 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7380 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7381 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7382 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7383 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7384 $sql .= $db->plimit(1);
7385
7386 $resql = $db->query($sql);
7387 if ($resql) {
7388 $obj = $db->fetch_object($resql);
7389 if ($obj) {
7390 if ($local == 1) {
7391 $ret = $obj->localtax1;
7392 } elseif ($local == 2) {
7393 $ret = $obj->localtax2;
7394 }
7395 }
7396 } else {
7397 dol_print_error($db);
7398 }
7399 }
7400
7401 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7402 return $ret;
7403}
7404
7421function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7422{
7423 global $conf;
7424
7425 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7426
7427 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7428 $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;
7429
7430 $seller_country_code = $thirdparty_seller->country_code;
7431 $seller_in_cee = isInEEC($thirdparty_seller);
7432
7433 $buyer_country_code = $thirdparty_buyer->country_code;
7434 $buyer_in_cee = isInEEC($thirdparty_buyer);
7435
7436 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 : ''));
7437
7438 // 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)
7439 // we use the buyer VAT.
7440 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7441 if ($seller_in_cee && $buyer_in_cee) {
7442 $isacompany = $thirdparty_buyer->isACompany();
7443 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7444 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7445 if (!isValidVATID($thirdparty_buyer)) {
7446 $isacompany = 0;
7447 }
7448 }
7449
7450 if (!$isacompany) {
7451 //print 'VATRULE 0';
7452 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7453 }
7454 }
7455 }
7456
7457 // If seller does not use VAT, default VAT is 0. End of rule.
7458 if (!$seller_use_vat) {
7459 //print 'VATRULE 1';
7460 return 0;
7461 }
7462
7463 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7464 if (($seller_country_code == $buyer_country_code)
7465 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7466 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7467 ) { // Warning ->country_code not always defined
7468 //print 'VATRULE 2';
7469 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7470
7471 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7472 // Special case for india.
7473 //print 'VATRULE 2b';
7474 $reg = array();
7475 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7476 // we must revert the C+S into I
7477 $tmpvat = str_replace("C+S", "I", $tmpvat);
7478 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7479 // we must revert the I into C+S
7480 $tmpvat = str_replace("I", "C+S", $tmpvat);
7481 }
7482 }
7483
7484 return $tmpvat;
7485 }
7486
7487 // 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.
7488 // 'VATRULE 3' - Not supported
7489
7490 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7491 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7492 if (($seller_in_cee && $buyer_in_cee)) {
7493 $isacompany = $thirdparty_buyer->isACompany();
7494 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7495 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7496 if (!isValidVATID($thirdparty_buyer)) {
7497 $isacompany = 0;
7498 }
7499 }
7500
7501 if (!$isacompany) {
7502 //print 'VATRULE 4';
7503 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7504 } else {
7505 //print 'VATRULE 5';
7506 return 0;
7507 }
7508 }
7509
7510 // 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
7511 // I don't see any use case that need this rule.
7512 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7513 $isacompany = $thirdparty_buyer->isACompany();
7514 if (!$isacompany) {
7515 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7516 //print 'VATRULE extra';
7517 }
7518 }
7519
7520 // Otherwise the VAT proposed by default=0. End of rule.
7521 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7522 //print 'VATRULE 6';
7523 return 0;
7524}
7525
7526
7537function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7538{
7539 global $db;
7540
7541 if ($idprodfournprice > 0) {
7542 if (!class_exists('ProductFournisseur')) {
7543 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7544 }
7545 $prodprice = new ProductFournisseur($db);
7546 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7547 return $prodprice->fourn_tva_npr;
7548 } elseif ($idprod > 0) {
7549 if (!class_exists('Product')) {
7550 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7551 }
7552 $prod = new Product($db);
7553 $prod->fetch($idprod);
7554 return $prod->tva_npr;
7555 }
7556
7557 return 0;
7558}
7559
7573function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7574{
7575 global $mysoc;
7576
7577 if (!is_object($thirdparty_seller)) {
7578 return -1;
7579 }
7580 if (!is_object($thirdparty_buyer)) {
7581 return -1;
7582 }
7583
7584 if ($local == 1) { // Localtax 1
7585 if ($mysoc->country_code == 'ES') {
7586 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7587 return 0;
7588 }
7589 } else {
7590 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7591 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7592 return 0;
7593 }
7594 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7595 return 0;
7596 }
7597 }
7598 } elseif ($local == 2) { //I Localtax 2
7599 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7600 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7601 return 0;
7602 }
7603 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7604 return 0;
7605 }
7606 }
7607
7608 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7609 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7610 }
7611
7612 return 0;
7613}
7614
7623function yn($yesno, $case = 1, $color = 0)
7624{
7625 global $langs;
7626
7627 $result = 'unknown';
7628 $classname = '';
7629 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7630 $result = $langs->trans('yes');
7631 if ($case == 1 || $case == 3) {
7632 $result = $langs->trans("Yes");
7633 }
7634 if ($case == 2) {
7635 $result = '<input type="checkbox" value="1" checked disabled>';
7636 }
7637 if ($case == 3) {
7638 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7639 }
7640 if ($case == 4) {
7641 $result = img_picto('check', 'check');
7642 }
7643
7644 $classname = 'ok';
7645 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7646 $result = $langs->trans("no");
7647 if ($case == 1 || $case == 3) {
7648 $result = $langs->trans("No");
7649 }
7650 if ($case == 2) {
7651 $result = '<input type="checkbox" value="0" disabled>';
7652 }
7653 if ($case == 3) {
7654 $result = '<input type="checkbox" value="0" disabled> '.$result;
7655 }
7656 if ($case == 4) {
7657 $result = img_picto('uncheck', 'uncheck');
7658 }
7659
7660 if ($color == 2) {
7661 $classname = 'ok';
7662 } else {
7663 $classname = 'error';
7664 }
7665 }
7666 if ($color) {
7667 return '<span class="'.$classname.'">'.$result.'</span>';
7668 }
7669 return $result;
7670}
7671
7690function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7691{
7692 if (empty($modulepart) && is_object($object)) {
7693 if (!empty($object->module)) {
7694 $modulepart = $object->module;
7695 } elseif (!empty($object->element)) {
7696 $modulepart = $object->element;
7697 }
7698 }
7699
7700 $path = '';
7701
7702 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7703 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7704 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7705 $arrayforoldpath['product'] = 2;
7706 }
7707
7708 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7709 $level = $arrayforoldpath[$modulepart];
7710 }
7711
7712 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7713 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7714 if (empty($num) && is_object($object)) {
7715 $num = $object->id;
7716 }
7717 if (empty($alpha)) {
7718 $num = preg_replace('/([^0-9])/i', '', $num);
7719 } else {
7720 $num = preg_replace('/^.*\-/i', '', $num);
7721 }
7722 $num = substr("000".$num, -$level);
7723 if ($level == 1) {
7724 $path = substr($num, 0, 1);
7725 }
7726 if ($level == 2) {
7727 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7728 }
7729 if ($level == 3) {
7730 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7731 }
7732 } else {
7733 // We will enhance here a common way of forging path for document storage.
7734 // In a future, we may distribute directories on several levels depending on setup and object.
7735 // Here, $object->id, $object->ref and $modulepart are required.
7736 //var_dump($modulepart);
7737 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7738 }
7739
7740 if (empty($withoutslash) && !empty($path)) {
7741 $path .= '/';
7742 }
7743
7744 return $path;
7745}
7746
7755function dol_mkdir($dir, $dataroot = '', $newmask = '')
7756{
7757 global $conf;
7758
7759 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7760
7761 $dir_osencoded = dol_osencode($dir);
7762 if (@is_dir($dir_osencoded)) {
7763 return 0;
7764 }
7765
7766 $nberr = 0;
7767 $nbcreated = 0;
7768
7769 $ccdir = '';
7770 if (!empty($dataroot)) {
7771 // Remove data root from loop
7772 $dir = str_replace($dataroot.'/', '', $dir);
7773 $ccdir = $dataroot.'/';
7774 }
7775
7776 $cdir = explode("/", $dir);
7777 $num = count($cdir);
7778 for ($i = 0; $i < $num; $i++) {
7779 if ($i > 0) {
7780 $ccdir .= '/'.$cdir[$i];
7781 } else {
7782 $ccdir .= $cdir[$i];
7783 }
7784 $regs = array();
7785 if (preg_match("/^.:$/", $ccdir, $regs)) {
7786 continue; // If the Windows path is incomplete, continue with next directory
7787 }
7788
7789 // Attention, is_dir() can fail event if the directory exists
7790 // (i.e. according the open_basedir configuration)
7791 if ($ccdir) {
7792 $ccdir_osencoded = dol_osencode($ccdir);
7793 if (!@is_dir($ccdir_osencoded)) {
7794 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7795
7796 umask(0);
7797 $dirmaskdec = octdec((string) $newmask);
7798 if (empty($newmask)) {
7799 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7800 }
7801 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7802 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7803 // If the is_dir has returned a false information, we arrive here
7804 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7805 $nberr++;
7806 } else {
7807 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7808 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7809 $nbcreated++;
7810 }
7811 } else {
7812 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7813 }
7814 }
7815 }
7816 return ($nberr ? -$nberr : $nbcreated);
7817}
7818
7819
7827function dolChmod($filepath, $newmask = '')
7828{
7829 global $conf;
7830
7831 if (!empty($newmask)) {
7832 @chmod($filepath, octdec($newmask));
7833 } elseif (getDolGlobalString('MAIN_UMASK')) {
7834 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7835 }
7836}
7837
7838
7844function picto_required()
7845{
7846 return '<span class="fieldrequired">*</span>';
7847}
7848
7849
7866function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7867{
7868 if (is_null($stringtoclean)) {
7869 return '';
7870 }
7871
7872 if ($removelinefeed == 2) {
7873 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7874 }
7875 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7876
7877 // 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)
7878 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7879
7880 $temp = str_replace('< ', '__ltspace__', $temp);
7881 $temp = str_replace('<:', '__lttwopoints__', $temp);
7882
7883 if ($strip_tags) {
7884 $temp = strip_tags($temp);
7885 } else {
7886 // 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).
7887 $pattern = "/<[^<>]+>/";
7888 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7889 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7890 // pass 2 - $temp after pass 2: 0000-021
7891 $tempbis = $temp;
7892 do {
7893 $temp = $tempbis;
7894 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7895 $tempbis = preg_replace($pattern, '', $tempbis);
7896 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7897 } while ($tempbis != $temp);
7898
7899 $temp = $tempbis;
7900
7901 // 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).
7902 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7903 }
7904
7905 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7906
7907 // Remove also carriage returns
7908 if ($removelinefeed == 1) {
7909 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7910 }
7911
7912 // And double quotes
7913 if ($removedoublespaces) {
7914 while (strpos($temp, " ")) {
7915 $temp = str_replace(" ", " ", $temp);
7916 }
7917 }
7918
7919 $temp = str_replace('__ltspace__', '< ', $temp);
7920 $temp = str_replace('__lttwopoints__', '<:', $temp);
7921
7922 return trim($temp);
7923}
7924
7940function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7941{
7942 if (empty($allowed_tags)) {
7943 $allowed_tags = array(
7944 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7945 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
7946 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
7947 );
7948 }
7949 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7950 if ($allowiframe) {
7951 if (!in_array('iframe', $allowed_tags)) {
7952 $allowed_tags[] = "iframe";
7953 }
7954 }
7955 if ($allowlink) {
7956 if (!in_array('link', $allowed_tags)) {
7957 $allowed_tags[] = "link";
7958 }
7959 }
7960
7961 $allowed_tags_string = implode("><", $allowed_tags);
7962 $allowed_tags_string = '<'.$allowed_tags_string.'>';
7963
7964 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7965
7966 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7967
7968 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7969 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7970
7971 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7972 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7973
7974 // Remove all HTML tags
7975 $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
7976
7977 if ($cleanalsosomestyles) { // Clean for remaining html tags
7978 $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
7979 }
7980 if ($removeclassattribute) { // Clean for remaining html tags
7981 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7982 }
7983
7984 // Remove 'javascript:' that we should not find into a text with
7985 // 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)).
7986 if ($cleanalsojavascript) {
7987 $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);
7988 }
7989
7990 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
7991
7992 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
7993
7994
7995 return $temp;
7996}
7997
7998
8011function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8012{
8013 if (is_null($allowed_attributes)) {
8014 $allowed_attributes = array(
8015 "allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width",
8016 // HTML5
8017 "header", "footer", "nav", "section", "menu", "menuitem"
8018 );
8019 }
8020
8021 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8022 $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
8023
8024 // Warning: loadHTML does not support HTML5 on old libxml versions.
8025 $dom = new DOMDocument('', 'UTF-8');
8026 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8027 $savwarning = error_reporting();
8028 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8029 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8030 error_reporting($savwarning);
8031
8032 if ($dom instanceof DOMDocument) {
8033 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8034 $el = $els->item($i);
8035 if (!$el instanceof DOMElement) {
8036 continue;
8037 }
8038 $attrs = $el->attributes;
8039 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8040 //var_dump($attrs->item($ii));
8041 if (!empty($attrs->item($ii)->name)) {
8042 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8043 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8044 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8045 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8046 // If attribute is 'style'
8047 $valuetoclean = $attrs->item($ii)->value;
8048
8049 if (isset($valuetoclean)) {
8050 do {
8051 $oldvaluetoclean = $valuetoclean;
8052 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8053 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8054 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8055 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8056 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8057 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8058 }
8059
8060 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8061 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8062 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8063 } while ($oldvaluetoclean != $valuetoclean);
8064 }
8065
8066 $attrs->item($ii)->value = $valuetoclean;
8067 }
8068 }
8069 }
8070 }
8071 }
8072
8073 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8074 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8075
8076 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8077 $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
8078 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
8079 return trim($return);
8080 } else {
8081 return $stringtoclean;
8082 }
8083}
8084
8096function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8097{
8098 $temp = $stringtoclean;
8099 foreach ($disallowed_tags as $tagtoremove) {
8100 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8101 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8102 }
8103
8104 if ($cleanalsosomestyles) {
8105 $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
8106 }
8107
8108 return $temp;
8109}
8110
8111
8121function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8122{
8123 if ($nboflines == 1) {
8124 if (dol_textishtml($text)) {
8125 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8126 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8127 } else {
8128 if (isset($text)) {
8129 $firstline = preg_replace('/[\n\r].*/', '', $text);
8130 } else {
8131 $firstline = '';
8132 }
8133 }
8134 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8135 } else {
8136 $ishtml = 0;
8137 if (dol_textishtml($text)) {
8138 $text = preg_replace('/\n/', '', $text);
8139 $ishtml = 1;
8140 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8141 } else {
8142 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8143 }
8144
8145 $text = strtr($text, $repTable);
8146 if ($charset == 'UTF-8') {
8147 $pattern = '/(<br[^>]*>)/Uu';
8148 } else {
8149 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8150 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8151 }
8152 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8153
8154 $firstline = '';
8155 $i = 0;
8156 $countline = 0;
8157 $lastaddediscontent = 1;
8158 while ($countline < $nboflines && isset($a[$i])) {
8159 if (preg_match('/<br[^>]*>/', $a[$i])) {
8160 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8161 $firstline .= ($ishtml ? "<br>\n" : "\n");
8162 // Is it a br for a new line of after a printed line ?
8163 if (!$lastaddediscontent) {
8164 $countline++;
8165 }
8166 $lastaddediscontent = 0;
8167 }
8168 } else {
8169 $firstline .= $a[$i];
8170 $lastaddediscontent = 1;
8171 $countline++;
8172 }
8173 $i++;
8174 }
8175
8176 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8177 //unset($a);
8178 $ret = $firstline.($adddots ? '...' : '');
8179 //exit;
8180 return $ret;
8181 }
8182}
8183
8184
8196function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8197{
8198 if (is_null($stringtoencode)) {
8199 return '';
8200 }
8201
8202 if (!$nl2brmode) {
8203 return nl2br($stringtoencode, $forxml);
8204 } else {
8205 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8206 return $ret;
8207 }
8208}
8209
8219function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8220{
8221 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8222 // TODO using sandbox on inline html content is not possible yet with current browsers
8223 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8224 //$s .= $stringtoencode;
8225 //$s .= '</body></html></iframe>';
8226 return $stringtoencode;
8227 } else {
8228 $out = $stringtoencode;
8229
8230 // First clean HTML content
8231 do {
8232 $oldstringtoclean = $out;
8233
8234 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8235 try {
8236 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8237 if (LIBXML_VERSION < 20900) {
8238 // Avoid load of external entities (security problem).
8239 // Required only if LIBXML_VERSION < 20900
8240 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8241 libxml_disable_entity_loader(true);
8242 }
8243
8244 $dom = new DOMDocument();
8245 // Add a trick to solve pb with text without parent tag
8246 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8247 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8248
8249 if (dol_textishtml($out)) {
8250 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.$out.'</div>';
8251 } else {
8252 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.dol_nl2br($out).'</div>';
8253 }
8254
8255 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8256 $out = trim($dom->saveHTML());
8257
8258 // Remove the trick added to solve pb with text without parent tag
8259 $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8260 $out = preg_replace('/<\/div>$/', '', $out);
8261 } catch (Exception $e) {
8262 // If error, invalid HTML string with no way to clean it
8263 //print $e->getMessage();
8264 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8265 }
8266 }
8267
8268 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && $check != 'restricthtmlallowunvalid') {
8269 try {
8270 // Try cleaning using tidy
8271 if (extension_loaded('tidy') && class_exists("tidy")) {
8272 //print "aaa".$out."\n";
8273
8274 // See options at https://tidy.sourceforge.net/docs/quickref.html
8275 $config = array(
8276 'clean' => false,
8277 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8278 'doctype' => 'strict',
8279 'show-body-only' => true,
8280 "indent-attributes" => false,
8281 "vertical-space" => false,
8282 //'ident' => false, // Not always supported
8283 "wrap" => 0
8284 // HTML5 tags
8285 //'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',
8286 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8287 //'new-empty-tags' => 'command embed keygen source track wbr',
8288 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8289 );
8290
8291 // Tidy
8292 $tidy = new tidy();
8293 $out = $tidy->repairString($out, $config, 'utf8');
8294
8295 //print "xxx".$out;exit;
8296 }
8297 } catch (Exception $e) {
8298 // If error, invalid HTML string with no way to clean it
8299 //print $e->getMessage();
8300 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8301 }
8302 }
8303
8304 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8305 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8306
8307 // Clean some html entities that are useless so text is cleaner
8308 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8309
8310 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8311 // encoded using text entities) so we can then exclude all numeric entities.
8312 $out = preg_replace('/&#39;/i', '&apos;', $out);
8313
8314 // 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).
8315 // 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
8316 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8317 $out = preg_replace_callback(
8318 '/&#(x?[0-9][0-9a-f]+;?)/i',
8323 static function ($m) {
8324 return realCharForNumericEntities($m);
8325 },
8326 $out
8327 );
8328
8329 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8330 $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'.
8331
8332 // Keep only some html tags and remove also some 'javascript:' strings
8333 if ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8334 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8335 } else {
8336 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8337 }
8338
8339 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8340 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8342 }
8343
8344 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8345 $out = preg_replace('/&apos;/i', "&#39;", $out);
8346
8347 // Now remove js
8348 // 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
8349 $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)>
8350 $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);
8351 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8352 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8353 $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);
8354 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8355 // More not into the previous list
8356 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8357 } while ($oldstringtoclean != $out);
8358
8359 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8360 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8361 // 'url(' to avoid inline style like background: url(http...
8362 // '<link' to avoid <link href="http...">
8363 $reg = array();
8364 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8365 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8366 $nblinks = count($reg[0]);
8367 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8368 $out = 'ErrorTooManyLinksIntoHTMLString';
8369 }
8370
8371 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8372 if ($nblinks > 0) {
8373 $out = 'ErrorHTMLLinksNotAllowed';
8374 }
8375 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8376 $nblinks = 0;
8377 // Loop on each url in src= and url(
8378 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8379
8380 $matches = array();
8381 if (preg_match_all($pattern, $out, $matches)) {
8382 // URLs are into $matches[1]
8383 $urls = $matches[1];
8384
8385 // Affiche les URLs
8386 foreach ($urls as $url) {
8387 $nblinks++;
8388 echo "Found url = ".$url . "\n";
8389 }
8390 if ($nblinks > 0) {
8391 $out = 'ErrorHTMLExternalLinksNotAllowed';
8392 }
8393 }
8394 }
8395
8396 return $out;
8397 }
8398}
8399
8420function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8421{
8422 if (is_null($stringtoencode)) {
8423 return '';
8424 }
8425
8426 $newstring = $stringtoencode;
8427 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8428 $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.
8429 if ($removelasteolbr) {
8430 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8431 }
8432 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8433 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8434 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8435 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8436 } else {
8437 if ($removelasteolbr) {
8438 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8439 }
8440 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8441 }
8442 // Other substitutions that htmlentities does not do
8443 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8444 return $newstring;
8445}
8446
8454function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8455{
8456 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8457 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8458 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8459 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8460 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8461 return $ret;
8462}
8463
8470function dol_htmlcleanlastbr($stringtodecode)
8471{
8472 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8473 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8474 return $ret;
8475}
8476
8486function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8487{
8488 $newstring = $a;
8489 if ($keepsomeentities) {
8490 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8491 }
8492 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8493 if ($keepsomeentities) {
8494 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8495 }
8496 return $newstring;
8497}
8498
8510function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8511{
8512 return htmlentities($string, $flags, $encoding, $double_encode);
8513}
8514
8526function dol_string_is_good_iso($s, $clean = 0)
8527{
8528 $len = dol_strlen($s);
8529 $out = '';
8530 $ok = 1;
8531 for ($scursor = 0; $scursor < $len; $scursor++) {
8532 $ordchar = ord($s[$scursor]);
8533 //print $scursor.'-'.$ordchar.'<br>';
8534 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8535 $ok = 0;
8536 break;
8537 } elseif ($ordchar > 126 && $ordchar < 160) {
8538 $ok = 0;
8539 break;
8540 } elseif ($clean) {
8541 $out .= $s[$scursor];
8542 }
8543 }
8544 if ($clean) {
8545 return $out;
8546 }
8547 return $ok;
8548}
8549
8558function dol_nboflines($s, $maxchar = 0)
8559{
8560 if ($s == '') {
8561 return 0;
8562 }
8563 $arraystring = explode("\n", $s);
8564 $nb = count($arraystring);
8565
8566 return $nb;
8567}
8568
8569
8579function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8580{
8581 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8582 if (dol_textishtml($text)) {
8583 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8584 }
8585
8586 $text = strtr($text, $repTable);
8587 if ($charset == 'UTF-8') {
8588 $pattern = '/(<br[^>]*>)/Uu';
8589 } else {
8590 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8591 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8592 }
8593 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8594
8595 $nblines = (int) floor((count($a) + 1) / 2);
8596 // count possible auto line breaks
8597 if ($maxlinesize) {
8598 foreach ($a as $line) {
8599 if (dol_strlen($line) > $maxlinesize) {
8600 //$line_dec = html_entity_decode(strip_tags($line));
8601 $line_dec = html_entity_decode($line);
8602 if (dol_strlen($line_dec) > $maxlinesize) {
8603 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8604 $nblines += substr_count($line_dec, '\n');
8605 }
8606 }
8607 }
8608 }
8609
8610 unset($a);
8611 return $nblines;
8612}
8613
8622function dol_textishtml($msg, $option = 0)
8623{
8624 if (is_null($msg)) {
8625 return false;
8626 }
8627
8628 if ($option == 1) {
8629 if (preg_match('/<html/i', $msg)) {
8630 return true;
8631 } elseif (preg_match('/<body/i', $msg)) {
8632 return true;
8633 } elseif (preg_match('/<\/textarea/i', $msg)) {
8634 return true;
8635 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8636 return true;
8637 } elseif (preg_match('/<br/i', $msg)) {
8638 return true;
8639 }
8640 return false;
8641 } else {
8642 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8643 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8644 if (preg_match('/<html/i', $msg)) {
8645 return true;
8646 } elseif (preg_match('/<body/i', $msg)) {
8647 return true;
8648 } elseif (preg_match('/<\/textarea/i', $msg)) {
8649 return true;
8650 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8651 return true;
8652 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8653 return true;
8654 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8655 return true;
8656 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8657 return true;
8658 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8659 return true; // must accept <img src="http://example.com/aaa.png" />
8660 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8661 return true; // must accept <a href="http://example.com/aaa.png" />
8662 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8663 return true;
8664 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8665 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8666 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8667 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8668 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8669 }
8670
8671 return false;
8672 }
8673}
8674
8689function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8690{
8691 if (!empty($invert)) {
8692 $tmp = $text1;
8693 $text1 = $text2;
8694 $text2 = $tmp;
8695 }
8696
8697 $ret = '';
8698 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8699 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8700 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8701 return $ret;
8702}
8703
8704
8705
8719function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8720{
8721 global $db, $conf, $mysoc, $user, $extrafields;
8722
8723 $substitutionarray = array();
8724
8725 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
8726 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8727 // this will include signature content first and then replace var found into content of signature
8728 //var_dump($onlykey);
8729 $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()
8730 $usersignature = $user->signature;
8731 $substitutionarray = array_merge($substitutionarray, array(
8732 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8733 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8734 ));
8735
8736 if (is_object($user) && ($user instanceof User)) {
8737 $substitutionarray = array_merge($substitutionarray, array(
8738 '__USER_ID__' => (string) $user->id,
8739 '__USER_LOGIN__' => (string) $user->login,
8740 '__USER_EMAIL__' => (string) $user->email,
8741 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8742 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8743 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8744 '__USER_FAX__' => (string) $user->office_fax,
8745 '__USER_LASTNAME__' => (string) $user->lastname,
8746 '__USER_FIRSTNAME__' => (string) $user->firstname,
8747 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8748 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8749 '__USER_JOB__' => (string) $user->job,
8750 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8751 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8752 ));
8753 }
8754 }
8755 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8756 $substitutionarray = array_merge($substitutionarray, array(
8757 '__MYCOMPANY_NAME__' => $mysoc->name,
8758 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8759 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8760 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8761 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8762 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8763 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8764 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8765 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8766 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8767 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8768 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8769 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8770 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8771 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8772 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8773 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8774 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8775 '__MYCOMPANY_TOWN__' => $mysoc->town,
8776 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8777 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8778 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8779 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8780 ));
8781 }
8782
8783 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8784 if ($onlykey) {
8785 $substitutionarray['__ID__'] = '__ID__';
8786 $substitutionarray['__REF__'] = '__REF__';
8787 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8788 $substitutionarray['__LABEL__'] = '__LABEL__';
8789 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8790 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8791 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8792 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8793 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8794
8795 if (isModEnabled("societe")) { // Most objects are concerned
8796 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8797 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8798 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8799 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8800 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8801 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8802 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8803 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8804 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8805 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8806 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8807 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8808 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8809 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8810 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8811 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8812 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8813 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8814 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8815 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8816 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8817 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8818 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8819 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8820 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8821 }
8822 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
8823 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
8824 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
8825 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
8826 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
8827 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
8828 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
8829 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
8830 }
8831 // add substitution variables for ticket
8832 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
8833 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
8834 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
8835 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
8836 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
8837 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
8838 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
8839 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
8840 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
8841 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
8842 }
8843
8844 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
8845 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
8846 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
8847 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
8848 }
8849 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
8850 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
8851 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
8852 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
8853 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
8854 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
8855 }
8856 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
8857 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
8858 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
8859 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
8860 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
8861 }
8862 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
8863 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
8864 }
8865 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
8866 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
8867 }
8868 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
8869 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
8870 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
8871 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
8872 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
8873 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
8874 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
8875
8876 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
8877 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
8878 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
8879 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
8880 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
8881
8882 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
8883 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
8884 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
8885 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
8886 }
8887 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
8888 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
8889 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
8890 }
8891 } else {
8892 '@phan-var-force Adherent|Delivery $object';
8893 $substitutionarray['__ID__'] = $object->id;
8894 $substitutionarray['__REF__'] = $object->ref;
8895 $substitutionarray['__NEWREF__'] = $object->newref;
8896 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
8897 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8898 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8899 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
8900 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
8901
8902 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', 0, $outputlangs) : '');
8903 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', 0, $outputlangs) : '');
8904 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', 0, $outputlangs) : '');
8905
8906 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
8907 $date_delivery = null;
8908 if (property_exists($object, 'date_delivery')) {
8909 $date_delivery = $object->date_delivery;
8910 } elseif (property_exists($object, 'delivery_date')) {
8911 $date_delivery = $object->delivery_date;
8912 }
8913 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', 0, $outputlangs) : '');
8914 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
8915 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
8916 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
8917 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
8918 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
8919 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
8920 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
8921 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
8922
8923 // For backward compatibility (deprecated)
8924 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8925 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8926 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', 0, $outputlangs) : '');
8927 $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 : '')) : '');
8928 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
8929
8930 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
8931 '@phan-var-force Adherent $object';
8932 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
8933
8934 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
8935 if (method_exists($object, 'getCivilityLabel')) {
8936 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
8937 }
8938 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
8939 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
8940 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
8941 if (method_exists($object, 'getFullName')) {
8942 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
8943 }
8944 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
8945 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
8946 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
8947 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
8948 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
8949 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
8950 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
8951 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
8952 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
8953 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
8954 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
8955 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
8956 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
8957 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
8958 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
8959
8960 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
8961 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
8962 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
8963 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
8964 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
8965 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
8966 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
8967 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
8968 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
8969 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
8970 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
8971 }
8972
8973 if (is_object($object) && $object->element == 'societe') {
8974 '@phan-var-force Societe $object';
8975 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
8976 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
8977 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
8978 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
8979 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
8980 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
8981 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
8982 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
8983 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
8984 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
8985 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
8986 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
8987 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
8988 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
8989 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
8990 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
8991 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
8992 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
8993 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
8994 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
8995 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
8996 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
8997 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
8998 } elseif (is_object($object->thirdparty)) {
8999 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9000 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9001 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9002 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9003 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9004 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9005 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9006 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9007 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9008 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9009 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9010 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9011 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9012 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9013 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9014 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9015 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9016 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9017 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9018 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9019 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9020 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9021 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9022 }
9023
9024 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9025 '@phan-var-force RecruitmentCandidature $object';
9026 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9027 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9028 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9029 }
9030 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9031 '@phan-var-force ConferenceOrBoothAttendee $object';
9032 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9033 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9034 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9035 }
9036
9037 if (is_object($object) && $object->element == 'project') {
9038 '@phan-var-force Project $object';
9039 $substitutionarray['__PROJECT_ID__'] = $object->id;
9040 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9041 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9042 } elseif (is_object($object)) {
9043 $project = null;
9044 if (!empty($object->project)) {
9045 $project = $object->project;
9046 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9047 $project = $object->projet;
9048 }
9049 if (!is_null($project) && is_object($project)) {
9050 $substitutionarray['__PROJECT_ID__'] = $project->id;
9051 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9052 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9053 } else {
9054 // can substitute variables for project : uses lazy load in "make_substitutions" method
9055 $project_id = 0;
9056 if (!empty($object->fk_project) && $object->fk_project > 0) {
9057 $project_id = $object->fk_project;
9058 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9059 $project_id = $object->fk_project;
9060 }
9061 if ($project_id > 0) {
9062 // path:class:method:id
9063 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9064 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9065 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9066 }
9067 }
9068 }
9069
9070 if (is_object($object) && $object->element == 'facture') {
9071 '@phan-var-force Facture $object';
9072 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9073 }
9074 if (is_object($object) && $object->element == 'shipping') {
9075 '@phan-var-force Expedition $object';
9076 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9077 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9078 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9079 }
9080 if (is_object($object) && $object->element == 'reception') {
9081 '@phan-var-force Reception $object';
9082 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9083 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9084 }
9085
9086 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9087 '@phan-var-force Contrat $object';
9088 $dateplannedstart = '';
9089 $datenextexpiration = '';
9090 foreach ($object->lines as $line) {
9091 if ($line->date_start > $dateplannedstart) {
9092 $dateplannedstart = $line->date_start;
9093 }
9094 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9095 $datenextexpiration = $line->date_end;
9096 }
9097 }
9098 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9099 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9100 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9101
9102 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9103 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9104 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9105 }
9106 // add substitution variables for ticket
9107 if (is_object($object) && $object->element == 'ticket') {
9108 '@phan-var-force Ticket $object';
9109 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9110 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9111 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9112 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9113 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9114 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9115 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9116 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9117 $userstat = new User($db);
9118 if ($object->fk_user_assign > 0) {
9119 $userstat->fetch($object->fk_user_assign);
9120 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9121 }
9122
9123 if ($object->fk_user_create > 0) {
9124 $userstat->fetch($object->fk_user_create);
9125 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9126 }
9127 }
9128
9129 // Create dynamic tags for __EXTRAFIELD_FIELD__
9130 if ($object->table_element && $object->id > 0) {
9131 if (!is_object($extrafields)) {
9132 $extrafields = new ExtraFields($db);
9133 }
9134 $extrafields->fetch_name_optionals_label($object->table_element, true);
9135
9136 if ($object->fetch_optionals() > 0) {
9137 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9138 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9139 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9140 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9141 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9142 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9143 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9144 $datetime = $object->array_options['options_'.$key];
9145 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9146 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9147 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9148 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9149 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9150 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9151 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9152 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9153 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9154 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9155 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9156 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9157 }
9158 }
9159 }
9160 }
9161 }
9162
9163 // Complete substitution array with the url to make online payment
9164 if (empty($substitutionarray['__REF__'])) {
9165 $paymenturl = '';
9166 } else {
9167 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9168 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9169 $outputlangs->loadLangs(array('paypal', 'other'));
9170
9171 $amounttouse = 0;
9172 $typeforonlinepayment = 'free';
9173 if (is_object($object) && $object->element == 'commande') {
9174 $typeforonlinepayment = 'order';
9175 }
9176 if (is_object($object) && $object->element == 'facture') {
9177 $typeforonlinepayment = 'invoice';
9178 }
9179 if (is_object($object) && $object->element == 'member') {
9180 $typeforonlinepayment = 'member';
9181 if (!empty($object->last_subscription_amount)) {
9182 $amounttouse = $object->last_subscription_amount;
9183 }
9184 }
9185 if (is_object($object) && $object->element == 'contrat') {
9186 $typeforonlinepayment = 'contract';
9187 }
9188 if (is_object($object) && $object->element == 'fichinter') {
9189 $typeforonlinepayment = 'ficheinter';
9190 }
9191
9192 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9193 $paymenturl = $url;
9194 }
9195
9196 if ($object->id > 0) {
9197 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9198 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9199
9200 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9201 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9202 } else {
9203 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9204 }
9205 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9206 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9207 } else {
9208 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9209 }
9210 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9211 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9212 } else {
9213 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9214 }
9215 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9216 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9217 } else {
9218 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9219 }
9220 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9221 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9222 } else {
9223 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9224 }
9225 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9226 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9227 } else {
9228 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9229 }
9230
9231 if (is_object($object) && $object->element == 'propal') {
9232 '@phan-var-force Propal $object';
9233 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9234 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9235 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9236 }
9237 if (is_object($object) && $object->element == 'commande') {
9238 '@phan-var-force Commande $object';
9239 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9240 }
9241 if (is_object($object) && $object->element == 'facture') {
9242 '@phan-var-force Facture $object';
9243 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9244 }
9245 if (is_object($object) && $object->element == 'contrat') {
9246 '@phan-var-force Contrat $object';
9247 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9248 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9249 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9250 }
9251 if (is_object($object) && $object->element == 'fichinter') {
9252 '@phan-var-force Fichinter $object';
9253 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9254 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9255 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9256 }
9257 if (is_object($object) && $object->element == 'supplier_proposal') {
9258 '@phan-var-force SupplierProposal $object';
9259 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9260 }
9261 if (is_object($object) && $object->element == 'invoice_supplier') {
9262 '@phan-var-force FactureFournisseur $object';
9263 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9264 }
9265 if (is_object($object) && $object->element == 'shipping') {
9266 '@phan-var-force Expedition $object';
9267 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9268 }
9269 }
9270
9271 if (is_object($object) && $object->element == 'action') {
9272 '@phan-var-force ActionComm $object';
9273 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9274 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9275 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9276 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9277 }
9278 }
9279 }
9280 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9281 '@phan-var-force Facture|FactureRec $object';
9282 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9283
9284 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
9285 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
9286 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', 0, $outputlangs) : null) : '';
9287 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', 0, $outputlangs) : null) : '';
9288
9289 $already_payed_all = 0;
9290 if (is_object($object) && ($object instanceof Facture)) {
9291 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9292 }
9293
9294 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9295 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9296 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9297
9298 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9299 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9300 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9301
9302 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9303
9304 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9305 $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)) : '';
9306 $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)) : '';
9307
9308 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9309 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9310 }
9311 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9312 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9313 }
9314
9315 // Amount keys formatted in a currency
9316 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9317 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9318 $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) : '';
9319 $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)) : '';
9320 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9321 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9322 }
9323 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9324 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9325 }
9326 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9327 if ($onlykey != 2) {
9328 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9329 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9330 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9331 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9332 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9333 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9334 }
9335 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9336 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9337 }
9338 }
9339
9340 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9341 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9342 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9343 // TODO Add other keys for foreign multicurrency
9344
9345 // For backward compatibility
9346 if ($onlykey != 2) {
9347 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9348 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9349 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9350 }
9351 }
9352
9353
9354 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9355 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9356
9357 $now = dol_now();
9358
9359 $tmp = dol_getdate($now, true);
9360 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9361 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9362 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9363 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9364
9365 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9366
9367 $substitutionarray = array_merge($substitutionarray, array(
9368 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9369 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9370 '__DAY__' => (string) $tmp['mday'],
9371 '__DAY_TEXT__' => $daytext, // Monday
9372 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9373 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9374 '__MONTH__' => (string) $tmp['mon'],
9375 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9376 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9377 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9378 '__YEAR__' => (string) $tmp['year'],
9379 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9380 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9381 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9382 '__NEXT_DAY__' => (string) $tmp4['day'],
9383 '__NEXT_MONTH__' => (string) $tmp5['month'],
9384 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9385 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9386 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9387 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9388 ));
9389 }
9390
9391 if (isModEnabled('multicompany')) {
9392 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9393 }
9394 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9395 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9396 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9397 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9398 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9399 }
9400
9401 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9402
9403 return $substitutionarray;
9404}
9405
9422function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9423{
9424 global $conf, $db, $langs;
9425
9426 if (!is_array($substitutionarray)) {
9427 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9428 }
9429
9430 if (empty($outputlangs)) {
9431 $outputlangs = $langs;
9432 }
9433
9434 // Is initial text HTML or simple text ?
9435 $msgishtml = 0;
9436 if (dol_textishtml($text, 1)) {
9437 $msgishtml = 1;
9438 }
9439
9440 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9441 if (is_object($outputlangs)) {
9442 $reg = array();
9443 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9444 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9445 $tmp = explode('|', $reg[1]);
9446 if (!empty($tmp[1])) {
9447 $outputlangs->load($tmp[1]);
9448 }
9449
9450 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9451
9452 if (empty($converttextinhtmlifnecessary)) {
9453 // convert $newval into HTML is necessary
9454 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9455 } else {
9456 if (! $msgishtml) {
9457 $valueishtml = dol_textishtml($value, 1);
9458 //var_dump("valueishtml=".$valueishtml);
9459
9460 if ($valueishtml) {
9461 $text = dol_htmlentitiesbr($text);
9462 $msgishtml = 1;
9463 }
9464 } else {
9465 $value = dol_nl2br("$value");
9466 }
9467
9468 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9469 }
9470 }
9471 }
9472
9473 // Make substitution for constant keys.
9474 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9475 $reg = array();
9476 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9477 $keyfound = $reg[1];
9478 if (isASecretKey($keyfound)) {
9479 $value = '*****forbidden*****';
9480 } else {
9481 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9482 }
9483
9484 if (empty($converttextinhtmlifnecessary)) {
9485 // convert $newval into HTML is necessary
9486 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9487 } else {
9488 if (! $msgishtml) {
9489 $valueishtml = dol_textishtml($value, 1);
9490
9491 if ($valueishtml) {
9492 $text = dol_htmlentitiesbr($text);
9493 $msgishtml = 1;
9494 }
9495 } else {
9496 $value = dol_nl2br("$value");
9497 }
9498
9499 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9500 }
9501 }
9502
9503 // Make substitution for array $substitutionarray
9504 foreach ($substitutionarray as $key => $value) {
9505 if (!isset($value)) {
9506 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9507 }
9508
9509 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9510 $value = ''; // Protection
9511 }
9512
9513 if (empty($converttextinhtmlifnecessary)) {
9514 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9515 } else {
9516 if (! $msgishtml) {
9517 $valueishtml = dol_textishtml($value, 1);
9518
9519 if ($valueishtml) {
9520 $text = dol_htmlentitiesbr($text);
9521 $msgishtml = 1;
9522 }
9523 } else {
9524 $value = dol_nl2br("$value");
9525 }
9526 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9527 }
9528 }
9529
9530 // TODO Implement the lazyload substitution
9531 /*
9532 add a loop to scan $substitutionarray:
9533 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.
9534 If no, we don't need to make replacement, so we do nothing.
9535 If yes, we can make the substitution:
9536
9537 include_once $path;
9538 $tmpobj = new $class($db);
9539 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9540 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9541 */
9542 $memory_object_list = array();
9543 foreach ($substitutionarray as $key => $value) {
9544 $lazy_load_arr = array();
9545 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9546 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9547 $key_to_substitute = $lazy_load_arr[1];
9548 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9549 $param_arr = explode(':', $value);
9550 // path:class:method:id
9551 if (count($param_arr) == 4) {
9552 $path = $param_arr[0];
9553 $class = $param_arr[1];
9554 $method = $param_arr[2];
9555 $id = (int) $param_arr[3];
9556
9557 // load class file and init object list in memory
9558 if (!isset($memory_object_list[$class])) {
9559 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9560 require_once DOL_DOCUMENT_ROOT . $path;
9561 if (class_exists($class)) {
9562 $memory_object_list[$class] = array(
9563 'list' => array(),
9564 );
9565 }
9566 }
9567 }
9568
9569 // fetch object and set substitution
9570 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9571 if (method_exists($class, $method)) {
9572 if (!isset($memory_object_list[$class]['list'][$id])) {
9573 $tmpobj = new $class($db);
9574 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9575 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9576 $memory_object_list[$class]['list'][$id] = $tmpobj;
9577 } else {
9578 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9579 $tmpobj = $memory_object_list[$class]['list'][$id];
9580 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9581 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9582 }
9583
9584 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9585 }
9586 }
9587 }
9588 }
9589 }
9590 }
9591 }
9592
9593 return $text;
9594}
9595
9608function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9609{
9610 global $conf, $user;
9611
9612 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9613
9614 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9615
9616 // Check if there is external substitution to do, requested by plugins
9617 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9618
9619 foreach ($dirsubstitutions as $reldir) {
9620 $dir = dol_buildpath($reldir, 0);
9621
9622 // Check if directory exists
9623 if (!dol_is_dir($dir)) {
9624 continue;
9625 }
9626
9627 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9628 foreach ($substitfiles as $substitfile) {
9629 $reg = array();
9630 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9631 $module = $reg[1];
9632
9633 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9634 // Include the user's functions file
9635 require_once $dir.$substitfile['name'];
9636 // Call the user's function, and only if it is defined
9637 $function_name = $module."_".$callfunc;
9638 if (function_exists($function_name)) {
9639 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9640 }
9641 }
9642 }
9643 }
9644 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9645 // to list all tags in odt template
9646 $tags = '';
9647 foreach ($substitutionarray as $key => $value) {
9648 $tags .= '{'.$key.'} => '.$value."\n";
9649 }
9650 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9651 }
9652}
9653
9663function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9664{
9665 print get_date_range($date_start, $date_end, $format, $outputlangs);
9666}
9667
9678function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9679{
9680 global $langs;
9681
9682 $out = '';
9683
9684 if (!is_object($outputlangs)) {
9685 $outputlangs = $langs;
9686 }
9687
9688 if ($date_start && $date_end) {
9689 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9690 }
9691 if ($date_start && !$date_end) {
9692 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9693 }
9694 if (!$date_start && $date_end) {
9695 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9696 }
9697
9698 return $out;
9699}
9700
9709function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9710{
9711 global $conf;
9712
9713 $ret = '';
9714 // If order not defined, we use the setup
9715 if ($nameorder < 0) {
9716 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9717 }
9718 if ($nameorder == 1) {
9719 $ret .= $firstname;
9720 if ($firstname && $lastname) {
9721 $ret .= ' ';
9722 }
9723 $ret .= $lastname;
9724 } elseif ($nameorder == 2 || $nameorder == 3) {
9725 $ret .= $firstname;
9726 if (empty($ret) && $nameorder == 3) {
9727 $ret .= $lastname;
9728 }
9729 } else { // 0, 4 or 5
9730 $ret .= $lastname;
9731 if (empty($ret) && $nameorder == 5) {
9732 $ret .= $firstname;
9733 }
9734 if ($nameorder == 0) {
9735 if ($firstname && $lastname) {
9736 $ret .= ' ';
9737 }
9738 $ret .= $firstname;
9739 }
9740 }
9741 return $ret;
9742}
9743
9744
9756function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
9757{
9758 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9759 if (!is_array($mesgs)) {
9760 $mesgs = trim((string) $mesgs);
9761 // If mesgs is a not an empty string
9762 if ($mesgs) {
9763 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9764 return;
9765 }
9766 $_SESSION['dol_events'][$style][] = $mesgs;
9767 }
9768 } else {
9769 // If mesgs is an array
9770 foreach ($mesgs as $mesg) {
9771 $mesg = trim((string) $mesg);
9772 if ($mesg) {
9773 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9774 return;
9775 }
9776 $_SESSION['dol_events'][$style][] = $mesg;
9777 }
9778 }
9779 }
9780}
9781
9794function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
9795{
9796 if (empty($mesg) && empty($mesgs)) {
9797 dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
9798 } else {
9799 if ($messagekey) {
9800 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9801 // TODO
9802 $mesg .= '';
9803 }
9804 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
9805 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9806 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9807 }
9808 if (empty($mesgs)) {
9809 setEventMessage($mesg, $style, $noduplicate);
9810 } else {
9811 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
9812 setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
9813 }
9814 setEventMessage($mesgs, $style, $noduplicate);
9815 }
9816 }
9817 }
9818}
9819
9829function dol_htmloutput_events($disabledoutputofmessages = 0)
9830{
9831 // Show mesgs
9832 if (isset($_SESSION['dol_events']['mesgs'])) {
9833 if (empty($disabledoutputofmessages)) {
9834 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
9835 }
9836 unset($_SESSION['dol_events']['mesgs']);
9837 }
9838 // Show errors
9839 if (isset($_SESSION['dol_events']['errors'])) {
9840 if (empty($disabledoutputofmessages)) {
9841 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
9842 }
9843 unset($_SESSION['dol_events']['errors']);
9844 }
9845
9846 // Show warnings
9847 if (isset($_SESSION['dol_events']['warnings'])) {
9848 if (empty($disabledoutputofmessages)) {
9849 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
9850 }
9851 unset($_SESSION['dol_events']['warnings']);
9852 }
9853}
9854
9869function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
9870{
9871 global $conf, $langs;
9872
9873 $ret = 0;
9874 $return = '';
9875 $out = '';
9876 $divstart = $divend = '';
9877
9878 // If inline message with no format, we add it.
9879 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
9880 $divstart = '<div class="'.$style.' clearboth">';
9881 $divend = '</div>';
9882 }
9883
9884 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
9885 $langs->load("errors");
9886 $out .= $divstart;
9887 if (is_array($mesgarray) && count($mesgarray)) {
9888 foreach ($mesgarray as $message) {
9889 $ret++;
9890 $out .= $langs->trans($message);
9891 if ($ret < count($mesgarray)) {
9892 $out .= "<br>\n";
9893 }
9894 }
9895 }
9896 if ($mesgstring) {
9897 $ret++;
9898 $out .= $langs->trans($mesgstring);
9899 }
9900 $out .= $divend;
9901 }
9902
9903 if ($out) {
9904 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
9905 $return = '<script nonce="'.getNonce().'">
9906 $(document).ready(function() {
9907 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
9908 if (block) {
9909 $.dolEventValid("","'.dol_escape_js($out).'");
9910 } else {
9911 /* jnotify(message, preset of message type, keepmessage) */
9912 $.jnotify("'.dol_escape_js($out).'",
9913 "'.($style == "ok" ? 3000 : $style).'",
9914 '.($style == "ok" ? "false" : "true").',
9915 { remove: function (){} } );
9916 }
9917 });
9918 </script>';
9919 } else {
9920 $return = $out;
9921 }
9922 }
9923
9924 return $return;
9925}
9926
9938function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
9939{
9940 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
9941}
9942
9956function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
9957{
9958 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
9959 return;
9960 }
9961
9962 $iserror = 0;
9963 $iswarning = 0;
9964 if (is_array($mesgarray)) {
9965 foreach ($mesgarray as $val) {
9966 if ($val && preg_match('/class="error"/i', $val)) {
9967 $iserror++;
9968 break;
9969 }
9970 if ($val && preg_match('/class="warning"/i', $val)) {
9971 $iswarning++;
9972 break;
9973 }
9974 }
9975 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
9976 $iserror++;
9977 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
9978 $iswarning++;
9979 }
9980 if ($style == 'error') {
9981 $iserror++;
9982 }
9983 if ($style == 'warning') {
9984 $iswarning++;
9985 }
9986
9987 if ($iserror || $iswarning) {
9988 // Remove div from texts
9989 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
9990 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
9991 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
9992 // Remove div from texts array
9993 if (is_array($mesgarray)) {
9994 $newmesgarray = array();
9995 foreach ($mesgarray as $val) {
9996 if (is_string($val)) {
9997 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
9998 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
9999 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10000 $newmesgarray[] = $tmpmesgstring;
10001 } else {
10002 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10003 }
10004 }
10005 $mesgarray = $newmesgarray;
10006 }
10007 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10008 } else {
10009 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10010 }
10011}
10012
10024function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10025{
10026 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10027}
10028
10042function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10043{
10044 // Clean parameters
10045 $order = strtolower($order);
10046
10047 if (is_array($array)) {
10048 $sizearray = count($array);
10049 if ($sizearray > 0) {
10050 $temp = array();
10051 foreach (array_keys($array) as $key) {
10052 if (is_object($array[$key])) {
10053 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10054 } else {
10055 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10056 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10057 }
10058 if ($natsort == -1) {
10059 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10060 }
10061 }
10062
10063 if (empty($natsort) || $natsort == -1) {
10064 if ($order == 'asc') {
10065 asort($temp);
10066 } else {
10067 arsort($temp);
10068 }
10069 } else {
10070 if ($case_sensitive) {
10071 natsort($temp);
10072 } else {
10073 natcasesort($temp); // natecasesort is not sensible to case
10074 }
10075 if ($order != 'asc') {
10076 $temp = array_reverse($temp, true);
10077 }
10078 }
10079
10080 $sorted = array();
10081
10082 foreach (array_keys($temp) as $key) {
10083 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10084 }
10085
10086 return $sorted;
10087 }
10088 }
10089 return $array;
10090}
10091
10092
10100function utf8_check($str)
10101{
10102 $str = (string) $str; // Sometimes string is an int.
10103
10104 // We must use here a binary strlen function (so not dol_strlen)
10105 $strLength = strlen($str);
10106 for ($i = 0; $i < $strLength; $i++) {
10107 if (ord($str[$i]) < 0x80) {
10108 continue; // 0bbbbbbb
10109 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10110 $n = 1; // 110bbbbb
10111 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10112 $n = 2; // 1110bbbb
10113 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10114 $n = 3; // 11110bbb
10115 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10116 $n = 4; // 111110bb
10117 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10118 $n = 5; // 1111110b
10119 } else {
10120 return false; // Does not match any model
10121 }
10122 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10123 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10124 return false;
10125 }
10126 }
10127 }
10128 return true;
10129}
10130
10138function utf8_valid($str)
10139{
10140 /* 2 other methods to test if string is utf8
10141 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10142 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10143 */
10144 return preg_match('//u', $str) ? true : false;
10145}
10146
10147
10154function ascii_check($str)
10155{
10156 if (function_exists('mb_check_encoding')) {
10157 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10158 if (!mb_check_encoding($str, 'ASCII')) {
10159 return false;
10160 }
10161 } else {
10162 if (preg_match('/[^\x00-\x7f]/', $str)) {
10163 return false; // Contains a byte > 7f
10164 }
10165 }
10166
10167 return true;
10168}
10169
10170
10178function dol_osencode($str)
10179{
10180 $tmp = ini_get("unicode.filesystem_encoding");
10181 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10182 $tmp = 'iso-8859-1'; // By default for windows
10183 }
10184 if (empty($tmp)) {
10185 $tmp = 'utf-8'; // By default for other
10186 }
10187 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10188 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10189 }
10190
10191 if ($tmp == 'iso-8859-1') {
10192 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10193 }
10194 return $str;
10195}
10196
10197
10213function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10214{
10215 global $conf;
10216
10217 // If key empty
10218 if ($key == '') {
10219 return 0;
10220 }
10221
10222 // Check in cache
10223 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10224 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10225 }
10226
10227 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10228
10229 $sql = "SELECT ".$fieldid." as valuetoget";
10230 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10231 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10232 if (!empty($entityfilter)) {
10233 $sql .= " AND entity IN (".getEntity($tablename).")";
10234 }
10235 if ($filters) {
10236 $sql .= $filters;
10237 }
10238
10239 $resql = $db->query($sql);
10240 if ($resql) {
10241 $obj = $db->fetch_object($resql);
10242 $valuetoget = '';
10243 if ($obj) {
10244 $valuetoget = $obj->valuetoget;
10245 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10246 } else {
10247 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10248 }
10249 $db->free($resql);
10250
10251 return $valuetoget;
10252 } else {
10253 return -1;
10254 }
10255}
10256
10266function isStringVarMatching($var, $regextext, $matchrule = 1)
10267{
10268 if ($matchrule == 1) {
10269 if ($var == 'mainmenu') {
10270 global $mainmenu;
10271 return (preg_match('/^'.$regextext.'/', $mainmenu));
10272 } elseif ($var == 'leftmenu') {
10273 global $leftmenu;
10274 return (preg_match('/^'.$regextext.'/', $leftmenu));
10275 } else {
10276 return 'This variable is not accessible with dol_eval';
10277 }
10278 } else {
10279 return 'This value for matchrule is not implemented';
10280 }
10281}
10282
10283
10293function verifCond($strToEvaluate, $onlysimplestring = '1')
10294{
10295 //print $strToEvaluate."<br>\n";
10296 $rights = true;
10297 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10298 //var_dump($strToEvaluate);
10299 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10300 $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
10301 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10302 //var_dump($rights);
10303 }
10304 return $rights;
10305}
10306
10321function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10322{
10323 // Only this global variables can be read by eval function and returned to caller
10324 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10325 global $db, $langs, $user, $website, $websitepage;
10326 global $action, $mainmenu, $leftmenu;
10327 global $mysoc;
10328 global $objectoffield; // To allow the use of $objectoffield in computed fields
10329
10330 // Old variables used
10331 global $object;
10332 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10333
10334 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10335 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10336 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10337 }
10338
10339 try {
10340 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10341 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10342 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10343 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10344 // 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"
10345
10346 // Check if there is dynamic call (first we check chars are all into use a whitelist chars)
10347 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10348 if ($onlysimplestring == '2') {
10349 $specialcharsallowed .= '[]';
10350 }
10351 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10352 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10353 }
10354 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10355 if ($returnvalue) {
10356 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10357 } else {
10358 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING);
10359 return '';
10360 }
10361 }
10362
10363 // Check if there is dynamic call (first we use black list patterns)
10364 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10365 if ($returnvalue) {
10366 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;
10367 } else {
10368 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);
10369 return '';
10370 }
10371 }
10372
10373 // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists)
10374 $savescheck = '';
10375 $scheck = $s;
10376 while ($scheck && $savescheck != $scheck) {
10377 $savescheck = $scheck;
10378 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10379 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10380 $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
10381 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10382 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10383 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10384 }
10385 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10386 if (strpos($scheck, '(') !== false) {
10387 if ($returnvalue) {
10388 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10389 } else {
10390 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);
10391 return '';
10392 }
10393 }
10394
10395 // TODO
10396 // We can exclude $ char that are not:
10397 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10398 }
10399 if (is_array($s) || $s === 'Array') {
10400 if ($returnvalue) {
10401 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10402 } else {
10403 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10404 return '';
10405 }
10406 }
10407 if (strpos($s, '::') !== false) {
10408 if ($returnvalue) {
10409 return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
10410 } else {
10411 dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s, LOG_WARNING);
10412 return '';
10413 }
10414 }
10415 if (strpos($s, '`') !== false) {
10416 if ($returnvalue) {
10417 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10418 } else {
10419 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10420 return '';
10421 }
10422 }
10423 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10424 if ($returnvalue) {
10425 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10426 } else {
10427 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10428 return '';
10429 }
10430 }
10431
10432 // We block use of php exec or php file functions
10433 $forbiddenphpstrings = array('$$', '$_', '}[');
10434 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10435
10436 $forbiddenphpfunctions = array();
10437 // @phpcs:ignore
10438 $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
10439 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10440 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10441 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10442 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
10443 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10444 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10445 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10446 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
10447 $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
10448
10449 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10450
10451 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10452
10453 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10454
10455 do {
10456 $oldstringtoclean = $s;
10457 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10458 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10459 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10460 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10461 } while ($oldstringtoclean != $s);
10462
10463
10464 if (strpos($s, '__forbiddenstring__') !== false) {
10465 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10466 if ($returnvalue) {
10467 return 'Bad string syntax to evaluate: '.$s;
10468 } else {
10469 dol_syslog('Bad string syntax to evaluate: '.$s);
10470 return '';
10471 }
10472 }
10473
10474 //print $s."<br>\n";
10475 if ($returnvalue) {
10476 if ($hideerrors) {
10477 ob_start(); // An evaluation has no reason to output data
10478 $isObBufferActive = true;
10479 $tmps = @eval('return '.$s.';');
10480 $tmpo = ob_get_clean();
10481 $isObBufferActive = false;
10482 if ($tmpo) {
10483 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10484 }
10485 return $tmps;
10486 } else {
10487 ob_start(); // An evaluation has no reason to output data
10488 $isObBufferActive = true;
10489 $tmps = eval('return '.$s.';');
10490 $tmpo = ob_get_clean();
10491 $isObBufferActive = false;
10492 if ($tmpo) {
10493 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10494 }
10495 return $tmps;
10496 }
10497 } else {
10498 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10499 if ($hideerrors) {
10500 @eval($s);
10501 } else {
10502 eval($s);
10503 }
10504 return '';
10505 }
10506 } catch (Error $e) {
10507 if ($isObBufferActive) {
10508 // Clean up buffer which was left behind due to exception.
10509 $tmpo = ob_get_clean();
10510 $isObBufferActive = false;
10511 }
10512 $error = 'dol_eval try/catch error : ';
10513 $error .= $e->getMessage();
10514 dol_syslog($error, LOG_WARNING);
10515 if ($returnvalue) {
10516 return 'Exception during evaluation: '.$s;
10517 } else {
10518 return '';
10519 }
10520 }
10521}
10522
10530function dol_validElement($element)
10531{
10532 return (trim($element) != '');
10533}
10534
10543function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10544{
10545 if (empty($codelang)) {
10546 return '';
10547 }
10548
10549 if ($codelang == 'auto') {
10550 return '<span class="fa fa-language"></span>';
10551 }
10552
10553 $langtocountryflag = array(
10554 'ar_AR' => '',
10555 'ca_ES' => 'catalonia',
10556 'da_DA' => 'dk',
10557 'fr_CA' => 'mq',
10558 'sv_SV' => 'se',
10559 'sw_SW' => 'unknown',
10560 'AQ' => 'unknown',
10561 'CW' => 'unknown',
10562 'IM' => 'unknown',
10563 'JE' => 'unknown',
10564 'MF' => 'unknown',
10565 'BL' => 'unknown',
10566 'SX' => 'unknown'
10567 );
10568
10569 if (isset($langtocountryflag[$codelang])) {
10570 $flagImage = $langtocountryflag[$codelang];
10571 } else {
10572 $tmparray = explode('_', $codelang);
10573 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10574 }
10575
10576 $morecss = '';
10577 $reg = array();
10578 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10579 $morecss = $reg[1];
10580 $moreatt = "";
10581 }
10582
10583 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10584 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10585}
10586
10594function getLanguageCodeFromCountryCode($countrycode)
10595{
10596 global $mysoc;
10597
10598 if (empty($countrycode)) {
10599 return null;
10600 }
10601
10602 if (strtoupper($countrycode) == 'MQ') {
10603 return 'fr_CA';
10604 }
10605 if (strtoupper($countrycode) == 'SE') {
10606 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10607 }
10608 if (strtoupper($countrycode) == 'CH') {
10609 if ($mysoc->country_code == 'FR') {
10610 return 'fr_CH';
10611 }
10612 if ($mysoc->country_code == 'DE') {
10613 return 'de_CH';
10614 }
10615 if ($mysoc->country_code == 'IT') {
10616 return 'it_CH';
10617 }
10618 }
10619
10620 // Locale list taken from:
10621 // http://stackoverflow.com/questions/3191664/
10622 // list-of-all-locales-and-their-short-codes
10623 $locales = array(
10624 'af-ZA',
10625 'am-ET',
10626 'ar-AE',
10627 'ar-BH',
10628 'ar-DZ',
10629 'ar-EG',
10630 'ar-IQ',
10631 'ar-JO',
10632 'ar-KW',
10633 'ar-LB',
10634 'ar-LY',
10635 'ar-MA',
10636 'ar-OM',
10637 'ar-QA',
10638 'ar-SA',
10639 'ar-SY',
10640 'ar-TN',
10641 'ar-YE',
10642 //'as-IN', // Moved after en-IN
10643 'ba-RU',
10644 'be-BY',
10645 'bg-BG',
10646 'bn-BD',
10647 //'bn-IN', // Moved after en-IN
10648 'bo-CN',
10649 'br-FR',
10650 'ca-ES',
10651 'co-FR',
10652 'cs-CZ',
10653 'cy-GB',
10654 'da-DK',
10655 'de-AT',
10656 'de-CH',
10657 'de-DE',
10658 'de-LI',
10659 'de-LU',
10660 'dv-MV',
10661 'el-GR',
10662 'en-AU',
10663 'en-BZ',
10664 'en-CA',
10665 'en-GB',
10666 'en-IE',
10667 'en-IN',
10668 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10669 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10670 'en-JM',
10671 'en-MY',
10672 'en-NZ',
10673 'en-PH',
10674 'en-SG',
10675 'en-TT',
10676 'en-US',
10677 'en-ZA',
10678 'en-ZW',
10679 'es-AR',
10680 'es-BO',
10681 'es-CL',
10682 'es-CO',
10683 'es-CR',
10684 'es-DO',
10685 'es-EC',
10686 'es-ES',
10687 'es-GT',
10688 'es-HN',
10689 'es-MX',
10690 'es-NI',
10691 'es-PA',
10692 'es-PE',
10693 'es-PR',
10694 'es-PY',
10695 'es-SV',
10696 'es-US',
10697 'es-UY',
10698 'es-VE',
10699 'et-EE',
10700 'eu-ES',
10701 'fa-IR',
10702 'fi-FI',
10703 'fo-FO',
10704 'fr-BE',
10705 'fr-CA',
10706 'fr-CH',
10707 'fr-FR',
10708 'fr-LU',
10709 'fr-MC',
10710 'fy-NL',
10711 'ga-IE',
10712 'gd-GB',
10713 'gl-ES',
10714 'gu-IN',
10715 'he-IL',
10716 'hi-IN',
10717 'hr-BA',
10718 'hr-HR',
10719 'hu-HU',
10720 'hy-AM',
10721 'id-ID',
10722 'ig-NG',
10723 'ii-CN',
10724 'is-IS',
10725 'it-CH',
10726 'it-IT',
10727 'ja-JP',
10728 'ka-GE',
10729 'kk-KZ',
10730 'kl-GL',
10731 'km-KH',
10732 'kn-IN',
10733 'ko-KR',
10734 'ky-KG',
10735 'lb-LU',
10736 'lo-LA',
10737 'lt-LT',
10738 'lv-LV',
10739 'mi-NZ',
10740 'mk-MK',
10741 'ml-IN',
10742 'mn-MN',
10743 'mr-IN',
10744 'ms-BN',
10745 'ms-MY',
10746 'mt-MT',
10747 'nb-NO',
10748 'ne-NP',
10749 'nl-BE',
10750 'nl-NL',
10751 'nn-NO',
10752 'oc-FR',
10753 'or-IN',
10754 'pa-IN',
10755 'pl-PL',
10756 'ps-AF',
10757 'pt-BR',
10758 'pt-PT',
10759 'rm-CH',
10760 'ro-MD',
10761 'ro-RO',
10762 'ru-RU',
10763 'rw-RW',
10764 'sa-IN',
10765 'se-FI',
10766 'se-NO',
10767 'se-SE',
10768 'si-LK',
10769 'sk-SK',
10770 'sl-SI',
10771 'sq-AL',
10772 'sv-FI',
10773 'sv-SE',
10774 'sw-KE',
10775 'ta-IN',
10776 'te-IN',
10777 'th-TH',
10778 'tk-TM',
10779 'tn-ZA',
10780 'tr-TR',
10781 'tt-RU',
10782 'ug-CN',
10783 'uk-UA',
10784 'ur-PK',
10785 'vi-VN',
10786 'wo-SN',
10787 'xh-ZA',
10788 'yo-NG',
10789 'zh-CN',
10790 'zh-HK',
10791 'zh-MO',
10792 'zh-SG',
10793 'zh-TW',
10794 'zu-ZA',
10795 );
10796
10797 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10798 if (in_array($buildprimarykeytotest, $locales)) {
10799 return strtolower($countrycode).'_'.strtoupper($countrycode);
10800 }
10801
10802 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10803 foreach ($locales as $locale) {
10804 $locale_language = locale_get_primary_language($locale);
10805 $locale_region = locale_get_region($locale);
10806 if (strtoupper($countrycode) == $locale_region) {
10807 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10808 return strtolower($locale_language).'_'.strtoupper($locale_region);
10809 }
10810 }
10811 } else {
10812 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10813 }
10814
10815 return null;
10816}
10817
10848function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
10849{
10850 global $hookmanager, $db;
10851
10852 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
10853 foreach ($conf->modules_parts['tabs'][$type] as $value) {
10854 $values = explode(':', $value);
10855
10856 $reg = array();
10857 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
10858 $newtab = array();
10859 $postab = $h;
10860 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
10861 $str = $values[1];
10862 $posstart = strpos($str, '(');
10863 if ($posstart > 0) {
10864 $posend = strpos($str, ')');
10865 if ($posstart > 0) {
10866 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
10867 if (is_numeric($res1)) {
10868 $postab = (int) $res1;
10869 $values[1] = '+' . substr($str, $posend + 1);
10870 }
10871 }
10872 }
10873 if (count($values) == 6) {
10874 // new declaration with permissions:
10875 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10876 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10877 if ($values[0] != $type) {
10878 continue;
10879 }
10880
10881 if (verifCond($values[4], '2')) {
10882 if ($values[3]) {
10883 if ($filterorigmodule) { // If a filter of module origin has been requested
10884 if (strpos($values[3], '@')) { // This is an external module
10885 if ($filterorigmodule != 'external') {
10886 continue;
10887 }
10888 } else { // This looks a core module
10889 if ($filterorigmodule != 'core') {
10890 continue;
10891 }
10892 }
10893 }
10894 $langs->load($values[3]);
10895 }
10896 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10897 // If label is "SUBSTITUION_..."
10898 $substitutionarray = array();
10899 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10900 $label = make_substitutions($reg[1], $substitutionarray);
10901 } else {
10902 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
10903 $labeltemp = explode(',', $values[2]);
10904 $label = $langs->trans($labeltemp[0]);
10905
10906 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
10907 dol_include_once($labeltemp[2]);
10908 $classtoload = $labeltemp[1];
10909 if (class_exists($classtoload)) {
10910 $obj = new $classtoload($db);
10911 $function = $labeltemp[3];
10912 if ($obj && $function && method_exists($obj, $function)) {
10913 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10914 $nbrec = $obj->$function($object->id, $obj);
10915 if (!empty($nbrec)) {
10916 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
10917 }
10918 }
10919 }
10920 }
10921 }
10922
10923 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
10924 $newtab[1] = $label;
10925 $newtab[2] = str_replace('+', '', $values[1]);
10926 $h++;
10927 } else {
10928 continue;
10929 }
10930 } elseif (count($values) == 5) { // case deprecated
10931 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
10932
10933 if ($values[0] != $type) {
10934 continue;
10935 }
10936 if ($values[3]) {
10937 if ($filterorigmodule) { // If a filter of module origin has been requested
10938 if (strpos($values[3], '@')) { // This is an external module
10939 if ($filterorigmodule != 'external') {
10940 continue;
10941 }
10942 } else { // This looks a core module
10943 if ($filterorigmodule != 'core') {
10944 continue;
10945 }
10946 }
10947 }
10948 $langs->load($values[3]);
10949 }
10950 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10951 $substitutionarray = array();
10952 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10953 $label = make_substitutions($reg[1], $substitutionarray);
10954 } else {
10955 $label = $langs->trans($values[2]);
10956 }
10957
10958 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
10959 $newtab[1] = $label;
10960 $newtab[2] = str_replace('+', '', $values[1]);
10961 $h++;
10962 }
10963 // set tab at its position
10964 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
10965 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
10966 if ($values[0] != $type) {
10967 continue;
10968 }
10969 $tabname = str_replace('-', '', $values[1]);
10970 foreach ($head as $key => $val) {
10971 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
10972 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
10973 if ($head[$key][2] == $tabname && $condition) {
10974 unset($head[$key]);
10975 break;
10976 }
10977 }
10978 }
10979 }
10980 }
10981
10982 // No need to make a return $head. Var is modified as a reference
10983 if (!empty($hookmanager)) {
10984 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
10985 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
10986 if ($reshook > 0) { // Hook ask to replace completely the array
10987 $head = $hookmanager->resArray;
10988 } else { // Hook
10989 $head = array_merge($head, $hookmanager->resArray);
10990 }
10991 $h = count($head);
10992 }
10993}
10994
11006function printCommonFooter($zone = 'private')
11007{
11008 global $conf, $hookmanager, $user, $langs;
11009 global $debugbar;
11010 global $action;
11011 global $micro_start_time;
11012
11013 if ($zone == 'private') {
11014 print "\n".'<!-- Common footer for private page -->'."\n";
11015 } else {
11016 print "\n".'<!-- Common footer for public page -->'."\n";
11017 }
11018
11019 // A div to store page_y POST parameter so we can read it using javascript
11020 print "\n<!-- A div to store page_y POST parameter -->\n";
11021 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11022
11023 $parameters = array();
11024 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11025 if (empty($reshook)) {
11026 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11027 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11028 }
11029
11030 print "\n";
11031 if (!empty($conf->use_javascript_ajax)) {
11032 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11033 print '<script>'."\n";
11034 print 'jQuery(document).ready(function() {'."\n";
11035
11036 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11037 print "\n";
11038 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11039 print 'jQuery("li.menuhider").click(function(event) {';
11040 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11041 print ' console.log("We click on .menuhider");'."\n";
11042 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11043 print '});'."\n";
11044 }
11045
11046 // Management of focus and mandatory for fields
11047 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"])))) {
11048 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11049 $relativepathstring = $_SERVER["PHP_SELF"];
11050 // Clean $relativepathstring
11051 if (constant('DOL_URL_ROOT')) {
11052 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11053 }
11054 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11055 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11056 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11057
11058 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11059 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11060 $qualified = 0;
11061 if ($defkey != '_noquery_') {
11062 $tmpqueryarraytohave = explode('&', $defkey);
11063 $foundintru = 0;
11064 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11065 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11066 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11067 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11068 $foundintru = 1;
11069 }
11070 }
11071 if (!$foundintru) {
11072 $qualified = 1;
11073 }
11074 //var_dump($defkey.'-'.$qualified);
11075 } else {
11076 $qualified = 1;
11077 }
11078
11079 if ($qualified) {
11080 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11081 foreach ($defval as $paramkey => $paramval) {
11082 // Set focus on field
11083 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11084 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11085 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11086 }
11087 }
11088 }
11089 }
11090 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11091 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11092 $qualified = 0;
11093 if ($defkey != '_noquery_') {
11094 $tmpqueryarraytohave = explode('&', $defkey);
11095 $foundintru = 0;
11096 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11097 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11098 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11099 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11100 $foundintru = 1;
11101 }
11102 }
11103 if (!$foundintru) {
11104 $qualified = 1;
11105 }
11106 //var_dump($defkey.'-'.$qualified);
11107 } else {
11108 $qualified = 1;
11109 }
11110
11111 if ($qualified) {
11112 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11113
11114 foreach ($defval as $paramkey => $paramval) {
11115 // Solution 1: Add handler on submit to check if mandatory fields are empty
11116 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11117 print "form.on('submit', function(event) {
11118 var submitter = $(this).find(':submit:focus').get(0);
11119 if (submitter) {
11120 var buttonName = $(submitter).attr('name');
11121 if (buttonName == 'cancel') {
11122 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11123 return true;
11124 }
11125 }
11126
11127 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11128
11129 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11130 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11131
11132 if (tmptypefield == 'textarea') {
11133 // We must instead check the content of ckeditor
11134 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11135 if (tmpeditor) {
11136 tmpvalue = tmpeditor.getData();
11137 console.log('For textarea tmpvalue is '+tmpvalue);
11138 }
11139 }
11140
11141 let tmpvalueisempty = false;
11142 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '') {
11143 tmpvalueisempty = true;
11144 }
11145 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
11146 tmpvalueisempty = true;
11147 }
11148 if (tmpvalueisempty && (buttonName == 'save')) {
11149 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11150 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11151 event.stopPropagation(); // Stop other handlers.
11152 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11153 return false;
11154 }
11155 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11156 return true;
11157 });
11158 \n";
11159
11160 // Solution 2: Add property 'required' on input
11161 // so browser will check value and try to focus on it when submitting the form.
11162 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11163 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11164 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11165 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11166 //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";
11167 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11168 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11169 // Add 'field required' class on closest td for all input elements : input, textarea and select
11170 //print '}, 500);'; // 500 milliseconds delay
11171
11172 // Now set the class "fieldrequired"
11173 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11174 }
11175
11176
11177 // If we submit using the cancel button, we remove the required attributes
11178 print 'jQuery("input[name=\'cancel\']").click(function() {
11179 console.log("We click on cancel button so removed all required attribute");
11180 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11181 });'."\n";
11182 }
11183 }
11184 }
11185 }
11186
11187 print '});'."\n";
11188
11189 // End of tuning
11190 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11191 print "\n";
11192 print "/* JS CODE TO ENABLE to add memory info */\n";
11193 print 'window.console && console.log("';
11194 if (getDolGlobalString('MEMCACHED_SERVER')) {
11195 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11196 }
11197 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11198 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11199 $micro_end_time = microtime(true);
11200 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11201 }
11202
11203 if (function_exists("memory_get_usage")) {
11204 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11205 }
11206 if (function_exists("memory_get_peak_usage")) {
11207 print ' - Real mem peak: '.memory_get_peak_usage(true);
11208 }
11209 if (function_exists("zend_loader_file_encoded")) {
11210 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11211 }
11212 print '");'."\n";
11213 }
11214
11215 print "\n".'</script>'."\n";
11216
11217 // Google Analytics
11218 // TODO Add a hook here
11219 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11220 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11221 foreach ($tmptagarray as $tmptag) {
11222 print "\n";
11223 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11224 print '
11225 <!-- Global site tag (gtag.js) - Google Analytics -->
11226 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11227 <script>
11228 window.dataLayer = window.dataLayer || [];
11229 function gtag(){dataLayer.push(arguments);}
11230 gtag(\'js\', new Date());
11231
11232 gtag(\'config\', \''.trim($tmptag).'\');
11233 </script>';
11234 print "\n";
11235 }
11236 }
11237 }
11238
11239 // Add Xdebug coverage of code
11240 if (defined('XDEBUGCOVERAGE')) {
11241 print_r(xdebug_get_code_coverage());
11242 }
11243
11244 // Add DebugBar data
11245 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11246 if (isset($debugbar['time'])) {
11247 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11248 $debugbar['time']->stopMeasure('pageaftermaster');
11249 }
11250 print '<!-- Output debugbar data -->'."\n";
11251 $renderer = $debugbar->getJavascriptRenderer();
11252 print $renderer->render();
11253 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11254 print "\n";
11255 print "<!-- Start of log output\n";
11256 //print '<div class="hidden">'."\n";
11257 foreach ($conf->logbuffer as $logline) {
11258 print $logline."<br>\n";
11259 }
11260 //print '</div>'."\n";
11261 print "End of log output -->\n";
11262 }
11263 }
11264}
11265
11275function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11276{
11277 if (is_null($string)) {
11278 return array();
11279 }
11280
11281 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11282 // This is a regex string
11283 $newdelimiter = $delimiter;
11284 } else {
11285 // This is a simple string
11286 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11287 $newdelimiter = preg_quote($delimiter, '/');
11288 }
11289
11290 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11291 $ka = array();
11292 foreach ($a as $s) { // each part
11293 if ($s) {
11294 if ($pos = strpos($s, $kv)) { // key/value delimiter
11295 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11296 } else { // key delimiter not found
11297 $ka[] = trim($s);
11298 }
11299 }
11300 }
11301 return $ka;
11302 }
11303
11304 return array();
11305}
11306
11307
11314function dol_set_focus($selector)
11315{
11316 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11317 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11318}
11319
11320
11328function dol_getmypid()
11329{
11330 if (!function_exists('getmypid')) {
11331 return mt_rand(99900000, 99965535);
11332 } else {
11333 return getmypid(); // May be a number on 64 bits (depending on OS)
11334 }
11335}
11336
11337
11359function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11360{
11361 global $db, $langs;
11362
11363 $value = trim($value);
11364
11365 if ($mode == 0) {
11366 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11367 }
11368 if ($mode == 1) {
11369 $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
11370 }
11371
11372 $value = preg_replace('/\s*\|\s*/', '|', $value);
11373
11374 $crits = explode(' ', $value);
11375 $res = '';
11376 if (!is_array($fields)) {
11377 $fields = array($fields);
11378 }
11379
11380 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11381 foreach ($crits as $crit) { // Loop on each AND criteria
11382 $crit = trim($crit);
11383 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11384 $newres = '';
11385 foreach ($fields as $field) {
11386 if ($mode == 1) {
11387 $tmpcrits = explode('|', $crit);
11388 $i3 = 0; // count the nb of valid criteria added for this current field
11389 foreach ($tmpcrits as $tmpcrit) {
11390 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11391 continue;
11392 }
11393 $tmpcrit = trim($tmpcrit);
11394
11395 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11396
11397 $operator = '=';
11398 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11399
11400 $reg = array();
11401 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11402 if (!empty($reg[1])) {
11403 $operator = $reg[1];
11404 }
11405 if ($newcrit != '') {
11406 $numnewcrit = price2num($newcrit);
11407 if (is_numeric($numnewcrit)) {
11408 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11409 } else {
11410 $newres .= '1 = 2'; // force false, we received a corrupted data
11411 }
11412 $i3++; // a criteria was added to string
11413 }
11414 }
11415 $i2++; // a criteria for 1 more field was added to string
11416 } elseif ($mode == 2 || $mode == -2) {
11417 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11418 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11419 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11420 if ($mode == -2) {
11421 $newres .= ' OR '.$field.' IS NULL';
11422 }
11423 $i2++; // a criteria for 1 more field was added to string
11424 } elseif ($mode == 3 || $mode == -3) {
11425 $tmparray = explode(',', $crit);
11426 if (count($tmparray)) {
11427 $listofcodes = '';
11428 foreach ($tmparray as $val) {
11429 $val = trim($val);
11430 if ($val) {
11431 $listofcodes .= ($listofcodes ? ',' : '');
11432 $listofcodes .= "'".$db->escape($val)."'";
11433 }
11434 }
11435 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
11436 $i2++; // a criteria for 1 more field was added to string
11437 }
11438 if ($mode == -3) {
11439 $newres .= ' OR '.$field.' IS NULL';
11440 }
11441 } elseif ($mode == 4) {
11442 $tmparray = explode(',', $crit);
11443 if (count($tmparray)) {
11444 $listofcodes = '';
11445 foreach ($tmparray as $val) {
11446 $val = trim($val);
11447 if ($val) {
11448 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11449 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11450 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11451 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11452 $newres .= ')';
11453 $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)
11454 }
11455 }
11456 }
11457 } else { // $mode=0
11458 $tmpcrits = explode('|', $crit);
11459 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11460 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11461 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11462 continue;
11463 }
11464 $tmpcrit = trim($tmpcrit);
11465
11466 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11467 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11468 } else {
11469 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11470 }
11471
11472 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11473 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11474 } else {
11475 $tmpcrit2 = $tmpcrit;
11476 $tmpbefore = '%';
11477 $tmpafter = '%';
11478 $tmps = '';
11479
11480 if (preg_match('/^!/', $tmpcrit)) {
11481 $tmps .= $field." NOT LIKE "; // ! as exclude character
11482 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11483 } else {
11484 $tmps .= $field." LIKE ";
11485 }
11486 $tmps .= "'";
11487
11488 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11489 $tmpbefore = '';
11490 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11491 }
11492 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11493 $tmpafter = '';
11494 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11495 }
11496
11497 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11498 $tmps = "(".$tmps;
11499 }
11500 $newres .= $tmps;
11501 $newres .= $tmpbefore;
11502 $newres .= $db->escape($tmpcrit2);
11503 $newres .= $tmpafter;
11504 $newres .= "'";
11505 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11506 $newres .= " OR ".$field." IS NULL)";
11507 }
11508 }
11509
11510 $i3++;
11511 }
11512
11513 $i2++; // a criteria for 1 more field was added to string
11514 }
11515 }
11516
11517 if ($newres) {
11518 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11519 }
11520 $i1++;
11521 }
11522 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11523
11524 return $res;
11525}
11526
11533function showDirectDownloadLink($object)
11534{
11535 global $conf, $langs;
11536
11537 $out = '';
11538 $url = $object->getLastMainDocLink($object->element);
11539
11540 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11541 if ($url) {
11542 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11543 $out .= ajax_autoselect("directdownloadlink", 0);
11544 } else {
11545 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11546 }
11547
11548 return $out;
11549}
11550
11559function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11560{
11561 $dirName = dirname($file);
11562 if ($dirName == '.') {
11563 $dirName = '';
11564 }
11565
11566 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
11567 $fileName = basename($fileName);
11568
11569 if (empty($extImgTarget)) {
11570 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11571 }
11572 if (empty($extImgTarget)) {
11573 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11574 }
11575 if (empty($extImgTarget)) {
11576 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11577 }
11578 if (empty($extImgTarget)) {
11579 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11580 }
11581 if (empty($extImgTarget)) {
11582 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11583 }
11584 if (empty($extImgTarget)) {
11585 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11586 }
11587
11588 if (!$extImgTarget) {
11589 return $file;
11590 }
11591
11592 $subdir = '';
11593 if ($extName) {
11594 $subdir = 'thumbs/';
11595 }
11596
11597 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11598}
11599
11600
11610function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11611{
11612 global $conf, $langs;
11613
11614 if (empty($conf->use_javascript_ajax)) {
11615 return '';
11616 }
11617
11618 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11619
11620 if ($alldata == 1) {
11621 if ($isAllowedForPreview) {
11622 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));
11623 } else {
11624 return array();
11625 }
11626 }
11627
11628 // old behavior, return a string
11629 if ($isAllowedForPreview) {
11630 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11631 $title = $langs->transnoentities("Preview");
11632 //$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().
11633 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
11634
11635 // 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,
11636 // 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.
11637 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
11638 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
11639 } else {
11640 return '';
11641 }
11642}
11643
11644
11653function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11654{
11655 global $langs;
11656 $out = '<script nonce="'.getNonce().'">
11657 jQuery(document).ready(function () {
11658 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11659 });
11660 </script>';
11661 if ($addlink) {
11662 if ($textonlink === 'image') {
11663 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11664 } else {
11665 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11666 }
11667 }
11668 return $out;
11669}
11670
11678function dolIsAllowedForPreview($file)
11679{
11680 // Check .noexe extension in filename
11681 if (preg_match('/\.noexe$/i', $file)) {
11682 return 0;
11683 }
11684
11685 // Check mime types
11686 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11687 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11688 $mime_preview[] = 'svg+xml';
11689 }
11690 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11691 //$mime_preview[]='archive';
11692 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11693 if ($num_mime !== false) {
11694 return 1;
11695 }
11696
11697 // By default, not allowed for preview
11698 return 0;
11699}
11700
11701
11711function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11712{
11713 $mime = $default;
11714 $imgmime = 'other.png';
11715 $famime = 'file-o';
11716 $srclang = '';
11717
11718 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11719
11720 // Plain text files
11721 if (preg_match('/\.txt$/i', $tmpfile)) {
11722 $mime = 'text/plain';
11723 $imgmime = 'text.png';
11724 $famime = 'file-alt';
11725 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11726 $mime = 'text/richtext';
11727 $imgmime = 'text.png';
11728 $famime = 'file-alt';
11729 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11730 $mime = 'text/csv';
11731 $imgmime = 'text.png';
11732 $famime = 'file-csv';
11733 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11734 $mime = 'text/tab-separated-values';
11735 $imgmime = 'text.png';
11736 $famime = 'file-alt';
11737 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11738 $mime = 'text/plain';
11739 $imgmime = 'text.png';
11740 $famime = 'file-alt';
11741 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11742 $mime = 'text/plain';
11743 $imgmime = 'text.png';
11744 $srclang = 'ini';
11745 $famime = 'file-alt';
11746 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11747 $mime = 'text/plain';
11748 $imgmime = 'text.png';
11749 $srclang = 'md';
11750 $famime = 'file-alt';
11751 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11752 $mime = 'text/css';
11753 $imgmime = 'css.png';
11754 $srclang = 'css';
11755 $famime = 'file-alt';
11756 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11757 $mime = 'text/plain';
11758 $imgmime = 'text.png';
11759 $srclang = 'lang';
11760 $famime = 'file-alt';
11761 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11762 $mime = 'text/plain';
11763 $imgmime = 'text.png';
11764 $famime = 'file-alt';
11765 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11766 $mime = 'text/html';
11767 $imgmime = 'html.png';
11768 $srclang = 'html';
11769 $famime = 'file-alt';
11770 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11771 $mime = 'text/xml';
11772 $imgmime = 'other.png';
11773 $srclang = 'xml';
11774 $famime = 'file-alt';
11775 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11776 $mime = 'text/xml';
11777 $imgmime = 'other.png';
11778 $srclang = 'xaml';
11779 $famime = 'file-alt';
11780 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11781 $mime = 'text/plain';
11782 $imgmime = 'text.png';
11783 $srclang = 'bas';
11784 $famime = 'file-code';
11785 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11786 $mime = 'text/plain';
11787 $imgmime = 'text.png';
11788 $srclang = 'c';
11789 $famime = 'file-code';
11790 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11791 $mime = 'text/plain';
11792 $imgmime = 'text.png';
11793 $srclang = 'cpp';
11794 $famime = 'file-code';
11795 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11796 $mime = 'text/plain';
11797 $imgmime = 'text.png';
11798 $srclang = 'cs';
11799 $famime = 'file-code';
11800 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11801 $mime = 'text/plain';
11802 $imgmime = 'text.png';
11803 $srclang = 'h';
11804 $famime = 'file-code';
11805 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11806 $mime = 'text/plain';
11807 $imgmime = 'text.png';
11808 $srclang = 'java';
11809 $famime = 'file-code';
11810 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11811 $mime = 'text/plain';
11812 $imgmime = 'php.png';
11813 $srclang = 'php';
11814 $famime = 'file-code';
11815 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
11816 $mime = 'text/plain';
11817 $imgmime = 'php.png';
11818 $srclang = 'php';
11819 $famime = 'file-code';
11820 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
11821 $mime = 'text/plain';
11822 $imgmime = 'pl.png';
11823 $srclang = 'perl';
11824 $famime = 'file-code';
11825 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
11826 $mime = 'text/plain';
11827 $imgmime = 'text.png';
11828 $srclang = 'sql';
11829 $famime = 'file-code';
11830 } elseif (preg_match('/\.js$/i', $tmpfile)) {
11831 $mime = 'text/x-javascript';
11832 $imgmime = 'jscript.png';
11833 $srclang = 'js';
11834 $famime = 'file-code';
11835 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
11836 $mime = 'application/vnd.oasis.opendocument.presentation';
11837 $imgmime = 'ooffice.png';
11838 $famime = 'file-powerpoint';
11839 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
11840 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
11841 $imgmime = 'ooffice.png';
11842 $famime = 'file-excel';
11843 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
11844 $mime = 'application/vnd.oasis.opendocument.text';
11845 $imgmime = 'ooffice.png';
11846 $famime = 'file-word';
11847 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
11848 $mime = 'application/msaccess';
11849 $imgmime = 'mdb.png';
11850 $famime = 'file';
11851 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
11852 $mime = 'application/msword';
11853 $imgmime = 'doc.png';
11854 $famime = 'file-word';
11855 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
11856 $mime = 'application/msword';
11857 $imgmime = 'doc.png';
11858 $famime = 'file-word';
11859 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
11860 $mime = 'application/vnd.ms-excel';
11861 $imgmime = 'xls.png';
11862 $famime = 'file-excel';
11863 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
11864 $mime = 'application/vnd.ms-excel';
11865 $imgmime = 'xls.png';
11866 $famime = 'file-excel';
11867 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
11868 $mime = 'application/vnd.ms-excel';
11869 $imgmime = 'xls.png';
11870 $famime = 'file-excel';
11871 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
11872 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
11873 $imgmime = 'xls.png';
11874 $famime = 'file-excel';
11875 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
11876 $mime = 'application/vnd.ms-powerpoint';
11877 $imgmime = 'ppt.png';
11878 $famime = 'file-powerpoint';
11879 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
11880 $mime = 'application/x-mspowerpoint';
11881 $imgmime = 'ppt.png';
11882 $famime = 'file-powerpoint';
11883 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
11884 $mime = 'application/pdf';
11885 $imgmime = 'pdf.png';
11886 $famime = 'file-pdf';
11887 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
11888 $mime = 'text/x-bat';
11889 $imgmime = 'script.png';
11890 $srclang = 'dos';
11891 $famime = 'file-code';
11892 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
11893 $mime = 'text/x-sh';
11894 $imgmime = 'script.png';
11895 $srclang = 'bash';
11896 $famime = 'file-code';
11897 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
11898 $mime = 'text/x-ksh';
11899 $imgmime = 'script.png';
11900 $srclang = 'bash';
11901 $famime = 'file-code';
11902 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
11903 $mime = 'text/x-bash';
11904 $imgmime = 'script.png';
11905 $srclang = 'bash';
11906 $famime = 'file-code';
11907 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
11908 $mime = 'image/x-icon';
11909 $imgmime = 'image.png';
11910 $famime = 'file-image';
11911 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
11912 $mime = 'image/jpeg';
11913 $imgmime = 'image.png';
11914 $famime = 'file-image';
11915 } elseif (preg_match('/\.png$/i', $tmpfile)) {
11916 $mime = 'image/png';
11917 $imgmime = 'image.png';
11918 $famime = 'file-image';
11919 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
11920 $mime = 'image/gif';
11921 $imgmime = 'image.png';
11922 $famime = 'file-image';
11923 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
11924 $mime = 'image/bmp';
11925 $imgmime = 'image.png';
11926 $famime = 'file-image';
11927 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
11928 $mime = 'image/tiff';
11929 $imgmime = 'image.png';
11930 $famime = 'file-image';
11931 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
11932 $mime = 'image/svg+xml';
11933 $imgmime = 'image.png';
11934 $famime = 'file-image';
11935 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
11936 $mime = 'image/webp';
11937 $imgmime = 'image.png';
11938 $famime = 'file-image';
11939 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
11940 $mime = 'text/calendar';
11941 $imgmime = 'other.png';
11942 $famime = 'file-alt';
11943 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
11944 $mime = 'text/calendar';
11945 $imgmime = 'other.png';
11946 $famime = 'file-alt';
11947 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
11948 $mime = 'application/x-bittorrent';
11949 $imgmime = 'other.png';
11950 $famime = 'file-o';
11951 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
11952 $mime = 'audio';
11953 $imgmime = 'audio.png';
11954 $famime = 'file-audio';
11955 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
11956 $mime = 'video/mp4';
11957 $imgmime = 'video.png';
11958 $famime = 'file-video';
11959 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
11960 $mime = 'video/ogg';
11961 $imgmime = 'video.png';
11962 $famime = 'file-video';
11963 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
11964 $mime = 'video/webm';
11965 $imgmime = 'video.png';
11966 $famime = 'file-video';
11967 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
11968 $mime = 'video/x-msvideo';
11969 $imgmime = 'video.png';
11970 $famime = 'file-video';
11971 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
11972 $mime = 'video/divx';
11973 $imgmime = 'video.png';
11974 $famime = 'file-video';
11975 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
11976 $mime = 'video/xvid';
11977 $imgmime = 'video.png';
11978 $famime = 'file-video';
11979 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
11980 $mime = 'video';
11981 $imgmime = 'video.png';
11982 $famime = 'file-video';
11983 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
11984 // application/xxx where zzz is zip, ...
11985 $mime = 'archive';
11986 $imgmime = 'archive.png';
11987 $famime = 'file-archive';
11988 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
11989 $mime = 'application/octet-stream';
11990 $imgmime = 'other.png';
11991 $famime = 'file-o';
11992 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
11993 $mime = 'library';
11994 $imgmime = 'library.png';
11995 $famime = 'file-o';
11996 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
11997 $mime = 'error';
11998 $imgmime = 'error.png';
11999 $famime = 'file-alt';
12000 }
12001
12002 // Return mimetype string
12003 switch ((int) $mode) {
12004 case 1:
12005 $tmp = explode('/', $mime);
12006 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12007 case 2:
12008 return $imgmime;
12009 case 3:
12010 return $srclang;
12011 case 4:
12012 return $famime;
12013 }
12014 return $mime;
12015}
12016
12028function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12029{
12030 global $conf, $db;
12031
12032 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12033
12034 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12035
12036 if (is_null($dictvalues)) {
12037 $dictvalues = array();
12038
12039 $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
12040 if ($checkentity) {
12041 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12042 }
12043
12044 $resql = $db->query($sql);
12045 if ($resql) {
12046 while ($obj = $db->fetch_object($resql)) {
12047 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12048 }
12049 } else {
12050 dol_print_error($db);
12051 }
12052
12053 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12054 }
12055
12056 if (!empty($dictvalues[$id])) {
12057 // Found
12058 $tmp = $dictvalues[$id];
12059 return (property_exists($tmp, $field) ? $tmp->$field : '');
12060 } else {
12061 // Not found
12062 return '';
12063 }
12064}
12065
12072function colorIsLight($stringcolor)
12073{
12074 $stringcolor = str_replace('#', '', $stringcolor);
12075 $res = -1;
12076 if (!empty($stringcolor)) {
12077 $res = 0;
12078 $tmp = explode(',', $stringcolor);
12079 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12080 $r = $tmp[0];
12081 $g = $tmp[1];
12082 $b = $tmp[2];
12083 } else {
12084 $hexr = $stringcolor[0].$stringcolor[1];
12085 $hexg = $stringcolor[2].$stringcolor[3];
12086 $hexb = $stringcolor[4].$stringcolor[5];
12087 $r = hexdec($hexr);
12088 $g = hexdec($hexg);
12089 $b = hexdec($hexb);
12090 }
12091 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12092 if ($bright > 0.6) {
12093 $res = 1;
12094 }
12095 }
12096 return $res;
12097}
12098
12107function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12108{
12109 global $conf;
12110
12111 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12112 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12113 if (empty($menuentry['enabled'])) {
12114 return 0; // Entry disabled by condition
12115 }
12116 if ($type_user && $menuentry['module']) {
12117 $tmploops = explode('|', $menuentry['module']);
12118 $found = 0;
12119 foreach ($tmploops as $tmploop) {
12120 if (in_array($tmploop, $listofmodulesforexternal)) {
12121 $found++;
12122 break;
12123 }
12124 }
12125 if (!$found) {
12126 return 0; // Entry is for menus all excluded to external users
12127 }
12128 }
12129 if (!$menuentry['perms'] && $type_user) {
12130 return 0; // No permissions and user is external
12131 }
12132 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12133 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12134 }
12135 if (!$menuentry['perms']) {
12136 return 2; // No permissions and user is external
12137 }
12138 return 1;
12139}
12140
12148function roundUpToNextMultiple($n, $x = 5)
12149{
12150 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12151 return (int) $result;
12152}
12153
12165function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12166{
12167 $csstouse = 'badge';
12168 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12169 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12170 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12171
12172 $attr = array(
12173 'class' => $csstouse
12174 );
12175
12176 if (empty($html)) {
12177 $html = $label;
12178 }
12179
12180 if (!empty($url)) {
12181 $attr['href'] = $url;
12182 }
12183
12184 if ($mode === 'dot') {
12185 $attr['class'] .= ' classfortooltip';
12186 $attr['title'] = $html;
12187 $attr['aria-label'] = $label;
12188 $html = '';
12189 }
12190
12191 // Override attr
12192 if (!empty($params['attr']) && is_array($params['attr'])) {
12193 foreach ($params['attr'] as $key => $value) {
12194 if ($key == 'class') {
12195 $attr['class'] .= ' '.$value;
12196 } elseif ($key == 'classOverride') {
12197 $attr['class'] = $value;
12198 } else {
12199 $attr[$key] = $value;
12200 }
12201 }
12202 }
12203
12204 // TODO: add hook
12205
12206 // escape all attribute
12207 $attr = array_map('dol_escape_htmltag', $attr);
12208
12209 $TCompiledAttr = array();
12210 foreach ($attr as $key => $value) {
12211 $TCompiledAttr[] = $key.'="'.$value.'"';
12212 }
12213
12214 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12215
12216 $tag = !empty($url) ? 'a' : 'span';
12217
12218 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12219}
12220
12221
12234function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12235{
12236 global $conf;
12237
12238 $return = '';
12239 $dolGetBadgeParams = array();
12240
12241 if (!empty($params['badgeParams'])) {
12242 $dolGetBadgeParams = $params['badgeParams'];
12243 }
12244
12245 // TODO : add a hook
12246 if ($displayMode == 0) {
12247 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12248 } elseif ($displayMode == 1) {
12249 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12250 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12251 // Use status with images (for backward compatibility)
12252 $return = '';
12253 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12254 $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>' : '');
12255
12256 // For small screen, we always use the short label instead of long label.
12257 if (!empty($conf->dol_optimize_smallscreen)) {
12258 if ($displayMode == 0) {
12259 $displayMode = 1;
12260 } elseif ($displayMode == 4) {
12261 $displayMode = 2;
12262 } elseif ($displayMode == 6) {
12263 $displayMode = 5;
12264 }
12265 }
12266
12267 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12268 $statusImg = array(
12269 'status0' => 'statut0',
12270 'status1' => 'statut1',
12271 'status2' => 'statut2',
12272 'status3' => 'statut3',
12273 'status4' => 'statut4',
12274 'status5' => 'statut5',
12275 'status6' => 'statut6',
12276 'status7' => 'statut7',
12277 'status8' => 'statut8',
12278 'status9' => 'statut9'
12279 );
12280
12281 if (!empty($statusImg[$statusType])) {
12282 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12283 } else {
12284 $htmlImg = img_picto($statusLabel, $statusType);
12285 }
12286
12287 if ($displayMode === 2) {
12288 $return = $htmlImg.' '.$htmlLabelShort;
12289 } elseif ($displayMode === 3) {
12290 $return = $htmlImg;
12291 } elseif ($displayMode === 4) {
12292 $return = $htmlImg.' '.$htmlLabel;
12293 } elseif ($displayMode === 5) {
12294 $return = $htmlLabelShort.' '.$htmlImg;
12295 } else { // $displayMode >= 6
12296 $return = $htmlLabel.' '.$htmlImg;
12297 }
12298 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12299 // Use new badge
12300 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12301
12302 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12303 if (empty($dolGetBadgeParams['attr']['title'])) {
12304 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12305 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12306 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12307 // And if we use tooltip, we can output title in HTML
12308 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12309 }
12310
12311 if ($displayMode == 3) {
12312 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12313 } elseif ($displayMode === 5) {
12314 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12315 } else {
12316 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12317 }
12318 }
12319
12320 return $return;
12321}
12322
12323
12361function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12362{
12363 global $hookmanager, $action, $object, $langs;
12364
12365 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12366 if (is_array($url)) {
12367 // Loop on $url array to remove entries of disabled modules
12368 foreach ($url as $key => $subbutton) {
12369 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12370 unset($url[$key]);
12371 }
12372 }
12373
12374 $out = '';
12375
12376 if (isset($params["areDropdownButtons"]) && $params["areDropdownButtons"] === false) {
12377 foreach ($url as $button) {
12378 if (!empty($button['lang'])) {
12379 $langs->load($button['lang']);
12380 }
12381 $label = $langs->trans($button['label']);
12382 $text = $button['text'] ?? '';
12383 $actionType = $button['actionType'] ?? '';
12384 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12385 $id = $button['id'] ?? '';
12386 $userRight = $button['perm'] ?? 1;
12387 $button['params'] = $button['params'] ?? [];
12388
12389 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12390 }
12391 return $out;
12392 }
12393
12394 if (count($url) > 1) {
12395 $out .= '<div class="dropdown inline-block dropdown-holder">';
12396 $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>';
12397 $out .= '<div class="dropdown-content">';
12398 foreach ($url as $subbutton) {
12399 if (!empty($subbutton['lang'])) {
12400 $langs->load($subbutton['lang']);
12401 }
12402
12403 if (!empty($subbutton['urlraw'])) {
12404 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12405 } else {
12406 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12407 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12408 }
12409
12410 $subbuttonparam = array();
12411 if (!empty($subbutton['attr'])) {
12412 $subbuttonparam['attr'] = $subbutton['attr'];
12413 }
12414 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown']??false) : $params['isDropDown']);
12415
12416 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12417 }
12418 $out .= "</div>";
12419 $out .= "</div>";
12420 } else {
12421 foreach ($url as $subbutton) { // Should loop on 1 record only
12422 if (!empty($subbutton['lang'])) {
12423 $langs->load($subbutton['lang']);
12424 }
12425
12426 if (!empty($subbutton['urlraw'])) {
12427 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12428 } else {
12429 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12430 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12431 }
12432
12433 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12434 }
12435 }
12436
12437 return $out;
12438 }
12439
12440 // Here, $url is a simple link
12441
12442 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12443 $class = "dropdown-item";
12444 } else {
12445 $class = 'butAction';
12446 if ($actionType == 'danger' || $actionType == 'delete') {
12447 $class = 'butActionDelete';
12448 if (!empty($url) && strpos($url, 'token=') === false) {
12449 $url .= '&token='.newToken();
12450 }
12451 }
12452 }
12453 $attr = array(
12454 'class' => $class,
12455 'href' => empty($url) ? '' : $url,
12456 'title' => $label
12457 );
12458
12459 if (empty($text)) {
12460 $text = $label;
12461 $attr['title'] = ''; // if html not set, leave label on title is redundant
12462 } else {
12463 $attr['title'] = $label;
12464 $attr['aria-label'] = $label;
12465 }
12466
12467 if (empty($userRight)) {
12468 $attr['class'] = 'butActionRefused';
12469 $attr['href'] = '';
12470 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12471 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12472 }
12473
12474 if (!empty($id)) {
12475 $attr['id'] = $id;
12476 }
12477
12478 // Override attr
12479 if (!empty($params['attr']) && is_array($params['attr'])) {
12480 foreach ($params['attr'] as $key => $value) {
12481 if ($key == 'class') {
12482 $attr['class'] .= ' '.$value;
12483 } elseif ($key == 'classOverride') {
12484 $attr['class'] = $value;
12485 } else {
12486 $attr[$key] = $value;
12487 }
12488 }
12489 }
12490
12491 // automatic add tooltip when title is detected
12492 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12493 $attr['class'] .= ' classfortooltip';
12494 }
12495
12496 // Js Confirm button
12497 if ($userRight && !empty($params['confirm'])) {
12498 if (!is_array($params['confirm'])) {
12499 $params['confirm'] = array();
12500 }
12501
12502 if (empty($params['confirm']['url'])) {
12503 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12504 }
12505
12506 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12507 $attr['data-confirm-url'] = $params['confirm']['url'];
12508 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12509 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12510 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12511 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12512 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12513 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12514
12515 $attr['class'] .= ' butActionConfirm';
12516 }
12517
12518 if (isset($attr['href']) && empty($attr['href'])) {
12519 unset($attr['href']);
12520 }
12521
12522 // escape all attribute
12523 $attr = array_map('dol_escape_htmltag', $attr);
12524
12525 $TCompiledAttr = array();
12526 foreach ($attr as $key => $value) {
12527 $TCompiledAttr[] = $key.'= "'.$value.'"';
12528 }
12529
12530 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12531
12532 $tag = !empty($attr['href']) ? 'a' : 'span';
12533
12534
12535 $parameters = array(
12536 'TCompiledAttr' => $TCompiledAttr, // array
12537 'compiledAttributes' => $compiledAttributes, // string
12538 'attr' => $attr,
12539 'tag' => $tag,
12540 'label' => $label,
12541 'html' => $text,
12542 'actionType' => $actionType,
12543 'url' => $url,
12544 'id' => $id,
12545 'userRight' => $userRight,
12546 'params' => $params
12547 );
12548
12549 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12550 if ($reshook < 0) {
12551 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12552 }
12553
12554 if (empty($reshook)) {
12555 if (dol_textishtml($text)) { // If content already HTML encoded
12556 return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
12557 } else {
12558 return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
12559 }
12560 } else {
12561 return $hookmanager->resPrint;
12562 }
12563}
12564
12565
12574function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12575{
12576 if (empty($url)) {
12577 return '';
12578 }
12579
12580 $parsedUrl = parse_url($url);
12581 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12582 return $url;
12583 }
12584
12585 if (!empty($parsedUrl['query'])) {
12586 // Use parse_str() function to parse the string passed via URL
12587 parse_str($parsedUrl['query'], $urlQuery);
12588 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12589 $url.= '&amp;backtopage='.urlencode($params['backtopage']);
12590 }
12591 }
12592
12593 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12594 $url = DOL_URL_ROOT.$url;
12595 }
12596
12597 return $url;
12598}
12599
12600
12607function dolGetButtonTitleSeparator($moreClass = "")
12608{
12609 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12610}
12611
12618function getFieldErrorIcon($fieldValidationErrorMsg)
12619{
12620 $out = '';
12621 if (!empty($fieldValidationErrorMsg)) {
12622 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12623 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12624 $out .= '</span>';
12625 }
12626
12627 return $out;
12628}
12629
12642function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12643{
12644 global $langs, $conf, $user;
12645
12646 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12647 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12648 return '';
12649 }
12650
12651 $class = 'btnTitle';
12652 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12653 $class .= ' btnTitlePlus';
12654 }
12655 $useclassfortooltip = 1;
12656
12657 if (!empty($params['morecss'])) {
12658 $class .= ' '.$params['morecss'];
12659 }
12660
12661 $attr = array(
12662 'class' => $class,
12663 'href' => empty($url) ? '' : $url
12664 );
12665
12666 if (!empty($helpText)) {
12667 $attr['title'] = dol_escape_htmltag($helpText);
12668 } elseif (empty($attr['title']) && $label) {
12669 $attr['title'] = $label;
12670 $useclassfortooltip = 0;
12671 }
12672
12673 if ($status == 2) {
12674 $attr['class'] .= ' btnTitleSelected';
12675 } elseif ($status <= 0) {
12676 $attr['class'] .= ' refused';
12677
12678 $attr['href'] = '';
12679
12680 if ($status == -1) { // disable
12681 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12682 } elseif ($status == 0) { // Not enough permissions
12683 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12684 }
12685 }
12686
12687 if (!empty($attr['title']) && $useclassfortooltip) {
12688 $attr['class'] .= ' classfortooltip';
12689 }
12690
12691 if (!empty($id)) {
12692 $attr['id'] = $id;
12693 }
12694
12695 // Override attr
12696 if (!empty($params['attr']) && is_array($params['attr'])) {
12697 foreach ($params['attr'] as $key => $value) {
12698 if ($key == 'class') {
12699 $attr['class'] .= ' '.$value;
12700 } elseif ($key == 'classOverride') {
12701 $attr['class'] = $value;
12702 } else {
12703 $attr[$key] = $value;
12704 }
12705 }
12706 }
12707
12708 if (isset($attr['href']) && empty($attr['href'])) {
12709 unset($attr['href']);
12710 }
12711
12712 // TODO : add a hook
12713
12714 // escape all attribute
12715 $attr = array_map('dol_escape_htmltag', $attr);
12716
12717 $TCompiledAttr = array();
12718 foreach ($attr as $key => $value) {
12719 $TCompiledAttr[] = $key.'="'.$value.'"';
12720 }
12721
12722 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12723
12724 $tag = (empty($attr['href']) ? 'span' : 'a');
12725
12726 $button = '<'.$tag.' '.$compiledAttributes.'>';
12727 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12728 if (!empty($params['forcenohideoftext'])) {
12729 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12730 }
12731 $button .= '</'.$tag.'>';
12732
12733 return $button;
12734}
12735
12745function getElementProperties($elementType)
12746{
12747 global $conf, $db, $hookmanager;
12748
12749 $regs = array();
12750
12751 //$element_type='facture';
12752
12753 $classfile = $classname = $classpath = $subdir = $dir_output = '';
12754
12755 // Parse element/subelement
12756 $module = $elementType;
12757 $element = $elementType;
12758 $subelement = $elementType;
12759 $table_element = $elementType;
12760
12761 // If we ask a resource form external module (instead of default path)
12762 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12763 $element = $subelement = $regs[1];
12764 $module = $regs[2];
12765 }
12766
12767 // If we ask a resource for a string with an element and a subelement
12768 // Example 'project_task'
12769 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12770 $module = $element = $regs[1];
12771 $subelement = $regs[2];
12772 }
12773
12774 // Object lines will use parent classpath and module ref
12775 if (substr($elementType, -3) == 'det') {
12776 $module = preg_replace('/det$/', '', $element);
12777 $subelement = preg_replace('/det$/', '', $subelement);
12778 $classpath = $module.'/class';
12779 $classfile = $module;
12780 $classname = preg_replace('/det$/', 'Line', $element);
12781 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12782 $classname = preg_replace('/det$/', 'Ligne', $element);
12783 }
12784 }
12785 // For compatibility and to work with non standard path
12786 if ($elementType == "action" || $elementType == "actioncomm") {
12787 $classpath = 'comm/action/class';
12788 $subelement = 'Actioncomm';
12789 $module = 'agenda';
12790 $table_element = 'actioncomm';
12791 } elseif ($elementType == 'cronjob') {
12792 $classpath = 'cron/class';
12793 $module = 'cron';
12794 $table_element = 'cron';
12795 } elseif ($elementType == 'adherent_type') {
12796 $classpath = 'adherents/class';
12797 $classfile = 'adherent_type';
12798 $module = 'adherent';
12799 $subelement = 'adherent_type';
12800 $classname = 'AdherentType';
12801 $table_element = 'adherent_type';
12802 } elseif ($elementType == 'bank_account') {
12803 $classpath = 'compta/bank/class';
12804 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12805 $classfile = 'account';
12806 $classname = 'Account';
12807 } elseif ($elementType == 'category') {
12808 $classpath = 'categories/class';
12809 $module = 'categorie';
12810 $subelement = 'categorie';
12811 $table_element = 'categorie';
12812 } elseif ($elementType == 'contact') {
12813 $classpath = 'contact/class';
12814 $classfile = 'contact';
12815 $module = 'societe';
12816 $subelement = 'contact';
12817 $table_element = 'socpeople';
12818 } elseif ($elementType == 'inventory') {
12819 $module = 'product';
12820 $classpath = 'product/inventory/class';
12821 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
12822 $module = 'stock';
12823 $classpath = 'product/stock/class';
12824 $classfile = 'entrepot';
12825 $classname = 'Entrepot';
12826 $table_element = 'entrepot';
12827 } elseif ($elementType == 'project') {
12828 $classpath = 'projet/class';
12829 $module = 'projet';
12830 $table_element = 'projet';
12831 } elseif ($elementType == 'project_task') {
12832 $classpath = 'projet/class';
12833 $module = 'projet';
12834 $subelement = 'task';
12835 $table_element = 'projet_task';
12836 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
12837 $classpath = 'compta/facture/class';
12838 $module = 'facture';
12839 $subelement = 'facture';
12840 $table_element = 'facture';
12841 } elseif ($elementType == 'facturerec') {
12842 $classpath = 'compta/facture/class';
12843 $module = 'facture';
12844 $classname = 'FactureRec';
12845 } elseif ($elementType == 'commande' || $elementType == 'order') {
12846 $classpath = 'commande/class';
12847 $module = 'commande';
12848 $subelement = 'commande';
12849 $table_element = 'commande';
12850 } elseif ($elementType == 'propal') {
12851 $classpath = 'comm/propal/class';
12852 $table_element = 'propal';
12853 } elseif ($elementType == 'shipping') {
12854 $classpath = 'expedition/class';
12855 $classfile = 'expedition';
12856 $classname = 'Expedition';
12857 $module = 'expedition';
12858 $table_element = 'expedition';
12859 } elseif ($elementType == 'delivery_note') {
12860 $classpath = 'delivery/class';
12861 $subelement = 'delivery';
12862 $module = 'expedition';
12863 } elseif ($elementType == 'delivery') {
12864 $classpath = 'delivery/class';
12865 $subelement = 'delivery';
12866 $module = 'expedition';
12867 } elseif ($elementType == 'supplier_proposal') {
12868 $classpath = 'supplier_proposal/class';
12869 $module = 'supplier_proposal';
12870 $element = 'supplierproposal';
12871 $classfile = 'supplier_proposal';
12872 $subelement = 'supplierproposal';
12873 } elseif ($elementType == 'contract') {
12874 $classpath = 'contrat/class';
12875 $module = 'contrat';
12876 $subelement = 'contrat';
12877 $table_element = 'contract';
12878 } elseif ($elementType == 'mailing') {
12879 $classpath = 'comm/mailing/class';
12880 $module = 'mailing';
12881 $classfile = 'mailing';
12882 $classname = 'Mailing';
12883 $subelement = '';
12884 } elseif ($elementType == 'member' || $elementType == 'adherent') {
12885 $classpath = 'adherents/class';
12886 $module = 'adherent';
12887 $subelement = 'adherent';
12888 $table_element = 'adherent';
12889 } elseif ($elementType == 'usergroup') {
12890 $classpath = 'user/class';
12891 $module = 'user';
12892 } elseif ($elementType == 'mo') {
12893 $classpath = 'mrp/class';
12894 $classfile = 'mo';
12895 $classname = 'Mo';
12896 $module = 'mrp';
12897 $subelement = '';
12898 $table_element = 'mrp_mo';
12899 } elseif ($elementType == 'cabinetmed_cons') {
12900 $classpath = 'cabinetmed/class';
12901 $module = 'cabinetmed';
12902 $subelement = 'cabinetmedcons';
12903 $table_element = 'cabinetmedcons';
12904 } elseif ($elementType == 'fichinter') {
12905 $classpath = 'fichinter/class';
12906 $module = 'ficheinter';
12907 $subelement = 'fichinter';
12908 $table_element = 'fichinter';
12909 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
12910 $classpath = 'resource/class';
12911 $module = 'resource';
12912 $subelement = 'dolresource';
12913 $table_element = 'resource';
12914 } elseif ($elementType == 'propaldet') {
12915 $classpath = 'comm/propal/class';
12916 $module = 'propal';
12917 $subelement = 'propaleligne';
12918 } elseif ($elementType == 'opensurvey_sondage') {
12919 $classpath = 'opensurvey/class';
12920 $module = 'opensurvey';
12921 $subelement = 'opensurveysondage';
12922 } elseif ($elementType == 'order_supplier') {
12923 $classpath = 'fourn/class';
12924 $module = 'fournisseur';
12925 $classfile = 'fournisseur.commande';
12926 $element = 'order_supplier';
12927 $subelement = '';
12928 $classname = 'CommandeFournisseur';
12929 $table_element = 'commande_fournisseur';
12930 } elseif ($elementType == 'commande_fournisseurdet') {
12931 $classpath = 'fourn/class';
12932 $module = 'fournisseur';
12933 $classfile = 'fournisseur.commande';
12934 $element = 'commande_fournisseurdet';
12935 $subelement = '';
12936 $classname = 'CommandeFournisseurLigne';
12937 $table_element = 'commande_fournisseurdet';
12938 } elseif ($elementType == 'invoice_supplier') {
12939 $classpath = 'fourn/class';
12940 $module = 'fournisseur';
12941 $classfile = 'fournisseur.facture';
12942 $element = 'invoice_supplier';
12943 $subelement = '';
12944 $classname = 'FactureFournisseur';
12945 $table_element = 'facture_fourn';
12946 } elseif ($elementType == "service") {
12947 $classpath = 'product/class';
12948 $subelement = 'product';
12949 $table_element = 'product';
12950 } elseif ($elementType == 'salary') {
12951 $classpath = 'salaries/class';
12952 $module = 'salaries';
12953 } elseif ($elementType == 'payment_salary') {
12954 $classpath = 'salaries/class';
12955 $classfile = 'paymentsalary';
12956 $classname = 'PaymentSalary';
12957 $module = 'salaries';
12958 } elseif ($elementType == 'productlot') {
12959 $module = 'productbatch';
12960 $classpath = 'product/stock/class';
12961 $classfile = 'productlot';
12962 $classname = 'Productlot';
12963 $element = 'productlot';
12964 $subelement = '';
12965 $table_element = 'product_lot';
12966 } elseif ($elementType == 'societeaccount') {
12967 $classpath = 'societe/class';
12968 $classfile = 'societeaccount';
12969 $classname = 'SocieteAccount';
12970 $module = 'societe';
12971 } elseif ($elementType == 'websitepage') {
12972 $classpath = 'website/class';
12973 $classfile = 'websitepage';
12974 $classname = 'Websitepage';
12975 $module = 'website';
12976 $subelement = 'websitepage';
12977 $table_element = 'website_page';
12978 } elseif ($elementType == 'fiscalyear') {
12979 $classpath = 'core/class';
12980 $module = 'accounting';
12981 $subelement = 'fiscalyear';
12982 } elseif ($elementType == 'chargesociales') {
12983 $classpath = 'compta/sociales/class';
12984 $module = 'tax';
12985 $table_element = 'chargesociales';
12986 } elseif ($elementType == 'tva') {
12987 $classpath = 'compta/tva/class';
12988 $module = 'tax';
12989 $subdir = '/vat';
12990 $table_element = 'tva';
12991 } elseif ($elementType == 'emailsenderprofile') {
12992 $module = '';
12993 $classpath = 'core/class';
12994 $classfile = 'emailsenderprofile';
12995 $classname = 'EmailSenderProfile';
12996 $table_element = 'c_email_senderprofile';
12997 $subelement = '';
12998 } elseif ($elementType == 'conferenceorboothattendee') {
12999 $classpath = 'eventorganization/class';
13000 $classfile = 'conferenceorboothattendee';
13001 $classname = 'ConferenceOrBoothAttendee';
13002 $module = 'eventorganization';
13003 } elseif ($elementType == 'conferenceorbooth') {
13004 $classpath = 'eventorganization/class';
13005 $classfile = 'conferenceorbooth';
13006 $classname = 'ConferenceOrBooth';
13007 $module = 'eventorganization';
13008 } elseif ($elementType == 'ccountry') {
13009 $module = '';
13010 $classpath = 'core/class';
13011 $classfile = 'ccountry';
13012 $classname = 'Ccountry';
13013 $table_element = 'c_country';
13014 $subelement = '';
13015 }
13016
13017 if (empty($classfile)) {
13018 $classfile = strtolower($subelement);
13019 }
13020 if (empty($classname)) {
13021 $classname = ucfirst($subelement);
13022 }
13023 if (empty($classpath)) {
13024 $classpath = $module.'/class';
13025 }
13026
13027 //print 'getElementProperties subdir='.$subdir;
13028
13029 // Set dir_output
13030 if ($module && isset($conf->$module)) { // The generic case
13031 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13032 $dir_output = $conf->$module->multidir_output[$conf->entity];
13033 } elseif (!empty($conf->$module->output[$conf->entity])) {
13034 $dir_output = $conf->$module->output[$conf->entity];
13035 } elseif (!empty($conf->$module->dir_output)) {
13036 $dir_output = $conf->$module->dir_output;
13037 }
13038 }
13039
13040 // Overwrite value for special cases
13041 if ($element == 'order_supplier') {
13042 $dir_output = $conf->fournisseur->commande->dir_output;
13043 } elseif ($element == 'invoice_supplier') {
13044 $dir_output = $conf->fournisseur->facture->dir_output;
13045 }
13046 $dir_output .= $subdir;
13047
13048 $elementProperties = array(
13049 'module' => $module,
13050 'element' => $element,
13051 'table_element' => $table_element,
13052 'subelement' => $subelement,
13053 'classpath' => $classpath,
13054 'classfile' => $classfile,
13055 'classname' => $classname,
13056 'dir_output' => $dir_output
13057 );
13058
13059
13060 // Add hook
13061 if (!is_object($hookmanager)) {
13062 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13063 $hookmanager = new HookManager($db);
13064 }
13065 $hookmanager->initHooks(array('elementproperties'));
13066
13067
13068 // Hook params
13069 $parameters = array(
13070 'elementType' => $elementType,
13071 'elementProperties' => $elementProperties
13072 );
13073
13074 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13075
13076 if ($reshook) {
13077 $elementProperties = $hookmanager->resArray;
13078 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13079 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13080 }
13081
13082 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13083 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13084 unset($hookmanager->contextarray[$key]);
13085 }
13086
13087 return $elementProperties;
13088}
13089
13102function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13103{
13104 global $db, $conf;
13105
13106 $ret = 0;
13107
13108 $element_prop = getElementProperties($element_type);
13109
13110 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13111 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13112 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13113 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13114 // of service and we will return properties of a product.
13115 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13116 } elseif ($element_prop['module'] == 'societeaccount') {
13117 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13118 } else {
13119 $ismodenabled = isModEnabled($element_prop['module']);
13120 }
13121 //var_dump('element_type='.$element_type);
13122 //var_dump($element_prop);
13123 //var_dump($element_prop['module'].' '.$ismodenabled);
13124 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13125 if ($useCache === 1
13126 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13127 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13128 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13129 ) {
13130 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13131 }
13132
13133 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13134
13135 if (class_exists($element_prop['classname'])) {
13136 $className = $element_prop['classname'];
13137 $objecttmp = new $className($db);
13138 '@phan-var-force CommonObject $objecttmp';
13139
13140 if ($element_id > 0 || !empty($element_ref)) {
13141 $ret = $objecttmp->fetch($element_id, $element_ref);
13142 if ($ret >= 0) {
13143 if (empty($objecttmp->module)) {
13144 $objecttmp->module = $element_prop['module'];
13145 }
13146
13147 if ($useCache > 0) {
13148 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13149 $conf->cache['fetchObjectByElement'][$element_type] = [];
13150 }
13151
13152 // Manage cache limit
13153 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13154 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13155 }
13156
13157 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13158 }
13159
13160 return $objecttmp;
13161 }
13162 } else {
13163 return $objecttmp; // returned an object without fetch
13164 }
13165 } else {
13166 return -1;
13167 }
13168 }
13169
13170 return $ret;
13171}
13172
13179function isAFileWithExecutableContent($filename)
13180{
13181 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)) {
13182 return true;
13183 }
13184
13185 return false;
13186}
13187
13195function newToken()
13196{
13197 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13198}
13199
13207function currentToken()
13208{
13209 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13210}
13211
13217function getNonce()
13218{
13219 global $conf;
13220
13221 if (empty($conf->cache['nonce'])) {
13222 $conf->cache['nonce'] = dolGetRandomBytes(8);
13223 }
13224
13225 return $conf->cache['nonce'];
13226}
13227
13228
13242function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13243{
13244 global $langs;
13245
13246 print '<div class="div-table-responsive-no-min">';
13247 print '<table class="noborder centpercent">';
13248 print '<tr class="liste_titre">';
13249
13250 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13251
13252 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13253
13254 if (!empty($link)) {
13255 if (!empty($arguments)) {
13256 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13257 } else {
13258 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13259 }
13260 }
13261
13262 if ($number > -1) {
13263 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13264 } elseif (!empty($link)) {
13265 print '<span class="badge marginleftonlyshort">...</span>';
13266 }
13267
13268 if (!empty($link)) {
13269 print '</a>';
13270 }
13271
13272 print '</th>';
13273
13274 if ($number < 0 && !empty($link)) {
13275 print '<th class="right">';
13276 print '</th>';
13277 }
13278
13279 print '</tr>';
13280}
13281
13290function finishSimpleTable($addLineBreak = false)
13291{
13292 print '</table>';
13293 print '</div>';
13294
13295 if ($addLineBreak) {
13296 print '<br>';
13297 }
13298}
13299
13311function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13312{
13313 global $langs;
13314
13315 if ($num === 0) {
13316 print '<tr class="oddeven">';
13317 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13318 print '</tr>';
13319 return;
13320 }
13321
13322 if ($nbofloop === 0) {
13323 // don't show a summary line
13324 return;
13325 }
13326
13327 if ($num === 0) {
13328 $colspan = $tableColumnCount;
13329 } elseif ($num > $nbofloop) {
13330 $colspan = $tableColumnCount;
13331 } else {
13332 $colspan = $tableColumnCount - 1;
13333 }
13334
13335 if ($extraRightColumn) {
13336 $colspan--;
13337 }
13338
13339 print '<tr class="liste_total">';
13340
13341 if ($nbofloop > 0 && $num > $nbofloop) {
13342 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13343 } else {
13344 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13345 print '<td class="right centpercent">'.price($total).'</td>';
13346 }
13347
13348 if ($extraRightColumn) {
13349 print '<td></td>';
13350 }
13351
13352 print '</tr>';
13353}
13354
13363function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13364{
13365 if ($method == -1) {
13366 $method = 0;
13367 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13368 $method = 1;
13369 }
13370 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13371 $method = 2;
13372 }
13373 }
13374
13375 // Be sure we don't have output buffering enabled to have readfile working correctly
13376 while (ob_get_level()) {
13377 ob_end_flush();
13378 }
13379
13380 // Solution 0
13381 if ($method == 0) {
13382 readfile($fullpath_original_file_osencoded);
13383 } elseif ($method == 1) {
13384 // Solution 1
13385 $handle = fopen($fullpath_original_file_osencoded, "rb");
13386 while (!feof($handle)) {
13387 print fread($handle, 8192);
13388 }
13389 fclose($handle);
13390 } elseif ($method == 2) {
13391 // Solution 2
13392 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13393 $handle2 = fopen("php://output", "wb");
13394 stream_copy_to_stream($handle1, $handle2);
13395 fclose($handle1);
13396 fclose($handle2);
13397 }
13398}
13399
13409function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13410{
13411 /*
13412 global $conf;
13413
13414 if (!empty($conf->dol_no_mouse_hover)) {
13415 $showonlyonhover = 0;
13416 }*/
13417
13418 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13419 if ($texttoshow === 'none') {
13420 $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>';
13421 } elseif ($texttoshow) {
13422 $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>';
13423 } else {
13424 $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>';
13425 }
13426
13427 return $result;
13428}
13429
13430
13437function jsonOrUnserialize($stringtodecode)
13438{
13439 $result = json_decode($stringtodecode);
13440 if ($result === null) {
13441 $result = unserialize($stringtodecode);
13442 }
13443
13444 return $result;
13445}
13446
13447
13464function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13465{
13466 global $db, $user;
13467
13468 if (is_null($filter) || !is_string($filter) || $filter === '') {
13469 return '';
13470 }
13471 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13472 $filter = '(' . $filter . ')';
13473 }
13474
13475 $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'
13476 $firstandlastparenthesis = 0;
13477
13478 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13479 if ($noerror) {
13480 return '1 = 2';
13481 } else {
13482 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13483 }
13484 }
13485
13486 // Test the filter syntax
13487 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13488 $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
13489 // If the string result contains something else than '()', the syntax was wrong
13490
13491 if (preg_match('/[^\‍(\‍)]/', $t)) {
13492 $tmperrorstr = 'Bad syntax of the search string';
13493 $errorstr = 'Bad syntax of the search string: '.$filter;
13494 if ($noerror) {
13495 return '1 = 2';
13496 } else {
13497 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13498 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13499 }
13500 }
13501
13502 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
13503
13504 if (is_object($db)) {
13505 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
13506 }
13507 if (is_object($user)) {
13508 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13509 }
13510
13511 return $ret;
13512}
13513
13521function dolForgeExplodeAnd($sqlfilters)
13522{
13523 $arrayofandtags = array();
13524 $nbofchars = dol_strlen($sqlfilters);
13525
13526 $error = '';
13527 $parenthesislevel = 0;
13528 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13529 if (!$result) {
13530 return array();
13531 }
13532 if ($parenthesislevel >= 1) {
13533 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13534 }
13535
13536 $i = 0;
13537 $s = '';
13538 $countparenthesis = 0;
13539 while ($i < $nbofchars) {
13540 $char = dol_substr($sqlfilters, $i, 1);
13541
13542 if ($char == '(') {
13543 $countparenthesis++;
13544 } elseif ($char == ')') {
13545 $countparenthesis--;
13546 }
13547
13548 if ($countparenthesis == 0) {
13549 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13550 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13551 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13552 // We found a AND
13553 $s = trim($s);
13554 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13555 $s = '('.$s.')';
13556 }
13557 $arrayofandtags[] = $s;
13558 $s = '';
13559 $i += 2;
13560 } else {
13561 $s .= $char;
13562 }
13563 } else {
13564 $s .= $char;
13565 }
13566 $i++;
13567 }
13568 if ($s) {
13569 $s = trim($s);
13570 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13571 $s = '('.$s.')';
13572 }
13573 $arrayofandtags[] = $s;
13574 }
13575
13576 return $arrayofandtags;
13577}
13578
13588function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13589{
13590 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13591 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13592 $tmp = $sqlfilters;
13593
13594 $nb = dol_strlen($tmp);
13595 $counter = 0;
13596 $parenthesislevel = 0;
13597
13598 $error = '';
13599
13600 $i = 0;
13601 while ($i < $nb) {
13602 $char = dol_substr($tmp, $i, 1);
13603
13604 if ($char == '(') {
13605 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13606 // We open a parenthesis and it is the first char
13607 $parenthesislevel++;
13608 }
13609 $counter++;
13610 } elseif ($char == ')') {
13611 $nbcharremaining = ($nb - $i - 1);
13612 if ($nbcharremaining >= $counter) {
13613 $parenthesislevel = min($parenthesislevel, $counter - 1);
13614 }
13615 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13616 $parenthesislevel = $counter;
13617 }
13618 $counter--;
13619 }
13620
13621 if ($counter < 0) {
13622 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13623 $parenthesislevel = 0;
13624 dol_syslog($error, LOG_WARNING);
13625 return false;
13626 }
13627
13628 $i++;
13629 }
13630
13631 if ($counter > 0) {
13632 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13633 $parenthesislevel = 0;
13634 dol_syslog($error, LOG_WARNING);
13635 return false;
13636 }
13637
13638 return true;
13639}
13640
13648function dolForgeDummyCriteriaCallback($matches)
13649{
13650 //dol_syslog("Convert matches ".$matches[1]);
13651 if (empty($matches[1])) {
13652 return '';
13653 }
13654 $tmp = explode(':', $matches[1]);
13655 if (count($tmp) < 3) {
13656 return '';
13657 }
13658
13659 return '()'; // An empty criteria
13660}
13661
13670function dolForgeCriteriaCallback($matches)
13671{
13672 global $db;
13673
13674 //dol_syslog("Convert matches ".$matches[1]);
13675 if (empty($matches[1])) {
13676 return '';
13677 }
13678 $tmp = explode(':', $matches[1], 3);
13679 if (count($tmp) < 3) {
13680 return '';
13681 }
13682
13683 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13684
13685 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13686
13687 $realOperator = [
13688 'NOTLIKE' => 'NOT LIKE',
13689 'ISNOT' => 'IS NOT',
13690 'NOTIN' => 'NOT IN',
13691 '!=' => '<>',
13692 ];
13693
13694 if (array_key_exists($operator, $realOperator)) {
13695 $operator = $realOperator[$operator];
13696 }
13697
13698 $tmpescaped = $tmp[2];
13699
13700 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13701
13702 $regbis = array();
13703
13704 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13705 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13706 $tmpescaped2 = '(';
13707 // Explode and sanitize each element in list
13708 $tmpelemarray = explode(',', $tmpescaped);
13709 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13710 $reg = array();
13711 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13712 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13713 } else {
13714 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13715 }
13716 }
13717 $tmpescaped2 .= implode(',', $tmpelemarray);
13718 $tmpescaped2 .= ')';
13719
13720 $tmpescaped = $tmpescaped2;
13721 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
13722 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
13723 $tmpescaped = $regbis[1];
13724 }
13725 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
13726 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
13727 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
13728 // TODO Retrieve type of field for $operand field name.
13729 // So we can complete format. For example we could complete a year with month and day.
13730 $tmpescaped = "'".$db->escape($regbis[1])."'";
13731 } else {
13732 if (strtoupper($tmpescaped) == 'NULL') {
13733 $tmpescaped = 'NULL';
13734 } elseif (is_int($tmpescaped)) {
13735 $tmpescaped = (int) $tmpescaped;
13736 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
13737 $tmpescaped = (float) $tmpescaped;
13738 } else {
13739 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
13740 }
13741 }
13742
13743 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
13744}
13745
13746
13756function getTimelineIcon($actionstatic, &$histo, $key)
13757{
13758 global $langs;
13759
13760 $out = '<!-- timeline icon -->'."\n";
13761 $iconClass = 'fa fa-comments';
13762 $img_picto = '';
13763 $colorClass = '';
13764 $pictoTitle = '';
13765
13766 if ($histo[$key]['percent'] == -1) {
13767 $colorClass = 'timeline-icon-not-applicble';
13768 $pictoTitle = $langs->trans('StatusNotApplicable');
13769 } elseif ($histo[$key]['percent'] == 0) {
13770 $colorClass = 'timeline-icon-todo';
13771 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
13772 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
13773 $colorClass = 'timeline-icon-in-progress';
13774 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
13775 } elseif ($histo[$key]['percent'] >= 100) {
13776 $colorClass = 'timeline-icon-done';
13777 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
13778 }
13779
13780 if ($actionstatic->code == 'AC_TICKET_CREATE') {
13781 $iconClass = 'fa fa-ticket';
13782 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
13783 $iconClass = 'fa fa-pencilxxx';
13784 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
13785 $iconClass = 'fa fa-comments';
13786 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
13787 $iconClass = 'fa fa-mask';
13788 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13789 if ($actionstatic->type_picto) {
13790 $img_picto = img_picto('', $actionstatic->type_picto);
13791 } else {
13792 if ($actionstatic->type_code == 'AC_RDV') {
13793 $iconClass = 'fa fa-handshake';
13794 } elseif ($actionstatic->type_code == 'AC_TEL') {
13795 $iconClass = 'fa fa-phone';
13796 } elseif ($actionstatic->type_code == 'AC_FAX') {
13797 $iconClass = 'fa fa-fax';
13798 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
13799 $iconClass = 'fa fa-envelope';
13800 } elseif ($actionstatic->type_code == 'AC_INT') {
13801 $iconClass = 'fa fa-shipping-fast';
13802 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
13803 $iconClass = 'fa fa-robot';
13804 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
13805 $iconClass = 'fa fa-robot';
13806 }
13807 }
13808 }
13809
13810 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
13811 return $out;
13812}
13813
13821{
13822 global $conf, $db;
13823
13824 $documents = array();
13825
13826 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
13827 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
13828 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
13829 //$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
13830 $sql .= ' ORDER BY ecm.position ASC';
13831
13832 $resql = $db->query($sql);
13833 if ($resql) {
13834 if ($db->num_rows($resql)) {
13835 while ($obj = $db->fetch_object($resql)) {
13836 $documents[$obj->id] = $obj;
13837 }
13838 }
13839 }
13840
13841 return $documents;
13842}
13843
13844
13862function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
13863{
13864 global $user, $conf;
13865 global $form;
13866
13867 global $param, $massactionbutton;
13868
13869 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
13870
13871 // Check parameters
13872 if (!is_object($filterobj) && !is_object($objcon)) {
13873 dol_print_error(null, 'BadParameter');
13874 }
13875
13876 $histo = array();
13877 '@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';
13878
13879 $numaction = 0;
13880 $now = dol_now();
13881
13882 $sortfield_list = explode(',', $sortfield);
13883 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
13884 $sortfield_new_list = array();
13885 foreach ($sortfield_list as $sortfield_value) {
13886 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
13887 }
13888 $sortfield_new = implode(',', $sortfield_new_list);
13889
13890 $sql = null;
13891 $sql2 = null;
13892
13893 if (isModEnabled('agenda')) {
13894 // Search histo on actioncomm
13895 if (is_object($objcon) && $objcon->id > 0) {
13896 $sql = "SELECT DISTINCT a.id, a.label as label,";
13897 } else {
13898 $sql = "SELECT a.id, a.label as label,";
13899 }
13900 $sql .= " a.datep as dp,";
13901 $sql .= " a.note as message,";
13902 $sql .= " a.datep2 as dp2,";
13903 $sql .= " a.percent as percent, 'action' as type,";
13904 $sql .= " a.fk_element, a.elementtype,";
13905 $sql .= " a.fk_contact,";
13906 $sql .= " a.email_from as msg_from,";
13907 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
13908 $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";
13909 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13910 $sql .= ", sp.lastname, sp.firstname";
13911 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13912 $sql .= ", m.lastname, m.firstname";
13913 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13914 $sql .= ", o.ref";
13915 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13916 $sql .= ", o.ref";
13917 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13918 $sql .= ", o.ref";
13919 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13920 $sql .= ", o.ref";
13921 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13922 $sql .= ", o.ref";
13923 }
13924 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
13925 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
13926 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
13927
13928 $force_filter_contact = $filterobj instanceof User;
13929
13930 if (is_object($objcon) && $objcon->id > 0) {
13931 $force_filter_contact = true;
13932 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
13933 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
13934 }
13935
13936 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13937 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
13938 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
13939 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
13940 $sql .= " ON er.resource_type = 'dolresource'";
13941 $sql .= " AND er.element_id = a.id";
13942 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
13943 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13944 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
13945 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13946 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
13947 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13948 $sql .= ", ".MAIN_DB_PREFIX."product as o";
13949 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13950 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
13951 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13952 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
13953 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13954 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
13955 }
13956
13957 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
13958 if (!$force_filter_contact) {
13959 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
13960 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
13961 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
13962 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
13963 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13964 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
13965 if ($filterobj->id) {
13966 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13967 }
13968 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13969 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
13970 if ($filterobj->id) {
13971 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13972 }
13973 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13974 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
13975 if ($filterobj->id) {
13976 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13977 }
13978 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13979 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
13980 if ($filterobj->id) {
13981 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13982 }
13983 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13984 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
13985 if ($filterobj->id) {
13986 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13987 }
13988 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13989 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
13990 if ($filterobj->id) {
13991 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13992 }
13993 }
13994 } else {
13995 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
13996 }
13997
13998 // Condition on actioncode
13999 if (!empty($actioncode)) {
14000 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14001 if ($actioncode == 'AC_NON_AUTO') {
14002 $sql .= " AND c.type != 'systemauto'";
14003 } elseif ($actioncode == 'AC_ALL_AUTO') {
14004 $sql .= " AND c.type = 'systemauto'";
14005 } else {
14006 if ($actioncode == 'AC_OTH') {
14007 $sql .= " AND c.type != 'systemauto'";
14008 } elseif ($actioncode == 'AC_OTH_AUTO') {
14009 $sql .= " AND c.type = 'systemauto'";
14010 }
14011 }
14012 } else {
14013 if ($actioncode == 'AC_NON_AUTO') {
14014 $sql .= " AND c.type != 'systemauto'";
14015 } elseif ($actioncode == 'AC_ALL_AUTO') {
14016 $sql .= " AND c.type = 'systemauto'";
14017 } else {
14018 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14019 }
14020 }
14021 }
14022 if ($donetodo == 'todo') {
14023 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14024 } elseif ($donetodo == 'done') {
14025 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14026 }
14027 if (is_array($filters) && $filters['search_agenda_label']) {
14028 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14029 }
14030 }
14031
14032 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14033 if (isModEnabled('mailing') && !empty($objcon->email)
14034 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14035 $langs->load("mails");
14036
14037 $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";
14038 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14039 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14040 $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
14041 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14042 $sql2 .= ", '' as lastname, '' as firstname";
14043 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14044 $sql2 .= ", '' as lastname, '' as firstname";
14045 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14046 $sql2 .= ", '' as ref";
14047 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14048 $sql2 .= ", '' as ref";
14049 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14050 $sql2 .= ", '' as ref";
14051 }
14052 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14053 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14054 $sql2 .= " AND mc.statut = 1";
14055 $sql2 .= " AND u.rowid = m.fk_user_valid";
14056 $sql2 .= " AND mc.fk_mailing=m.rowid";
14057 }
14058
14059 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14060 if (!empty($sql) && !empty($sql2)) {
14061 $sql = $sql." UNION ".$sql2;
14062 } elseif (empty($sql) && !empty($sql2)) {
14063 $sql = $sql2;
14064 }
14065
14066 //TODO Add navigation with this limits...
14067 $offset = 0;
14068 $limit = 1000;
14069
14070 // Complete request and execute it with limit
14071 $sql .= $db->order($sortfield_new, $sortorder);
14072 if ($limit) {
14073 $sql .= $db->plimit($limit + 1, $offset);
14074 }
14075
14076 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14077 $resql = $db->query($sql);
14078 if ($resql) {
14079 $i = 0;
14080 $num = $db->num_rows($resql);
14081
14082 $imaxinloop = ($limit ? min($num, $limit) : $num);
14083 while ($i < $imaxinloop) {
14084 $obj = $db->fetch_object($resql);
14085
14086 if ($obj->type == 'action') {
14087 $contactaction = new ActionComm($db);
14088 $contactaction->id = $obj->id;
14089 $result = $contactaction->fetchResources();
14090 if ($result < 0) {
14091 dol_print_error($db);
14092 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14093 }
14094
14095 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14096 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14097 $tododone = '';
14098 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14099 $tododone = 'todo';
14100 }
14101
14102 $histo[$numaction] = array(
14103 'type' => $obj->type,
14104 'tododone' => $tododone,
14105 'id' => $obj->id,
14106 'datestart' => $db->jdate($obj->dp),
14107 'dateend' => $db->jdate($obj->dp2),
14108 'note' => $obj->label,
14109 'message' => dol_htmlentitiesbr($obj->message),
14110 'percent' => $obj->percent,
14111
14112 'userid' => $obj->user_id,
14113 'login' => $obj->user_login,
14114 'userfirstname' => $obj->user_firstname,
14115 'userlastname' => $obj->user_lastname,
14116 'userphoto' => $obj->user_photo,
14117 'msg_from' => $obj->msg_from,
14118
14119 'contact_id' => $obj->fk_contact,
14120 'socpeopleassigned' => $contactaction->socpeopleassigned,
14121 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14122 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14123 'fk_element' => $obj->fk_element,
14124 'elementtype' => $obj->elementtype,
14125 // Type of event
14126 'acode' => $obj->acode,
14127 'alabel' => $obj->alabel,
14128 'libelle' => $obj->alabel, // deprecated
14129 'apicto' => $obj->apicto
14130 );
14131 } else {
14132 $histo[$numaction] = array(
14133 'type' => $obj->type,
14134 'tododone' => 'done',
14135 'id' => $obj->id,
14136 'datestart' => $db->jdate($obj->dp),
14137 'dateend' => $db->jdate($obj->dp2),
14138 'note' => $obj->label,
14139 'message' => dol_htmlentitiesbr($obj->message),
14140 'percent' => $obj->percent,
14141 'acode' => $obj->acode,
14142
14143 'userid' => $obj->user_id,
14144 'login' => $obj->user_login,
14145 'userfirstname' => $obj->user_firstname,
14146 'userlastname' => $obj->user_lastname,
14147 'userphoto' => $obj->user_photo
14148 );
14149 }
14150
14151 $numaction++;
14152 $i++;
14153 }
14154 } else {
14155 dol_print_error($db);
14156 }
14157 }
14158
14159 // Set $out to show events
14160 $out = '';
14161
14162 if (!isModEnabled('agenda')) {
14163 $langs->loadLangs(array("admin", "errors"));
14164 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14165 }
14166
14167 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14168 $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
14169
14170 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14171 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14172 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14173 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14174
14175 $formactions = new FormActions($db);
14176
14177 $actionstatic = new ActionComm($db);
14178 $userstatic = new User($db);
14179 $contactstatic = new Contact($db);
14180 $userGetNomUrlCache = array();
14181 $contactGetNomUrlCache = array();
14182
14183 $out .= '<div class="filters-container" >';
14184 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14185 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14186
14187 if ($objcon && get_class($objcon) == 'Contact' &&
14188 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14189 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14190 } else {
14191 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14192 }
14193 if (($filterobj && get_class($filterobj) == 'Societe')) {
14194 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14195 } else {
14196 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14197 }
14198
14199 $out .= "\n";
14200
14201 $out .= '<div class="div-table-responsive-no-min">';
14202 $out .= '<table class="noborder borderbottom centpercent">';
14203
14204 $out .= '<tr class="liste_titre">';
14205
14206 // Action column
14207 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14208 $out .= '<th class="liste_titre width50 middle">';
14209 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14210 $out .= $searchpicto;
14211 $out .= '</th>';
14212 }
14213
14214 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
14215
14216 $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14217 if ($donetodo) {
14218 $out .= '<th class="liste_titre"></th>';
14219 }
14220 $out .= '<th class="liste_titre">';
14221 $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14222 //$out .= img_picto($langs->trans("Type"), 'type');
14223 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', !getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : -1, 0, 0, 1, 'minwidth200imp');
14224 $out .= '</th>';
14225 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14226 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14227 $out .= '</th>';
14228
14229 // Action column
14230 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14231 $out .= '<th class="liste_titre width50 middle">';
14232 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14233 $out .= $searchpicto;
14234 $out .= '</th>';
14235 }
14236
14237 $out .= '</tr>';
14238
14239
14240 $out .= '</table>';
14241
14242 $out .= '</form>';
14243 $out .= '</div>';
14244
14245 $out .= "\n";
14246
14247 $out .= '<ul class="timeline">';
14248
14249 if ($donetodo) {
14250 $tmp = '';
14251 if ($filterobj instanceof Societe) {
14252 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14253 }
14254 if ($filterobj instanceof User) {
14255 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14256 }
14257 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14258 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14259 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14260 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14261 if ($filterobj instanceof Societe) {
14262 $tmp .= '</a>';
14263 }
14264 if ($filterobj instanceof User) {
14265 $tmp .= '</a>';
14266 }
14267 $out .= getTitleFieldOfList($tmp);
14268 }
14269
14270 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14271 $caction = new CActionComm($db);
14272 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14273
14274 $actualCycleDate = false;
14275
14276 // Loop on each event to show it
14277 foreach ($histo as $key => $value) {
14278 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14279
14280 $actionstatic->type_picto = $histo[$key]['apicto'];
14281 $actionstatic->type_code = $histo[$key]['acode'];
14282
14283 $labeltype = $actionstatic->type_code;
14284 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14285 $labeltype = 'AC_OTH';
14286 }
14287 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14288 $labeltype = $langs->trans("Message");
14289 } else {
14290 if (!empty($arraylist[$labeltype])) {
14291 $labeltype = $arraylist[$labeltype];
14292 }
14293 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14294 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14295 }
14296 }
14297
14298 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14299
14300 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14301
14302 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14303 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14304 $out .= '<!-- timeline time label -->';
14305 $out .= '<li class="time-label">';
14306 $out .= '<span class="timeline-badge-date">';
14307 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14308 $out .= '</span>';
14309 $out .= '</li>';
14310 $out .= '<!-- /.timeline-label -->';
14311 }
14312
14313
14314 $out .= '<!-- timeline item -->'."\n";
14315 $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
14316
14317 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14318 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14319 //$out .= $timelineicon;
14320 //var_dump($timelineicon);
14321 $out .= $typeicon;
14322
14323 $out .= '<div class="timeline-item">'."\n";
14324
14325 $out .= '<span class="time timeline-header-action2">';
14326
14327 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14328 $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").' ';
14329 $out .= $histo[$key]['id'];
14330 $out .= '</a> ';
14331 } else {
14332 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14333 }
14334
14335 if ($user->hasRight('agenda', 'allactions', 'create') ||
14336 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14337 $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).'">';
14338 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14339 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14340 $out .= '</a>';
14341 }
14342
14343 $out .= '</span>';
14344
14345 // Date
14346 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14347 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14348 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14349 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14350 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14351 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14352 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14353 } else {
14354 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14355 }
14356 }
14357 $late = 0;
14358 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14359 $late = 1;
14360 }
14361 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14362 $late = 1;
14363 }
14364 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14365 $late = 1;
14366 }
14367 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14368 $late = 1;
14369 }
14370 if ($late) {
14371 $out .= img_warning($langs->trans("Late")).' ';
14372 }
14373 $out .= "</span></span>\n";
14374
14375 // Ref
14376 $out .= '<h3 class="timeline-header">';
14377
14378 // Author of event
14379 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14380 if ($histo[$key]['userid'] > 0) {
14381 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14382 $userstatic->fetch($histo[$key]['userid']);
14383 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14384 }
14385 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14386 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14387 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14388 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14389 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14390 } else {
14391 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14392 }
14393 }
14394 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14395 }
14396 $out .= '</div>';
14397
14398 // Title
14399 $out .= ' <div class="messaging-title inline-block">';
14400 //$out .= $actionstatic->getTypePicto();
14401 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14402 $out .= $labeltype.' - ';
14403 }
14404
14405 $libelle = '';
14406 if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14407 $out .= $langs->trans('TicketNewMessage');
14408 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14409 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14410 } elseif (isset($histo[$key]['type'])) {
14411 if ($histo[$key]['type'] == 'action') {
14412 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14413 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14414 $libelle = $histo[$key]['note'];
14415 $actionstatic->id = $histo[$key]['id'];
14416 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14417 } elseif ($histo[$key]['type'] == 'mailing') {
14418 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14419 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14420 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14421 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14422 } else {
14423 $libelle .= $histo[$key]['note'];
14424 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14425 }
14426 }
14427
14428 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14429 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14430 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14431 } else {
14432 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14433 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14434 }
14435 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14436 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14437 }
14438 if ($link) {
14439 $out .= ' - '.$link;
14440 }
14441 }
14442
14443 $out .= '</div>';
14444
14445 $out .= '</h3>';
14446
14447 // Message
14448 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14449 && $actionstatic->code != 'AC_TICKET_CREATE'
14450 && $actionstatic->code != 'AC_TICKET_MODIFY'
14451 ) {
14452 $out .= '<div class="timeline-body wordbreak small">';
14453 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14454 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14455 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14456 $out .= '<div class="readmore-block --closed" >';
14457 $out .= ' <div class="readmore-block__excerpt">';
14458 $out .= dolPrintHTML($truncatedText);
14459 $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>';
14460 $out .= ' </div>';
14461 $out .= ' <div class="readmore-block__full-text" >';
14462 $out .= dolPrintHTML($histo[$key]['message']);
14463 $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>';
14464 $out .= ' </div>';
14465 $out .= '</div>';
14466 } else {
14467 $out .= dolPrintHTML($histo[$key]['message']);
14468 }
14469
14470 $out .= '</div>';
14471 }
14472
14473 // Timeline footer
14474 $footer = '';
14475
14476 // Contact for this action
14477 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14478 $contactList = '';
14479 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14480 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14481 $contact = new Contact($db);
14482 $contact->fetch($cid);
14483 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14484 } else {
14485 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14486 }
14487
14488 if ($contact) {
14489 $contactList .= !empty($contactList) ? ', ' : '';
14490 $contactList .= $contact->getNomUrl(1);
14491 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14492 if (!empty($contact->phone_pro)) {
14493 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14494 }
14495 }
14496 }
14497 }
14498
14499 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14500 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14501 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14502 $contact = new Contact($db);
14503 $result = $contact->fetch($histo[$key]['contact_id']);
14504 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14505 } else {
14506 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14507 $result = ($contact instanceof Contact) ? $contact->id : 0;
14508 }
14509
14510 if ($result > 0) {
14511 $footer .= $contact->getNomUrl(1);
14512 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14513 if (!empty($contact->phone_pro)) {
14514 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14515 }
14516 }
14517 }
14518 }
14519
14520 $documents = getActionCommEcmList($actionstatic);
14521 if (!empty($documents)) {
14522 $footer .= '<div class="timeline-documents-container">';
14523 foreach ($documents as $doc) {
14524 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14525 $footer .= ' data-id="'.$doc->id.'" ';
14526 $footer .= ' data-path="'.$doc->filepath.'"';
14527 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14528 $footer .= '>';
14529
14530 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14531 $mime = dol_mimetype($filePath);
14532 $file = $actionstatic->id.'/'.$doc->filename;
14533 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14534 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14535 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14536
14537 $mimeAttr = ' mime="'.$mime.'" ';
14538 $class = '';
14539 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14540 $class .= ' documentpreview';
14541 }
14542
14543 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14544 $footer .= img_mime($filePath).' '.$doc->filename;
14545 $footer .= '</a>';
14546
14547 $footer .= '</span>';
14548 }
14549 $footer .= '</div>';
14550 }
14551
14552 if (!empty($footer)) {
14553 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14554 }
14555
14556 $out .= '</div>'."\n"; // end timeline-item
14557
14558 $out .= '</li>';
14559 $out .= '<!-- END timeline item -->';
14560 }
14561
14562 $out .= "</ul>\n";
14563
14564 $out .= '<script>
14565 jQuery(document).ready(function () {
14566 $(document).on("click", "[data-read-more-action]", function(e){
14567 let readMoreBloc = $(this).closest(".readmore-block");
14568 if(readMoreBloc.length > 0){
14569 e.preventDefault();
14570 if($(this).attr("data-read-more-action") == "close"){
14571 readMoreBloc.addClass("--closed").removeClass("--open");
14572 $("html, body").animate({
14573 scrollTop: readMoreBloc.offset().top - 200
14574 }, 100);
14575 }else{
14576 readMoreBloc.addClass("--open").removeClass("--closed");
14577 }
14578 }
14579 });
14580 });
14581 </script>';
14582
14583
14584 if (empty($histo)) {
14585 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14586 }
14587 }
14588
14589 if ($noprint) {
14590 return $out;
14591 } else {
14592 print $out;
14593 }
14594}
14595
14606function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
14607{
14608 $m = array();
14609 if ($hourTime === 'getpost') {
14610 $hour = GETPOSTINT($prefix . 'hour');
14611 $minute = GETPOSTINT($prefix . 'minute');
14612 $second = GETPOSTINT($prefix . 'second');
14613 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
14614 $hour = intval($m[1]);
14615 $minute = intval($m[2]);
14616 $second = intval($m[3]);
14617 } else {
14618 $hour = $minute = $second = 0;
14619 }
14620 // normalize out of range values
14621 $hour = min($hour, 23);
14622 $minute = min($minute, 59);
14623 $second = min($second, 59);
14624 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
14625}
14626
14638function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14639{
14640 if ($timestamp === null) {
14641 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14642 }
14643 $TParam = array(
14644 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14645 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14646 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14647 );
14648 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14649 $TParam = array_merge($TParam, array(
14650 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14651 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
14652 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
14653 ));
14654 }
14655
14656 return '&' . http_build_query($TParam);
14657}
14658
14678function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14679{
14680 global $conf, $db, $langs, $hookmanager;
14681 global $action, $object;
14682
14683 if (!is_object($langs)) {
14684 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14685 $langs = new Translate('', $conf);
14686 $langs->setDefaultLang();
14687 }
14688
14689 $langs->load("errors");
14690
14691 if ($printheader) {
14692 if (function_exists("llxHeader")) {
14693 llxHeader('');
14694 } elseif (function_exists("llxHeaderVierge")) {
14695 llxHeaderVierge('');
14696 }
14697 }
14698
14699 print '<div class="error">';
14700 if (empty($message)) {
14701 print $langs->trans("ErrorRecordNotFound");
14702 } else {
14703 print $langs->trans($message);
14704 }
14705 print '</div>';
14706 print '<br>';
14707
14708 if (empty($showonlymessage)) {
14709 if (empty($hookmanager)) {
14710 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14711 $hookmanager = new HookManager($db);
14712 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
14713 $hookmanager->initHooks(array('main'));
14714 }
14715
14716 $parameters = array('message' => $message, 'params' => $params);
14717 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14718 print $hookmanager->resPrint;
14719 }
14720
14721 if ($printfooter && function_exists("llxFooter")) {
14722 llxFooter();
14723 }
14724 exit(0);
14725}
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:2010