dolibarr 20.0.5
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2022 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2019-2023 Thibault Foucart <support@ptibogxiv.net>
18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21 * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2023-2024 Joachim Kueter <git-jk@bloxera.com>
24 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 *
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 3 of the License, or
30 * (at your option) any later version.
31 *
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with this program. If not, see <https://www.gnu.org/licenses/>.
39 * or see https://www.gnu.org/
40 */
41
48include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
49
50// Function for better PHP x compatibility
51if (!function_exists('utf8_encode')) {
59 function utf8_encode($elements)
60 {
61 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
62 }
63}
64
65if (!function_exists('utf8_decode')) {
73 function utf8_decode($elements)
74 {
75 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
76 }
77}
78if (!function_exists('str_starts_with')) {
87 function str_starts_with($haystack, $needle)
88 {
89 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
90 }
91}
92if (!function_exists('str_ends_with')) {
101 function str_ends_with($haystack, $needle)
102 {
103 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
104 }
105}
106if (!function_exists('str_contains')) {
115 function str_contains($haystack, $needle)
116 {
117 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
118 }
119}
120
121
133function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
134{
135 global $conf;
136
137 if (!is_object($object) && empty($module)) {
138 return null;
139 }
140 if (empty($module) && !empty($object->element)) {
141 $module = $object->element;
142 }
143
144 // Special case for backward compatibility
145 if ($module == 'fichinter') {
146 $module = 'ficheinter';
147 } elseif ($module == 'invoice_supplier') {
148 $module = 'supplier_invoice';
149 } elseif ($module == 'order_supplier') {
150 $module = 'supplier_order';
151 }
152
153 // Get the relative path of directory
154 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
155 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
156 $s = '';
157 if ($mode != 'outputrel') {
158 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
159 }
160 if ($forobject && $object->id > 0) {
161 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
162 }
163 return $s;
164 } else {
165 return 'error-diroutput-not-defined-for-this-object='.$module;
166 }
167 } elseif ($mode == 'temp') {
168 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
169 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
170 } else {
171 return 'error-dirtemp-not-defined-for-this-object='.$module;
172 }
173 } else {
174 return 'error-bad-value-for-mode';
175 }
176}
177
187function getMultidirTemp($object, $module = '', $forobject = 0)
188{
189 return getMultidirOutput($object, $module, $forobject, 'temp');
190}
191
201function getMultidirVersion($object, $module = '', $forobject = 0)
202{
203 return getMultidirOutput($object, $module, $forobject, 'version');
204}
205
206
215function getDolGlobalString($key, $default = '')
216{
217 global $conf;
218 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
219}
220
230function getDolGlobalInt($key, $default = 0)
231{
232 global $conf;
233 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
234}
235
245function getDolUserString($key, $default = '', $tmpuser = null)
246{
247 if (empty($tmpuser)) {
248 global $user;
249 $tmpuser = $user;
250 }
251
252 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
253}
254
263function getDolUserInt($key, $default = 0, $tmpuser = null)
264{
265 if (empty($tmpuser)) {
266 global $user;
267 $tmpuser = $user;
268 }
269
270 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key: $default);
271}
272
273
283define(
284 'MODULE_MAPPING',
285 array(
286 // Map deprecated names to new names
287 'adherent' => 'member', // Has new directory
288 'member_type' => 'adherent_type', // No directory, but file called adherent_type
289 'banque' => 'bank', // Has new directory
290 'contrat' => 'contract', // Has new directory
291 'entrepot' => 'stock', // Has new directory
292 'projet' => 'project', // Has new directory
293 'categorie' => 'category', // Has old directory
294 'commande' => 'order', // Has old directory
295 'expedition' => 'shipping', // Has old directory
296 'facture' => 'invoice', // Has old directory
297 'fichinter' => 'intervention', // Has old directory
298 'ficheinter' => 'intervention', // Backup for 'fichinter'
299 'propale' => 'propal', // Has old directory
300 'socpeople' => 'contact', // Has old directory
301 'fournisseur' => 'supplier', // Has old directory
302
303 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
304 'product_price' => 'productprice', // NO directory
305 'product_fournisseur_price' => 'productsupplierprice', // NO directory
306 )
307);
308
315function isModEnabled($module)
316{
317 global $conf;
318
319 // Fix old names (map to new names)
320 $arrayconv = MODULE_MAPPING;
321 $arrayconvbis = array_flip(MODULE_MAPPING);
322
323 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
324 // Special cases: both use the same module.
325 $arrayconv['supplier_order'] = 'fournisseur';
326 $arrayconv['supplier_invoice'] = 'fournisseur';
327 }
328 // Special case.
329 // @TODO Replace isModEnabled('delivery_note') with
330 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
331 if ($module == 'delivery_note') {
332 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
333 return false;
334 } else {
335 $module = 'shipping';
336 }
337 }
338
339 $module_alt = $module;
340 if (!empty($arrayconv[$module])) {
341 $module_alt = $arrayconv[$module];
342 }
343 $module_bis = $module;
344 if (!empty($arrayconvbis[$module])) {
345 $module_bis = $arrayconvbis[$module];
346 }
347
348 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
349 //return !empty($conf->$module->enabled);
350}
351
358function isDolTms($timestamp)
359{
360 if ($timestamp === '') {
361 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"], LOG_NOTICE);
362 return false;
363 }
364 if (is_null($timestamp) || !is_numeric($timestamp)) {
365 return false;
366 }
367
368 return true;
369}
370
382function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
383{
384 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
385
386 $class = 'DoliDB'.ucfirst($type);
387 $db = new $class($type, $host, $user, $pass, $name, $port);
388 return $db;
389}
390
408function getEntity($element, $shared = 1, $currentobject = null)
409{
410 global $conf, $mc, $hookmanager, $object, $action, $db;
411
412 if (!is_object($hookmanager)) {
413 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
414 $hookmanager = new HookManager($db);
415 }
416
417 // fix different element names (France to English)
418 switch ($element) {
419 case 'projet':
420 $element = 'project';
421 break;
422 case 'contrat':
423 $element = 'contract';
424 break; // "/contrat/class/contrat.class.php"
425 case 'order_supplier':
426 $element = 'supplier_order';
427 break; // "/fourn/class/fournisseur.commande.class.php"
428 case 'invoice_supplier':
429 $element = 'supplier_invoice';
430 break; // "/fourn/class/fournisseur.facture.class.php"
431 }
432
433 if (is_object($mc)) {
434 $out = $mc->getEntity($element, $shared, $currentobject);
435 } else {
436 $out = '';
437 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
438 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
439 $addzero[] = 'c_holiday_types';
440 }
441 if (in_array($element, $addzero)) {
442 $out .= '0,';
443 }
444 $out .= ((int) $conf->entity);
445 }
446
447 // Manipulate entities to query on the fly
448 $parameters = array(
449 'element' => $element,
450 'shared' => $shared,
451 'object' => $object,
452 'currentobject' => $currentobject,
453 'out' => $out
454 );
455 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
456
457 if (is_numeric($reshook)) {
458 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
459 $out .= ','.$hookmanager->resPrint; // add
460 } elseif ($reshook == 1) {
461 $out = $hookmanager->resPrint; // replace
462 }
463 }
464
465 return $out;
466}
467
474function setEntity($currentobject)
475{
476 global $conf, $mc;
477
478 if (is_object($mc) && method_exists($mc, 'setEntity')) {
479 return $mc->setEntity($currentobject);
480 } else {
481 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
482 }
483}
484
491function isASecretKey($keyname)
492{
493 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
494}
495
496
503function num2Alpha($n)
504{
505 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
506 $r = chr($n % 26 + 0x41) . $r;
507 }
508 return $r;
509}
510
511
528function getBrowserInfo($user_agent)
529{
530 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
531
532 $name = 'unknown';
533 $version = '';
534 $os = 'unknown';
535 $phone = '';
536
537 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
538
539 $detectmobile = new Mobile_Detect(null, $user_agent);
540 $tablet = $detectmobile->isTablet();
541
542 if ($detectmobile->isMobile()) {
543 $phone = 'unknown';
544
545 // If phone/smartphone, we set phone os name.
546 if ($detectmobile->is('AndroidOS')) {
547 $os = $phone = 'android';
548 } elseif ($detectmobile->is('BlackBerryOS')) {
549 $os = $phone = 'blackberry';
550 } elseif ($detectmobile->is('iOS')) {
551 $os = 'ios';
552 $phone = 'iphone';
553 } elseif ($detectmobile->is('PalmOS')) {
554 $os = $phone = 'palm';
555 } elseif ($detectmobile->is('SymbianOS')) {
556 $os = 'symbian';
557 } elseif ($detectmobile->is('webOS')) {
558 $os = 'webos';
559 } elseif ($detectmobile->is('MaemoOS')) {
560 $os = 'maemo';
561 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
562 $os = 'windows';
563 }
564 }
565
566 // OS
567 if (preg_match('/linux/i', $user_agent)) {
568 $os = 'linux';
569 } elseif (preg_match('/macintosh/i', $user_agent)) {
570 $os = 'macintosh';
571 } elseif (preg_match('/windows/i', $user_agent)) {
572 $os = 'windows';
573 }
574
575 // Name
576 $reg = array();
577 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
578 $name = 'firefox';
579 $version = empty($reg[2]) ? '' : $reg[2];
580 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
581 $name = 'edge';
582 $version = empty($reg[2]) ? '' : $reg[2];
583 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
584 $name = 'chrome';
585 $version = empty($reg[2]) ? '' : $reg[2];
586 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
587 // we can have 'chrome (Mozilla...) chrome x.y' in one string
588 $name = 'chrome';
589 } elseif (preg_match('/iceweasel/i', $user_agent)) {
590 $name = 'iceweasel';
591 } elseif (preg_match('/epiphany/i', $user_agent)) {
592 $name = 'epiphany';
593 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
594 $name = 'safari';
595 $version = empty($reg[2]) ? '' : $reg[2];
596 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
597 // Safari is often present in string for mobile but its not.
598 $name = 'opera';
599 $version = empty($reg[2]) ? '' : $reg[2];
600 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
601 $name = 'ie';
602 $version = end($reg);
603 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
604 // MS products at end
605 $name = 'ie';
606 $version = end($reg);
607 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
608 // MS products at end
609 $name = 'textbrowser';
610 $version = empty($reg[3]) ? '' : $reg[3];
611 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
612 // MS products at end
613 $name = 'textbrowser';
614 $version = empty($reg[1]) ? '' : $reg[1];
615 }
616
617 if ($tablet) {
618 $layout = 'tablet';
619 } elseif ($phone) {
620 $layout = 'phone';
621 } else {
622 $layout = 'classic';
623 }
624
625 return array(
626 'browsername' => $name,
627 'browserversion' => $version,
628 'browseros' => $os,
629 'browserua' => $user_agent,
630 'layout' => $layout, // tablet, phone, classic
631 'phone' => $phone, // deprecated
632 'tablet' => $tablet // deprecated
633 );
634}
635
641function dol_shutdown()
642{
643 global $db;
644 $disconnectdone = false;
645 $depth = 0;
646 if (is_object($db) && !empty($db->connected)) {
647 $depth = $db->transaction_opened;
648 $disconnectdone = $db->close();
649 }
650 dol_syslog("--- End access to ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]).(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
651}
652
662function GETPOSTISSET($paramname)
663{
664 $isset = false;
665
666 $relativepathstring = $_SERVER["PHP_SELF"];
667 // Clean $relativepathstring
668 if (constant('DOL_URL_ROOT')) {
669 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
670 }
671 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
672 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
673 //var_dump($relativepathstring);
674 //var_dump($user->default_values);
675
676 // Code for search criteria persistence.
677 // Retrieve values if restore_lastsearch_values
678 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
679 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
680 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
681 if (is_array($tmp)) {
682 foreach ($tmp as $key => $val) {
683 if ($key == $paramname) { // We are on the requested parameter
684 $isset = true;
685 break;
686 }
687 }
688 }
689 }
690 // If there is saved contextpage, limit, page or mode
691 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
692 $isset = true;
693 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
694 $isset = true;
695 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
696 $isset = true;
697 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
698 $isset = true;
699 }
700 } else {
701 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
702 }
703
704 return $isset;
705}
706
715function GETPOSTISARRAY($paramname, $method = 0)
716{
717 // for $method test need return the same $val as GETPOST
718 if (empty($method)) {
719 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
720 } elseif ($method == 1) {
721 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
722 } elseif ($method == 2) {
723 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
724 } elseif ($method == 3) {
725 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
726 } else {
727 $val = 'BadFirstParameterForGETPOST';
728 }
729
730 return is_array($val);
731}
732
762function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
763{
764 global $mysoc, $user, $conf;
765
766 if (empty($paramname)) { // Explicit test for null for phan.
767 return 'BadFirstParameterForGETPOST';
768 }
769 if (empty($check)) {
770 dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and a 2nd param that is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
771 // Enable this line to know who call the GETPOST with '' $check parameter.
772 //var_dump(debug_backtrace()[0]);
773 }
774
775 if (empty($method)) {
776 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
777 } elseif ($method == 1) {
778 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
779 } elseif ($method == 2) {
780 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
781 } elseif ($method == 3) {
782 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
783 } else {
784 return 'BadThirdParameterForGETPOST';
785 }
786
787 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
788
789 if (empty($method) || $method == 3 || $method == 4) {
790 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
791 // Clean $relativepathstring
792 if (constant('DOL_URL_ROOT')) {
793 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
794 }
795 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
796 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
797 //var_dump($relativepathstring);
798 //var_dump($user->default_values);
799
800 // Code for search criteria persistence.
801 // Retrieve saved values if restore_lastsearch_values is set
802 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
803 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
804 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
805 if (is_array($tmp)) {
806 foreach ($tmp as $key => $val) {
807 if ($key == $paramname) { // We are on the requested parameter
808 $out = $val;
809 break;
810 }
811 }
812 }
813 }
814 // If there is saved contextpage, page or limit
815 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
816 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
817 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
818 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
819 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
820 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
821 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
822 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
823 }
824 } elseif (!isset($_GET['sortfield'])) {
825 // Else, retrieve default values if we are not doing a sort
826 // If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
827 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
828 // Search default value from $object->field
829 global $object;
830 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
831 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
832 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
833 $out = $object->fields[$paramname]['default'];
834 }
835 }
836 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
837 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
838 // Now search in setup to overwrite default values
839 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
840 if (isset($user->default_values[$relativepathstring]['createform'])) {
841 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
842 $qualified = 0;
843 if ($defkey != '_noquery_') {
844 $tmpqueryarraytohave = explode('&', $defkey);
845 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
846 $foundintru = 0;
847 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
848 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
849 $foundintru = 1;
850 }
851 }
852 if (!$foundintru) {
853 $qualified = 1;
854 }
855 //var_dump($defkey.'-'.$qualified);
856 } else {
857 $qualified = 1;
858 }
859
860 if ($qualified) {
861 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
862 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
863 break;
864 }
865 }
866 }
867 }
868 }
869 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
870 // Management of default search_filters and sort order
871 if (!empty($user->default_values)) {
872 // $user->default_values defined from menu 'Setup - Default values'
873 //var_dump($user->default_values[$relativepathstring]);
874 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
875 // Sorted on which fields ? ASC or DESC ?
876 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
877 // Even if paramname is sortfield, data are stored into ['sortorder...']
878 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
879 $qualified = 0;
880 if ($defkey != '_noquery_') {
881 $tmpqueryarraytohave = explode('&', $defkey);
882 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
883 $foundintru = 0;
884 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
885 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
886 $foundintru = 1;
887 }
888 }
889 if (!$foundintru) {
890 $qualified = 1;
891 }
892 //var_dump($defkey.'-'.$qualified);
893 } else {
894 $qualified = 1;
895 }
896
897 if ($qualified) {
898 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
899 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
900 if ($out) {
901 $out .= ', ';
902 }
903 if ($paramname == 'sortfield') {
904 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
905 }
906 if ($paramname == 'sortorder') {
907 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
908 }
909 }
910 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
911 }
912 }
913 }
914 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
915 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
916 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
917 continue;
918 }
919 $qualified = 0;
920 if ($defkey != '_noquery_') {
921 $tmpqueryarraytohave = explode('&', $defkey);
922 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
923 $foundintru = 0;
924 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
925 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
926 $foundintru = 1;
927 }
928 }
929 if (!$foundintru) {
930 $qualified = 1;
931 }
932 //var_dump($defkey.'-'.$qualified);
933 } else {
934 $qualified = 1;
935 }
936
937 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
938 // We must keep $_POST and $_GET here
939 if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
940 // We made a search from quick search menu, do we still use default filter ?
941 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
942 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
943 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
944 }
945 } else {
946 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
947 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
948 }
949 break;
950 }
951 }
952 }
953 }
954 }
955 }
956 }
957 }
958
959 // Substitution variables for GETPOST (used to get final url with variable parameters or final default value, when using variable parameters __XXX__ in the GET URL)
960 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
961 // We do this only if var is a GET. If it is a POST, may be we want to post the text with vars as the setup text.
962 '@phan-var-force string $paramname';
963 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
964 $reg = array();
965 $regreplace = array();
966 $maxloop = 20;
967 $loopnb = 0; // Protection against infinite loop
968 while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/i', $out, $reg) && ($loopnb < $maxloop)) { // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
969 $loopnb++;
970 $newout = '';
971
972 if ($reg[1] == 'DAY') {
973 $tmp = dol_getdate(dol_now(), true);
974 $newout = $tmp['mday'];
975 } elseif ($reg[1] == 'MONTH') {
976 $tmp = dol_getdate(dol_now(), true);
977 $newout = $tmp['mon'];
978 } elseif ($reg[1] == 'YEAR') {
979 $tmp = dol_getdate(dol_now(), true);
980 $newout = $tmp['year'];
981 } elseif ($reg[1] == 'PREVIOUS_DAY') {
982 $tmp = dol_getdate(dol_now(), true);
983 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
984 $newout = $tmp2['day'];
985 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
986 $tmp = dol_getdate(dol_now(), true);
987 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
988 $newout = $tmp2['month'];
989 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
990 $tmp = dol_getdate(dol_now(), true);
991 $newout = ($tmp['year'] - 1);
992 } elseif ($reg[1] == 'NEXT_DAY') {
993 $tmp = dol_getdate(dol_now(), true);
994 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
995 $newout = $tmp2['day'];
996 } elseif ($reg[1] == 'NEXT_MONTH') {
997 $tmp = dol_getdate(dol_now(), true);
998 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
999 $newout = $tmp2['month'];
1000 } elseif ($reg[1] == 'NEXT_YEAR') {
1001 $tmp = dol_getdate(dol_now(), true);
1002 $newout = ($tmp['year'] + 1);
1003 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1004 $newout = $mysoc->country_id;
1005 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1006 $newout = $user->id;
1007 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1008 $newout = $user->fk_user;
1009 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1010 $newout = $conf->entity;
1011 } elseif ($reg[1] == 'ID') {
1012 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1013 } else {
1014 $newout = 'REGREPLACE_'.$loopnb; // Key not found, we replace with temporary string to reload later
1015 $regreplace[$loopnb] = $reg[0];
1016 }
1017 //var_dump('__'.$reg[1].'__ -> '.$newout);
1018 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1019 }
1020 if (!empty($regreplace)) {
1021 foreach ($regreplace as $key => $value) {
1022 $out = preg_replace('/REGREPLACE_'.$key.'/', $value, $out);
1023 }
1024 }
1025 }
1026
1027 // Check type of variable and make sanitization according to this
1028 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1029 if (!is_array($out) || empty($out)) {
1030 $out = array();
1031 } else {
1032 $tmparray = explode(':', $check);
1033 if (!empty($tmparray[1])) {
1034 $tmpcheck = $tmparray[1];
1035 } else {
1036 $tmpcheck = 'alphanohtml';
1037 }
1038 foreach ($out as $outkey => $outval) {
1039 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1040 }
1041 }
1042 } else {
1043 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1044 // we use the < or > to make a search on a numeric value to do higher or lower so we can add a space to break html tags
1045 if (strpos($paramname, 'search_') === 0) {
1046 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1047 }
1048
1049 // @phan-suppress-next-line UnknownSanitizeType
1050 $out = sanitizeVal($out, $check, $filter, $options);
1051 }
1052
1053 // Sanitizing for special parameters.
1054 // Note: There is no reason to allow the backtopage/backtopageforcancel/backtopagejs, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1055 if (preg_match('/backtopage/', $paramname) || $paramname == 'backtolist' || $paramname == 'backtourl') {
1056 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1057 $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retrieve it after other replacements.
1058 do {
1059 $oldstringtoclean = $out;
1060 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1061 $out = preg_replace(array('/^[^\?]*%/'), '', $out); // We remove any % chars before the ?. Example in url: '/product/stock/card.php?action=create&backtopage=%2Fdolibarr_dev%2Fhtdocs%2Fpro%25duct%2Fcard.php%3Fid%3Dabc'
1062 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1063 } while ($oldstringtoclean != $out);
1064 }
1065
1066 // Code for search criteria persistence.
1067 // Save data into session if key start with 'search_'
1068 if (empty($method) || $method == 3 || $method == 4) {
1069 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1070 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1071
1072 // We save search key only if $out not empty that means:
1073 // - posted value not empty, or
1074 // - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
1075
1076 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1077 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1078 }
1079 }
1080 }
1081
1082 return $out;
1083}
1084
1094function GETPOSTINT($paramname, $method = 0)
1095{
1096 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1097}
1098
1099
1108function GETPOSTFLOAT($paramname, $rounding = '')
1109{
1110 // price2num() is used to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
1111 return (float) price2num(GETPOST($paramname), $rounding, 2);
1112}
1113
1114
1125function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1126{
1127 return sanitizeVal($out, $check, $filter, $options);
1128}
1129
1139function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1140{
1141 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1142 // Check is done after replacement
1143 switch ($check) {
1144 case 'none':
1145 break;
1146 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1147 if (!is_numeric($out)) {
1148 $out = '';
1149 }
1150 break;
1151 case 'intcomma':
1152 if (is_array($out)) {
1153 $out = implode(',', $out);
1154 }
1155 if (preg_match('/[^0-9,-]+/i', $out)) {
1156 $out = '';
1157 }
1158 break;
1159 case 'san_alpha':
1160 $out = filter_var($out, FILTER_SANITIZE_STRING);
1161 break;
1162 case 'email':
1163 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1164 break;
1165 case 'aZ':
1166 if (!is_array($out)) {
1167 $out = trim($out);
1168 if (preg_match('/[^a-z]+/i', $out)) {
1169 $out = '';
1170 }
1171 }
1172 break;
1173 case 'aZ09':
1174 if (!is_array($out)) {
1175 $out = trim($out);
1176 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1177 $out = '';
1178 }
1179 }
1180 break;
1181 case 'aZ09arobase': // great to sanitize $objecttype parameter
1182 if (!is_array($out)) {
1183 $out = trim($out);
1184 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1185 $out = '';
1186 }
1187 }
1188 break;
1189 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1190 if (!is_array($out)) {
1191 $out = trim($out);
1192 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1193 $out = '';
1194 }
1195 }
1196 break;
1197 case 'alpha': // No html and no ../ and "
1198 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1199 if (!is_array($out)) {
1200 $out = trim($out);
1201 do {
1202 $oldstringtoclean = $out;
1203 // Remove html tags
1204 $out = dol_string_nohtmltag($out, 0);
1205 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1206 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1207 // Remove also other dangerous string sequences
1208 // '../' or '..\' is dangerous because it allows dir transversals
1209 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1210 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1211 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1212 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1213 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1214 } while ($oldstringtoclean != $out);
1215 // keep lines feed
1216 }
1217 break;
1218 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email>". Less secured than 'alphanohtml'
1219 if (!is_array($out)) {
1220 $out = trim($out);
1221 do {
1222 $oldstringtoclean = $out;
1223 // Decode html entities
1224 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1225 // Refuse octal syntax \999, hexa syntax \x999 and unicode syntax \u{999} by replacing the \ into / (so if it is a \ for a windows path, it is still ok).
1226 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1227 // Remove also other dangerous string sequences
1228 // '../' or '..\' is dangerous because it allows dir transversals
1229 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1230 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1231 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1232 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1233 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1234 } while ($oldstringtoclean != $out);
1235 }
1236 break;
1237 case 'nohtml': // No html
1238 $out = dol_string_nohtmltag($out, 0);
1239 break;
1240 case 'restricthtmlnolink':
1241 case 'restricthtml': // Recommended for most html textarea
1242 case 'restricthtmlallowclass':
1243 case 'restricthtmlallowunvalid':
1244 $out = dol_htmlwithnojs($out, 1, $check);
1245 break;
1246 case 'custom':
1247 if (!empty($out)) {
1248 if (empty($filter)) {
1249 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1250 }
1251 if (is_null($options)) {
1252 $options = 0;
1253 }
1254 $out = filter_var($out, $filter, $options);
1255 }
1256 break;
1257 default:
1258 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1259 $out = GETPOST($out, 'alphanohtml');
1260 break;
1261 }
1262
1263 return $out;
1264}
1265
1266
1267if (!function_exists('dol_getprefix')) {
1278 function dol_getprefix($mode = '')
1279 {
1280 // If prefix is for email (we need to have $conf already loaded for this case)
1281 if ($mode == 'email') {
1282 global $conf;
1283
1284 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1285 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1286 return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1287 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1288 return $_SERVER["SERVER_NAME"];
1289 }
1290 }
1291
1292 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1293 if (!empty($conf->file->instance_unique_id)) {
1294 return sha1('dolibarr'.$conf->file->instance_unique_id);
1295 }
1296
1297 // For backward compatibility when instance_unique_id is not set
1298 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1299 }
1300
1301 // If prefix is for session (no need to have $conf loaded)
1302 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1303 $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
1304
1305 // The recommended value (may be not defined for old versions)
1306 if (!empty($tmp_instance_unique_id)) {
1307 return sha1('dolibarr'.$tmp_instance_unique_id);
1308 }
1309
1310 // For backward compatibility when instance_unique_id is not set
1311 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1312 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1313 } else {
1314 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1315 }
1316 }
1317}
1318
1329function dol_include_once($relpath, $classname = '')
1330{
1331 global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var']
1332
1333 $fullpath = dol_buildpath($relpath);
1334
1335 if (!file_exists($fullpath)) {
1336 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1337 return false;
1338 }
1339
1340 if (!empty($classname) && !class_exists($classname)) {
1341 return include $fullpath;
1342 } else {
1343 return include_once $fullpath;
1344 }
1345}
1346
1347
1361function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1362{
1363 global $conf;
1364
1365 $path = preg_replace('/^\//', '', $path);
1366
1367 if (empty($type)) { // For a filesystem path
1368 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1369 if (is_array($conf->file->dol_document_root)) {
1370 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1371 if ($key == 'main') {
1372 continue;
1373 }
1374 // if (@file_exists($dirroot.'/'.$path)) {
1375 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1376 $res = $dirroot.'/'.$path;
1377 return $res;
1378 }
1379 }
1380 }
1381 if ($returnemptyifnotfound) {
1382 // Not found into alternate dir
1383 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1384 return '';
1385 }
1386 }
1387 } else {
1388 // For an url path
1389 // We try to get local path of file on filesystem from url
1390 // Note that trying to know if a file on disk exist by forging path on disk from url
1391 // works only for some web server and some setup. This is bugged when
1392 // using proxy, rewriting, virtual path, etc...
1393 $res = '';
1394 if ($type == 1) {
1395 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1396 }
1397 if ($type == 2) {
1398 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1399 }
1400 if ($type == 3) {
1401 $res = DOL_URL_ROOT.'/'.$path;
1402 }
1403
1404 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1405 if ($key == 'main') {
1406 if ($type == 3) {
1407 /*global $dolibarr_main_url_root;*/
1408
1409 // Define $urlwithroot
1410 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1411 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1412 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1413
1414 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1415 }
1416 continue;
1417 }
1418 $regs = array();
1419 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1420 if (!empty($regs[1])) {
1421 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1422 //if (file_exists($dirroot.'/'.$regs[1])) {
1423 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1424 if ($type == 1) {
1425 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1426 } elseif ($type == 2) {
1427 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1428 } elseif ($type == 3) {
1429 /*global $dolibarr_main_url_root;*/
1430
1431 // Define $urlwithroot
1432 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1433 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1434 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1435
1436 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).$conf->file->dol_url_root[$key].'/'.$path; // Test on start with http is for old conf syntax
1437 }
1438 break;
1439 }
1440 }
1441 }
1442 }
1443
1444 return $res;
1445}
1446
1457function dol_get_object_properties($obj, $properties = [])
1458{
1459 // Get real properties using get_object_vars() if $properties is empty
1460 if (empty($properties)) {
1461 return get_object_vars($obj);
1462 }
1463
1464 $existingProperties = [];
1465 $realProperties = get_object_vars($obj);
1466
1467 // Get the real or magic property values
1468 foreach ($properties as $property) {
1469 if (array_key_exists($property, $realProperties)) {
1470 // Real property, add the value
1471 $existingProperties[$property] = $obj->{$property};
1472 } elseif (property_exists($obj, $property)) {
1473 // Magic property
1474 $existingProperties[$property] = $obj->{$property};
1475 }
1476 }
1477
1478 return $existingProperties;
1479}
1480
1481
1496function dol_clone($object, $native = 0)
1497{
1498 if ($native == 0) {
1499 // deprecated method, use the method with native = 2 instead
1500 $tmpsavdb = null;
1501 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1502 $tmpsavdb = $object->db;
1503 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1504 }
1505
1506 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1507
1508 if (!empty($tmpsavdb)) {
1509 $object->db = $tmpsavdb;
1510 }
1511 } elseif ($native == 2) {
1512 // recommended method to have a full isolated cloned object
1513 $myclone = new stdClass();
1514 $tmparray = get_object_vars($object); // return only public properties
1515
1516 if (is_array($tmparray)) {
1517 foreach ($tmparray as $propertykey => $propertyval) {
1518 if (is_scalar($propertyval) || is_array($propertyval)) {
1519 $myclone->$propertykey = $propertyval;
1520 }
1521 }
1522 }
1523 } else {
1524 $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (referring to the same target/variable)
1525 }
1526
1527 return $myclone;
1528}
1529
1539function dol_size($size, $type = '')
1540{
1541 global $conf;
1542 if (empty($conf->dol_optimize_smallscreen)) {
1543 return $size;
1544 }
1545 if ($type == 'width' && $size > 250) {
1546 return 250;
1547 } else {
1548 return 10;
1549 }
1550}
1551
1552
1564function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1565{
1566 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1567 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1568 // Char '/' and '\' are file delimiters.
1569 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1570 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1571 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1572 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1573 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1574 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1575 $tmp = str_replace('..', '', $tmp);
1576 return $tmp;
1577}
1578
1579
1591function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1592{
1593 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1594 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1595 // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1596 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1597 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1598 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1599 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1600 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1601 $tmp = str_replace('..', '', $tmp);
1602 return $tmp;
1603}
1604
1612function dol_sanitizeUrl($stringtoclean, $type = 1)
1613{
1614 // We clean string because some hacks try to obfuscate evil strings by inserting non printable chars. Example: 'java(ascci09)scr(ascii00)ipt' is processed like 'javascript' (whatever is place of evil ascii char)
1615 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1616 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1617 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1618 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1619
1620 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1621 if ($type == 1) {
1622 // removing : should disable links to external url like http:aaa)
1623 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1624 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1625 }
1626
1627 do {
1628 $oldstringtoclean = $stringtoclean;
1629 // removing '&colon' should disable links to external url like http:aaa)
1630 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1631 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1632 } while ($oldstringtoclean != $stringtoclean);
1633
1634 if ($type == 1) {
1635 // removing '//' should disable links to external url like //aaa or http//)
1636 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1637 }
1638
1639 return $stringtoclean;
1640}
1641
1648function dol_sanitizeEmail($stringtoclean)
1649{
1650 do {
1651 $oldstringtoclean = $stringtoclean;
1652 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1653 } while ($oldstringtoclean != $stringtoclean);
1654
1655 return $stringtoclean;
1656}
1657
1666function dol_string_unaccent($str)
1667{
1668 global $conf;
1669
1670 if (is_null($str)) {
1671 return '';
1672 }
1673
1674 if (utf8_check($str)) {
1675 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1676 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1677 return $transliterator->transliterate($str);
1678 }
1679 // See http://www.utf8-chartable.de/
1680 $string = rawurlencode($str);
1681 $replacements = array(
1682 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1683 '%C3%87' => 'C',
1684 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1685 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1686 '%C3%91' => 'N',
1687 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1688 '%C5%A0' => 'S',
1689 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1690 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1691 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1692 '%C3%A7' => 'c',
1693 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1694 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1695 '%C3%B1' => 'n',
1696 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1697 '%C5%A1' => 's',
1698 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1699 '%C3%BD' => 'y', '%C3%BF' => 'y'
1700 );
1701 $string = strtr($string, $replacements);
1702 return rawurldecode($string);
1703 } else {
1704 // See http://www.ascii-code.com/
1705 $string = strtr(
1706 $str,
1707 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1708 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1709 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1710 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1711 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1712 \xF9\xFA\xFB\xFC\xFD\xFF",
1713 "AAAAAAC
1714 EEEEIIIIDN
1715 OOOOOUUUY
1716 aaaaaaceeee
1717 iiiidnooooo
1718 uuuuyy"
1719 );
1720 $string = strtr($string, array("\xC4" => "Ae", "\xC6" => "AE", "\xD6" => "Oe", "\xDC" => "Ue", "\xDE" => "TH", "\xDF" => "ss", "\xE4" => "ae", "\xE6" => "ae", "\xF6" => "oe", "\xFC" => "ue", "\xFE" => "th"));
1721 return $string;
1722 }
1723}
1724
1738function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1739{
1740 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1741 if (empty($keepspaces)) {
1742 $forbidden_chars_to_replace[] = " ";
1743 }
1744 $forbidden_chars_to_remove = array();
1745 //$forbidden_chars_to_remove=array("(",")");
1746
1747 if (is_array($badcharstoreplace)) {
1748 $forbidden_chars_to_replace = $badcharstoreplace;
1749 }
1750 if (is_array($badcharstoremove)) {
1751 $forbidden_chars_to_remove = $badcharstoremove;
1752 }
1753
1754 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1755 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1756}
1757
1758
1772function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1773{
1774 if ($removetabcrlf) {
1775 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1776 } else {
1777 return preg_replace('/[\x00-\x08\x11-\x12\x14-\x1F\x7F]/u', '', $str); // /u operator should make UTF8 valid characters being ignored so are not included into the replace
1778 }
1779}
1780
1789function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1790{
1791 if (is_null($stringtoescape)) {
1792 return '';
1793 }
1794
1795 // escape quotes and backslashes, newlines, etc.
1796 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1797 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1798 if (empty($noescapebackslashn)) {
1799 $substitjs["\n"] = '\\n';
1800 $substitjs['\\'] = '\\\\';
1801 }
1802 if (empty($mode)) {
1803 $substitjs["'"] = "\\'";
1804 $substitjs['"'] = "\\'";
1805 } elseif ($mode == 1) {
1806 $substitjs["'"] = "\\'";
1807 } elseif ($mode == 2) {
1808 $substitjs['"'] = '\\"';
1809 } elseif ($mode == 3) {
1810 $substitjs["'"] = "\\'";
1811 $substitjs['"'] = "\\\"";
1812 }
1813 return strtr($stringtoescape, $substitjs);
1814}
1815
1825function dol_escape_uri($stringtoescape)
1826{
1827 return rawurlencode($stringtoescape);
1828}
1829
1836function dol_escape_json($stringtoescape)
1837{
1838 return str_replace('"', '\"', $stringtoescape);
1839}
1840
1848function dol_escape_php($stringtoescape, $stringforquotes = 2)
1849{
1850 if (is_null($stringtoescape)) {
1851 return '';
1852 }
1853
1854 if ($stringforquotes == 2) {
1855 return str_replace('"', "'", $stringtoescape);
1856 } elseif ($stringforquotes == 1) {
1857 // We remove the \ char.
1858 // If we allow the \ char, we can have $stringtoescape =
1859 // abc\';phpcodedanger; so the escapement will become
1860 // abc\\';phpcodedanger; and injecting this into
1861 // $a='...' will give $ac='abc\\';phpcodedanger;
1862 $stringtoescape = str_replace('\\', '', $stringtoescape);
1863 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1864 }
1865
1866 return 'Bad parameter for stringforquotes in dol_escape_php';
1867}
1868
1875function dol_escape_all($stringtoescape)
1876{
1877 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
1878}
1879
1886function dol_escape_xml($stringtoescape)
1887{
1888 return $stringtoescape;
1889}
1890
1898function dolPrintLabel($s)
1899{
1900 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1901}
1902
1912function dolPrintHTML($s, $allowiframe = 0)
1913{
1914 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1915}
1916
1924function dolPrintHTMLForAttribute($s)
1925{
1926 // The dol_htmlentitiesbr will convert simple text into html
1927 // The dol_escape_htmltag will escape html chars.
1928 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
1929}
1930
1939function dolPrintHTMLForTextArea($s, $allowiframe = 0)
1940{
1941 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
1942}
1943
1950function dolPrintPassword($s)
1951{
1952 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
1953}
1954
1955
1972function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1973{
1974 if ($noescapetags == 'common') {
1975 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
1976 // Add also html5 tags
1977 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
1978 }
1979 if ($cleanalsojavascript) {
1980 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1981 }
1982
1983 // escape quotes and backslashes, newlines, etc.
1984 if ($escapeonlyhtmltags) {
1985 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1986 } else {
1987 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); // This decode &egrave; into è so string is UTF8 (but &#39; is not decoded).
1988 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE', $tmp);
1989 }
1990 if (!$keepb) {
1991 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
1992 }
1993 if (!$keepn) {
1994 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1995 } elseif ($keepn == -1) {
1996 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
1997 }
1998
1999 if ($escapeonlyhtmltags) {
2000 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2001 } else {
2002 // Escape tags to keep
2003 $tmparrayoftags = array();
2004 if ($noescapetags) {
2005 $tmparrayoftags = explode(',', $noescapetags);
2006 }
2007 if (count($tmparrayoftags)) {
2008 $reg = array();
2009 $tmp = str_ireplace('__DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
2010
2011 foreach ($tmparrayoftags as $tagtoreplace) {
2012 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2013 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2014 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2015
2016 // For case of tag with attribute
2017 do {
2018 $tmpold = $tmp;
2019
2020 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2021 // We want to pprotect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() lafter
2022 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2023 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes); // TODO Try to remove this
2024 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2025 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2026 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2027 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2028 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2029 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2030
2031 // TODO Test the replacement by using a memory array for attributes to restore them
2032 // TODO Test a tag like '<a href="https://mydomain.com/aaa%20bbb">abc</a>'
2033 //$tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2034 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2035 }
2036 // TODO This may be already in previous case ? Try to remove this.
2037 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)(\s+)\/>/', $tmp, $reg)) {
2038 // We want to protect the attribute part ... in '<xxx ... />' to avoid transformation by htmlentities() lafter
2039 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must not have [ ] inside the attribute string
2040 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2041 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2042 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content.
2043 //$tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+'.preg_quote($reg[1], '/').'\s+\/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2044 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].$reg[3].'/>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2045 }
2046
2047 $diff = strcmp($tmpold, $tmp);
2048 } while ($diff);
2049 }
2050 }
2051
2052 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // Convert & into &amp; and more...
2053
2054 //print $result;
2055
2056 if (count($tmparrayoftags)) {
2057 foreach ($tmparrayoftags as $tagtoreplace) {
2058 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2059 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2060 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2061 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2062 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2063 }
2064
2065 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2066 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2067 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2068 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2069 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2070 }
2071
2072 $result = str_ireplace('__SIMPLEQUOTE', '&#39;', $result);
2073
2074 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2075
2076 return $result;
2077 }
2078}
2079
2087function dol_strtolower($string, $encoding = "UTF-8")
2088{
2089 if (function_exists('mb_strtolower')) {
2090 return mb_strtolower($string, $encoding);
2091 } else {
2092 return strtolower($string);
2093 }
2094}
2095
2104function dol_strtoupper($string, $encoding = "UTF-8")
2105{
2106 if (function_exists('mb_strtoupper')) {
2107 return mb_strtoupper($string, $encoding);
2108 } else {
2109 return strtoupper($string);
2110 }
2111}
2112
2121function dol_ucfirst($string, $encoding = "UTF-8")
2122{
2123 if (function_exists('mb_substr')) {
2124 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2125 } else {
2126 return ucfirst($string);
2127 }
2128}
2129
2138function dol_ucwords($string, $encoding = "UTF-8")
2139{
2140 if (function_exists('mb_convert_case')) {
2141 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2142 } else {
2143 return ucwords($string);
2144 }
2145}
2146
2169function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2170{
2171 global $conf, $user, $debugbar;
2172
2173 // If syslog module enabled
2174 if (!isModEnabled('syslog')) {
2175 return;
2176 }
2177
2178 // Check if we are into execution of code of a website
2179 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2180 global $website, $websitekey;
2181 if (is_object($website) && !empty($website->ref)) {
2182 $suffixinfilename .= '_website_'.$website->ref;
2183 } elseif (!empty($websitekey)) {
2184 $suffixinfilename .= '_website_'.$websitekey;
2185 }
2186 }
2187
2188 // Check if we have a forced suffix
2189 if (defined('USESUFFIXINLOG')) {
2190 $suffixinfilename .= constant('USESUFFIXINLOG');
2191 }
2192
2193 if ($ident < 0) {
2194 foreach ($conf->loghandlers as $loghandlerinstance) {
2195 $loghandlerinstance->setIdent($ident);
2196 }
2197 }
2198
2199 if (!empty($message)) {
2200 // Test log level
2201 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2202 $logLevels = array(LOG_EMERG => 'EMERG', LOG_ALERT => 'ALERT', LOG_CRIT => 'CRITICAL', LOG_ERR => 'ERR', LOG_WARNING => 'WARN', LOG_NOTICE => 'NOTICE', LOG_INFO => 'INFO', LOG_DEBUG => 'DEBUG');
2203
2204 if (!array_key_exists($level, $logLevels)) {
2205 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2206 $level = $logLevels[LOG_ERR];
2207 }
2208 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2209 return;
2210 }
2211
2212 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2213 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2214 }
2215
2216 // If adding log inside HTML page is required
2217 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2218 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2219 $ospid = sprintf("%7s", dol_trunc(getmypid(), 7, 'right', 'UTF-8', 1));
2220 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2221
2222 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2223 }
2224
2225 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2226 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2227 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2228 print "\n\n<!-- Log start\n";
2229 print dol_escape_htmltag($message)."\n";
2230 print "Log end -->\n";
2231 }
2232
2233 $data = array(
2234 'message' => $message,
2235 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2236 'level' => $level,
2237 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2238 'ip' => false,
2239 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2240 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2241 );
2242
2243 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2244 if (!empty($remoteip)) {
2245 $data['ip'] = $remoteip;
2246 // This is when server run behind a reverse proxy
2247 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2248 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2249 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2250 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2251 }
2252 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2253 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2254 $data['ip'] = $_SERVER['SERVER_ADDR'];
2255 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2256 // This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defines it).
2257 $data['ip'] = $_SERVER['COMPUTERNAME'];
2258 } else {
2259 $data['ip'] = '???';
2260 }
2261
2262 if (!empty($_SERVER['USERNAME'])) {
2263 // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2264 $data['osuser'] = $_SERVER['USERNAME'];
2265 } elseif (!empty($_SERVER['LOGNAME'])) {
2266 // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2267 $data['osuser'] = $_SERVER['LOGNAME'];
2268 }
2269
2270 // Loop on each log handler and send output
2271 foreach ($conf->loghandlers as $loghandlerinstance) {
2272 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2273 continue;
2274 }
2275 $loghandlerinstance->export($data, $suffixinfilename);
2276 }
2277 unset($data);
2278 }
2279
2280 if ($ident > 0) {
2281 foreach ($conf->loghandlers as $loghandlerinstance) {
2282 $loghandlerinstance->setIdent($ident);
2283 }
2284 }
2285}
2286
2298function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2299{
2300 global $langs, $db;
2301
2302 $form = new Form($db);
2303
2304 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2305 if (empty($templatenameforexport)) {
2306 $templatenameforexport = 'website_'.$website->ref;
2307 }
2308
2309 $out = '';
2310 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2311
2312 // for generate popup
2313 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2314 $out .= 'jQuery(document).ready(function () {';
2315 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2316 $out .= ' var dialogHtml = \'';
2317
2318 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2319 $dialogcontent .= ' <div style="margin-top: 20px;">';
2320 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2321 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2322 $dialogcontent .= ' </div>';
2323 $dialogcontent .= ' <br>';
2324 $dialogcontent .= ' <div style="margin-top: 20px;">';
2325 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2326 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2327 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2328 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2329 $dialogcontent .= ' <input type="text" autofocus name="export_path" id="export-path-'.$name.'" placeholder="'.$langs->trans('ExportPath').'" style="width:400px " value="'.dol_escape_htmltag($templatenameforexport).'"/><br>';
2330 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2331 $dialogcontent .= ' </form>';
2332 $dialogcontent .= ' </div>';
2333 $dialogcontent .= ' </div>';
2334
2335 $out .= dol_escape_js($dialogcontent);
2336
2337 $out .= '\';';
2338
2339
2340 // Add the content of the dialog to the body of the page
2341 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2342 $out .= ' if ($dialog.length > 0) {
2343 $dialog.remove();
2344 }
2345 jQuery("body").append(dialogHtml);';
2346
2347 // Configuration of popup
2348 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2349 $out .= ' autoOpen: false,';
2350 $out .= ' modal: true,';
2351 $out .= ' height: 290,';
2352 $out .= ' width: "40%",';
2353 $out .= ' title: "' . dol_escape_js($label) . '",';
2354 $out .= ' });';
2355
2356 // Simulate a click on the original "submit" input to export the site.
2357 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2358 $out .= ' console.log("Clic on exportsite.");';
2359 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2360 $out .= ' console.log("element founded:", target.length > 0);';
2361 $out .= ' if (target.length > 0) { target.click(); }';
2362 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2363 $out .= ' });';
2364
2365 // open popup
2366 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2367 $out .= ' return false;';
2368 $out .= ' });';
2369 $out .= '});';
2370 $out .= '</script>';
2371
2372 return $out;
2373}
2374
2375
2392function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2393{
2394 global $conf;
2395
2396 if (strpos($url, '?') > 0) {
2397 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2398 } else {
2399 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2400 }
2401
2402 $out = '';
2403
2404 $backtopagejsfieldsid = '';
2405 $backtopagejsfieldslabel = '';
2406 if ($backtopagejsfields) {
2407 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2408 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2409 $backtopagejsfields = $name.":".$backtopagejsfields;
2410 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2411 } else {
2412 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2413 }
2414 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2415 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2416 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2417 }
2418
2419 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2420 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2421 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2422 if (empty($conf->use_javascript_ajax)) {
2423 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2424 } elseif ($jsonopen) {
2425 $out .= ' href="#" onclick="'.$jsonopen.'"';
2426 } else {
2427 $out .= ' href="#"';
2428 }
2429 $out .= '>'.$buttonstring.'</a>';
2430
2431 if (!empty($conf->use_javascript_ajax)) {
2432 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2433 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2434 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2435 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2436 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2437
2438 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2439 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2440 jQuery(document).ready(function () {
2441 jQuery(".button_'.$name.'").click(function () {
2442 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2443 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2444 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2445 $tmpdialog.dialog({
2446 autoOpen: false,
2447 modal: true,
2448 height: (window.innerHeight - 150),
2449 width: \'80%\',
2450 title: \''.dol_escape_js($label).'\',
2451 open: function (event, ui) {
2452 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2453 },
2454 close: function (event, ui) {
2455 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2456 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2457 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2458 if (returnedid != "" && returnedid != "div for returned id") {
2459 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2460 }
2461 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2462 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2463 }
2464 }
2465 });
2466
2467 $tmpdialog.dialog(\'open\');
2468 return false;
2469 });
2470 });
2471 </script>';
2472 }
2473 return $out;
2474}
2475
2492function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2493{
2494 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2495}
2496
2513function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2514{
2515 global $conf, $langs, $hookmanager;
2516
2517 // Show title
2518 $showtitle = 1;
2519 if (!empty($conf->dol_optimize_smallscreen)) {
2520 $showtitle = 0;
2521 }
2522
2523 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2524
2525 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2526 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2527 }
2528
2529 // Show right part
2530 if ($morehtmlright) {
2531 $out .= '<div class="inline-block floatright tabsElem">'.$morehtmlright.'</div>'; // Output right area first so when space is missing, text is in front of tabs and not under.
2532 }
2533
2534 // Show title
2535 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2536 $limittitle = 30;
2537 $out .= '<a class="tabTitle">';
2538 if ($picto) {
2539 $noprefix = $pictoisfullpath;
2540 if (strpos($picto, 'fontawesome_') !== false) {
2541 $noprefix = 1;
2542 }
2543 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2544 }
2545 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2546 $out .= '</a>';
2547 }
2548
2549 // Show tabs
2550
2551 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2552 $maxkey = -1;
2553 if (is_array($links) && !empty($links)) {
2554 $keys = array_keys($links);
2555 if (count($keys)) {
2556 $maxkey = max($keys);
2557 }
2558 }
2559
2560 // Show tabs
2561 // if =0 we don't use the feature
2562 if (empty($limittoshow)) {
2563 $limittoshow = (!getDolGlobalString('MAIN_MAXTABS_IN_CARD') ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2564 }
2565 if (!empty($conf->dol_optimize_smallscreen)) {
2566 $limittoshow = 2;
2567 }
2568
2569 $displaytab = 0;
2570 $nbintab = 0;
2571 $popuptab = 0;
2572 $outmore = '';
2573 for ($i = 0; $i <= $maxkey; $i++) {
2574 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2575 // If active tab is already present
2576 if ($i >= $limittoshow) {
2577 $limittoshow--;
2578 }
2579 }
2580 }
2581
2582 for ($i = 0; $i <= $maxkey; $i++) {
2583 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2584 $isactive = true;
2585 } else {
2586 $isactive = false;
2587 }
2588
2589 if ($i < $limittoshow || $isactive) {
2590 // Output entry with a visible tab
2591 $out .= '<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((!$isactive && getDolGlobalString('MAIN_HIDE_INACTIVETAB_ON_PRINT')) ? ' hideonprint' : '').'"><!-- id tab = '.(empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])).' -->';
2592
2593 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2594 if (!empty($links[$i][0])) {
2595 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2596 } else {
2597 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2598 }
2599 } elseif (!empty($links[$i][1])) {
2600 //print "x $i $active ".$links[$i][2]." z";
2601 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2602 if (!empty($links[$i][0])) {
2603 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2604 $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tab inline-block valignmiddle'.($morecss ? ' '.$morecss : '').(!empty($links[$i][5]) ? ' '.$links[$i][5] : '').'" href="'.$links[$i][0].'" title="'.dol_escape_htmltag($titletoshow).'">';
2605 }
2606 $out .= $links[$i][1];
2607 if (!empty($links[$i][0])) {
2608 $out .= '</a>'."\n";
2609 }
2610 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2611 $out .= '</div>';
2612 }
2613
2614 $out .= '</div>';
2615 } else {
2616 // Add entry into the combo popup with the other tabs
2617 if (!$popuptab) {
2618 $popuptab = 1;
2619 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2620 }
2621 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2622 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2623 if (!empty($links[$i][0])) {
2624 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2625 } else {
2626 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2627 }
2628 } elseif (!empty($links[$i][1])) {
2629 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2630 $outmore .= preg_replace('/([a-z])\|([a-z])/i', '\\1 | \\2', $links[$i][1]); // Replace x|y with x | y to allow wrap on long composed texts.
2631 $outmore .= '</a>'."\n";
2632 }
2633 $outmore .= '</div>';
2634
2635 $nbintab++;
2636 }
2637 $displaytab = $i;
2638 }
2639 if ($popuptab) {
2640 $outmore .= '</div>';
2641 }
2642
2643 if ($popuptab) { // If there is some tabs not shown
2644 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2645 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2646 $widthofpopup = 200;
2647
2648 $tabsname = $moretabssuffix;
2649 if (empty($tabsname)) {
2650 $tabsname = str_replace("@", "", $picto);
2651 }
2652 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2653 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2654 $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a></div>'; // Do not use "reposition" class in the "More".
2655 }
2656 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2657 $out .= $outmore;
2658 $out .= '</div>';
2659 $out .= '<div></div>';
2660 $out .= "</div>\n";
2661
2662 $out .= '<script nonce="'.getNonce().'">';
2663 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2664 var x = this.offsetLeft, y = this.offsetTop;
2665 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2666 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2667 $('#moretabsList".$tabsname."').css('".$right."','8px');
2668 }
2669 $('#moretabsList".$tabsname."').css('".$left."','auto');
2670 });
2671 ";
2672 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2673 $out .= "</script>";
2674 }
2675
2676 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2677 $out .= "</div>\n";
2678 }
2679
2680 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2681 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom')));
2682 $out .= '">'."\n";
2683 }
2684 if (!empty($dragdropfile)) {
2685 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2686 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2687 }
2688 $parameters = array('tabname' => $active, 'out' => $out);
2689 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2690 if ($reshook > 0) {
2691 $out = $hookmanager->resPrint;
2692 }
2693
2694 return $out;
2695}
2696
2704function dol_fiche_end($notab = 0)
2705{
2706 print dol_get_fiche_end($notab);
2707}
2708
2715function dol_get_fiche_end($notab = 0)
2716{
2717 if (!$notab || $notab == -1) {
2718 return "\n</div>\n";
2719 } else {
2720 return '';
2721 }
2722}
2723
2743function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2744{
2745 global $conf, $form, $user, $langs, $hookmanager, $action;
2746
2747 $error = 0;
2748
2749 $maxvisiblephotos = 1;
2750 $showimage = 1;
2751 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2752 // @phan-suppress-next-line PhanUndeclaredMethod
2753 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2754 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2755 $showbarcode = 0;
2756 }
2757 $modulepart = 'unknown';
2758
2759 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2760 $modulepart = $object->element;
2761 } elseif ($object->element == 'member') {
2762 $modulepart = 'memberphoto';
2763 } elseif ($object->element == 'user') {
2764 $modulepart = 'userphoto';
2765 }
2766
2767 if (class_exists("Imagick")) {
2768 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2769 $modulepart = $object->element;
2770 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2771 $modulepart = 'ficheinter';
2772 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2773 $modulepart = 'contract';
2774 } elseif ($object->element == 'order_supplier') {
2775 $modulepart = 'supplier_order';
2776 } elseif ($object->element == 'invoice_supplier') {
2777 $modulepart = 'supplier_invoice';
2778 }
2779 }
2780
2781 if ($object->element == 'product') {
2783 '@phan-var-force Product $object';
2784 $width = 80;
2785 $cssclass = 'photowithmargin photoref';
2786 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2787 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2788 if ($conf->browser->layout == 'phone') {
2789 $maxvisiblephotos = 1;
2790 }
2791 if ($showimage) {
2792 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 1, $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2793 } else {
2794 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2795 $nophoto = '';
2796 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2797 } else { // Show no photo link
2798 $nophoto = '/public/theme/common/nophoto.png';
2799 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2800 }
2801 }
2802 } elseif ($object->element == 'category') {
2804 '@phan-var-force Categorie $object';
2805 $width = 80;
2806 $cssclass = 'photowithmargin photoref';
2807 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2808 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2809 if ($conf->browser->layout == 'phone') {
2810 $maxvisiblephotos = 1;
2811 }
2812 if ($showimage) {
2813 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('category', $conf->categorie->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2814 } else {
2815 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2816 $nophoto = '';
2817 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2818 } else { // Show no photo link
2819 $nophoto = '/public/theme/common/nophoto.png';
2820 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2821 }
2822 }
2823 } elseif ($object->element == 'bom') {
2825 '@phan-var-force Bom $object';
2826 $width = 80;
2827 $cssclass = 'photowithmargin photoref';
2828 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2829 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2830 if ($conf->browser->layout == 'phone') {
2831 $maxvisiblephotos = 1;
2832 }
2833 if ($showimage) {
2834 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('bom', $conf->bom->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2835 } else {
2836 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2837 $nophoto = '';
2838 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2839 } else { // Show no photo link
2840 $nophoto = '/public/theme/common/nophoto.png';
2841 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2842 }
2843 }
2844 } elseif ($object->element == 'ticket') {
2845 $width = 80;
2846 $cssclass = 'photoref';
2848 '@phan-var-force Ticket $object';
2849 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2850 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2851 if ($conf->browser->layout == 'phone') {
2852 $maxvisiblephotos = 1;
2853 }
2854
2855 if ($showimage) {
2856 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2857 if ($object->nbphoto > 0) {
2858 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2859 } else {
2860 $showimage = 0;
2861 }
2862 }
2863 if (!$showimage) {
2864 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2865 $nophoto = '';
2866 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2867 } else { // Show no photo link
2868 $nophoto = img_picto('No photo', 'object_ticket');
2869 $morehtmlleft .= '<!-- No photo to show -->';
2870 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2871 $morehtmlleft .= $nophoto;
2872 $morehtmlleft .= '</div></div>';
2873 }
2874 }
2875 } else {
2876 if ($showimage) {
2877 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2878 $phototoshow = '';
2879 // Check if a preview file is available
2880 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2881 $objectref = dol_sanitizeFileName($object->ref);
2882 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2883 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2884 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2885 $subdir .= ((!empty($subdir) && !preg_match('/\/$/', $subdir)) ? '/' : '').$objectref; // the objectref dir is not included into get_exdir when used with level=2, so we add it at end
2886 } else {
2887 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2888 }
2889 if (empty($subdir)) {
2890 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2891 }
2892
2893 $filepath = $dir_output.$subdir."/";
2894
2895 $filepdf = $filepath.$objectref.".pdf";
2896 $relativepath = $subdir.'/'.$objectref.'.pdf';
2897
2898 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2899 $fileimage = $filepdf.'_preview.png';
2900 $relativepathimage = $relativepath.'_preview.png';
2901
2902 $pdfexists = file_exists($filepdf);
2903
2904 // If PDF file exists
2905 if ($pdfexists) {
2906 // Conversion du PDF en image png si fichier png non existent
2907 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2908 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2909 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2910 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2911 if ($ret < 0) {
2912 $error++;
2913 }
2914 }
2915 }
2916 }
2917
2918 if ($pdfexists && !$error) {
2919 $heightforphotref = 80;
2920 if (!empty($conf->dol_optimize_smallscreen)) {
2921 $heightforphotref = 60;
2922 }
2923 // If the preview file is found
2924 if (file_exists($fileimage)) {
2925 $phototoshow = '<div class="photoref">';
2926 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2927 $phototoshow .= '</div>';
2928 }
2929 }
2930 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
2931 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2932 }
2933
2934 if ($phototoshow) {
2935 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2936 $morehtmlleft .= $phototoshow;
2937 $morehtmlleft .= '</div>';
2938 }
2939 }
2940
2941 if (empty($phototoshow)) { // Show No photo link (picto of object)
2942 if ($object->element == 'action') {
2943 $width = 80;
2944 $cssclass = 'photorefcenter';
2945 $nophoto = img_picto('No photo', 'title_agenda');
2946 } else {
2947 $width = 14;
2948 $cssclass = 'photorefcenter';
2949 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
2950 $prefix = 'object_';
2951 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
2952 $picto = 'project'; // instead of projectpub
2953 }
2954 if (strpos($picto, 'fontawesome_') !== false) {
2955 $prefix = '';
2956 }
2957 $nophoto = img_picto('No photo', $prefix.$picto);
2958 }
2959 $morehtmlleft .= '<!-- No photo to show -->';
2960 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2961 $morehtmlleft .= $nophoto;
2962 $morehtmlleft .= '</div></div>';
2963 }
2964 }
2965 }
2966
2967 // Show barcode
2968 if ($showbarcode) {
2969 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2970 }
2971
2972 if ($object->element == 'societe') {
2973 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2974 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2975 } else {
2976 $morehtmlstatus .= $object->getLibStatut(6);
2977 }
2978 } elseif ($object->element == 'product') {
2979 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2980 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2981 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2982 } else {
2983 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2984 }
2985 $morehtmlstatus .= ' &nbsp; ';
2986 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2987 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2988 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2989 } else {
2990 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2991 }
2992 } elseif (in_array($object->element, array('salary'))) {
2993 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
2994 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2995 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
2996 }
2997 $morehtmlstatus .= $tmptxt;
2998 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
2999 $totalallpayments = $object->getSommePaiement(0);
3000 $totalallpayments += $object->getSumCreditNotesUsed(0);
3001 $totalallpayments += $object->getSumDepositsUsed(0);
3002 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3003 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3004 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3005 }
3006 $morehtmlstatus .= $tmptxt;
3007 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3008 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3009 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3010 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3011 }
3012 $morehtmlstatus .= $tmptxt;
3013 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3014 if ($object->statut == 0) {
3015 $morehtmlstatus .= $object->getLibStatut(5);
3016 } else {
3017 $morehtmlstatus .= $object->getLibStatut(4);
3018 }
3019 } elseif ($object->element == 'facturerec') {
3020 '@phan-var-force FactureRec $object';
3021 if ($object->frequency == 0) {
3022 $morehtmlstatus .= $object->getLibStatut(2);
3023 } else {
3024 $morehtmlstatus .= $object->getLibStatut(5);
3025 }
3026 } elseif ($object->element == 'project_task') {
3027 $object->fk_statut = 1;
3028 if ($object->progress > 0) {
3029 $object->fk_statut = 2;
3030 }
3031 if ($object->progress >= 100) {
3032 $object->fk_statut = 3;
3033 }
3034 $tmptxt = $object->getLibStatut(5);
3035 $morehtmlstatus .= $tmptxt; // No status on task
3036 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3037 $tmptxt = $object->getLibStatut(6);
3038 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3039 $tmptxt = $object->getLibStatut(5);
3040 }
3041 $morehtmlstatus .= $tmptxt;
3042 }
3043
3044 // Add if object was dispatched "into accountancy"
3045 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3046 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3047 if (method_exists($object, 'getVentilExportCompta')) {
3048 $accounted = $object->getVentilExportCompta();
3049 $langs->load("accountancy");
3050 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3051 }
3052 }
3053
3054 // Add alias for thirdparty
3055 if (!empty($object->name_alias)) {
3056 '@phan-var-force Societe $object';
3057 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3058 }
3059
3060 // Add label
3061 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3062 if (!empty($object->label)) {
3063 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3064 }
3065 }
3066 // Show address and email
3067 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3068 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3069 if ($moreaddress) {
3070 $morehtmlref .= '<div class="refidno refaddress">';
3071 $morehtmlref .= $moreaddress;
3072 $morehtmlref .= '</div>';
3073 }
3074 }
3075 if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) {
3076 $morehtmlref .= '<div style="clear: both;"></div>';
3077 $morehtmlref .= '<div class="refidno opacitymedium">';
3078 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3079 $morehtmlref .= '</div>';
3080 }
3081
3082 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3083 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3084 if ($reshook < 0) {
3085 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3086 } elseif (empty($reshook)) {
3087 $morehtmlref .= $hookmanager->resPrint;
3088 } elseif ($reshook > 0) {
3089 $morehtmlref = $hookmanager->resPrint;
3090 }
3091
3092 // $morehtml is the right part (link "Back to list")
3093 // $morehtmlleft is the picto or photo of banner
3094 // $morehtmlstatus is part under the status
3095 // $morehtmlright is part of htmlright
3096
3097 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3098 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3099 print '</div>';
3100 print '<div class="underrefbanner clearboth"></div>';
3101}
3102
3112function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3113{
3114 global $langs;
3115 $ret = '';
3116 if ($fieldrequired) {
3117 $ret .= '<span class="fieldrequired">';
3118 }
3119 $ret .= '<label for="'.$fieldkey.'">';
3120 $ret .= $langs->trans($langkey);
3121 $ret .= '</label>';
3122 if ($fieldrequired) {
3123 $ret .= '</span>';
3124 }
3125 return $ret;
3126}
3127
3135function dol_bc($var, $moreclass = '')
3136{
3137 global $bc;
3138 $ret = ' '.$bc[$var];
3139 if ($moreclass) {
3140 $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
3141 }
3142 return $ret;
3143}
3144
3158function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3159{
3160 global $langs, $hookmanager;
3161
3162 $ret = '';
3163 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3164
3165 // See format of addresses on https://en.wikipedia.org/wiki/Address
3166 // Address
3167 if (empty($mode)) {
3168 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3169 }
3170 // Zip/Town/State
3171 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3172 // US: title firstname name \n address lines \n town, state, zip \n country
3173 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3174 $ret .= (($ret && $town) ? $sep : '').$town;
3175
3176 if (!empty($object->state)) {
3177 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3178 }
3179 if (!empty($object->zip)) {
3180 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3181 }
3182 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3183 // UK: title firstname name \n address lines \n town state \n zip \n country
3184 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3185 $ret .= ($ret ? $sep : '').$town;
3186 if (!empty($object->state)) {
3187 $ret .= ($ret ? ", " : '').$object->state;
3188 }
3189 if (!empty($object->zip)) {
3190 $ret .= ($ret ? $sep : '').$object->zip;
3191 }
3192 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3193 // ES: title firstname name \n address lines \n zip town \n state \n country
3194 $ret .= ($ret ? $sep : '').$object->zip;
3195 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3196 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3197 if (!empty($object->state)) {
3198 $ret .= $sep.$object->state;
3199 }
3200 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3201 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3202 // See https://www.sljfaq.org/afaq/addresses.html
3203 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3204 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3205 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3206 // IT: title firstname name\n address lines \n zip town state_code \n country
3207 $ret .= ($ret ? $sep : '').$object->zip;
3208 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3209 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3210 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3211 } else {
3212 // Other: title firstname name \n address lines \n zip town[, state] \n country
3213 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3214 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3215 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3216 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3217 $ret .= ($ret ? ", " : '').$object->state;
3218 }
3219 }
3220
3221 if (!is_object($outputlangs)) {
3222 $outputlangs = $langs;
3223 }
3224 if ($withcountry) {
3225 $langs->load("dict");
3226 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3227 }
3228 if ($hookmanager) {
3229 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3230 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3231 if ($reshook > 0) {
3232 $ret = '';
3233 }
3234 $ret .= $hookmanager->resPrint;
3235 }
3236
3237 return $ret;
3238}
3239
3240
3241
3251function dol_strftime($fmt, $ts = false, $is_gmt = false)
3252{
3253 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3254 return dol_print_date($ts, $fmt, $is_gmt);
3255 } else {
3256 return 'Error date outside supported range';
3257 }
3258}
3259
3281function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3282{
3283 global $conf, $langs;
3284
3285 // If date undefined or "", we return ""
3286 if (dol_strlen($time) == 0) {
3287 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3288 }
3289
3290 if ($tzoutput === 'auto') {
3291 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3292 }
3293
3294 // Clean parameters
3295 $to_gmt = false;
3296 $offsettz = $offsetdst = 0;
3297 if ($tzoutput) {
3298 $to_gmt = true; // For backward compatibility
3299 if (is_string($tzoutput)) {
3300 if ($tzoutput == 'tzserver') {
3301 $to_gmt = false;
3302 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3303 // @phan-suppress-next-line PhanPluginRedundantAssignment
3304 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3305 // @phan-suppress-next-line PhanPluginRedundantAssignment
3306 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3307 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3308 $to_gmt = true;
3309 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3310
3311 if (class_exists('DateTimeZone')) {
3312 $user_date_tz = new DateTimeZone($offsettzstring);
3313 $user_dt = new DateTime();
3314 $user_dt->setTimezone($user_date_tz);
3315 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3316 $offsettz = $user_dt->getOffset(); // should include dst ?
3317 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3318 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3319 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3320 }
3321 }
3322 }
3323 }
3324 if (!is_object($outputlangs)) {
3325 $outputlangs = $langs;
3326 }
3327 if (!$format) {
3328 $format = 'daytextshort';
3329 }
3330
3331 // Do we have to reduce the length of date (year on 2 chars) to save space.
3332 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3333 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3334 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3335 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3336 if ($formatwithoutreduce != $format) {
3337 $format = $formatwithoutreduce;
3338 $reduceformat = 1;
3339 } // so format 'dayreduceformat' is processed like day
3340
3341 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3342 // TODO Add format daysmallyear and dayhoursmallyear
3343 if ($format == 'day') {
3344 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3345 } elseif ($format == 'hour') {
3346 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3347 } elseif ($format == 'hourduration') {
3348 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3349 } elseif ($format == 'daytext') {
3350 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3351 } elseif ($format == 'daytextshort') {
3352 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3353 } elseif ($format == 'dayhour') {
3354 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3355 } elseif ($format == 'dayhoursec') {
3356 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3357 } elseif ($format == 'dayhourtext') {
3358 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3359 } elseif ($format == 'dayhourtextshort') {
3360 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3361 } elseif ($format == 'dayhourlog') {
3362 // Format not sensitive to language
3363 $format = '%Y%m%d%H%M%S';
3364 } elseif ($format == 'dayhourlogsmall') {
3365 // Format not sensitive to language
3366 $format = '%y%m%d%H%M';
3367 } elseif ($format == 'dayhourldap') {
3368 $format = '%Y%m%d%H%M%SZ';
3369 } elseif ($format == 'dayhourxcard') {
3370 $format = '%Y%m%dT%H%M%SZ';
3371 } elseif ($format == 'dayxcard') {
3372 $format = '%Y%m%d';
3373 } elseif ($format == 'dayrfc') {
3374 $format = '%Y-%m-%d'; // DATE_RFC3339
3375 } elseif ($format == 'dayhourrfc') {
3376 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3377 } elseif ($format == 'standard') {
3378 $format = '%Y-%m-%d %H:%M:%S';
3379 }
3380
3381 if ($reduceformat) {
3382 $format = str_replace('%Y', '%y', $format);
3383 $format = str_replace('yyyy', 'yy', $format);
3384 }
3385
3386 // Clean format
3387 if (preg_match('/%b/i', $format)) { // There is some text to translate
3388 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3389 $format = str_replace('%b', '__b__', $format);
3390 $format = str_replace('%B', '__B__', $format);
3391 }
3392 if (preg_match('/%a/i', $format)) { // There is some text to translate
3393 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3394 $format = str_replace('%a', '__a__', $format);
3395 $format = str_replace('%A', '__A__', $format);
3396 }
3397
3398 // Analyze date
3399 $reg = array();
3400 if (preg_match('/^([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])$/i', (string) $time, $reg)) { // Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
3401 dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]));
3402 return '';
3403 } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', (string) $time, $reg)) { // Still available to solve problems in extrafields of type date
3404 // This part of code should not be used anymore.
3405 dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]), LOG_WARNING);
3406 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3407 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3408 $syear = (!empty($reg[1]) ? $reg[1] : '');
3409 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3410 $sday = (!empty($reg[3]) ? $reg[3] : '');
3411 $shour = (!empty($reg[4]) ? $reg[4] : '');
3412 $smin = (!empty($reg[5]) ? $reg[5] : '');
3413 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3414
3415 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
3416
3417 if ($to_gmt) {
3418 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3419 } else {
3420 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3421 }
3422 $dtts = new DateTime();
3423 $dtts->setTimestamp($time);
3424 $dtts->setTimezone($tzo);
3425 $newformat = str_replace(
3426 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3427 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3428 $format
3429 );
3430 $ret = $dtts->format($newformat);
3431 $ret = str_replace(
3432 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3433 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3434 $ret
3435 );
3436 } else {
3437 // Date is a timestamps
3438 if ($time < 100000000000) { // Protection against bad date values
3439 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3440
3441 if ($to_gmt) {
3442 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3443 } else {
3444 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3445 }
3446 $dtts = new DateTime();
3447 $dtts->setTimestamp($timetouse);
3448 $dtts->setTimezone($tzo);
3449 $newformat = str_replace(
3450 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3451 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3452 $format
3453 );
3454 $ret = $dtts->format($newformat);
3455 $ret = str_replace(
3456 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3457 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3458 $ret
3459 );
3460 //var_dump($ret);exit;
3461 } else {
3462 $ret = 'Bad value '.$time.' for date';
3463 }
3464 }
3465
3466 if (preg_match('/__b__/i', $format)) {
3467 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3468
3469 if ($to_gmt) {
3470 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3471 } else {
3472 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3473 }
3474 $dtts = new DateTime();
3475 $dtts->setTimestamp($timetouse);
3476 $dtts->setTimezone($tzo);
3477 $month = (int) $dtts->format("m");
3478 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3479 if ($encodetooutput) {
3480 $monthtext = $outputlangs->transnoentities('Month'.$month);
3481 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3482 } else {
3483 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3484 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3485 }
3486 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3487 $ret = str_replace('__b__', $monthtextshort, $ret);
3488 $ret = str_replace('__B__', $monthtext, $ret);
3489 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3490 //return $ret;
3491 }
3492 if (preg_match('/__a__/i', $format)) {
3493 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3494 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3495
3496 if ($to_gmt) {
3497 $tzo = new DateTimeZone('UTC');
3498 } else {
3499 $tzo = new DateTimeZone(date_default_timezone_get());
3500 }
3501 $dtts = new DateTime();
3502 $dtts->setTimestamp($timetouse);
3503 $dtts->setTimezone($tzo);
3504 $w = $dtts->format("w");
3505 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3506
3507 $ret = str_replace('__A__', $dayweek, $ret);
3508 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3509 }
3510
3511 return $ret;
3512}
3513
3514
3535function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3536{
3537 if ($timestamp === '') {
3538 return array();
3539 }
3540
3541 $datetimeobj = new DateTime();
3542 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3543 if ($forcetimezone) {
3544 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3545 }
3546 $arrayinfo = array(
3547 'year' => ((int) date_format($datetimeobj, 'Y')),
3548 'mon' => ((int) date_format($datetimeobj, 'm')),
3549 'mday' => ((int) date_format($datetimeobj, 'd')),
3550 'wday' => ((int) date_format($datetimeobj, 'w')),
3551 'yday' => ((int) date_format($datetimeobj, 'z')),
3552 'hours' => ((int) date_format($datetimeobj, 'H')),
3553 'minutes' => ((int) date_format($datetimeobj, 'i')),
3554 'seconds' => ((int) date_format($datetimeobj, 's')),
3555 '0' => $timestamp
3556 );
3557
3558 return $arrayinfo;
3559}
3560
3582function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3583{
3584 global $conf;
3585 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3586
3587 if ($gm === 'auto') {
3588 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3589 }
3590 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3591
3592 // Clean parameters
3593 if ($hour == -1 || empty($hour)) {
3594 $hour = 0;
3595 }
3596 if ($minute == -1 || empty($minute)) {
3597 $minute = 0;
3598 }
3599 if ($second == -1 || empty($second)) {
3600 $second = 0;
3601 }
3602
3603 // Check parameters
3604 if ($check) {
3605 if (!$month || !$day) {
3606 return '';
3607 }
3608 if ($day > 31) {
3609 return '';
3610 }
3611 if ($month > 12) {
3612 return '';
3613 }
3614 if ($hour < 0 || $hour > 24) {
3615 return '';
3616 }
3617 if ($minute < 0 || $minute > 60) {
3618 return '';
3619 }
3620 if ($second < 0 || $second > 60) {
3621 return '';
3622 }
3623 }
3624
3625 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3626 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3627 $localtz = new DateTimeZone($default_timezone);
3628 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3629 // We use dol_tz_string first because it is more reliable.
3630 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3631 try {
3632 $localtz = new DateTimeZone($default_timezone);
3633 } catch (Exception $e) {
3634 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3635 $default_timezone = @date_default_timezone_get();
3636 }
3637 } elseif (strrpos($gm, "tz,") !== false) {
3638 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3639 try {
3640 $localtz = new DateTimeZone($timezone);
3641 } catch (Exception $e) {
3642 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3643 }
3644 }
3645
3646 if (empty($localtz)) {
3647 $localtz = new DateTimeZone('UTC');
3648 }
3649 //var_dump($localtz);
3650 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3651 $dt = new DateTime('now', $localtz);
3652 $dt->setDate((int) $year, (int) $month, (int) $day);
3653 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3654 $date = $dt->getTimestamp(); // should include daylight saving time
3655 //var_dump($date);
3656 return $date;
3657}
3658
3659
3670function dol_now($mode = 'auto')
3671{
3672 $ret = 0;
3673
3674 if ($mode === 'auto') {
3675 $mode = 'gmt';
3676 }
3677
3678 if ($mode == 'gmt') {
3679 $ret = time(); // Time for now at greenwich.
3680 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3681 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3682 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3683 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3684 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3685 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3686 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3687 // $ret=dol_now('gmt')+($tzsecond*3600);
3688 //}
3689 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3690 // Time for now with user timezone added
3691 //print 'time: '.time();
3692 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3693 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3694 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3695 }
3696
3697 return $ret;
3698}
3699
3700
3709function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3710{
3711 global $conf, $langs;
3712 $level = 1024;
3713
3714 if (!empty($conf->dol_optimize_smallscreen)) {
3715 $shortunit = 1;
3716 }
3717
3718 // Set value text
3719 if (empty($shortvalue) || $size < ($level * 10)) {
3720 $ret = $size;
3721 $textunitshort = $langs->trans("b");
3722 $textunitlong = $langs->trans("Bytes");
3723 } else {
3724 $ret = round($size / $level, 0);
3725 $textunitshort = $langs->trans("Kb");
3726 $textunitlong = $langs->trans("KiloBytes");
3727 }
3728 // Use long or short text unit
3729 if (empty($shortunit)) {
3730 $ret .= ' '.$textunitlong;
3731 } else {
3732 $ret .= ' '.$textunitshort;
3733 }
3734
3735 return $ret;
3736}
3737
3748function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3749{
3750 global $langs;
3751
3752 if (empty($url)) {
3753 return '';
3754 }
3755
3756 $linkstart = '<a href="';
3757 if (!preg_match('/^http/i', $url)) {
3758 $linkstart .= 'http://';
3759 }
3760 $linkstart .= $url;
3761 $linkstart .= '"';
3762 if ($target) {
3763 $linkstart .= ' target="'.$target.'"';
3764 }
3765 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3766 $linkstart .= '>';
3767
3768 $link = '';
3769 if (!preg_match('/^http/i', $url)) {
3770 $link .= 'http://';
3771 }
3772 $link .= dol_trunc($url, $max);
3773
3774 $linkend = '</a>';
3775
3776 if ($morecss == 'float') { // deprecated
3777 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3778 } else {
3779 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3780 }
3781}
3782
3795function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3796{
3797 global $user, $langs, $hookmanager;
3798
3799 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3800 //$showinvalid = 1; $email = 'rrrrr';
3801
3802 $newemail = dol_escape_htmltag($email);
3803
3804 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3805 $withpicto = 0;
3806 }
3807
3808 if (empty($email)) {
3809 return '&nbsp;';
3810 }
3811
3812 if (!empty($addlink)) {
3813 $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="';
3814 if (!preg_match('/^mailto:/i', $email)) {
3815 $newemail .= 'mailto:';
3816 }
3817 $newemail .= $email;
3818 $newemail .= '">';
3819
3820 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3821
3822 $newemail .= dol_trunc($email, $max);
3823 $newemail .= '</a>';
3824 if ($showinvalid && !isValidEmail($email)) {
3825 $langs->load("errors");
3826 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3827 }
3828
3829 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3830 $type = 'AC_EMAIL';
3831 $linktoaddaction = '';
3832 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3833 $linktoaddaction = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.urlencode($type).'&amp;contactid='.((int) $cid).'&amp;socid='.((int) $socid).'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3834 }
3835 if ($linktoaddaction) {
3836 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3837 }
3838 }
3839 } else {
3840 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3841
3842 if ($showinvalid && !isValidEmail($email)) {
3843 $langs->load("errors");
3844 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3845 }
3846 }
3847
3848 //$rep = '<div class="nospan" style="margin-right: 10px">';
3849 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3850 //$rep .= '</div>';
3851 $rep = $newemail;
3852
3853 if ($hookmanager) {
3854 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3855
3856 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3857 if ($reshook > 0) {
3858 $rep = '';
3859 }
3860 $rep .= $hookmanager->resPrint;
3861 }
3862
3863 return $rep;
3864}
3865
3871function getArrayOfSocialNetworks()
3872{
3873 global $conf, $db;
3874
3875 $socialnetworks = array();
3876 // Enable caching of array
3877 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3878 $cachekey = 'socialnetworks_' . $conf->entity;
3879 $dataretrieved = dol_getcache($cachekey);
3880 if (!is_null($dataretrieved)) {
3881 $socialnetworks = $dataretrieved;
3882 } else {
3883 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3884 $sql .= " WHERE entity=".$conf->entity;
3885 $resql = $db->query($sql);
3886 if ($resql) {
3887 while ($obj = $db->fetch_object($resql)) {
3888 $socialnetworks[$obj->code] = array(
3889 'rowid' => $obj->rowid,
3890 'label' => $obj->label,
3891 'url' => $obj->url,
3892 'icon' => $obj->icon,
3893 'active' => $obj->active,
3894 );
3895 }
3896 }
3897 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3898 }
3899 return $socialnetworks;
3900}
3901
3912function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3913{
3914 global $user, $langs;
3915
3916 $htmllink = $value;
3917
3918 if (empty($value)) {
3919 return '&nbsp;';
3920 }
3921
3922 if (!empty($type)) {
3923 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3924 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3925 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3926 if ($type == 'skype') {
3927 $htmllink .= dol_escape_htmltag($value);
3928 $htmllink .= '&nbsp; <a href="skype:';
3929 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3930 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3931 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3932 $htmllink .= '</a><a href="skype:';
3933 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3934 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3935 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3936 $htmllink .= '</a>';
3937 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3938 $addlink = 'AC_SKYPE';
3939 $link = '';
3940 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
3941 $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$addlink.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3942 }
3943 $htmllink .= ($link ? ' '.$link : '');
3944 }
3945 } else {
3946 if (!empty($dictsocialnetworks[$type]['url'])) {
3947 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3948 if (preg_match('/^https?:\/\//i', $value)) {
3949 $htmllink .= '<a href="'.dol_sanitizeUrl($value, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3950 } else {
3951 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3952 }
3953 } else {
3954 $htmllink .= dol_escape_htmltag($value);
3955 }
3956 }
3957 $htmllink .= '</div>';
3958 }
3959 return $htmllink;
3960}
3961
3971function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
3972{
3973 global $mysoc;
3974
3975 if (empty($profID) || empty($profIDtype)) {
3976 return '';
3977 }
3978 if (empty($countrycode)) {
3979 $countrycode = $mysoc->country_code;
3980 }
3981 $newProfID = $profID;
3982 $id = substr($profIDtype, -1);
3983 $ret = '';
3984 if (strtoupper($countrycode) == 'FR') {
3985 // France
3986 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
3987
3988 if ($id == 1 && dol_strlen($newProfID) == 9) {
3989 // SIREN (ex: 123 123 123)
3990 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
3991 }
3992 if ($id == 2 && dol_strlen($newProfID) == 14) {
3993 // SIRET (ex: 123 123 123 12345)
3994 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
3995 }
3996 if ($id == 3 && dol_strlen($newProfID) == 5) {
3997 // NAF/APE (ex: 69.20Z)
3998 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
3999 }
4000 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4001 // TVA intracommunautaire (ex: FR12 123 123 123)
4002 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4003 }
4004 }
4005 if (!empty($addcpButton)) {
4006 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4007 } else {
4008 $ret = $newProfID;
4009 }
4010 return $ret;
4011}
4012
4028function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = '')
4029{
4030 global $conf, $user, $langs, $mysoc, $hookmanager;
4031
4032 // Clean phone parameter
4033 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4034 if (empty($phone)) {
4035 return '';
4036 }
4037 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4038 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4039 }
4040 if (empty($countrycode) && is_object($mysoc)) {
4041 $countrycode = $mysoc->country_code;
4042 }
4043
4044 // Short format for small screens
4045 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4046 $separ = '';
4047 }
4048
4049 $newphone = $phone;
4050 $newphonewa = $phone;
4051 if (strtoupper($countrycode) == "FR") {
4052 // France
4053 if (dol_strlen($phone) == 10) {
4054 $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);
4055 } elseif (dol_strlen($phone) == 7) {
4056 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4057 } elseif (dol_strlen($phone) == 9) {
4058 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4059 } elseif (dol_strlen($phone) == 11) {
4060 $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);
4061 } elseif (dol_strlen($phone) == 12) {
4062 $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);
4063 } elseif (dol_strlen($phone) == 13) {
4064 $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);
4065 }
4066 } elseif (strtoupper($countrycode) == "CA") {
4067 if (dol_strlen($phone) == 10) {
4068 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4069 }
4070 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4071 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4072 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4073 }
4074 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4075 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4076 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4077 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4078 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4079 }
4080 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4081 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4082 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4083 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4084 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4085 }
4086 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4087 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4088 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4089 }
4090 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4091 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4092 $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);
4093 }
4094 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4095 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4096 $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);
4097 }
4098 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4099 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4100 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4101 }
4102 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4103 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4104 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4105 }
4106 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4107 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4108 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4109 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4110 $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);
4111 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4112 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4113 }
4114 } elseif (strtoupper($countrycode) == "ML") {//Mali
4115 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4116 $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);
4117 }
4118 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4119 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4120 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4121 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4122 $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);
4123 }
4124 } elseif (strtoupper($countrycode) == "MU") {
4125 //Maurice
4126 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4127 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4128 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4129 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4130 }
4131 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4132 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4133 $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);
4134 }
4135 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4136 if (dol_strlen($phone) == 12) {//ex: +963_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 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4139 $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);
4140 }
4141 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4142 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4143 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4144 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4145 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4146 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4147 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4148 }
4149 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4150 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4151 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4152 }
4153 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4154 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4155 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4156 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4157 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4158 }
4159 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4160 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4161 $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);
4162 }
4163 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4164 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4165 $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);
4166 }
4167 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4168 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4169 $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);
4170 }
4171 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4172 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4173 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4174 }
4175 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4176 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4177 $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);
4178 }
4179 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4180 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4181 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4182 }
4183 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4184 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4185 $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);
4186 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4187 $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);
4188 }
4189 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4190 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4191 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4192 }
4193 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4194 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4195 $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);
4196 }
4197 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4198 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4199 $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);
4200 }
4201 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4202 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4203 $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);
4204 }
4205 } elseif (strtoupper($countrycode) == "IT") {//Italie
4206 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4207 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4208 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4209 $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);
4210 }
4211 } elseif (strtoupper($countrycode) == "AU") {
4212 //Australie
4213 if (dol_strlen($phone) == 12) {
4214 //ex: +61_A_BCDE_FGHI
4215 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4216 }
4217 } elseif (strtoupper($countrycode) == "LU") {
4218 // Luxembourg
4219 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4220 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4221 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4222 $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);
4223 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4224 $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);
4225 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4226 $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);
4227 }
4228 } elseif (strtoupper($countrycode) == "PE") {
4229 // Peru
4230 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4231 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4232 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4233 $newphonewa = '+51'.$newphone;
4234 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4235 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4236 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4237 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4238 $newphonewa = $newphone;
4239 $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);
4240 }
4241 }
4242
4243 $newphoneastart = $newphoneaend = '';
4244 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4245 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
4246 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4247 $newphoneaend .= '</a>';
4248 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4249 if (empty($user->clicktodial_loaded)) {
4250 $user->fetch_clicktodial();
4251 }
4252
4253 // Define urlmask
4254 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4255 if (!empty($user->clicktodial_url)) {
4256 $urlmask = $user->clicktodial_url;
4257 }
4258
4259 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4260 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4261 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4262 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4263 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4264 // Those lines are for substitution
4265 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4266 '__PHONETO__' => urlencode($phone),
4267 '__LOGIN__' => $clicktodial_login,
4268 '__PASS__' => $clicktodial_password);
4269 $url = make_substitutions($url, $substitarray);
4270 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4271 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4272 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4273 $newphoneaend = '</a>';
4274 } else {
4275 // Old method
4276 $newphoneastart = '<a href="'.$url.'"';
4277 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4278 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4279 }
4280 $newphoneastart .= '>';
4281 $newphoneaend .= '</a>';
4282 }
4283 }
4284
4285 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4286 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4287 $type = 'AC_TEL';
4288 $addlinktoagenda = '';
4289 if ($addlink == 'AC_FAX') {
4290 $type = 'AC_FAX';
4291 }
4292 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4293 $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>';
4294 }
4295 if ($addlinktoagenda) {
4296 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4297 }
4298 }
4299 }
4300
4301 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4302 // Link to Whatsapp
4303 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4304 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4305 }
4306
4307 if (empty($titlealt)) {
4308 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4309 }
4310 $rep = '';
4311
4312 if ($hookmanager) {
4313 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4314 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4315 $rep .= $hookmanager->resPrint;
4316 }
4317 if (empty($reshook)) {
4318 $picto = '';
4319 if ($withpicto) {
4320 if ($withpicto == 'fax') {
4321 $picto = 'phoning_fax';
4322 } elseif ($withpicto == 'phone') {
4323 $picto = 'phoning';
4324 } elseif ($withpicto == 'mobile') {
4325 $picto = 'phoning_mobile';
4326 } else {
4327 $picto = '';
4328 }
4329 }
4330 if ($adddivfloat == 1) {
4331 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">';
4332 } elseif (empty($adddivfloat)) {
4333 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').' style="margin-right: 10px;">';
4334 }
4335
4336 $rep .= $newphoneastart;
4337 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4338 if ($separ != 'hidenum') {
4339 $rep .= ($withpicto ? ' ' : '').$newphone;
4340 }
4341 $rep .= $newphoneaend;
4342
4343 if ($adddivfloat == 1) {
4344 $rep .= '</div>';
4345 } elseif (empty($adddivfloat)) {
4346 $rep .= '</span>';
4347 }
4348 }
4349
4350 return $rep;
4351}
4352
4360function dol_print_ip($ip, $mode = 0)
4361{
4362 global $langs;
4363
4364 $ret = '';
4365
4366 if (empty($mode)) {
4367 $ret .= $ip;
4368 }
4369
4370 if ($mode != 2) {
4371 $countrycode = dolGetCountryCodeFromIp($ip);
4372 if ($countrycode) { // If success, countrycode is us, fr, ...
4373 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4374 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4375 } else {
4376 $ret .= ' ('.$countrycode.')';
4377 }
4378 } else {
4379 // Nothing
4380 }
4381 }
4382
4383 return $ret;
4384}
4385
4394function getUserRemoteIP()
4395{
4396 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4397 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4398 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4399 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4400 } else {
4401 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4402 }
4403 } else {
4404 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4405 }
4406 } else {
4407 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4408 }
4409 return $ip;
4410}
4411
4420function isHTTPS()
4421{
4422 $isSecure = false;
4423 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4424 $isSecure = true;
4425 } 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') {
4426 $isSecure = true;
4427 }
4428 return $isSecure;
4429}
4430
4437function dolGetCountryCodeFromIp($ip)
4438{
4439 global $conf;
4440
4441 $countrycode = '';
4442
4443 if (!empty($conf->geoipmaxmind->enabled)) {
4444 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4445 //$ip='24.24.24.24';
4446 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4447 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4448 $geoip = new DolGeoIP('country', $datafile);
4449 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4450 $countrycode = $geoip->getCountryCodeFromIP($ip);
4451 }
4452
4453 return $countrycode;
4454}
4455
4456
4463function dol_user_country()
4464{
4465 global $conf, $langs, $user;
4466
4467 //$ret=$user->xxx;
4468 $ret = '';
4469 if (!empty($conf->geoipmaxmind->enabled)) {
4470 $ip = getUserRemoteIP();
4471 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4472 //$ip='24.24.24.24';
4473 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4474 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4475 $geoip = new DolGeoIP('country', $datafile);
4476 $countrycode = $geoip->getCountryCodeFromIP($ip);
4477 $ret = $countrycode;
4478 }
4479 return $ret;
4480}
4481
4494function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4495{
4496 global $conf, $user, $langs, $hookmanager;
4497
4498 $out = '';
4499
4500 if ($address) {
4501 if ($hookmanager) {
4502 $parameters = array('element' => $element, 'id' => $id);
4503 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4504 $out .= $hookmanager->resPrint;
4505 }
4506 if (empty($reshook)) {
4507 if (empty($charfornl)) {
4508 $out .= nl2br($address);
4509 } else {
4510 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4511 }
4512
4513 // TODO Remove this block, we can add this using the hook now
4514 $showgmap = $showomap = 0;
4515 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4516 $showgmap = 1;
4517 }
4518 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4519 $showgmap = 1;
4520 }
4521 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4522 $showgmap = 1;
4523 }
4524 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4525 $showgmap = 1;
4526 }
4527 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4528 $showomap = 1;
4529 }
4530 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4531 $showomap = 1;
4532 }
4533 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4534 $showomap = 1;
4535 }
4536 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4537 $showomap = 1;
4538 }
4539 if ($showgmap) {
4540 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4541 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4542 }
4543 if ($showomap) {
4544 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4545 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4546 }
4547 }
4548 }
4549 if ($noprint) {
4550 return $out;
4551 } else {
4552 print $out;
4553 }
4554}
4555
4556
4566function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4567{
4568 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4569 return true;
4570 }
4571 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4572 return true;
4573 }
4574 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4575 return true;
4576 }
4577
4578 return false;
4579}
4580
4590function isValidMXRecord($domain)
4591{
4592 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4593 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4594 return 0;
4595 }
4596 if (function_exists('getmxrr')) {
4597 $mxhosts = array();
4598 $weight = array();
4599 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4600 if (count($mxhosts) > 1) {
4601 return 1;
4602 }
4603 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4604 return 1;
4605 }
4606
4607 return 0;
4608 }
4609 }
4610
4611 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4612 return -1;
4613}
4614
4622function isValidPhone($phone)
4623{
4624 return true;
4625}
4626
4627
4637function dolGetFirstLetters($s, $nbofchar = 1)
4638{
4639 $ret = '';
4640 $tmparray = explode(' ', $s);
4641 foreach ($tmparray as $tmps) {
4642 $ret .= dol_substr($tmps, 0, $nbofchar);
4643 }
4644
4645 return $ret;
4646}
4647
4648
4656function dol_strlen($string, $stringencoding = 'UTF-8')
4657{
4658 if (is_null($string)) {
4659 return 0;
4660 }
4661
4662 if (function_exists('mb_strlen')) {
4663 return mb_strlen($string, $stringencoding);
4664 } else {
4665 return strlen($string);
4666 }
4667}
4668
4679function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4680{
4681 global $langs;
4682
4683 if (empty($stringencoding)) {
4684 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4685 }
4686
4687 $ret = '';
4688 if (empty($trunconbytes)) {
4689 if (function_exists('mb_substr')) {
4690 $ret = mb_substr($string, $start, $length, $stringencoding);
4691 } else {
4692 $ret = substr($string, $start, $length);
4693 }
4694 } else {
4695 if (function_exists('mb_strcut')) {
4696 $ret = mb_strcut($string, $start, $length, $stringencoding);
4697 } else {
4698 $ret = substr($string, $start, $length);
4699 }
4700 }
4701 return $ret;
4702}
4703
4704
4718function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4719{
4720 global $conf;
4721
4722 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4723 return $string;
4724 }
4725
4726 if (empty($stringencoding)) {
4727 $stringencoding = 'UTF-8';
4728 }
4729 // reduce for small screen
4730 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4731 $size = round($size / 3);
4732 }
4733
4734 // We go always here
4735 if ($trunc == 'right') {
4736 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4737 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4738 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4739 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4740 } else {
4741 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4742 return $string;
4743 }
4744 } elseif ($trunc == 'middle') {
4745 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4746 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4747 $size1 = round($size / 2);
4748 $size2 = round($size / 2);
4749 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4750 } else {
4751 return $string;
4752 }
4753 } elseif ($trunc == 'left') {
4754 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4755 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4756 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4757 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4758 } else {
4759 return $string;
4760 }
4761 } elseif ($trunc == 'wrap') {
4762 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4763 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4764 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4765 } else {
4766 return $string;
4767 }
4768 } else {
4769 return 'BadParam3CallingDolTrunc';
4770 }
4771}
4772
4780function getPictoForType($key, $morecss = '')
4781{
4782 // Set array with type -> picto
4783 $type2picto = array(
4784 'varchar' => 'font',
4785 'text' => 'font',
4786 'html' => 'code',
4787 'int' => 'sort-numeric-down',
4788 'double' => 'sort-numeric-down',
4789 'price' => 'currency',
4790 'pricecy' => 'multicurrency',
4791 'password' => 'key',
4792 'boolean' => 'check-square',
4793 'date' => 'calendar',
4794 'datetime' => 'calendar',
4795 'phone' => 'phone',
4796 'mail' => 'email',
4797 'url' => 'url',
4798 'ip' => 'country',
4799 'select' => 'list',
4800 'sellist' => 'list',
4801 'radio' => 'check-circle',
4802 'checkbox' => 'list',
4803 'chkbxlst' => 'list',
4804 'link' => 'link',
4805 'icon' => "question",
4806 'point' => "country",
4807 'multipts' => 'country',
4808 'linestrg' => "country",
4809 'polygon' => "country",
4810 'separate' => 'minus'
4811 );
4812
4813 if (!empty($type2picto[$key])) {
4814 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4815 }
4816
4817 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4818}
4819
4820
4842function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4843{
4844 global $conf;
4845
4846 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4847 $url = DOL_URL_ROOT;
4848 $theme = isset($conf->theme) ? $conf->theme : null;
4849 $path = 'theme/'.$theme;
4850 if (empty($picto)) {
4851 $picto = 'generic';
4852 }
4853
4854 // Define fullpathpicto to use into src
4855 if ($pictoisfullpath) {
4856 // Clean parameters
4857 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4858 $picto .= '.png';
4859 }
4860 $fullpathpicto = $picto;
4861 $reg = array();
4862 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4863 $morecss .= ($morecss ? ' ' : '').$reg[1];
4864 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4865 }
4866 } else {
4867 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
4868 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4869 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4870
4871 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
4872 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
4873 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4874 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4875
4876 // Compatibility with old fontawesome versions
4877 if ($pictowithouttext == 'file-o') {
4878 $pictowithouttext = 'file';
4879 }
4880
4881 $pictowithouttextarray = explode('_', $pictowithouttext);
4882 $marginleftonlyshort = 0;
4883
4884 if (!empty($pictowithouttextarray[1])) {
4885 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4886 $fakey = 'fa-'.$pictowithouttextarray[0];
4887 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
4888 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4889 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4890 } else {
4891 $fakey = 'fa-'.$pictowithouttext;
4892 $faprefix = 'fas';
4893 $facolor = '';
4894 $fasize = '';
4895 }
4896
4897 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4898 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4899 $morestyle = '';
4900 $reg = array();
4901 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4902 $morecss .= ($morecss ? ' ' : '').$reg[1];
4903 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4904 }
4905 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4906 $morestyle = $reg[1];
4907 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4908 }
4909 $moreatt = trim($moreatt);
4910
4911 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4912 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4913 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4914 $enabledisablehtml .= $titlealt;
4915 }*/
4916 $enabledisablehtml .= '</span>';
4917
4918 return $enabledisablehtml;
4919 }
4920
4921 if (empty($srconly) && in_array($pictowithouttext, array(
4922 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4923 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
4924 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
4925 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4926 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4927 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
4928 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
4929 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
4930 'commercial', 'companies',
4931 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4932 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4933 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
4934 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4935 'hands-helping', 'help', 'holiday',
4936 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4937 'key', 'knowledgemanagement',
4938 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4939 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4940 'off', 'on', 'order',
4941 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4942 'stock', 'resize', 'service', 'stats',
4943 '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',
4944 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4945 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4946 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4947 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4948 'technic', 'ticket',
4949 'error', 'warning',
4950 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4951 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4952 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
4953 'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4954 'conferenceorbooth', 'eventorganization',
4955 'stamp', 'signature',
4956 'webportal'
4957 ))) {
4958 $fakey = $pictowithouttext;
4959 $facolor = '';
4960 $fasize = '';
4961 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
4962 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'))) {
4963 $fa = 'far';
4964 }
4965 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4966 $fa = 'fab';
4967 }
4968
4969 $arrayconvpictotofa = array(
4970 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
4971 'asset' => 'money-check-alt', 'autofill' => 'fill',
4972 'bank_account' => 'university',
4973 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
4974 'bookcal' => 'calendar-check',
4975 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
4976 'bom' => 'shapes',
4977 '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',
4978 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
4979 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
4980 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
4981 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
4982 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
4983 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
4984 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
4985 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
4986 'generic' => 'file', 'holiday' => 'umbrella-beach',
4987 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
4988 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
4989 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
4990 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
4991 'sign-out' => 'sign-out-alt',
4992 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
4993 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
4994 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
4995 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
4996 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
4997 'other' => 'square',
4998 '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',
4999 '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',
5000 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5001 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5002 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5003 'service' => 'concierge-bell',
5004 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5005 'status' => 'stop-circle',
5006 'stripe' => 'stripe-s', 'supplier' => 'building',
5007 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5008 'title_agenda' => 'calendar-alt',
5009 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5010 'jabber' => 'comment-o',
5011 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5012 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5013 'webportal' => 'door-open'
5014 );
5015 if ($conf->currency == 'EUR') {
5016 $arrayconvpictotofa['currency'] = 'euro-sign';
5017 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5018 } else {
5019 $arrayconvpictotofa['currency'] = 'dollar-sign';
5020 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5021 }
5022 if ($pictowithouttext == 'off') {
5023 $fakey = 'fa-square';
5024 $fasize = '1.3em';
5025 } elseif ($pictowithouttext == 'on') {
5026 $fakey = 'fa-check-square';
5027 $fasize = '1.3em';
5028 } elseif ($pictowithouttext == 'listlight') {
5029 $fakey = 'fa-download';
5030 $marginleftonlyshort = 1;
5031 } elseif ($pictowithouttext == 'printer') {
5032 $fakey = 'fa-print';
5033 $fasize = '1.2em';
5034 } elseif ($pictowithouttext == 'note') {
5035 $fakey = 'fa-sticky-note';
5036 $marginleftonlyshort = 1;
5037 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5038 $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');
5039 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5040 if (preg_match('/selected/', $pictowithouttext)) {
5041 $facolor = '#888';
5042 }
5043 $marginleftonlyshort = 1;
5044 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5045 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5046 } else {
5047 $fakey = 'fa-'.$pictowithouttext;
5048 }
5049
5050 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5051 $morecss .= ' em092';
5052 }
5053 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5054 $morecss .= ' em088';
5055 }
5056 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5057 $morecss .= ' em080';
5058 }
5059
5060 // Define $marginleftonlyshort
5061 $arrayconvpictotomarginleftonly = array(
5062 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5063 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
5064 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5065 );
5066 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
5067 $marginleftonlyshort = 0;
5068 }
5069
5070 // Add CSS
5071 $arrayconvpictotomorcess = array(
5072 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5073 'bank_account' => 'infobox-bank_account',
5074 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5075 'bookcal' => 'infobox-action',
5076 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5077 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5078 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5079 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5080 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5081 'incoterm' => 'infobox-supplier_proposal',
5082 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5083 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5084 'order' => 'infobox-commande',
5085 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5086 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5087 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5088 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5089 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5090 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5091 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5092 'resource' => 'infobox-action',
5093 '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',
5094 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5095 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5096 'vat' => 'infobox-bank_account',
5097 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5098 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5099 );
5100 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5101 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5102 }
5103
5104 // Define $color
5105 $arrayconvpictotocolor = array(
5106 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5107 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5108 'dynamicprice' => '#a69944',
5109 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5110 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5111 'lock' => '#ddd', 'lot' => '#a69944',
5112 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5113 'other' => '#ddd', 'world' => '#986c6a',
5114 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5115 //'shipment'=>'#a69944',
5116 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5117 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5118 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5119 'website' => '#304', 'workstation' => '#a69944'
5120 );
5121 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5122 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5123 }
5124
5125 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5126 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5127 $morestyle = '';
5128 $reg = array();
5129 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5130 $morecss .= ($morecss ? ' ' : '').$reg[1];
5131 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5132 }
5133 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5134 $morestyle = $reg[1];
5135 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5136 }
5137 $moreatt = trim($moreatt);
5138
5139 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5140 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5141 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5142 $enabledisablehtml .= $titlealt;
5143 }*/
5144 $enabledisablehtml .= '</span>';
5145
5146 return $enabledisablehtml;
5147 }
5148
5149 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5150 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5151 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5152 $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
5153 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5154 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5155 }
5156
5157 // If we ask an image into $url/$mymodule/img (instead of default path)
5158 $regs = array();
5159 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5160 $picto = $regs[1];
5161 $path = $regs[2]; // $path is $mymodule
5162 }
5163
5164 // Clean parameters
5165 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5166 $picto .= '.png';
5167 }
5168 // If alt path are defined, define url where img file is, according to physical path
5169 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5170 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5171 if ($type == 'main') {
5172 continue;
5173 }
5174 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5175 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5176 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5177 break;
5178 }
5179 }
5180
5181 // $url is '' or '/custom', $path is current theme or
5182 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5183 }
5184
5185 if ($srconly) {
5186 return $fullpathpicto;
5187 }
5188
5189 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5190 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
5191}
5192
5206function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5207{
5208 if (strpos($picto, '^') === 0) {
5209 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5210 } else {
5211 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5212 }
5213}
5214
5226function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5227{
5228 global $conf;
5229
5230 if (is_numeric($picto)) {
5231 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5232 //$picto = $leveltopicto[$picto];
5233 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5234 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5235 $picto .= '.png';
5236 }
5237
5238 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5239
5240 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5241}
5242
5254function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5255{
5256 global $conf;
5257
5258 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5259 $picto .= '.png';
5260 }
5261
5262 if ($pictoisfullpath) {
5263 $path = $picto;
5264 } else {
5265 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5266
5267 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5268 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5269
5270 if (file_exists($themepath)) {
5271 $path = $themepath;
5272 }
5273 }
5274 }
5275
5276 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5277}
5278
5292function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5293{
5294 global $langs;
5295
5296 if (empty($titlealt) || $titlealt == 'default') {
5297 if ($numaction == '-1' || $numaction == 'ST_NO') {
5298 $numaction = -1;
5299 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5300 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5301 $numaction = 0;
5302 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5303 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5304 $numaction = 1;
5305 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5306 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5307 $numaction = 2;
5308 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5309 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5310 $numaction = 3;
5311 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5312 } else {
5313 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5314 $numaction = 0;
5315 }
5316 }
5317 if (!is_numeric($numaction)) {
5318 $numaction = 0;
5319 }
5320
5321 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5322}
5323
5331function img_pdf($titlealt = 'default', $size = 3)
5332{
5333 global $langs;
5334
5335 if ($titlealt == 'default') {
5336 $titlealt = $langs->trans('Show');
5337 }
5338
5339 return img_picto($titlealt, 'pdf'.$size.'.png');
5340}
5341
5349function img_edit_add($titlealt = 'default', $other = '')
5350{
5351 global $langs;
5352
5353 if ($titlealt == 'default') {
5354 $titlealt = $langs->trans('Add');
5355 }
5356
5357 return img_picto($titlealt, 'edit_add.png', $other);
5358}
5366function img_edit_remove($titlealt = 'default', $other = '')
5367{
5368 global $langs;
5369
5370 if ($titlealt == 'default') {
5371 $titlealt = $langs->trans('Remove');
5372 }
5373
5374 return img_picto($titlealt, 'edit_remove.png', $other);
5375}
5376
5385function img_edit($titlealt = 'default', $float = 0, $other = '')
5386{
5387 global $langs;
5388
5389 if ($titlealt == 'default') {
5390 $titlealt = $langs->trans('Modify');
5391 }
5392
5393 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5394}
5395
5404function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5405{
5406 global $langs;
5407
5408 if ($titlealt == 'default') {
5409 $titlealt = $langs->trans('View');
5410 }
5411
5412 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5413
5414 return img_picto($titlealt, 'eye', $moreatt);
5415}
5416
5425function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5426{
5427 global $langs;
5428
5429 if ($titlealt == 'default') {
5430 $titlealt = $langs->trans('Delete');
5431 }
5432
5433 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5434}
5435
5443function img_printer($titlealt = "default", $other = '')
5444{
5445 global $langs;
5446 if ($titlealt == "default") {
5447 $titlealt = $langs->trans("Print");
5448 }
5449 return img_picto($titlealt, 'printer.png', $other);
5450}
5451
5459function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5460{
5461 global $langs;
5462
5463 if ($titlealt == 'default') {
5464 $titlealt = $langs->trans('Split');
5465 }
5466
5467 return img_picto($titlealt, 'split.png', $other);
5468}
5469
5477function img_help($usehelpcursor = 1, $usealttitle = 1)
5478{
5479 global $langs;
5480
5481 if ($usealttitle) {
5482 if (is_string($usealttitle)) {
5483 $usealttitle = dol_escape_htmltag($usealttitle);
5484 } else {
5485 $usealttitle = $langs->trans('Info');
5486 }
5487 }
5488
5489 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5490}
5491
5498function img_info($titlealt = 'default')
5499{
5500 global $langs;
5501
5502 if ($titlealt == 'default') {
5503 $titlealt = $langs->trans('Informations');
5504 }
5505
5506 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5507}
5508
5517function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5518{
5519 global $langs;
5520
5521 if ($titlealt == 'default') {
5522 $titlealt = $langs->trans('Warning');
5523 }
5524
5525 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5526 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5527}
5528
5535function img_error($titlealt = 'default')
5536{
5537 global $langs;
5538
5539 if ($titlealt == 'default') {
5540 $titlealt = $langs->trans('Error');
5541 }
5542
5543 return img_picto($titlealt, 'error.png');
5544}
5545
5553function img_next($titlealt = 'default', $moreatt = '')
5554{
5555 global $langs;
5556
5557 if ($titlealt == 'default') {
5558 $titlealt = $langs->trans('Next');
5559 }
5560
5561 //return img_picto($titlealt, 'next.png', $moreatt);
5562 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5563}
5564
5572function img_previous($titlealt = 'default', $moreatt = '')
5573{
5574 global $langs;
5575
5576 if ($titlealt == 'default') {
5577 $titlealt = $langs->trans('Previous');
5578 }
5579
5580 //return img_picto($titlealt, 'previous.png', $moreatt);
5581 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5582}
5583
5592function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5593{
5594 global $langs;
5595
5596 if ($titlealt == 'default') {
5597 $titlealt = $langs->trans('Down');
5598 }
5599
5600 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5601}
5602
5611function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5612{
5613 global $langs;
5614
5615 if ($titlealt == 'default') {
5616 $titlealt = $langs->trans('Up');
5617 }
5618
5619 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5620}
5621
5630function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5631{
5632 global $langs;
5633
5634 if ($titlealt == 'default') {
5635 $titlealt = $langs->trans('Left');
5636 }
5637
5638 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5639}
5640
5649function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5650{
5651 global $langs;
5652
5653 if ($titlealt == 'default') {
5654 $titlealt = $langs->trans('Right');
5655 }
5656
5657 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5658}
5659
5667function img_allow($allow, $titlealt = 'default')
5668{
5669 global $langs;
5670
5671 if ($titlealt == 'default') {
5672 $titlealt = $langs->trans('Active');
5673 }
5674
5675 if ($allow == 1) {
5676 return img_picto($titlealt, 'tick.png');
5677 }
5678
5679 return '-';
5680}
5681
5689function img_credit_card($brand, $morecss = null)
5690{
5691 if (is_null($morecss)) {
5692 $morecss = 'fa-2x';
5693 }
5694
5695 if ($brand == 'visa' || $brand == 'Visa') {
5696 $brand = 'cc-visa';
5697 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5698 $brand = 'cc-mastercard';
5699 } elseif ($brand == 'amex' || $brand == 'American Express') {
5700 $brand = 'cc-amex';
5701 } elseif ($brand == 'discover' || $brand == 'Discover') {
5702 $brand = 'cc-discover';
5703 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5704 $brand = 'cc-jcb';
5705 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5706 $brand = 'cc-diners-club';
5707 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5708 $brand = 'credit-card';
5709 }
5710
5711 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5712}
5713
5722function img_mime($file, $titlealt = '', $morecss = '')
5723{
5724 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5725
5726 $mimetype = dol_mimetype($file, '', 1);
5727 $mimeimg = dol_mimetype($file, '', 2);
5728 $mimefa = dol_mimetype($file, '', 4);
5729
5730 if (empty($titlealt)) {
5731 $titlealt = 'Mime type: '.$mimetype;
5732 }
5733
5734 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5735 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5736}
5737
5738
5746function img_search($titlealt = 'default', $other = '')
5747{
5748 global $langs;
5749
5750 if ($titlealt == 'default') {
5751 $titlealt = $langs->trans('Search');
5752 }
5753
5754 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5755
5756 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5757 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5758
5759 return $input;
5760}
5761
5769function img_searchclear($titlealt = 'default', $other = '')
5770{
5771 global $langs;
5772
5773 if ($titlealt == 'default') {
5774 $titlealt = $langs->trans('Search');
5775 }
5776
5777 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5778
5779 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5780 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5781
5782 return $input;
5783}
5784
5797function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5798{
5799 global $conf, $langs;
5800
5801 if ($infoonimgalt) {
5802 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5803 } else {
5804 if (empty($conf->use_javascript_ajax)) {
5805 $textfordropdown = '';
5806 }
5807
5808 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5809 $fa = 'info-circle';
5810 if ($picto == 'warning') {
5811 $fa = 'exclamation-triangle';
5812 }
5813 $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> ';
5814 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5815 $result .= ($nodiv ? '' : '</div>');
5816
5817 if ($textfordropdown) {
5818 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5819 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5820 jQuery(document).ready(function() {
5821 jQuery(".'.$class.'text").click(function() {
5822 console.log("toggle text");
5823 jQuery(".'.$class.'").toggle();
5824 });
5825 });
5826 </script>';
5827
5828 $result = $tmpresult.$result;
5829 }
5830 }
5831
5832 return $result;
5833}
5834
5835
5847function dol_print_error($db = null, $error = '', $errors = null)
5848{
5849 global $conf, $langs, $user, $argv;
5850 global $dolibarr_main_prod;
5851
5852 $out = '';
5853 $syslog = '';
5854
5855 // If error occurs before the $lang object was loaded
5856 if (!$langs) {
5857 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5858 $langs = new Translate('', $conf);
5859 $langs->load("main");
5860 }
5861
5862 // Load translation files required by the error messages
5863 $langs->loadLangs(array('main', 'errors'));
5864
5865 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5866 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5867 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5868 $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";
5869 }
5870 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5871
5872 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5873 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5874 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5875 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5876 }
5877 if ($user instanceof User) {
5878 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
5879 }
5880 if (function_exists("phpversion")) {
5881 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5882 }
5883 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5884 if (function_exists("php_uname")) {
5885 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5886 }
5887 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5888 $out .= "<br>\n";
5889 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5890 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5891 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5892 $out .= "<br>\n";
5893 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5894 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5895 } else { // Mode CLI
5896 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5897 $syslog .= "pid=".dol_getmypid();
5898 }
5899
5900 if (!empty($conf->modules)) {
5901 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
5902 }
5903
5904 if (is_object($db)) {
5905 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5906 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5907 $lastqueryerror = $db->lastqueryerror();
5908 if (!utf8_check($lastqueryerror)) {
5909 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5910 }
5911 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5912 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5913 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5914 $out .= "<br>\n";
5915 } else { // Mode CLI
5916 // No dol_escape_htmltag for output, we are in CLI mode
5917 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5918 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5919 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5920 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5921 }
5922 $syslog .= ", sql=".$db->lastquery();
5923 $syslog .= ", db_error=".$db->lasterror();
5924 }
5925
5926 if ($error || $errors) {
5927 // Merge all into $errors array
5928 if (is_array($error) && is_array($errors)) {
5929 $errors = array_merge($error, $errors);
5930 } elseif (is_array($error)) { // deprecated, use second parameters
5931 $errors = $error;
5932 } elseif (is_array($errors) && !empty($error)) {
5933 $errors = array_merge(array($error), $errors);
5934 } elseif (!empty($error)) {
5935 $errors = array_merge(array($error), array($errors));
5936 }
5937
5938 $langs->load("errors");
5939
5940 foreach ($errors as $msg) {
5941 if (empty($msg)) {
5942 continue;
5943 }
5944 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5945 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5946 } else { // Mode CLI
5947 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5948 }
5949 $syslog .= ", msg=".$msg;
5950 }
5951 }
5952 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5953 xdebug_print_function_stack();
5954 $out .= '<b>XDebug information:</b>'."<br>\n";
5955 $out .= 'File: '.xdebug_call_file()."<br>\n";
5956 $out .= 'Line: '.xdebug_call_line()."<br>\n";
5957 $out .= 'Function: '.xdebug_call_function()."<br>\n";
5958 $out .= "<br>\n";
5959 }
5960
5961 // Return a http header with error code if possible
5962 if (!headers_sent()) {
5963 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5964 top_httphead();
5965 }
5966 //http_response_code(500); // If we use 500, message is not output with some command line tools
5967 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
5968 }
5969
5970 if (empty($dolibarr_main_prod)) {
5971 print $out;
5972 } else {
5973 if (empty($langs->defaultlang)) {
5974 $langs->setDefaultLang();
5975 }
5976 $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.
5977 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5978 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";
5979 print $langs->trans("DolibarrHasDetectedError").'. ';
5980 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5981 if (!defined("MAIN_CORE_ERROR")) {
5982 define("MAIN_CORE_ERROR", 1);
5983 }
5984 }
5985
5986 dol_syslog("Error ".$syslog, LOG_ERR);
5987}
5988
5999function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6000{
6001 global $langs;
6002
6003 if (empty($email)) {
6004 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6005 }
6006
6007 $langs->load("errors");
6008 $now = dol_now();
6009
6010 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6011 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6012 if ($errormessage) {
6013 print '<br><br>'.$errormessage;
6014 }
6015 if (is_array($errormessages) && count($errormessages)) {
6016 foreach ($errormessages as $mesgtoshow) {
6017 print '<br><br>'.$mesgtoshow;
6018 }
6019 }
6020 print '</div></div>';
6021}
6022
6039function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6040{
6041 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6042}
6043
6062function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6063{
6064 global $langs, $form;
6065 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6066
6067 if ($moreattrib == 'class="right"') {
6068 $prefix .= 'right '; // For backward compatibility
6069 }
6070
6071 $sortorder = strtoupper($sortorder);
6072 $out = '';
6073 $sortimg = '';
6074
6075 $tag = 'th';
6076 if ($thead == 2) {
6077 $tag = 'div';
6078 }
6079
6080 $tmpsortfield = explode(',', $sortfield);
6081 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6082 $tmpfield = explode(',', $field);
6083 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6084
6085 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6086 $prefix = 'wrapcolumntitle '.$prefix;
6087 }
6088
6089 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6090 // If field is used as sort criteria we use a specific css class liste_titre_sel
6091 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6092 $liste_titre = 'liste_titre';
6093 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6094 $liste_titre = 'liste_titre_sel';
6095 }
6096
6097 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6098 //$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)).'"' : '');
6099 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6100 $tagstart .= '>';
6101
6102 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6103 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6104 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6105 $options = preg_replace('/&+/i', '&', $options);
6106 if (!preg_match('/^&/', $options)) {
6107 $options = '&'.$options;
6108 }
6109
6110 $sortordertouseinlink = '';
6111 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6112 if (preg_match('/^DESC/i', $sortorder)) {
6113 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6114 } else { // We reverse the var $sortordertouseinlink
6115 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6116 }
6117 } else { // We are on field that is the first current sorting criteria
6118 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6119 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6120 } else {
6121 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6122 }
6123 }
6124 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6125 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6126 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6127 $out .= '>';
6128 }
6129 if ($tooltip) {
6130 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6131 if (preg_match('/:\w+$/', $tooltip)) {
6132 $tmptooltip = explode(':', $tooltip);
6133 } else {
6134 $tmptooltip = array($tooltip);
6135 }
6136 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6137 } else {
6138 $out .= $langs->trans($name);
6139 }
6140
6141 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6142 $out .= '</a>';
6143 }
6144
6145 if (empty($thead) && $field) { // If this is a sort field
6146 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6147 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6148 $options = preg_replace('/&+/i', '&', $options);
6149 if (!preg_match('/^&/', $options)) {
6150 $options = '&'.$options;
6151 }
6152
6153 if (!$sortorder || ($field1 != $sortfield1)) {
6154 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6155 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6156 } else {
6157 if (preg_match('/^DESC/', $sortorder)) {
6158 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6159 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6160 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6161 }
6162 if (preg_match('/^ASC/', $sortorder)) {
6163 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6164 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6165 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6166 }
6167 }
6168 }
6169
6170 $tagend = '</'.$tag.'>';
6171
6172 $out = $tagstart.$sortimg.$out.$tagend;
6173
6174 return $out;
6175}
6176
6185function print_titre($title)
6186{
6187 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6188
6189 print '<div class="titre">'.$title.'</div>';
6190}
6191
6203function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6204{
6205 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6206}
6207
6221function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6222{
6223 $return = '';
6224
6225 if ($picto == 'setup') {
6226 $picto = 'generic';
6227 }
6228
6229 $return .= "\n";
6230 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
6231 $return .= '<tr class="titre">';
6232 if ($picto) {
6233 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6234 }
6235 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6236 $return .= '<div class="titre inline-block">';
6237 $return .= $title; // $title is already HTML sanitized content
6238 $return .= '</div>';
6239 $return .= '</td>';
6240 if (dol_strlen($morehtmlcenter)) {
6241 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6242 }
6243 if (dol_strlen($morehtmlright)) {
6244 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6245 }
6246 $return .= '</tr></table>'."\n";
6247
6248 return $return;
6249}
6250
6274function 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 = '')
6275{
6276 global $conf;
6277
6278 $savlimit = $limit;
6279 $savtotalnboflines = $totalnboflines;
6280 if (is_numeric($totalnboflines)) {
6281 $totalnboflines = abs($totalnboflines);
6282 }
6283
6284 $page = (int) $page;
6285
6286 if ($picto == 'setup') {
6287 $picto = 'title_setup.png';
6288 }
6289 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6290 $picto = 'title.gif';
6291 }
6292 if ($limit < 0) {
6293 $limit = $conf->liste_limit;
6294 }
6295
6296 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6297 $nextpage = 1;
6298 } else {
6299 $nextpage = 0;
6300 }
6301 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
6302
6303 print "\n";
6304 print "<!-- Begin title -->\n";
6305 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
6306
6307 // Left
6308
6309 if ($picto && $title) {
6310 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
6311 }
6312
6313 print '<td class="nobordernopadding valignmiddle col-title">';
6314 print '<div class="titre inline-block">';
6315 print $title; // $title may contains HTML
6316 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
6317 print '<span class="opacitymedium colorblack paddingleft totalnboflines">('.$totalnboflines.')</span>';
6318 }
6319 print '</div></td>';
6320
6321 // Center
6322 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6323 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6324 }
6325
6326 // Right
6327 print '<td class="nobordernopadding valignmiddle right col-right">';
6328 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6329 if ($sortfield) {
6330 $options .= "&sortfield=".urlencode($sortfield);
6331 }
6332 if ($sortorder) {
6333 $options .= "&sortorder=".urlencode($sortorder);
6334 }
6335 // Show navigation bar
6336 $pagelist = '';
6337 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6338 if ($totalnboflines) { // If we know total nb of lines
6339 // Define nb of extra page links before and after selected page + ... + first or last
6340 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6341
6342 if ($limit > 0) {
6343 $nbpages = ceil($totalnboflines / $limit);
6344 } else {
6345 $nbpages = 1;
6346 }
6347 $cpt = ($page - $maxnbofpage);
6348 if ($cpt < 0) {
6349 $cpt = 0;
6350 }
6351
6352 if ($cpt >= 1) {
6353 if (empty($pagenavastextinput)) {
6354 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6355 if ($cpt > 2) {
6356 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6357 } elseif ($cpt == 2) {
6358 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6359 }
6360 }
6361 }
6362
6363 do {
6364 if ($pagenavastextinput) {
6365 if ($cpt == $page) {
6366 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6367 $pagelist .= '/';
6368 }
6369 } else {
6370 if ($cpt == $page) {
6371 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6372 } else {
6373 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6374 }
6375 }
6376 $cpt++;
6377 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6378
6379 if (empty($pagenavastextinput)) {
6380 if ($cpt < $nbpages) {
6381 if ($cpt < $nbpages - 2) {
6382 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6383 } elseif ($cpt == $nbpages - 2) {
6384 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6385 }
6386 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6387 }
6388 } else {
6389 //var_dump($page.' '.$cpt.' '.$nbpages);
6390 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6391 }
6392 } else {
6393 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6394 }
6395 }
6396
6397 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6398 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
6399 }
6400
6401 // js to autoselect page field on focus
6402 if ($pagenavastextinput) {
6403 print ajax_autoselect('.pageplusone');
6404 }
6405
6406 print '</td>';
6407 print '</tr>';
6408
6409 print '</table>'."\n";
6410
6411 // Center
6412 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6413 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6414 }
6415
6416 print "<!-- End title -->\n\n";
6417}
6418
6435function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
6436{
6437 global $conf, $langs;
6438
6439 print '<div class="pagination"><ul>';
6440 if ($beforearrows) {
6441 print '<li class="paginationbeforearrows">';
6442 print $beforearrows;
6443 print '</li>';
6444 }
6445
6446 if (empty($hidenavigation)) {
6447 if ((int) $limit > 0 && empty($hideselectlimit)) {
6448 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6449 $pagesizechoices .= ',5000:5000,10000:10000';
6450 //$pagesizechoices .= ',20000:20000'; // Memory trouble on browsers
6451 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6452 //$pagesizechoices .= ',2:2';
6453 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6454 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6455 }
6456
6457 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6458 print '<li class="pagination">';
6459 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.'">';
6460 print '<datalist id="limitlist">';
6461 } else {
6462 print '<li class="paginationcombolimit valignmiddle">';
6463 print '<select id="limit" class="flat selectlimit nopadding maxwidth75 center" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6464 }
6465 $tmpchoice = explode(',', $pagesizechoices);
6466 $tmpkey = $limit.':'.$limit;
6467 if (!in_array($tmpkey, $tmpchoice)) {
6468 $tmpchoice[] = $tmpkey;
6469 }
6470 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6471 if (!in_array($tmpkey, $tmpchoice)) {
6472 $tmpchoice[] = $tmpkey;
6473 }
6474 asort($tmpchoice, SORT_NUMERIC);
6475 foreach ($tmpchoice as $val) {
6476 $selected = '';
6477 $tmp = explode(':', $val);
6478 $key = $tmp[0];
6479 $val = $tmp[1];
6480 if ($key != '' && $val != '') {
6481 if ((int) $key == (int) $limit) {
6482 $selected = ' selected="selected"';
6483 }
6484 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6485 }
6486 }
6487 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6488 print '</datalist>';
6489 } else {
6490 print '</select>';
6491 print ajax_combobox("limit", array(), 0, 0, 'resolve', -1, 'limit');
6492 //print ajax_combobox("limit");
6493 }
6494
6495 if ($conf->use_javascript_ajax) {
6496 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6497 <script>
6498 jQuery(document).ready(function () {
6499 jQuery(".selectlimit").change(function() {
6500 console.log("Change limit. Send submit");
6501 $(this).parents(\'form:first\').submit();
6502 });
6503 });
6504 </script>
6505 ';
6506 }
6507 print '</li>';
6508 }
6509 if ($page > 0) {
6510 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>';
6511 }
6512 if ($betweenarrows) {
6513 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6514 print $betweenarrows;
6515 print '<!--</div>-->';
6516 }
6517 if ($nextpage > 0) {
6518 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>';
6519 }
6520 if ($afterarrows) {
6521 print '<li class="paginationafterarrows">';
6522 print $afterarrows;
6523 print '</li>';
6524 }
6525 }
6526 print '</ul></div>'."\n";
6527}
6528
6529
6541function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6542{
6543 $morelabel = '';
6544
6545 if (preg_match('/%/', $rate)) {
6546 $rate = str_replace('%', '', $rate);
6547 $addpercent = true;
6548 }
6549 $reg = array();
6550 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6551 $morelabel = ' ('.$reg[1].')';
6552 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6553 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6554 }
6555 if (preg_match('/\*/', $rate)) {
6556 $rate = str_replace('*', '', $rate);
6557 $info_bits |= 1;
6558 }
6559
6560 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6561 if (!preg_match('/\//', $rate)) {
6562 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6563 } else {
6564 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6565 $ret = $rate.($addpercent ? '%' : '');
6566 }
6567 if (($info_bits & 1) && $usestarfornpr >= 0) {
6568 $ret .= ' *';
6569 }
6570 $ret .= $morelabel;
6571 return $ret;
6572}
6573
6574
6590function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6591{
6592 global $langs, $conf;
6593
6594 // Clean parameters
6595 if (empty($amount)) {
6596 $amount = 0; // To have a numeric value if amount not defined or = ''
6597 }
6598 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6599 if ($rounding == -1) {
6600 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6601 }
6602 $nbdecimal = $rounding;
6603
6604 if ($outlangs === 'none') {
6605 // Use international separators
6606 $dec = '.';
6607 $thousand = '';
6608 } else {
6609 // Output separators by default (french)
6610 $dec = ',';
6611 $thousand = ' ';
6612
6613 // If $outlangs not forced, we use use language
6614 if (!($outlangs instanceof Translate)) {
6615 $outlangs = $langs;
6616 }
6617
6618 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6619 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6620 }
6621 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6622 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6623 }
6624 if ($thousand == 'None') {
6625 $thousand = '';
6626 } elseif ($thousand == 'Space') {
6627 $thousand = ' ';
6628 }
6629 }
6630 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6631
6632 //print "amount=".$amount."-";
6633 $amount = str_replace(',', '.', $amount); // should be useless
6634 //print $amount."-";
6635 $data = explode('.', $amount);
6636 $decpart = isset($data[1]) ? $data[1] : '';
6637 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6638 //print "decpart=".$decpart."<br>";
6639 $end = '';
6640
6641 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6642 if (dol_strlen($decpart) > $nbdecimal) {
6643 $nbdecimal = dol_strlen($decpart);
6644 }
6645
6646 // If nbdecimal is higher than max to show
6647 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6648 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6649 $nbdecimal = $nbdecimalmaxshown;
6650 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6651 // If output is truncated, we show ...
6652 $end = '...';
6653 }
6654 }
6655
6656 // If force rounding
6657 if ((string) $forcerounding != '-1') {
6658 if ($forcerounding === 'MU') {
6659 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6660 } elseif ($forcerounding === 'MT') {
6661 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
6662 } elseif ($forcerounding >= 0) {
6663 $nbdecimal = $forcerounding;
6664 }
6665 }
6666
6667 // Format number
6668 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6669 if ($form) {
6670 $output = preg_replace('/\s/', '&nbsp;', $output);
6671 $output = preg_replace('/\'/', '&#039;', $output);
6672 }
6673 // Add symbol of currency if requested
6674 $cursymbolbefore = $cursymbolafter = '';
6675 if ($currency_code && is_object($outlangs)) {
6676 if ($currency_code == 'auto') {
6677 $currency_code = $conf->currency;
6678 }
6679
6680 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6681 $listoflanguagesbefore = array('nl_NL');
6682 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6683 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6684 } else {
6685 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6686 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6687 }
6688 }
6689 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6690
6691 return $output;
6692}
6693
6718function price2num($amount, $rounding = '', $option = 0)
6719{
6720 global $langs, $conf;
6721
6722 // Clean parameters
6723 if (is_null($amount)) {
6724 $amount = '';
6725 }
6726
6727 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6728 // Numbers must be '1234.56'
6729 // Decimal delimiter for PHP and database SQL requests must be '.'
6730 $dec = ',';
6731 $thousand = ' ';
6732 if (is_null($langs)) { // $langs is not defined, we use english values.
6733 $dec = '.';
6734 $thousand = ',';
6735 } else {
6736 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6737 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6738 }
6739 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6740 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6741 }
6742 }
6743 if ($thousand == 'None') {
6744 $thousand = '';
6745 } elseif ($thousand == 'Space') {
6746 $thousand = ' ';
6747 }
6748 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6749
6750 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6751 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6752 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6753 if (!is_numeric($amount)) {
6754 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6755 }
6756
6757 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
6758 $amount = str_replace($thousand, '', $amount);
6759 }
6760
6761 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6762 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6763 // So if number was already a good number, it is converted into local Dolibarr setup.
6764 if (is_numeric($amount)) {
6765 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6766 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6767 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6768 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6769 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6770 }
6771 //print "QQ".$amount."<br>\n";
6772
6773 // Now make replace (the main goal of function)
6774 if ($thousand != ',' && $thousand != '.') {
6775 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6776 }
6777
6778 $amount = str_replace(' ', '', $amount); // To avoid spaces
6779 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6780 $amount = str_replace($dec, '.', $amount);
6781
6782 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6783 }
6784 //print ' XX'.$amount.' '.$rounding;
6785
6786 // Now, $amount is a real PHP float number. We make a rounding if required.
6787 if ($rounding) {
6788 $nbofdectoround = '';
6789 if ($rounding == 'MU') {
6790 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6791 } elseif ($rounding == 'MT') {
6792 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6793 } elseif ($rounding == 'MS') {
6794 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6795 } elseif ($rounding == 'CU') {
6796 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6797 } elseif ($rounding == 'CT') {
6798 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6799 } elseif (is_numeric($rounding)) {
6800 $nbofdectoround = (int) $rounding;
6801 }
6802
6803 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6804 if (dol_strlen($nbofdectoround)) {
6805 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6806 } else {
6807 return 'ErrorBadParameterProvidedToFunction';
6808 }
6809 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6810
6811 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6812 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6813 if (is_numeric($amount)) {
6814 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6815 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6816 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6817 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6818 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6819 }
6820 //print "TT".$amount.'<br>';
6821
6822 // Always make replace because each math function (like round) replace
6823 // with local values and we want a number that has a SQL string format x.y
6824 if ($thousand != ',' && $thousand != '.') {
6825 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6826 }
6827
6828 $amount = str_replace(' ', '', $amount); // To avoid spaces
6829 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6830 $amount = str_replace($dec, '.', $amount);
6831
6832 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6833 }
6834
6835 return $amount;
6836}
6837
6850function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6851{
6852 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6853
6854 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6855 $dimension *= 1000000;
6856 $unit -= 6;
6857 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6858 $dimension *= 1000;
6859 $unit -= 3;
6860 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6861 $dimension /= 1000000;
6862 $unit += 6;
6863 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6864 $dimension /= 1000;
6865 $unit += 3;
6866 }
6867 // Special case when we want output unit into pound or ounce
6868 /* TODO
6869 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6870 {
6871 $dimension = // convert dimension from standard unit into ounce or pound
6872 $unit = $forceunitoutput;
6873 }
6874 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6875 {
6876 $dimension = // convert dimension from standard unit into ounce or pound
6877 $unit = $forceunitoutput;
6878 }*/
6879
6880 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6881 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6882 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6883
6884 return $ret;
6885}
6886
6887
6900function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
6901{
6902 global $db, $conf, $mysoc;
6903
6904 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6905 $thirdparty_seller = $mysoc;
6906 }
6907
6908 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);
6909
6910 $vatratecleaned = $vatrate;
6911 $reg = array();
6912 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
6913 $vatratecleaned = trim($reg[1]);
6914 $vatratecode = $reg[2];
6915 }
6916
6917 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6918 {
6919 return 0;
6920 }*/
6921
6922 // Some test to guess with no need to make database access
6923 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6924 if ($local == 1) {
6925 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6926 return 0;
6927 }
6928 if ($thirdparty_seller->id == $mysoc->id) {
6929 if (!$thirdparty_buyer->localtax1_assuj) {
6930 return 0;
6931 }
6932 } else {
6933 if (!$thirdparty_seller->localtax1_assuj) {
6934 return 0;
6935 }
6936 }
6937 }
6938
6939 if ($local == 2) {
6940 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6941 if (!$mysoc->localtax2_assuj) {
6942 return 0; // If main vat is 0, IRPF may be different than 0.
6943 }
6944 if ($thirdparty_seller->id == $mysoc->id) {
6945 if (!$thirdparty_buyer->localtax2_assuj) {
6946 return 0;
6947 }
6948 } else {
6949 if (!$thirdparty_seller->localtax2_assuj) {
6950 return 0;
6951 }
6952 }
6953 }
6954 } else {
6955 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6956 return 0;
6957 }
6958 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6959 return 0;
6960 }
6961 }
6962
6963 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6964 if (in_array($mysoc->country_code, array('ES'))) {
6965 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6966 }
6967
6968 // Search local taxes
6969 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
6970 if ($local == 1) {
6971 if ($thirdparty_seller != $mysoc) {
6972 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6973 return $thirdparty_seller->localtax1_value;
6974 }
6975 } else { // i am the seller
6976 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6977 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6978 }
6979 }
6980 }
6981 if ($local == 2) {
6982 if ($thirdparty_seller != $mysoc) {
6983 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6984 // TODO We should also return value defined on thirdparty only if defined
6985 return $thirdparty_seller->localtax2_value;
6986 }
6987 } else { // i am the seller
6988 if (in_array($mysoc->country_code, array('ES'))) {
6989 return $thirdparty_buyer->localtax2_value;
6990 } else {
6991 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6992 }
6993 }
6994 }
6995 }
6996
6997 // By default, search value of local tax on line of common tax
6998 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6999 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7000 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7001 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7002 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7003 if (!empty($vatratecode)) {
7004 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7005 } else {
7006 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7007 }
7008
7009 $resql = $db->query($sql);
7010
7011 if ($resql) {
7012 $obj = $db->fetch_object($resql);
7013 if ($obj) {
7014 if ($local == 1) {
7015 return $obj->localtax1;
7016 } elseif ($local == 2) {
7017 return $obj->localtax2;
7018 }
7019 }
7020 }
7021
7022 return 0;
7023}
7024
7025
7034function isOnlyOneLocalTax($local)
7035{
7036 $tax = get_localtax_by_third($local);
7037
7038 $valors = explode(":", $tax);
7039
7040 if (count($valors) > 1) {
7041 return false;
7042 } else {
7043 return true;
7044 }
7045}
7046
7053function get_localtax_by_third($local)
7054{
7055 global $db, $mysoc;
7056
7057 $sql = " SELECT t.localtax".$local." as localtax";
7058 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7059 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7060 $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";
7061 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7062 $sql .= " AND t.localtax".$local."_type <> '0'";
7063 $sql .= " ORDER BY t.rowid DESC";
7064
7065 $resql = $db->query($sql);
7066 if ($resql) {
7067 $obj = $db->fetch_object($resql);
7068 if ($obj) {
7069 return $obj->localtax;
7070 } else {
7071 return '0';
7072 }
7073 }
7074
7075 return 'Error';
7076}
7077
7078
7090function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7091{
7092 global $db;
7093
7094 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7095
7096 // Search local taxes
7097 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7098 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7099 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7100 if ($firstparamisid) {
7101 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7102 } else {
7103 $vatratecleaned = $vatrate;
7104 $vatratecode = '';
7105 $reg = array();
7106 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7107 $vatratecleaned = $reg[1];
7108 $vatratecode = $reg[2];
7109 }
7110
7111 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7112 /*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 ??
7113 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7114 $sql .= " WHERE t.fk_pays = c.rowid";
7115 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7116 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7117 } else {
7118 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7119 }
7120 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7121 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7122 if ($vatratecode) {
7123 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7124 }
7125 }
7126
7127 $resql = $db->query($sql);
7128 if ($resql) {
7129 $obj = $db->fetch_object($resql);
7130 if ($obj) {
7131 return array(
7132 'rowid' => $obj->rowid,
7133 'code' => $obj->code,
7134 'rate' => $obj->rate,
7135 'localtax1' => $obj->localtax1,
7136 'localtax1_type' => $obj->localtax1_type,
7137 'localtax2' => $obj->localtax2,
7138 'localtax2_type' => $obj->localtax2_type,
7139 'npr' => $obj->npr,
7140 'accountancy_code_sell' => $obj->accountancy_code_sell,
7141 'accountancy_code_buy' => $obj->accountancy_code_buy
7142 );
7143 } else {
7144 return array();
7145 }
7146 } else {
7147 dol_print_error($db);
7148 }
7149
7150 return array();
7151}
7152
7169function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7170{
7171 global $db, $mysoc;
7172
7173 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7174
7175 // Search local taxes
7176 $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";
7177 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7178 if ($firstparamisid) {
7179 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7180 } else {
7181 $vatratecleaned = $vatrate;
7182 $vatratecode = '';
7183 $reg = array();
7184 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7185 $vatratecleaned = $reg[1];
7186 $vatratecode = $reg[2];
7187 }
7188
7189 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7190 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7191 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7192 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7193 } else {
7194 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7195 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7196 }
7197 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7198 if ($vatratecode) {
7199 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7200 }
7201 }
7202
7203 $resql = $db->query($sql);
7204 if ($resql) {
7205 $obj = $db->fetch_object($resql);
7206
7207 if ($obj) {
7208 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7209
7210 if ($local == 1) {
7211 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7212 } elseif ($local == 2) {
7213 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7214 } else {
7215 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);
7216 }
7217 }
7218 }
7219
7220 return array();
7221}
7222
7233function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7234{
7235 global $db, $mysoc;
7236
7237 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7238
7239 $ret = 0;
7240 $found = 0;
7241
7242 if ($idprod > 0) {
7243 // Load product
7244 $product = new Product($db);
7245 $product->fetch($idprod);
7246
7247 if (($mysoc->country_code == $thirdpartytouse->country_code)
7248 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7249 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7250 ) {
7251 // If country of thirdparty to consider is ours
7252 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7253 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7254 if ($result > 0) {
7255 $ret = $product->vatrate_supplier;
7256 if ($product->default_vat_code_supplier) {
7257 $ret .= ' ('.$product->default_vat_code_supplier.')';
7258 }
7259 $found = 1;
7260 }
7261 }
7262 if (!$found) {
7263 $ret = $product->tva_tx; // Default sales vat of product
7264 if ($product->default_vat_code) {
7265 $ret .= ' ('.$product->default_vat_code.')';
7266 }
7267 $found = 1;
7268 }
7269 } else {
7270 // TODO Read default product vat according to product and an other countrycode.
7271 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7272 }
7273 }
7274
7275 if (!$found) {
7276 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7277 // 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).
7278 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7279 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7280 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7281 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7282 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7283 $sql .= $db->plimit(1);
7284
7285 $resql = $db->query($sql);
7286 if ($resql) {
7287 $obj = $db->fetch_object($resql);
7288 if ($obj) {
7289 $ret = $obj->vat_rate;
7290 if ($obj->default_vat_code) {
7291 $ret .= ' ('.$obj->default_vat_code.')';
7292 }
7293 }
7294 $db->free($resql);
7295 } else {
7296 dol_print_error($db);
7297 }
7298 } else {
7299 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7300 // '1.23'
7301 // or '1.23 (CODE)'
7302 $defaulttx = '';
7303 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7304 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7305 }
7306 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7307 $defaultcode = $reg[1];
7308 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7309 }*/
7310
7311 $ret = $defaulttx;
7312 }
7313 }
7314
7315 dol_syslog("get_product_vat_for_country: ret=".$ret);
7316 return $ret;
7317}
7318
7328function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7329{
7330 global $db, $mysoc;
7331
7332 if (!class_exists('Product')) {
7333 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7334 }
7335
7336 $ret = 0;
7337 $found = 0;
7338
7339 if ($idprod > 0) {
7340 // Load product
7341 $product = new Product($db);
7342 $result = $product->fetch($idprod);
7343
7344 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7345 /* Not defined yet, so we don't use this
7346 if ($local==1) $ret=$product->localtax1_tx;
7347 elseif ($local==2) $ret=$product->localtax2_tx;
7348 $found=1;
7349 */
7350 } else {
7351 // TODO Read default product vat according to product and another countrycode.
7352 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7353 }
7354 }
7355
7356 if (!$found) {
7357 // If vat of product for the country not found or not defined, we return higher vat of country.
7358 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7359 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7360 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7361 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7362 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7363 $sql .= $db->plimit(1);
7364
7365 $resql = $db->query($sql);
7366 if ($resql) {
7367 $obj = $db->fetch_object($resql);
7368 if ($obj) {
7369 if ($local == 1) {
7370 $ret = $obj->localtax1;
7371 } elseif ($local == 2) {
7372 $ret = $obj->localtax2;
7373 }
7374 }
7375 } else {
7376 dol_print_error($db);
7377 }
7378 }
7379
7380 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7381 return $ret;
7382}
7383
7400function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7401{
7402 global $conf;
7403
7404 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7405
7406 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7407 $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;
7408
7409 $seller_country_code = $thirdparty_seller->country_code;
7410 $seller_in_cee = isInEEC($thirdparty_seller);
7411
7412 $buyer_country_code = $thirdparty_buyer->country_code;
7413 $buyer_in_cee = isInEEC($thirdparty_buyer);
7414
7415 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 : ''));
7416
7417 // 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)
7418 // we use the buyer VAT.
7419 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7420 if ($seller_in_cee && $buyer_in_cee) {
7421 $isacompany = $thirdparty_buyer->isACompany();
7422 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7423 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7424 if (!isValidVATID($thirdparty_buyer)) {
7425 $isacompany = 0;
7426 }
7427 }
7428
7429 if (!$isacompany) {
7430 //print 'VATRULE 0';
7431 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7432 }
7433 }
7434 }
7435
7436 // If seller does not use VAT, default VAT is 0. End of rule.
7437 if (!$seller_use_vat) {
7438 //print 'VATRULE 1';
7439 return 0;
7440 }
7441
7442 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7443 if (($seller_country_code == $buyer_country_code)
7444 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7445 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7446 ) { // Warning ->country_code not always defined
7447 //print 'VATRULE 2';
7448 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7449
7450 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7451 // Special case for india.
7452 //print 'VATRULE 2b';
7453 $reg = array();
7454 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7455 // we must revert the C+S into I
7456 $tmpvat = str_replace("C+S", "I", $tmpvat);
7457 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7458 // we must revert the I into C+S
7459 $tmpvat = str_replace("I", "C+S", $tmpvat);
7460 }
7461 }
7462
7463 return $tmpvat;
7464 }
7465
7466 // 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.
7467 // 'VATRULE 3' - Not supported
7468
7469 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7470 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7471 if (($seller_in_cee && $buyer_in_cee)) {
7472 $isacompany = $thirdparty_buyer->isACompany();
7473 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7474 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7475 if (!isValidVATID($thirdparty_buyer)) {
7476 $isacompany = 0;
7477 }
7478 }
7479
7480 if (!$isacompany) {
7481 //print 'VATRULE 4';
7482 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7483 } else {
7484 //print 'VATRULE 5';
7485 return 0;
7486 }
7487 }
7488
7489 // 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
7490 // I don't see any use case that need this rule.
7491 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7492 $isacompany = $thirdparty_buyer->isACompany();
7493 if (!$isacompany) {
7494 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7495 //print 'VATRULE extra';
7496 }
7497 }
7498
7499 // Otherwise the VAT proposed by default=0. End of rule.
7500 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7501 //print 'VATRULE 6';
7502 return 0;
7503}
7504
7505
7516function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7517{
7518 global $db;
7519
7520 if ($idprodfournprice > 0) {
7521 if (!class_exists('ProductFournisseur')) {
7522 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7523 }
7524 $prodprice = new ProductFournisseur($db);
7525 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7526 return $prodprice->fourn_tva_npr;
7527 } elseif ($idprod > 0) {
7528 if (!class_exists('Product')) {
7529 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7530 }
7531 $prod = new Product($db);
7532 $prod->fetch($idprod);
7533 return $prod->tva_npr;
7534 }
7535
7536 return 0;
7537}
7538
7552function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7553{
7554 global $mysoc;
7555
7556 if (!is_object($thirdparty_seller)) {
7557 return -1;
7558 }
7559 if (!is_object($thirdparty_buyer)) {
7560 return -1;
7561 }
7562
7563 if ($local == 1) { // Localtax 1
7564 if ($mysoc->country_code == 'ES') {
7565 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7566 return 0;
7567 }
7568 } else {
7569 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7570 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7571 return 0;
7572 }
7573 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7574 return 0;
7575 }
7576 }
7577 } elseif ($local == 2) { //I Localtax 2
7578 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7579 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7580 return 0;
7581 }
7582 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7583 return 0;
7584 }
7585 }
7586
7587 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7588 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7589 }
7590
7591 return 0;
7592}
7593
7602function yn($yesno, $case = 1, $color = 0)
7603{
7604 global $langs;
7605
7606 $result = 'unknown';
7607 $classname = '';
7608 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7609 $result = $langs->trans('yes');
7610 if ($case == 1 || $case == 3) {
7611 $result = $langs->trans("Yes");
7612 }
7613 if ($case == 2) {
7614 $result = '<input type="checkbox" value="1" checked disabled>';
7615 }
7616 if ($case == 3) {
7617 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7618 }
7619 if ($case == 4) {
7620 $result = img_picto('check', 'check');
7621 }
7622
7623 $classname = 'ok';
7624 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7625 $result = $langs->trans("no");
7626 if ($case == 1 || $case == 3) {
7627 $result = $langs->trans("No");
7628 }
7629 if ($case == 2) {
7630 $result = '<input type="checkbox" value="0" disabled>';
7631 }
7632 if ($case == 3) {
7633 $result = '<input type="checkbox" value="0" disabled> '.$result;
7634 }
7635 if ($case == 4) {
7636 $result = img_picto('uncheck', 'uncheck');
7637 }
7638
7639 if ($color == 2) {
7640 $classname = 'ok';
7641 } else {
7642 $classname = 'error';
7643 }
7644 }
7645 if ($color) {
7646 return '<span class="'.$classname.'">'.$result.'</span>';
7647 }
7648 return $result;
7649}
7650
7669function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7670{
7671 if (empty($modulepart) && is_object($object)) {
7672 if (!empty($object->module)) {
7673 $modulepart = $object->module;
7674 } elseif (!empty($object->element)) {
7675 $modulepart = $object->element;
7676 }
7677 }
7678
7679 $path = '';
7680
7681 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7682 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7683 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7684 $arrayforoldpath['product'] = 2;
7685 }
7686
7687 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7688 $level = $arrayforoldpath[$modulepart];
7689 }
7690 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7691 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7692 if (empty($num) && is_object($object)) {
7693 $num = $object->id;
7694 }
7695 if (empty($alpha)) {
7696 $num = preg_replace('/([^0-9])/i', '', $num);
7697 } else {
7698 $num = preg_replace('/^.*\-/i', '', $num);
7699 }
7700 $num = substr("000".$num, -$level);
7701 if ($level == 1) {
7702 $path = substr($num, 0, 1);
7703 }
7704 if ($level == 2) {
7705 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7706 }
7707 if ($level == 3) {
7708 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7709 }
7710 } else {
7711 // We will enhance here a common way of forging path for document storage.
7712 // In a future, we may distribute directories on several levels depending on setup and object.
7713 // Here, $object->id, $object->ref and $modulepart are required.
7714 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
7715 // Special case for thirdparty, where the ref is a company name that is not unique so path on disk is using the ID instead of the ref
7716 $path = dol_sanitizeFileName($object->id);
7717 } else {
7718 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7719 }
7720 }
7721
7722 if (empty($withoutslash) && !empty($path)) {
7723 $path .= '/';
7724 }
7725
7726 return $path;
7727}
7728
7737function dol_mkdir($dir, $dataroot = '', $newmask = '')
7738{
7739 global $conf;
7740
7741 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7742
7743 $dir_osencoded = dol_osencode($dir);
7744 if (@is_dir($dir_osencoded)) {
7745 return 0;
7746 }
7747
7748 $nberr = 0;
7749 $nbcreated = 0;
7750
7751 $ccdir = '';
7752 if (!empty($dataroot)) {
7753 // Remove data root from loop
7754 $dir = str_replace($dataroot.'/', '', $dir);
7755 $ccdir = $dataroot.'/';
7756 }
7757
7758 $cdir = explode("/", $dir);
7759 $num = count($cdir);
7760 for ($i = 0; $i < $num; $i++) {
7761 if ($i > 0) {
7762 $ccdir .= '/'.$cdir[$i];
7763 } else {
7764 $ccdir .= $cdir[$i];
7765 }
7766 $regs = array();
7767 if (preg_match("/^.:$/", $ccdir, $regs)) {
7768 continue; // If the Windows path is incomplete, continue with next directory
7769 }
7770
7771 // Attention, is_dir() can fail event if the directory exists
7772 // (i.e. according the open_basedir configuration)
7773 if ($ccdir) {
7774 $ccdir_osencoded = dol_osencode($ccdir);
7775 if (!@is_dir($ccdir_osencoded)) {
7776 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7777
7778 umask(0);
7779 $dirmaskdec = octdec((string) $newmask);
7780 if (empty($newmask)) {
7781 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7782 }
7783 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7784 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7785 // If the is_dir has returned a false information, we arrive here
7786 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7787 $nberr++;
7788 } else {
7789 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7790 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7791 $nbcreated++;
7792 }
7793 } else {
7794 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7795 }
7796 }
7797 }
7798 return ($nberr ? -$nberr : $nbcreated);
7799}
7800
7801
7809function dolChmod($filepath, $newmask = '')
7810{
7811 global $conf;
7812
7813 if (!empty($newmask)) {
7814 @chmod($filepath, octdec($newmask));
7815 } elseif (getDolGlobalString('MAIN_UMASK')) {
7816 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7817 }
7818}
7819
7820
7826function picto_required()
7827{
7828 return '<span class="fieldrequired">*</span>';
7829}
7830
7831
7848function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7849{
7850 if (is_null($stringtoclean)) {
7851 return '';
7852 }
7853
7854 if ($removelinefeed == 2) {
7855 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7856 }
7857 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7858
7859 // 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)
7860 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7861
7862 $temp = str_replace('< ', '__ltspace__', $temp);
7863 $temp = str_replace('<:', '__lttwopoints__', $temp);
7864
7865 if ($strip_tags) {
7866 $temp = strip_tags($temp);
7867 } else {
7868 // 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).
7869 $pattern = "/<[^<>]+>/";
7870 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7871 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7872 // pass 2 - $temp after pass 2: 0000-021
7873 $tempbis = $temp;
7874 do {
7875 $temp = $tempbis;
7876 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7877 $tempbis = preg_replace($pattern, '', $tempbis);
7878 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7879 } while ($tempbis != $temp);
7880
7881 $temp = $tempbis;
7882
7883 // 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).
7884 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7885 }
7886
7887 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7888
7889 // Remove also carriage returns
7890 if ($removelinefeed == 1) {
7891 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7892 }
7893
7894 // And double quotes
7895 if ($removedoublespaces) {
7896 while (strpos($temp, " ")) {
7897 $temp = str_replace(" ", " ", $temp);
7898 }
7899 }
7900
7901 $temp = str_replace('__ltspace__', '< ', $temp);
7902 $temp = str_replace('__lttwopoints__', '<:', $temp);
7903
7904 return trim($temp);
7905}
7906
7922function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7923{
7924 if (empty($allowed_tags)) {
7925 $allowed_tags = array(
7926 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7927 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
7928 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
7929 );
7930 }
7931 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7932 if ($allowiframe) {
7933 if (!in_array('iframe', $allowed_tags)) {
7934 $allowed_tags[] = "iframe";
7935 }
7936 }
7937 if ($allowlink) {
7938 if (!in_array('link', $allowed_tags)) {
7939 $allowed_tags[] = "link";
7940 }
7941 }
7942
7943 $allowed_tags_string = implode("><", $allowed_tags);
7944 $allowed_tags_string = '<'.$allowed_tags_string.'>';
7945
7946 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7947
7948 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7949
7950 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7951 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7952
7953 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7954 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7955
7956 // Remove all HTML tags
7957 $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
7958
7959 if ($cleanalsosomestyles) { // Clean for remaining html tags
7960 $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
7961 }
7962 if ($removeclassattribute) { // Clean for remaining html tags
7963 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7964 }
7965
7966 // Remove 'javascript:' that we should not find into a text with
7967 // 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)).
7968 if ($cleanalsojavascript) {
7969 $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);
7970 }
7971
7972 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
7973
7974 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
7975
7976
7977 return $temp;
7978}
7979
7980
7993function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
7994{
7995 if (is_null($allowed_attributes)) {
7996 $allowed_attributes = array(
7997 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
7998 // HTML5
7999 "header", "footer", "nav", "section", "menu", "menuitem"
8000 );
8001 }
8002 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8003 if (!in_array("content", $allowed_attributes)) {
8004 $allowed_attributes[] = "content";
8005 }
8006 if (!in_array("http-equiv", $allowed_attributes)) {
8007 $allowed_attributes[] = "http-equiv";
8008 }
8009
8010 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8011 $stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8012
8013 // Warning: loadHTML does not support HTML5 on old libxml versions.
8014 $dom = new DOMDocument('', 'UTF-8');
8015 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8016 $savwarning = error_reporting();
8017 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8018 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8019 error_reporting($savwarning);
8020
8021 if ($dom instanceof DOMDocument) {
8022 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8023 $el = $els->item($i);
8024 if (!$el instanceof DOMElement) {
8025 continue;
8026 }
8027 $attrs = $el->attributes;
8028 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8029 //var_dump($attrs->item($ii));
8030 if (!empty($attrs->item($ii)->name)) {
8031 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8032 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8033 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8034 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8035 // If attribute is 'style'
8036 $valuetoclean = $attrs->item($ii)->value;
8037
8038 if (isset($valuetoclean)) {
8039 do {
8040 $oldvaluetoclean = $valuetoclean;
8041 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8042 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8043 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8044 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8045 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8046 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8047 }
8048
8049 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8050 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8051 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8052 } while ($oldvaluetoclean != $valuetoclean);
8053 }
8054
8055 $attrs->item($ii)->value = $valuetoclean;
8056 }
8057 }
8058 }
8059 }
8060 }
8061
8062 $dom->encoding = 'UTF-8';
8063
8064 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8065 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8066
8067 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8068 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8069 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8070
8071 return trim($return);
8072 } else {
8073 return $stringtoclean;
8074 }
8075}
8076
8088function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8089{
8090 $temp = $stringtoclean;
8091 foreach ($disallowed_tags as $tagtoremove) {
8092 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8093 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8094 }
8095
8096 if ($cleanalsosomestyles) {
8097 $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/', '', $temp); // Note: If hacker try to introduce css comment into string to avoid this, string should be encoded by the dol_htmlentitiesbr so be harmless
8098 }
8099
8100 return $temp;
8101}
8102
8103
8113function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8114{
8115 if ($nboflines == 1) {
8116 if (dol_textishtml($text)) {
8117 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8118 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8119 } else {
8120 if (isset($text)) {
8121 $firstline = preg_replace('/[\n\r].*/', '', $text);
8122 } else {
8123 $firstline = '';
8124 }
8125 }
8126 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8127 } else {
8128 $ishtml = 0;
8129 if (dol_textishtml($text)) {
8130 $text = preg_replace('/\n/', '', $text);
8131 $ishtml = 1;
8132 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8133 } else {
8134 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8135 }
8136
8137 $text = strtr($text, $repTable);
8138 if ($charset == 'UTF-8') {
8139 $pattern = '/(<br[^>]*>)/Uu';
8140 } else {
8141 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8142 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8143 }
8144 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8145
8146 $firstline = '';
8147 $i = 0;
8148 $countline = 0;
8149 $lastaddediscontent = 1;
8150 while ($countline < $nboflines && isset($a[$i])) {
8151 if (preg_match('/<br[^>]*>/', $a[$i])) {
8152 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8153 $firstline .= ($ishtml ? "<br>\n" : "\n");
8154 // Is it a br for a new line of after a printed line ?
8155 if (!$lastaddediscontent) {
8156 $countline++;
8157 }
8158 $lastaddediscontent = 0;
8159 }
8160 } else {
8161 $firstline .= $a[$i];
8162 $lastaddediscontent = 1;
8163 $countline++;
8164 }
8165 $i++;
8166 }
8167
8168 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8169 //unset($a);
8170 $ret = $firstline.($adddots ? '...' : '');
8171 //exit;
8172 return $ret;
8173 }
8174}
8175
8176
8188function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8189{
8190 if (is_null($stringtoencode)) {
8191 return '';
8192 }
8193
8194 if (!$nl2brmode) {
8195 return nl2br($stringtoencode, $forxml);
8196 } else {
8197 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8198 return $ret;
8199 }
8200}
8201
8211function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8212{
8213 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8214 // TODO using sandbox on inline html content is not possible yet with current browsers
8215 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8216 //$s .= $stringtoencode;
8217 //$s .= '</body></html></iframe>';
8218 return $stringtoencode;
8219 } else {
8220 $out = $stringtoencode;
8221
8222 // First clean HTML content
8223 do {
8224 $oldstringtoclean = $out;
8225
8226 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8227 try {
8228 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8229 if (LIBXML_VERSION < 20900) {
8230 // Avoid load of external entities (security problem).
8231 // Required only if LIBXML_VERSION < 20900
8232 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8233 libxml_disable_entity_loader(true);
8234 }
8235
8236 $dom = new DOMDocument();
8237 // Add a trick to solve pb with text without parent tag
8238 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8239 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8240
8241 if (dol_textishtml($out)) {
8242 $out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8243 } else {
8244 $out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8245 }
8246
8247 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8248
8249 $dom->encoding = 'UTF-8';
8250
8251 $out = trim($dom->saveHTML());
8252
8253 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8254 $out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8255 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8256 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8257 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8258 // $out = preg_replace('/<\/div>$/', '', $out);
8259 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8260 } catch (Exception $e) {
8261 // If error, invalid HTML string with no way to clean it
8262 //print $e->getMessage();
8263 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8264 }
8265 }
8266
8267 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && $check != 'restricthtmlallowunvalid') {
8268 try {
8269 // Try cleaning using tidy
8270 if (extension_loaded('tidy') && class_exists("tidy")) {
8271 //print "aaa".$out."\n";
8272
8273 // See options at https://tidy.sourceforge.net/docs/quickref.html
8274 $config = array(
8275 'clean' => false,
8276 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8277 'doctype' => 'strict',
8278 'show-body-only' => true,
8279 "indent-attributes" => false,
8280 "vertical-space" => false,
8281 //'ident' => false, // Not always supported
8282 "wrap" => 0
8283 // HTML5 tags
8284 //'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',
8285 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8286 //'new-empty-tags' => 'command embed keygen source track wbr',
8287 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8288 );
8289
8290 // Tidy
8291 $tidy = new tidy();
8292 $out = $tidy->repairString($out, $config, 'utf8');
8293
8294 //print "xxx".$out;exit;
8295 }
8296 } catch (Exception $e) {
8297 // If error, invalid HTML string with no way to clean it
8298 //print $e->getMessage();
8299 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8300 }
8301 }
8302
8303 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8304 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8305
8306 // Clean some html entities that are useless so text is cleaner
8307 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8308
8309 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8310 // encoded using text entities) so we can then exclude all numeric entities.
8311 $out = preg_replace('/&#39;/i', '&apos;', $out);
8312
8313 // 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).
8314 // 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
8315 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8316 $out = preg_replace_callback(
8317 '/&#(x?[0-9][0-9a-f]+;?)/i',
8322 static function ($m) {
8323 return realCharForNumericEntities($m);
8324 },
8325 $out
8326 );
8327
8328 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8329 $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'.
8330
8331 // Keep only some html tags and remove also some 'javascript:' strings
8332 if ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8333 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8334 } else {
8335 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8336 }
8337
8338 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8339 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8341 }
8342
8343 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8344 $out = preg_replace('/&apos;/i', "&#39;", $out);
8345
8346 // Now remove js
8347 // 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
8348 $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)>
8349 $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);
8350 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8351 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8352 $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);
8353 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8354 // More not into the previous list
8355 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8356 } while ($oldstringtoclean != $out);
8357
8358 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8359 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8360 // 'url(' to avoid inline style like background: url(http...
8361 // '<link' to avoid <link href="http...">
8362 $reg = array();
8363 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8364 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8365 $nblinks = count($reg[0]);
8366 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8367 $out = 'ErrorTooManyLinksIntoHTMLString';
8368 }
8369
8370 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8371 if ($nblinks > 0) {
8372 $out = 'ErrorHTMLLinksNotAllowed';
8373 }
8374 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8375 $nblinks = 0;
8376 // Loop on each url in src= and url(
8377 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8378
8379 $matches = array();
8380 if (preg_match_all($pattern, $out, $matches)) {
8381 // URLs are into $matches[1]
8382 $urls = $matches[1];
8383
8384 // Affiche les URLs
8385 foreach ($urls as $url) {
8386 $nblinks++;
8387 echo "Found url = ".$url . "\n";
8388 }
8389 if ($nblinks > 0) {
8390 $out = 'ErrorHTMLExternalLinksNotAllowed';
8391 }
8392 }
8393 }
8394
8395 return $out;
8396 }
8397}
8398
8419function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8420{
8421 if (is_null($stringtoencode)) {
8422 return '';
8423 }
8424
8425 $newstring = $stringtoencode;
8426 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8427 $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.
8428 if ($removelasteolbr) {
8429 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8430 }
8431 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8432 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8433 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8434 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8435 } else {
8436 if ($removelasteolbr) {
8437 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8438 }
8439 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8440 }
8441 // Other substitutions that htmlentities does not do
8442 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8443 return $newstring;
8444}
8445
8453function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8454{
8455 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8456 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8457 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8458 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8459 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8460 return $ret;
8461}
8462
8469function dol_htmlcleanlastbr($stringtodecode)
8470{
8471 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8472 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8473 return $ret;
8474}
8475
8485function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8486{
8487 $newstring = $a;
8488 if ($keepsomeentities) {
8489 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8490 }
8491 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8492 if ($keepsomeentities) {
8493 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8494 }
8495 return $newstring;
8496}
8497
8509function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8510{
8511 return htmlentities($string, $flags, $encoding, $double_encode);
8512}
8513
8525function dol_string_is_good_iso($s, $clean = 0)
8526{
8527 $len = dol_strlen($s);
8528 $out = '';
8529 $ok = 1;
8530 for ($scursor = 0; $scursor < $len; $scursor++) {
8531 $ordchar = ord($s[$scursor]);
8532 //print $scursor.'-'.$ordchar.'<br>';
8533 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8534 $ok = 0;
8535 break;
8536 } elseif ($ordchar > 126 && $ordchar < 160) {
8537 $ok = 0;
8538 break;
8539 } elseif ($clean) {
8540 $out .= $s[$scursor];
8541 }
8542 }
8543 if ($clean) {
8544 return $out;
8545 }
8546 return $ok;
8547}
8548
8557function dol_nboflines($s, $maxchar = 0)
8558{
8559 if ($s == '') {
8560 return 0;
8561 }
8562 $arraystring = explode("\n", $s);
8563 $nb = count($arraystring);
8564
8565 return $nb;
8566}
8567
8568
8578function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8579{
8580 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8581 if (dol_textishtml($text)) {
8582 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8583 }
8584
8585 $text = strtr($text, $repTable);
8586 if ($charset == 'UTF-8') {
8587 $pattern = '/(<br[^>]*>)/Uu';
8588 } else {
8589 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8590 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8591 }
8592 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8593
8594 $nblines = (int) floor((count($a) + 1) / 2);
8595 // count possible auto line breaks
8596 if ($maxlinesize) {
8597 foreach ($a as $line) {
8598 if (dol_strlen($line) > $maxlinesize) {
8599 //$line_dec = html_entity_decode(strip_tags($line));
8600 $line_dec = html_entity_decode($line);
8601 if (dol_strlen($line_dec) > $maxlinesize) {
8602 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8603 $nblines += substr_count($line_dec, '\n');
8604 }
8605 }
8606 }
8607 }
8608
8609 unset($a);
8610 return $nblines;
8611}
8612
8621function dol_textishtml($msg, $option = 0)
8622{
8623 if (is_null($msg)) {
8624 return false;
8625 }
8626
8627 if ($option == 1) {
8628 if (preg_match('/<html/i', $msg)) {
8629 return true;
8630 } elseif (preg_match('/<body/i', $msg)) {
8631 return true;
8632 } elseif (preg_match('/<\/textarea/i', $msg)) {
8633 return true;
8634 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8635 return true;
8636 } elseif (preg_match('/<br/i', $msg)) {
8637 return true;
8638 }
8639 return false;
8640 } else {
8641 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8642 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8643 if (preg_match('/<html/i', $msg)) {
8644 return true;
8645 } elseif (preg_match('/<body/i', $msg)) {
8646 return true;
8647 } elseif (preg_match('/<\/textarea/i', $msg)) {
8648 return true;
8649 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8650 return true;
8651 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8652 return true;
8653 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8654 return true;
8655 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8656 return true;
8657 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8658 return true; // must accept <img src="http://example.com/aaa.png" />
8659 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8660 return true; // must accept <a href="http://example.com/aaa.png" />
8661 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8662 return true;
8663 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8664 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8665 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8666 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8667 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8668 }
8669
8670 return false;
8671 }
8672}
8673
8688function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8689{
8690 if (!empty($invert)) {
8691 $tmp = $text1;
8692 $text1 = $text2;
8693 $text2 = $tmp;
8694 }
8695
8696 $ret = '';
8697 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8698 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8699 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8700 return $ret;
8701}
8702
8703
8704
8718function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8719{
8720 global $db, $conf, $mysoc, $user, $extrafields;
8721
8722 $substitutionarray = array();
8723
8724 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
8725 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8726 // this will include signature content first and then replace var found into content of signature
8727 //var_dump($onlykey);
8728 $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()
8729 $usersignature = $user->signature;
8730 $substitutionarray = array_merge($substitutionarray, array(
8731 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8732 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8733 ));
8734
8735 if (is_object($user) && ($user instanceof User)) {
8736 $substitutionarray = array_merge($substitutionarray, array(
8737 '__USER_ID__' => (string) $user->id,
8738 '__USER_LOGIN__' => (string) $user->login,
8739 '__USER_EMAIL__' => (string) $user->email,
8740 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8741 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8742 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8743 '__USER_FAX__' => (string) $user->office_fax,
8744 '__USER_LASTNAME__' => (string) $user->lastname,
8745 '__USER_FIRSTNAME__' => (string) $user->firstname,
8746 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8747 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8748 '__USER_JOB__' => (string) $user->job,
8749 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8750 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8751 ));
8752 }
8753 }
8754 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8755 $substitutionarray = array_merge($substitutionarray, array(
8756 '__MYCOMPANY_NAME__' => $mysoc->name,
8757 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8758 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8759 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8760 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8761 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8762 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8763 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8764 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8765 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8766 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8767 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8768 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8769 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8770 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8771 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8772 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8773 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8774 '__MYCOMPANY_TOWN__' => $mysoc->town,
8775 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8776 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8777 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8778 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8779 ));
8780 }
8781
8782 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8783 if ($onlykey) {
8784 $substitutionarray['__ID__'] = '__ID__';
8785 $substitutionarray['__REF__'] = '__REF__';
8786 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8787 $substitutionarray['__LABEL__'] = '__LABEL__';
8788 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8789 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8790 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8791 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8792 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8793
8794 if (isModEnabled("societe")) { // Most objects are concerned
8795 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8796 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8797 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8798 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8799 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8800 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8801 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8802 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8803 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8804 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8805 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8806 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8807 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8808 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8809 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8810 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8811 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8812 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8813 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8814 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8815 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8816 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8817 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8818 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8819 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8820 }
8821 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
8822 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
8823 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
8824 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
8825 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
8826 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
8827 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
8828 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
8829 }
8830 // add substitution variables for ticket
8831 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
8832 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
8833 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
8834 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
8835 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
8836 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
8837 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
8838 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
8839 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
8840 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
8841 }
8842
8843 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
8844 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
8845 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
8846 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
8847 }
8848 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
8849 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
8850 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
8851 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
8852 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
8853 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
8854 }
8855 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
8856 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
8857 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
8858 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
8859 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
8860 }
8861 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
8862 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
8863 }
8864 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
8865 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
8866 }
8867 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
8868 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
8869 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
8870 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
8871 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
8872 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
8873 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
8874
8875 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
8876 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
8877 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
8878 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
8879 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
8880
8881 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
8882 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
8883 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
8884 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
8885 }
8886 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
8887 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
8888 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
8889 }
8890 } else {
8891 '@phan-var-force Adherent|Delivery $object';
8892 $substitutionarray['__ID__'] = $object->id;
8893 $substitutionarray['__REF__'] = $object->ref;
8894 $substitutionarray['__NEWREF__'] = $object->newref;
8895 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
8896 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8897 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8898 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
8899 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
8900
8901 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', 0, $outputlangs) : '');
8902 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', 0, $outputlangs) : '');
8903 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', 0, $outputlangs) : '');
8904
8905 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
8906 $date_delivery = null;
8907 if (property_exists($object, 'date_delivery')) {
8908 $date_delivery = $object->date_delivery;
8909 } elseif (property_exists($object, 'delivery_date')) {
8910 $date_delivery = $object->delivery_date;
8911 }
8912 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', 0, $outputlangs) : '');
8913 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
8914 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
8915 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
8916 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
8917 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
8918 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
8919 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
8920 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
8921
8922 // For backward compatibility (deprecated)
8923 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8924 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8925 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', 0, $outputlangs) : '');
8926 $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 : '')) : '');
8927 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
8928
8929 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
8930 '@phan-var-force Adherent $object';
8931 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
8932
8933 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
8934 if (method_exists($object, 'getCivilityLabel')) {
8935 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
8936 }
8937 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
8938 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
8939 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
8940 if (method_exists($object, 'getFullName')) {
8941 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
8942 }
8943 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
8944 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
8945 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
8946 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
8947 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
8948 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
8949 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
8950 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
8951 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
8952 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
8953 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
8954 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
8955 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
8956 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
8957 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
8958
8959 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
8960 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
8961 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
8962 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
8963 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
8964 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
8965 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
8966 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
8967 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
8968 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
8969 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
8970 }
8971
8972 if (is_object($object) && $object->element == 'societe') {
8973 '@phan-var-force Societe $object';
8974 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
8975 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
8976 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
8977 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
8978 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
8979 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
8980 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
8981 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
8982 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
8983 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
8984 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
8985 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
8986 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
8987 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
8988 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
8989 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
8990 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
8991 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
8992 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
8993 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
8994 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
8995 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
8996 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
8997 } elseif (is_object($object->thirdparty)) {
8998 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
8999 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9000 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9001 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9002 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9003 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9004 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9005 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9006 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9007 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9008 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9009 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9010 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9011 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9012 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9013 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9014 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9015 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9016 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9017 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9018 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9019 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9020 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9021 }
9022
9023 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9024 '@phan-var-force RecruitmentCandidature $object';
9025 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9026 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9027 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9028 }
9029 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9030 '@phan-var-force ConferenceOrBoothAttendee $object';
9031 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9032 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9033 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9034 }
9035
9036 if (is_object($object) && $object->element == 'project') {
9037 '@phan-var-force Project $object';
9038 $substitutionarray['__PROJECT_ID__'] = $object->id;
9039 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9040 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9041 } elseif (is_object($object)) {
9042 $project = null;
9043 if (!empty($object->project)) {
9044 $project = $object->project;
9045 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9046 $project = $object->projet;
9047 }
9048 if (!is_null($project) && is_object($project)) {
9049 $substitutionarray['__PROJECT_ID__'] = $project->id;
9050 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9051 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9052 } else {
9053 // can substitute variables for project : uses lazy load in "make_substitutions" method
9054 $project_id = 0;
9055 if (!empty($object->fk_project) && $object->fk_project > 0) {
9056 $project_id = $object->fk_project;
9057 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9058 $project_id = $object->fk_project;
9059 }
9060 if ($project_id > 0) {
9061 // path:class:method:id
9062 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9063 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9064 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9065 }
9066 }
9067 }
9068
9069 if (is_object($object) && $object->element == 'facture') {
9070 '@phan-var-force Facture $object';
9071 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9072 }
9073 if (is_object($object) && $object->element == 'shipping') {
9074 '@phan-var-force Expedition $object';
9075 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9076 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9077 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9078 }
9079 if (is_object($object) && $object->element == 'reception') {
9080 '@phan-var-force Reception $object';
9081 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9082 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9083 }
9084
9085 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9086 '@phan-var-force Contrat $object';
9087 $dateplannedstart = '';
9088 $datenextexpiration = '';
9089 foreach ($object->lines as $line) {
9090 if ($line->date_start > $dateplannedstart) {
9091 $dateplannedstart = $line->date_start;
9092 }
9093 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9094 $datenextexpiration = $line->date_end;
9095 }
9096 }
9097 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9098 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9099 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9100
9101 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9102 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9103 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9104 }
9105 // add substitution variables for ticket
9106 if (is_object($object) && $object->element == 'ticket') {
9107 '@phan-var-force Ticket $object';
9108 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9109 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9110 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9111 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9112 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9113 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9114 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9115 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9116 $userstat = new User($db);
9117 if ($object->fk_user_assign > 0) {
9118 $userstat->fetch($object->fk_user_assign);
9119 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9120 }
9121
9122 if ($object->fk_user_create > 0) {
9123 $userstat->fetch($object->fk_user_create);
9124 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9125 }
9126 }
9127
9128 // Create dynamic tags for __EXTRAFIELD_FIELD__
9129 if ($object->table_element && $object->id > 0) {
9130 if (!is_object($extrafields)) {
9131 $extrafields = new ExtraFields($db);
9132 }
9133 $extrafields->fetch_name_optionals_label($object->table_element, true);
9134
9135 if ($object->fetch_optionals() > 0) {
9136 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9137 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9138 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9139 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9140 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9141 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9142 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9143 $datetime = $object->array_options['options_'.$key];
9144 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9145 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9146 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9147 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9148 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9149 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9150 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9151 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9152 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9153 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9154 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9155 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9156 }
9157 }
9158 }
9159 }
9160 }
9161
9162 // Complete substitution array with the url to make online payment
9163 if (empty($substitutionarray['__REF__'])) {
9164 $paymenturl = '';
9165 } else {
9166 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9167 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9168 $outputlangs->loadLangs(array('paypal', 'other'));
9169
9170 $amounttouse = 0;
9171 $typeforonlinepayment = 'free';
9172 if (is_object($object) && $object->element == 'commande') {
9173 $typeforonlinepayment = 'order';
9174 }
9175 if (is_object($object) && $object->element == 'facture') {
9176 $typeforonlinepayment = 'invoice';
9177 }
9178 if (is_object($object) && $object->element == 'member') {
9179 $typeforonlinepayment = 'member';
9180 if (!empty($object->last_subscription_amount)) {
9181 $amounttouse = $object->last_subscription_amount;
9182 }
9183 }
9184 if (is_object($object) && $object->element == 'contrat') {
9185 $typeforonlinepayment = 'contract';
9186 }
9187 if (is_object($object) && $object->element == 'fichinter') {
9188 $typeforonlinepayment = 'ficheinter';
9189 }
9190
9191 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9192 $paymenturl = $url;
9193 }
9194
9195 if ($object->id > 0) {
9196 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9197 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9198
9199 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9200 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9201 } else {
9202 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9203 }
9204 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9205 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9206 } else {
9207 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9208 }
9209 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9210 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9211 } else {
9212 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9213 }
9214 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9215 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9216 } else {
9217 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9218 }
9219 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9220 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9221 } else {
9222 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9223 }
9224 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9225 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9226 } else {
9227 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9228 }
9229
9230 if (is_object($object) && $object->element == 'propal') {
9231 '@phan-var-force Propal $object';
9232 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9233 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9234 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9235 }
9236 if (is_object($object) && $object->element == 'commande') {
9237 '@phan-var-force Commande $object';
9238 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9239 }
9240 if (is_object($object) && $object->element == 'facture') {
9241 '@phan-var-force Facture $object';
9242 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9243 }
9244 if (is_object($object) && $object->element == 'contrat') {
9245 '@phan-var-force Contrat $object';
9246 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9247 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9248 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9249 }
9250 if (is_object($object) && $object->element == 'fichinter') {
9251 '@phan-var-force Fichinter $object';
9252 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9253 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9254 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9255 }
9256 if (is_object($object) && $object->element == 'supplier_proposal') {
9257 '@phan-var-force SupplierProposal $object';
9258 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9259 }
9260 if (is_object($object) && $object->element == 'invoice_supplier') {
9261 '@phan-var-force FactureFournisseur $object';
9262 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9263 }
9264 if (is_object($object) && $object->element == 'shipping') {
9265 '@phan-var-force Expedition $object';
9266 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9267 }
9268 }
9269
9270 if (is_object($object) && $object->element == 'action') {
9271 '@phan-var-force ActionComm $object';
9272 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9273 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9274 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9275 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9276 }
9277 }
9278 }
9279 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9280 '@phan-var-force Facture|FactureRec $object';
9281 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9282
9283 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
9284 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
9285 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', 0, $outputlangs) : null) : '';
9286 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', 0, $outputlangs) : null) : '';
9287
9288 $already_payed_all = 0;
9289 if (is_object($object) && ($object instanceof Facture)) {
9290 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9291 }
9292
9293 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9294 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9295 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9296
9297 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9298 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9299 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9300
9301 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9302
9303 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9304 $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)) : '';
9305 $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)) : '';
9306
9307 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9308 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9309 }
9310 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9311 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9312 }
9313
9314 // Amount keys formatted in a currency
9315 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9316 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9317 $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) : '';
9318 $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)) : '';
9319 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9320 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9321 }
9322 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9323 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9324 }
9325 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9326 if ($onlykey != 2) {
9327 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9328 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9329 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9330 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9331 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9332 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9333 }
9334 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9335 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9336 }
9337 }
9338
9339 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9340 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9341 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9342 // TODO Add other keys for foreign multicurrency
9343
9344 // For backward compatibility
9345 if ($onlykey != 2) {
9346 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9347 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9348 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9349 }
9350 }
9351
9352
9353 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9354 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9355
9356 $now = dol_now();
9357
9358 $tmp = dol_getdate($now, true);
9359 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9360 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9361 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9362 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9363
9364 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9365
9366 $substitutionarray = array_merge($substitutionarray, array(
9367 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9368 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9369 '__DAY__' => (string) $tmp['mday'],
9370 '__DAY_TEXT__' => $daytext, // Monday
9371 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9372 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9373 '__MONTH__' => (string) $tmp['mon'],
9374 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9375 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9376 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9377 '__YEAR__' => (string) $tmp['year'],
9378 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9379 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9380 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9381 '__NEXT_DAY__' => (string) $tmp4['day'],
9382 '__NEXT_MONTH__' => (string) $tmp5['month'],
9383 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9384 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9385 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9386 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9387 ));
9388 }
9389
9390 if (isModEnabled('multicompany')) {
9391 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9392 }
9393 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9394 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9395 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9396 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9397 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9398 }
9399
9400 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9401
9402 return $substitutionarray;
9403}
9404
9421function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9422{
9423 global $conf, $db, $langs;
9424
9425 if (!is_array($substitutionarray)) {
9426 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9427 }
9428
9429 if (empty($outputlangs)) {
9430 $outputlangs = $langs;
9431 }
9432
9433 // Is initial text HTML or simple text ?
9434 $msgishtml = 0;
9435 if (dol_textishtml($text, 1)) {
9436 $msgishtml = 1;
9437 }
9438
9439 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9440 if (is_object($outputlangs)) {
9441 $reg = array();
9442 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9443 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9444 $tmp = explode('|', $reg[1]);
9445 if (!empty($tmp[1])) {
9446 $outputlangs->load($tmp[1]);
9447 }
9448
9449 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9450
9451 if (empty($converttextinhtmlifnecessary)) {
9452 // convert $newval into HTML is necessary
9453 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9454 } else {
9455 if (! $msgishtml) {
9456 $valueishtml = dol_textishtml($value, 1);
9457 //var_dump("valueishtml=".$valueishtml);
9458
9459 if ($valueishtml) {
9460 $text = dol_htmlentitiesbr($text);
9461 $msgishtml = 1;
9462 }
9463 } else {
9464 $value = dol_nl2br("$value");
9465 }
9466
9467 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9468 }
9469 }
9470 }
9471
9472 // Make substitution for constant keys.
9473 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9474 $reg = array();
9475 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9476 $keyfound = $reg[1];
9477 if (isASecretKey($keyfound)) {
9478 $value = '*****forbidden*****';
9479 } else {
9480 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9481 }
9482
9483 if (empty($converttextinhtmlifnecessary)) {
9484 // convert $newval into HTML is necessary
9485 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9486 } else {
9487 if (! $msgishtml) {
9488 $valueishtml = dol_textishtml($value, 1);
9489
9490 if ($valueishtml) {
9491 $text = dol_htmlentitiesbr($text);
9492 $msgishtml = 1;
9493 }
9494 } else {
9495 $value = dol_nl2br("$value");
9496 }
9497
9498 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9499 }
9500 }
9501
9502 // Make substitution for array $substitutionarray
9503 foreach ($substitutionarray as $key => $value) {
9504 if (!isset($value)) {
9505 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9506 }
9507
9508 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9509 $value = ''; // Protection
9510 }
9511
9512 if (empty($converttextinhtmlifnecessary)) {
9513 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9514 } else {
9515 if (! $msgishtml) {
9516 $valueishtml = dol_textishtml($value, 1);
9517
9518 if ($valueishtml) {
9519 $text = dol_htmlentitiesbr($text);
9520 $msgishtml = 1;
9521 }
9522 } else {
9523 $value = dol_nl2br("$value");
9524 }
9525 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9526 }
9527 }
9528
9529 // TODO Implement the lazyload substitution
9530 /*
9531 add a loop to scan $substitutionarray:
9532 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.
9533 If no, we don't need to make replacement, so we do nothing.
9534 If yes, we can make the substitution:
9535
9536 include_once $path;
9537 $tmpobj = new $class($db);
9538 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9539 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9540 */
9541 $memory_object_list = array();
9542 foreach ($substitutionarray as $key => $value) {
9543 $lazy_load_arr = array();
9544 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9545 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9546 $key_to_substitute = $lazy_load_arr[1];
9547 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9548 $param_arr = explode(':', $value);
9549 // path:class:method:id
9550 if (count($param_arr) == 4) {
9551 $path = $param_arr[0];
9552 $class = $param_arr[1];
9553 $method = $param_arr[2];
9554 $id = (int) $param_arr[3];
9555
9556 // load class file and init object list in memory
9557 if (!isset($memory_object_list[$class])) {
9558 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9559 require_once DOL_DOCUMENT_ROOT . $path;
9560 if (class_exists($class)) {
9561 $memory_object_list[$class] = array(
9562 'list' => array(),
9563 );
9564 }
9565 }
9566 }
9567
9568 // fetch object and set substitution
9569 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9570 if (method_exists($class, $method)) {
9571 if (!isset($memory_object_list[$class]['list'][$id])) {
9572 $tmpobj = new $class($db);
9573 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9574 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9575 $memory_object_list[$class]['list'][$id] = $tmpobj;
9576 } else {
9577 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9578 $tmpobj = $memory_object_list[$class]['list'][$id];
9579 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9580 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9581 }
9582
9583 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9584 }
9585 }
9586 }
9587 }
9588 }
9589 }
9590 }
9591
9592 return $text;
9593}
9594
9607function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9608{
9609 global $conf, $user;
9610
9611 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9612
9613 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9614
9615 // Check if there is external substitution to do, requested by plugins
9616 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9617
9618 foreach ($dirsubstitutions as $reldir) {
9619 $dir = dol_buildpath($reldir, 0);
9620
9621 // Check if directory exists
9622 if (!dol_is_dir($dir)) {
9623 continue;
9624 }
9625
9626 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9627 foreach ($substitfiles as $substitfile) {
9628 $reg = array();
9629 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9630 $module = $reg[1];
9631
9632 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9633 // Include the user's functions file
9634 require_once $dir.$substitfile['name'];
9635 // Call the user's function, and only if it is defined
9636 $function_name = $module."_".$callfunc;
9637 if (function_exists($function_name)) {
9638 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9639 }
9640 }
9641 }
9642 }
9643 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9644 // to list all tags in odt template
9645 $tags = '';
9646 foreach ($substitutionarray as $key => $value) {
9647 $tags .= '{'.$key.'} => '.$value."\n";
9648 }
9649 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9650 }
9651}
9652
9662function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9663{
9664 print get_date_range($date_start, $date_end, $format, $outputlangs);
9665}
9666
9677function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9678{
9679 global $langs;
9680
9681 $out = '';
9682
9683 if (!is_object($outputlangs)) {
9684 $outputlangs = $langs;
9685 }
9686
9687 if ($date_start && $date_end) {
9688 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9689 }
9690 if ($date_start && !$date_end) {
9691 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9692 }
9693 if (!$date_start && $date_end) {
9694 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9695 }
9696
9697 return $out;
9698}
9699
9708function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9709{
9710 global $conf;
9711
9712 $ret = '';
9713 // If order not defined, we use the setup
9714 if ($nameorder < 0) {
9715 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9716 }
9717 if ($nameorder == 1) {
9718 $ret .= $firstname;
9719 if ($firstname && $lastname) {
9720 $ret .= ' ';
9721 }
9722 $ret .= $lastname;
9723 } elseif ($nameorder == 2 || $nameorder == 3) {
9724 $ret .= $firstname;
9725 if (empty($ret) && $nameorder == 3) {
9726 $ret .= $lastname;
9727 }
9728 } else { // 0, 4 or 5
9729 $ret .= $lastname;
9730 if (empty($ret) && $nameorder == 5) {
9731 $ret .= $firstname;
9732 }
9733 if ($nameorder == 0) {
9734 if ($firstname && $lastname) {
9735 $ret .= ' ';
9736 }
9737 $ret .= $firstname;
9738 }
9739 }
9740 return $ret;
9741}
9742
9743
9755function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
9756{
9757 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9758 if (!is_array($mesgs)) {
9759 $mesgs = trim((string) $mesgs);
9760 // If mesgs is a not an empty string
9761 if ($mesgs) {
9762 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9763 return;
9764 }
9765 $_SESSION['dol_events'][$style][] = $mesgs;
9766 }
9767 } else {
9768 // If mesgs is an array
9769 foreach ($mesgs as $mesg) {
9770 $mesg = trim((string) $mesg);
9771 if ($mesg) {
9772 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9773 return;
9774 }
9775 $_SESSION['dol_events'][$style][] = $mesg;
9776 }
9777 }
9778 }
9779}
9780
9793function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
9794{
9795 if (empty($mesg) && empty($mesgs)) {
9796 dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
9797 } else {
9798 if ($messagekey) {
9799 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9800 // TODO
9801 $mesg .= '';
9802 }
9803 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
9804 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9805 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9806 }
9807 if (empty($mesgs)) {
9808 setEventMessage($mesg, $style, $noduplicate);
9809 } else {
9810 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
9811 setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
9812 }
9813 setEventMessage($mesgs, $style, $noduplicate);
9814 }
9815 }
9816 }
9817}
9818
9828function dol_htmloutput_events($disabledoutputofmessages = 0)
9829{
9830 // Show mesgs
9831 if (isset($_SESSION['dol_events']['mesgs'])) {
9832 if (empty($disabledoutputofmessages)) {
9833 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
9834 }
9835 unset($_SESSION['dol_events']['mesgs']);
9836 }
9837 // Show errors
9838 if (isset($_SESSION['dol_events']['errors'])) {
9839 if (empty($disabledoutputofmessages)) {
9840 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
9841 }
9842 unset($_SESSION['dol_events']['errors']);
9843 }
9844
9845 // Show warnings
9846 if (isset($_SESSION['dol_events']['warnings'])) {
9847 if (empty($disabledoutputofmessages)) {
9848 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
9849 }
9850 unset($_SESSION['dol_events']['warnings']);
9851 }
9852}
9853
9868function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
9869{
9870 global $conf, $langs;
9871
9872 $ret = 0;
9873 $return = '';
9874 $out = '';
9875 $divstart = $divend = '';
9876
9877 // If inline message with no format, we add it.
9878 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
9879 $divstart = '<div class="'.$style.' clearboth">';
9880 $divend = '</div>';
9881 }
9882
9883 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
9884 $langs->load("errors");
9885 $out .= $divstart;
9886 if (is_array($mesgarray) && count($mesgarray)) {
9887 foreach ($mesgarray as $message) {
9888 $ret++;
9889 $out .= $langs->trans($message);
9890 if ($ret < count($mesgarray)) {
9891 $out .= "<br>\n";
9892 }
9893 }
9894 }
9895 if ($mesgstring) {
9896 $ret++;
9897 $out .= $langs->trans($mesgstring);
9898 }
9899 $out .= $divend;
9900 }
9901
9902 if ($out) {
9903 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
9904 $return = '<script nonce="'.getNonce().'">
9905 $(document).ready(function() {
9906 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
9907 if (block) {
9908 $.dolEventValid("","'.dol_escape_js($out).'");
9909 } else {
9910 /* jnotify(message, preset of message type, keepmessage) */
9911 $.jnotify("'.dol_escape_js($out).'",
9912 "'.($style == "ok" ? 3000 : $style).'",
9913 '.($style == "ok" ? "false" : "true").',
9914 { remove: function (){} } );
9915 }
9916 });
9917 </script>';
9918 } else {
9919 $return = $out;
9920 }
9921 }
9922
9923 return $return;
9924}
9925
9937function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
9938{
9939 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
9940}
9941
9955function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
9956{
9957 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
9958 return;
9959 }
9960
9961 $iserror = 0;
9962 $iswarning = 0;
9963 if (is_array($mesgarray)) {
9964 foreach ($mesgarray as $val) {
9965 if ($val && preg_match('/class="error"/i', $val)) {
9966 $iserror++;
9967 break;
9968 }
9969 if ($val && preg_match('/class="warning"/i', $val)) {
9970 $iswarning++;
9971 break;
9972 }
9973 }
9974 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
9975 $iserror++;
9976 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
9977 $iswarning++;
9978 }
9979 if ($style == 'error') {
9980 $iserror++;
9981 }
9982 if ($style == 'warning') {
9983 $iswarning++;
9984 }
9985
9986 if ($iserror || $iswarning) {
9987 // Remove div from texts
9988 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
9989 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
9990 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
9991 // Remove div from texts array
9992 if (is_array($mesgarray)) {
9993 $newmesgarray = array();
9994 foreach ($mesgarray as $val) {
9995 if (is_string($val)) {
9996 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
9997 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
9998 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
9999 $newmesgarray[] = $tmpmesgstring;
10000 } else {
10001 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10002 }
10003 }
10004 $mesgarray = $newmesgarray;
10005 }
10006 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10007 } else {
10008 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10009 }
10010}
10011
10023function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10024{
10025 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10026}
10027
10041function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10042{
10043 // Clean parameters
10044 $order = strtolower($order);
10045
10046 if (is_array($array)) {
10047 $sizearray = count($array);
10048 if ($sizearray > 0) {
10049 $temp = array();
10050 foreach (array_keys($array) as $key) {
10051 if (is_object($array[$key])) {
10052 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10053 } else {
10054 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
10055 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10056 }
10057 if ($natsort == -1) {
10058 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10059 }
10060 }
10061
10062 if (empty($natsort) || $natsort == -1) {
10063 if ($order == 'asc') {
10064 asort($temp);
10065 } else {
10066 arsort($temp);
10067 }
10068 } else {
10069 if ($case_sensitive) {
10070 natsort($temp);
10071 } else {
10072 natcasesort($temp); // natecasesort is not sensible to case
10073 }
10074 if ($order != 'asc') {
10075 $temp = array_reverse($temp, true);
10076 }
10077 }
10078
10079 $sorted = array();
10080
10081 foreach (array_keys($temp) as $key) {
10082 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10083 }
10084
10085 return $sorted;
10086 }
10087 }
10088 return $array;
10089}
10090
10091
10099function utf8_check($str)
10100{
10101 $str = (string) $str; // Sometimes string is an int.
10102
10103 // We must use here a binary strlen function (so not dol_strlen)
10104 $strLength = strlen($str);
10105 for ($i = 0; $i < $strLength; $i++) {
10106 if (ord($str[$i]) < 0x80) {
10107 continue; // 0bbbbbbb
10108 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10109 $n = 1; // 110bbbbb
10110 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10111 $n = 2; // 1110bbbb
10112 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10113 $n = 3; // 11110bbb
10114 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10115 $n = 4; // 111110bb
10116 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10117 $n = 5; // 1111110b
10118 } else {
10119 return false; // Does not match any model
10120 }
10121 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10122 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10123 return false;
10124 }
10125 }
10126 }
10127 return true;
10128}
10129
10137function utf8_valid($str)
10138{
10139 /* 2 other methods to test if string is utf8
10140 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10141 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10142 */
10143 return preg_match('//u', $str) ? true : false;
10144}
10145
10146
10153function ascii_check($str)
10154{
10155 if (function_exists('mb_check_encoding')) {
10156 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10157 if (!mb_check_encoding($str, 'ASCII')) {
10158 return false;
10159 }
10160 } else {
10161 if (preg_match('/[^\x00-\x7f]/', $str)) {
10162 return false; // Contains a byte > 7f
10163 }
10164 }
10165
10166 return true;
10167}
10168
10169
10177function dol_osencode($str)
10178{
10179 $tmp = ini_get("unicode.filesystem_encoding");
10180 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10181 $tmp = 'iso-8859-1'; // By default for windows
10182 }
10183 if (empty($tmp)) {
10184 $tmp = 'utf-8'; // By default for other
10185 }
10186 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10187 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10188 }
10189
10190 if ($tmp == 'iso-8859-1') {
10191 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10192 }
10193 return $str;
10194}
10195
10196
10212function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10213{
10214 global $conf;
10215
10216 // If key empty
10217 if ($key == '') {
10218 return 0;
10219 }
10220
10221 // Check in cache
10222 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10223 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10224 }
10225
10226 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10227
10228 $sql = "SELECT ".$fieldid." as valuetoget";
10229 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10230 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10231 if (!empty($entityfilter)) {
10232 $sql .= " AND entity IN (".getEntity($tablename).")";
10233 }
10234 if ($filters) {
10235 $sql .= $filters;
10236 }
10237
10238 $resql = $db->query($sql);
10239 if ($resql) {
10240 $obj = $db->fetch_object($resql);
10241 $valuetoget = '';
10242 if ($obj) {
10243 $valuetoget = $obj->valuetoget;
10244 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10245 } else {
10246 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10247 }
10248 $db->free($resql);
10249
10250 return $valuetoget;
10251 } else {
10252 return -1;
10253 }
10254}
10255
10265function isStringVarMatching($var, $regextext, $matchrule = 1)
10266{
10267 if ($matchrule == 1) {
10268 if ($var == 'mainmenu') {
10269 global $mainmenu;
10270 return (preg_match('/^'.$regextext.'/', $mainmenu));
10271 } elseif ($var == 'leftmenu') {
10272 global $leftmenu;
10273 return (preg_match('/^'.$regextext.'/', $leftmenu));
10274 } else {
10275 return 'This variable is not accessible with dol_eval';
10276 }
10277 } else {
10278 return 'This value for matchrule is not implemented';
10279 }
10280}
10281
10282
10292function verifCond($strToEvaluate, $onlysimplestring = '1')
10293{
10294 //print $strToEvaluate."<br>\n";
10295 $rights = true;
10296 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10297 //var_dump($strToEvaluate);
10298 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10299 $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
10300 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10301 //var_dump($rights);
10302 }
10303 return $rights;
10304}
10305
10320function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10321{
10322 // Only this global variables can be read by eval function and returned to caller
10323 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10324 global $db, $langs, $user, $website, $websitepage;
10325 global $action, $mainmenu, $leftmenu;
10326 global $mysoc;
10327 global $objectoffield; // To allow the use of $objectoffield in computed fields
10328
10329 // Old variables used
10330 global $object;
10331 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10332
10333 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10334 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10335 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10336 }
10337
10338 try {
10339 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10340 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10341 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10342 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10343 // 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"
10344
10345 // Check if there is dynamic call (first we check chars are all into use a whitelist chars)
10346 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10347 if ($onlysimplestring == '2') {
10348 $specialcharsallowed .= '[]';
10349 }
10350 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10351 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10352 }
10353 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10354 if ($returnvalue) {
10355 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10356 } else {
10357 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s, LOG_WARNING);
10358 return '';
10359 }
10360 }
10361
10362 // Check if there is dynamic call (first we use black list patterns)
10363 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10364 if ($returnvalue) {
10365 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;
10366 } else {
10367 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);
10368 return '';
10369 }
10370 }
10371
10372 // Now we check if we try dynamic call (by removing white list pattern of using parenthesis then testing if a parenthesis exists)
10373 $savescheck = '';
10374 $scheck = $s;
10375 while ($scheck && $savescheck != $scheck) {
10376 $savescheck = $scheck;
10377 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10378 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10379 $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
10380 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10381 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10382 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10383 }
10384 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10385 if (strpos($scheck, '(') !== false) {
10386 if ($returnvalue) {
10387 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10388 } else {
10389 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);
10390 return '';
10391 }
10392 }
10393
10394 // TODO
10395 // We can exclude $ char that are not:
10396 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10397 }
10398 if (is_array($s) || $s === 'Array') {
10399 if ($returnvalue) {
10400 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10401 } else {
10402 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10403 return '';
10404 }
10405 }
10406
10407 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
10408 if ($returnvalue) {
10409 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
10410 } else {
10411 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
10412 return '';
10413 }
10414 }
10415
10416 if (strpos($s, '`') !== false) {
10417 if ($returnvalue) {
10418 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10419 } else {
10420 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10421 return '';
10422 }
10423 }
10424
10425 // Disallow also concat
10426 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
10427 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10428 if ($returnvalue) {
10429 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10430 } else {
10431 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10432 return '';
10433 }
10434 }
10435 }
10436
10437 // We block use of php exec or php file functions
10438 $forbiddenphpstrings = array('$$', '$_', '}[');
10439 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10440
10441 // We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
10442 // We must exclude all functions that allow to execute another function. This includes all function that has a parameter with type "callable" to avoid things
10443 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
10444 $forbiddenphpfunctions = array();
10445 // @phpcs:ignore
10446 $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
10447
10448 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10449 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10450 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
10451
10452 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("array_all", "array_any", "array_diff_ukey", "array_filter", "array_find", "array_find_key", "array_map", "array_reduce", "array_intersect_uassoc", "array_intersect_ukey", "array_walk", "array_walk_recursive"));
10453 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
10454 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("set_error_handler", "set_exception_handler", "libxml_set_external_entity_loader", "register_shutdown_function", "register_tick_function", "unregister_tick_function"));
10455 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
10456 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
10457
10458 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
10459
10460 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10461 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10462 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10463 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace", "mb_ereg_replace_callback")); // function with eval capabilities
10464 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
10465 $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
10466 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10467 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
10468
10469 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10470
10471 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10472
10473 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10474
10475 do {
10476 $oldstringtoclean = $s;
10477 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10478 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10479 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10480 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10481 } while ($oldstringtoclean != $s);
10482
10483
10484 if (strpos($s, '__forbiddenstring__') !== false) {
10485 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10486 if ($returnvalue) {
10487 return 'Bad string syntax to evaluate: '.$s;
10488 } else {
10489 dol_syslog('Bad string syntax to evaluate: '.$s);
10490 return '';
10491 }
10492 }
10493
10494 //print $s."<br>\n";
10495 if ($returnvalue) {
10496 if ($hideerrors) {
10497 ob_start(); // An evaluation has no reason to output data
10498 $isObBufferActive = true;
10499 $tmps = @eval('return '.$s.';');
10500 $tmpo = ob_get_clean();
10501 $isObBufferActive = false;
10502 if ($tmpo) {
10503 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10504 }
10505 return $tmps;
10506 } else {
10507 ob_start(); // An evaluation has no reason to output data
10508 $isObBufferActive = true;
10509 $tmps = eval('return '.$s.';');
10510 $tmpo = ob_get_clean();
10511 $isObBufferActive = false;
10512 if ($tmpo) {
10513 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10514 }
10515 return $tmps;
10516 }
10517 } else {
10518 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10519 if ($hideerrors) {
10520 @eval($s);
10521 } else {
10522 eval($s);
10523 }
10524 return '';
10525 }
10526 } catch (Error $e) {
10527 if ($isObBufferActive) {
10528 // Clean up buffer which was left behind due to exception.
10529 $tmpo = ob_get_clean();
10530 $isObBufferActive = false;
10531 }
10532 $error = 'dol_eval try/catch error : ';
10533 $error .= $e->getMessage();
10534 dol_syslog($error, LOG_WARNING);
10535 if ($returnvalue) {
10536 return 'Exception during evaluation: '.$s;
10537 } else {
10538 return '';
10539 }
10540 }
10541}
10542
10550function dol_validElement($element)
10551{
10552 return (trim($element) != '');
10553}
10554
10563function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10564{
10565 if (empty($codelang)) {
10566 return '';
10567 }
10568
10569 if ($codelang == 'auto') {
10570 return '<span class="fa fa-language"></span>';
10571 }
10572
10573 $langtocountryflag = array(
10574 'ar_AR' => '',
10575 'ca_ES' => 'catalonia',
10576 'da_DA' => 'dk',
10577 'fr_CA' => 'mq',
10578 'sv_SV' => 'se',
10579 'sw_SW' => 'unknown',
10580 'AQ' => 'unknown',
10581 'CW' => 'unknown',
10582 'IM' => 'unknown',
10583 'JE' => 'unknown',
10584 'MF' => 'unknown',
10585 'BL' => 'unknown',
10586 'SX' => 'unknown'
10587 );
10588
10589 if (isset($langtocountryflag[$codelang])) {
10590 $flagImage = $langtocountryflag[$codelang];
10591 } else {
10592 $tmparray = explode('_', $codelang);
10593 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10594 }
10595
10596 $morecss = '';
10597 $reg = array();
10598 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10599 $morecss = $reg[1];
10600 $moreatt = "";
10601 }
10602
10603 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10604 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10605}
10606
10614function getLanguageCodeFromCountryCode($countrycode)
10615{
10616 global $mysoc;
10617
10618 if (empty($countrycode)) {
10619 return null;
10620 }
10621
10622 if (strtoupper($countrycode) == 'MQ') {
10623 return 'fr_CA';
10624 }
10625 if (strtoupper($countrycode) == 'SE') {
10626 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10627 }
10628 if (strtoupper($countrycode) == 'CH') {
10629 if ($mysoc->country_code == 'FR') {
10630 return 'fr_CH';
10631 }
10632 if ($mysoc->country_code == 'DE') {
10633 return 'de_CH';
10634 }
10635 if ($mysoc->country_code == 'IT') {
10636 return 'it_CH';
10637 }
10638 }
10639
10640 // Locale list taken from:
10641 // http://stackoverflow.com/questions/3191664/
10642 // list-of-all-locales-and-their-short-codes
10643 $locales = array(
10644 'af-ZA',
10645 'am-ET',
10646 'ar-AE',
10647 'ar-BH',
10648 'ar-DZ',
10649 'ar-EG',
10650 'ar-IQ',
10651 'ar-JO',
10652 'ar-KW',
10653 'ar-LB',
10654 'ar-LY',
10655 'ar-MA',
10656 'ar-OM',
10657 'ar-QA',
10658 'ar-SA',
10659 'ar-SY',
10660 'ar-TN',
10661 'ar-YE',
10662 //'as-IN', // Moved after en-IN
10663 'ba-RU',
10664 'be-BY',
10665 'bg-BG',
10666 'bn-BD',
10667 //'bn-IN', // Moved after en-IN
10668 'bo-CN',
10669 'br-FR',
10670 'ca-ES',
10671 'co-FR',
10672 'cs-CZ',
10673 'cy-GB',
10674 'da-DK',
10675 'de-AT',
10676 'de-CH',
10677 'de-DE',
10678 'de-LI',
10679 'de-LU',
10680 'dv-MV',
10681 'el-GR',
10682 'en-AU',
10683 'en-BZ',
10684 'en-CA',
10685 'en-GB',
10686 'en-IE',
10687 'en-IN',
10688 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10689 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10690 'en-JM',
10691 'en-MY',
10692 'en-NZ',
10693 'en-PH',
10694 'en-SG',
10695 'en-TT',
10696 'en-US',
10697 'en-ZA',
10698 'en-ZW',
10699 'es-AR',
10700 'es-BO',
10701 'es-CL',
10702 'es-CO',
10703 'es-CR',
10704 'es-DO',
10705 'es-EC',
10706 'es-ES',
10707 'es-GT',
10708 'es-HN',
10709 'es-MX',
10710 'es-NI',
10711 'es-PA',
10712 'es-PE',
10713 'es-PR',
10714 'es-PY',
10715 'es-SV',
10716 'es-US',
10717 'es-UY',
10718 'es-VE',
10719 'et-EE',
10720 'eu-ES',
10721 'fa-IR',
10722 'fi-FI',
10723 'fo-FO',
10724 'fr-BE',
10725 'fr-CA',
10726 'fr-CH',
10727 'fr-FR',
10728 'fr-LU',
10729 'fr-MC',
10730 'fy-NL',
10731 'ga-IE',
10732 'gd-GB',
10733 'gl-ES',
10734 'gu-IN',
10735 'he-IL',
10736 'hi-IN',
10737 'hr-BA',
10738 'hr-HR',
10739 'hu-HU',
10740 'hy-AM',
10741 'id-ID',
10742 'ig-NG',
10743 'ii-CN',
10744 'is-IS',
10745 'it-CH',
10746 'it-IT',
10747 'ja-JP',
10748 'ka-GE',
10749 'kk-KZ',
10750 'kl-GL',
10751 'km-KH',
10752 'kn-IN',
10753 'ko-KR',
10754 'ky-KG',
10755 'lb-LU',
10756 'lo-LA',
10757 'lt-LT',
10758 'lv-LV',
10759 'mi-NZ',
10760 'mk-MK',
10761 'ml-IN',
10762 'mn-MN',
10763 'mr-IN',
10764 'ms-BN',
10765 'ms-MY',
10766 'mt-MT',
10767 'nb-NO',
10768 'ne-NP',
10769 'nl-BE',
10770 'nl-NL',
10771 'nn-NO',
10772 'oc-FR',
10773 'or-IN',
10774 'pa-IN',
10775 'pl-PL',
10776 'ps-AF',
10777 'pt-BR',
10778 'pt-PT',
10779 'rm-CH',
10780 'ro-MD',
10781 'ro-RO',
10782 'ru-RU',
10783 'rw-RW',
10784 'sa-IN',
10785 'se-FI',
10786 'se-NO',
10787 'se-SE',
10788 'si-LK',
10789 'sk-SK',
10790 'sl-SI',
10791 'sq-AL',
10792 'sv-FI',
10793 'sv-SE',
10794 'sw-KE',
10795 'ta-IN',
10796 'te-IN',
10797 'th-TH',
10798 'tk-TM',
10799 'tn-ZA',
10800 'tr-TR',
10801 'tt-RU',
10802 'ug-CN',
10803 'uk-UA',
10804 'ur-PK',
10805 'vi-VN',
10806 'wo-SN',
10807 'xh-ZA',
10808 'yo-NG',
10809 'zh-CN',
10810 'zh-HK',
10811 'zh-MO',
10812 'zh-SG',
10813 'zh-TW',
10814 'zu-ZA',
10815 );
10816
10817 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10818 if (in_array($buildprimarykeytotest, $locales)) {
10819 return strtolower($countrycode).'_'.strtoupper($countrycode);
10820 }
10821
10822 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10823 foreach ($locales as $locale) {
10824 $locale_language = locale_get_primary_language($locale);
10825 $locale_region = locale_get_region($locale);
10826 if (strtoupper($countrycode) == $locale_region) {
10827 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10828 return strtolower($locale_language).'_'.strtoupper($locale_region);
10829 }
10830 }
10831 } else {
10832 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10833 }
10834
10835 return null;
10836}
10837
10868function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
10869{
10870 global $hookmanager, $db;
10871
10872 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
10873 foreach ($conf->modules_parts['tabs'][$type] as $value) {
10874 $values = explode(':', $value);
10875
10876 $reg = array();
10877 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
10878 $newtab = array();
10879 $postab = $h;
10880 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
10881 $str = $values[1];
10882 $posstart = strpos($str, '(');
10883 if ($posstart > 0) {
10884 $posend = strpos($str, ')');
10885 if ($posstart > 0) {
10886 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
10887 if (is_numeric($res1)) {
10888 $postab = (int) $res1;
10889 $values[1] = '+' . substr($str, $posend + 1);
10890 }
10891 }
10892 }
10893 if (count($values) == 6) {
10894 // new declaration with permissions:
10895 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10896 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10897 if ($values[0] != $type) {
10898 continue;
10899 }
10900
10901 if (verifCond($values[4], '2')) {
10902 if ($values[3]) {
10903 if ($filterorigmodule) { // If a filter of module origin has been requested
10904 if (strpos($values[3], '@')) { // This is an external module
10905 if ($filterorigmodule != 'external') {
10906 continue;
10907 }
10908 } else { // This looks a core module
10909 if ($filterorigmodule != 'core') {
10910 continue;
10911 }
10912 }
10913 }
10914 $langs->load($values[3]);
10915 }
10916 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10917 // If label is "SUBSTITUION_..."
10918 $substitutionarray = array();
10919 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10920 $label = make_substitutions($reg[1], $substitutionarray);
10921 } else {
10922 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
10923 $labeltemp = explode(',', $values[2]);
10924 $label = $langs->trans($labeltemp[0]);
10925
10926 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
10927 dol_include_once($labeltemp[2]);
10928 $classtoload = $labeltemp[1];
10929 if (class_exists($classtoload)) {
10930 $obj = new $classtoload($db);
10931 $function = $labeltemp[3];
10932 if ($obj && $function && method_exists($obj, $function)) {
10933 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10934 $nbrec = $obj->$function($object->id, $obj);
10935 if (!empty($nbrec)) {
10936 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
10937 }
10938 }
10939 }
10940 }
10941 }
10942
10943 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
10944 $newtab[1] = $label;
10945 $newtab[2] = str_replace('+', '', $values[1]);
10946 $h++;
10947 } else {
10948 continue;
10949 }
10950 } elseif (count($values) == 5) { // case deprecated
10951 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
10952
10953 if ($values[0] != $type) {
10954 continue;
10955 }
10956 if ($values[3]) {
10957 if ($filterorigmodule) { // If a filter of module origin has been requested
10958 if (strpos($values[3], '@')) { // This is an external module
10959 if ($filterorigmodule != 'external') {
10960 continue;
10961 }
10962 } else { // This looks a core module
10963 if ($filterorigmodule != 'core') {
10964 continue;
10965 }
10966 }
10967 }
10968 $langs->load($values[3]);
10969 }
10970 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10971 $substitutionarray = array();
10972 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10973 $label = make_substitutions($reg[1], $substitutionarray);
10974 } else {
10975 $label = $langs->trans($values[2]);
10976 }
10977
10978 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
10979 $newtab[1] = $label;
10980 $newtab[2] = str_replace('+', '', $values[1]);
10981 $h++;
10982 }
10983 // set tab at its position
10984 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
10985 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
10986 if ($values[0] != $type) {
10987 continue;
10988 }
10989 $tabname = str_replace('-', '', $values[1]);
10990 foreach ($head as $key => $val) {
10991 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
10992 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
10993 if ($head[$key][2] == $tabname && $condition) {
10994 unset($head[$key]);
10995 break;
10996 }
10997 }
10998 }
10999 }
11000 }
11001
11002 // No need to make a return $head. Var is modified as a reference
11003 if (!empty($hookmanager)) {
11004 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11005 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11006 if ($reshook > 0) { // Hook ask to replace completely the array
11007 $head = $hookmanager->resArray;
11008 } else { // Hook
11009 $head = array_merge($head, $hookmanager->resArray);
11010 }
11011 $h = count($head);
11012 }
11013}
11014
11026function printCommonFooter($zone = 'private')
11027{
11028 global $conf, $hookmanager, $user, $langs;
11029 global $debugbar;
11030 global $action;
11031 global $micro_start_time;
11032
11033 if ($zone == 'private') {
11034 print "\n".'<!-- Common footer for private page -->'."\n";
11035 } else {
11036 print "\n".'<!-- Common footer for public page -->'."\n";
11037 }
11038
11039 // A div to store page_y POST parameter so we can read it using javascript
11040 print "\n<!-- A div to store page_y POST parameter -->\n";
11041 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11042
11043 $parameters = array();
11044 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11045 if (empty($reshook)) {
11046 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11047 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11048 }
11049
11050 print "\n";
11051 if (!empty($conf->use_javascript_ajax)) {
11052 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11053 print '<script>'."\n";
11054 print 'jQuery(document).ready(function() {'."\n";
11055
11056 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11057 print "\n";
11058 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11059 print 'jQuery("li.menuhider").click(function(event) {';
11060 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11061 print ' console.log("We click on .menuhider");'."\n";
11062 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11063 print '});'."\n";
11064 }
11065
11066 // Management of focus and mandatory for fields
11067 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"])))) {
11068 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11069 $relativepathstring = $_SERVER["PHP_SELF"];
11070 // Clean $relativepathstring
11071 if (constant('DOL_URL_ROOT')) {
11072 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11073 }
11074 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11075 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11076 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11077
11078 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11079 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11080 $qualified = 0;
11081 if ($defkey != '_noquery_') {
11082 $tmpqueryarraytohave = explode('&', $defkey);
11083 $foundintru = 0;
11084 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11085 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11086 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11087 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11088 $foundintru = 1;
11089 }
11090 }
11091 if (!$foundintru) {
11092 $qualified = 1;
11093 }
11094 //var_dump($defkey.'-'.$qualified);
11095 } else {
11096 $qualified = 1;
11097 }
11098
11099 if ($qualified) {
11100 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11101 foreach ($defval as $paramkey => $paramval) {
11102 // Set focus on field
11103 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11104 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11105 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11106 }
11107 }
11108 }
11109 }
11110 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11111 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11112 $qualified = 0;
11113 if ($defkey != '_noquery_') {
11114 $tmpqueryarraytohave = explode('&', $defkey);
11115 $foundintru = 0;
11116 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11117 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11118 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11119 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11120 $foundintru = 1;
11121 }
11122 }
11123 if (!$foundintru) {
11124 $qualified = 1;
11125 }
11126 //var_dump($defkey.'-'.$qualified);
11127 } else {
11128 $qualified = 1;
11129 }
11130
11131 if ($qualified) {
11132 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11133
11134 foreach ($defval as $paramkey => $paramval) {
11135 // Solution 1: Add handler on submit to check if mandatory fields are empty
11136 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11137 print "form.on('submit', function(event) {
11138 var submitter = $(this).find(':submit:focus').get(0);
11139 if (submitter) {
11140 var buttonName = $(submitter).attr('name');
11141 if (buttonName == 'cancel') {
11142 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11143 return true;
11144 }
11145 }
11146
11147 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11148
11149 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11150 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11151
11152 if (tmptypefield == 'textarea') {
11153 // We must instead check the content of ckeditor
11154 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11155 if (tmpeditor) {
11156 tmpvalue = tmpeditor.getData();
11157 console.log('For textarea tmpvalue is '+tmpvalue);
11158 }
11159 }
11160
11161 let tmpvalueisempty = false;
11162 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '') {
11163 tmpvalueisempty = true;
11164 }
11165 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
11166 tmpvalueisempty = true;
11167 }
11168 if (tmpvalueisempty && (buttonName == 'save')) {
11169 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11170 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11171 event.stopPropagation(); // Stop other handlers.
11172 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11173 return false;
11174 }
11175 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11176 return true;
11177 });
11178 \n";
11179
11180 // Solution 2: Add property 'required' on input
11181 // so browser will check value and try to focus on it when submitting the form.
11182 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11183 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11184 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11185 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11186 //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";
11187 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11188 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11189 // Add 'field required' class on closest td for all input elements : input, textarea and select
11190 //print '}, 500);'; // 500 milliseconds delay
11191
11192 // Now set the class "fieldrequired"
11193 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11194 }
11195
11196
11197 // If we submit using the cancel button, we remove the required attributes
11198 print 'jQuery("input[name=\'cancel\']").click(function() {
11199 console.log("We click on cancel button so removed all required attribute");
11200 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11201 });'."\n";
11202 }
11203 }
11204 }
11205 }
11206
11207 print '});'."\n";
11208
11209 // End of tuning
11210 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11211 print "\n";
11212 print "/* JS CODE TO ENABLE to add memory info */\n";
11213 print 'window.console && console.log("';
11214 if (getDolGlobalString('MEMCACHED_SERVER')) {
11215 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11216 }
11217 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11218 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11219 $micro_end_time = microtime(true);
11220 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11221 }
11222
11223 if (function_exists("memory_get_usage")) {
11224 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11225 }
11226 if (function_exists("memory_get_peak_usage")) {
11227 print ' - Real mem peak: '.memory_get_peak_usage(true);
11228 }
11229 if (function_exists("zend_loader_file_encoded")) {
11230 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11231 }
11232 print '");'."\n";
11233 }
11234
11235 print "\n".'</script>'."\n";
11236
11237 // Google Analytics
11238 // TODO Add a hook here
11239 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11240 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11241 foreach ($tmptagarray as $tmptag) {
11242 print "\n";
11243 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11244 print '
11245 <!-- Global site tag (gtag.js) - Google Analytics -->
11246 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11247 <script>
11248 window.dataLayer = window.dataLayer || [];
11249 function gtag(){dataLayer.push(arguments);}
11250 gtag(\'js\', new Date());
11251
11252 gtag(\'config\', \''.trim($tmptag).'\');
11253 </script>';
11254 print "\n";
11255 }
11256 }
11257 }
11258
11259 // Add Xdebug coverage of code
11260 if (defined('XDEBUGCOVERAGE')) {
11261 print_r(xdebug_get_code_coverage());
11262 }
11263
11264 // Add DebugBar data
11265 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11266 if (isset($debugbar['time'])) {
11267 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11268 $debugbar['time']->stopMeasure('pageaftermaster');
11269 }
11270 print '<!-- Output debugbar data -->'."\n";
11271 $renderer = $debugbar->getJavascriptRenderer();
11272 print $renderer->render();
11273 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11274 print "\n";
11275 print "<!-- Start of log output\n";
11276 //print '<div class="hidden">'."\n";
11277 foreach ($conf->logbuffer as $logline) {
11278 print $logline."<br>\n";
11279 }
11280 //print '</div>'."\n";
11281 print "End of log output -->\n";
11282 }
11283 }
11284}
11285
11295function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11296{
11297 if (is_null($string)) {
11298 return array();
11299 }
11300
11301 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11302 // This is a regex string
11303 $newdelimiter = $delimiter;
11304 } else {
11305 // This is a simple string
11306 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11307 $newdelimiter = preg_quote($delimiter, '/');
11308 }
11309
11310 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11311 $ka = array();
11312 foreach ($a as $s) { // each part
11313 if ($s) {
11314 if ($pos = strpos($s, $kv)) { // key/value delimiter
11315 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11316 } else { // key delimiter not found
11317 $ka[] = trim($s);
11318 }
11319 }
11320 }
11321 return $ka;
11322 }
11323
11324 return array();
11325}
11326
11327
11334function dol_set_focus($selector)
11335{
11336 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11337 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11338}
11339
11340
11348function dol_getmypid()
11349{
11350 if (!function_exists('getmypid')) {
11351 return mt_rand(99900000, 99965535);
11352 } else {
11353 return getmypid(); // May be a number on 64 bits (depending on OS)
11354 }
11355}
11356
11357
11379function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11380{
11381 global $db, $langs;
11382
11383 $value = trim($value);
11384
11385 if ($mode == 0) {
11386 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11387 }
11388 if ($mode == 1) {
11389 $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
11390 }
11391
11392 $value = preg_replace('/\s*\|\s*/', '|', $value);
11393
11394 //natural mode search type 3 allow spaces into search ...
11395 if ($mode == 3 || $mode == -3) {
11396 $crits = explode(',', $value);
11397 } else {
11398 $crits = explode(' ', $value);
11399 }
11400 $res = '';
11401 if (!is_array($fields)) {
11402 $fields = array($fields);
11403 }
11404
11405 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11406 foreach ($crits as $crit) { // Loop on each AND criteria
11407 $crit = trim($crit);
11408 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11409 $newres = '';
11410 foreach ($fields as $field) {
11411 if ($mode == 1) {
11412 $tmpcrits = explode('|', $crit);
11413 $i3 = 0; // count the nb of valid criteria added for this current field
11414 foreach ($tmpcrits as $tmpcrit) {
11415 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11416 continue;
11417 }
11418 $tmpcrit = trim($tmpcrit);
11419
11420 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11421
11422 $operator = '=';
11423 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11424
11425 $reg = array();
11426 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11427 if (!empty($reg[1])) {
11428 $operator = $reg[1];
11429 }
11430 if ($newcrit != '') {
11431 $numnewcrit = price2num($newcrit);
11432 if (is_numeric($numnewcrit)) {
11433 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11434 } else {
11435 $newres .= '1 = 2'; // force false, we received a corrupted data
11436 }
11437 $i3++; // a criteria was added to string
11438 }
11439 }
11440 $i2++; // a criteria for 1 more field was added to string
11441 } elseif ($mode == 2 || $mode == -2) {
11442 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11443 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11444 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11445 if ($mode == -2) {
11446 $newres .= ' OR '.$field.' IS NULL';
11447 }
11448 $i2++; // a criteria for 1 more field was added to string
11449 } elseif ($mode == 3 || $mode == -3) {
11450 $tmparray = explode(',', $crit);
11451 if (count($tmparray)) {
11452 $listofcodes = '';
11453 foreach ($tmparray as $val) {
11454 $val = trim($val);
11455 if ($val) {
11456 $listofcodes .= ($listofcodes ? ',' : '');
11457 $listofcodes .= "'".$db->escape($val)."'";
11458 }
11459 }
11460 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
11461 $i2++; // a criteria for 1 more field was added to string
11462 }
11463 if ($mode == -3) {
11464 $newres .= ' OR '.$field.' IS NULL';
11465 }
11466 } elseif ($mode == 4) {
11467 $tmparray = explode(',', $crit);
11468 if (count($tmparray)) {
11469 $listofcodes = '';
11470 foreach ($tmparray as $val) {
11471 $val = trim($val);
11472 if ($val) {
11473 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11474 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11475 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11476 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11477 $newres .= ')';
11478 $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)
11479 }
11480 }
11481 }
11482 } else { // $mode=0
11483 $tmpcrits = explode('|', $crit);
11484 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11485 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11486 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11487 continue;
11488 }
11489 $tmpcrit = trim($tmpcrit);
11490
11491 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11492 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11493 } else {
11494 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11495 }
11496
11497 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11498 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11499 } else {
11500 $tmpcrit2 = $tmpcrit;
11501 $tmpbefore = '%';
11502 $tmpafter = '%';
11503 $tmps = '';
11504
11505 if (preg_match('/^!/', $tmpcrit)) {
11506 $tmps .= $field." NOT LIKE "; // ! as exclude character
11507 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11508 } else {
11509 $tmps .= $field." LIKE ";
11510 }
11511 $tmps .= "'";
11512
11513 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11514 $tmpbefore = '';
11515 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11516 }
11517 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11518 $tmpafter = '';
11519 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11520 }
11521
11522 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11523 $tmps = "(".$tmps;
11524 }
11525 $newres .= $tmps;
11526 $newres .= $tmpbefore;
11527 $newres .= $db->escape($tmpcrit2);
11528 $newres .= $tmpafter;
11529 $newres .= "'";
11530 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11531 $newres .= " OR ".$field." IS NULL)";
11532 }
11533 }
11534
11535 $i3++;
11536 }
11537
11538 $i2++; // a criteria for 1 more field was added to string
11539 }
11540 }
11541
11542 if ($newres) {
11543 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11544 }
11545 $i1++;
11546 }
11547 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11548
11549 return $res;
11550}
11551
11558function showDirectDownloadLink($object)
11559{
11560 global $conf, $langs;
11561
11562 $out = '';
11563 $url = $object->getLastMainDocLink($object->element);
11564
11565 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11566 if ($url) {
11567 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11568 $out .= ajax_autoselect("directdownloadlink", 0);
11569 } else {
11570 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11571 }
11572
11573 return $out;
11574}
11575
11584function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11585{
11586 $dirName = dirname($file);
11587 if ($dirName == '.') {
11588 $dirName = '';
11589 }
11590
11591 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
11592 $fileName = basename($fileName);
11593
11594 if (empty($extImgTarget)) {
11595 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11596 }
11597 if (empty($extImgTarget)) {
11598 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11599 }
11600 if (empty($extImgTarget)) {
11601 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11602 }
11603 if (empty($extImgTarget)) {
11604 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11605 }
11606 if (empty($extImgTarget)) {
11607 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11608 }
11609 if (empty($extImgTarget)) {
11610 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11611 }
11612
11613 if (!$extImgTarget) {
11614 return $file;
11615 }
11616
11617 $subdir = '';
11618 if ($extName) {
11619 $subdir = 'thumbs/';
11620 }
11621
11622 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11623}
11624
11625
11635function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11636{
11637 global $conf, $langs;
11638
11639 if (empty($conf->use_javascript_ajax)) {
11640 return '';
11641 }
11642
11643 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11644
11645 if ($alldata == 1) {
11646 if ($isAllowedForPreview) {
11647 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));
11648 } else {
11649 return array();
11650 }
11651 }
11652
11653 // old behavior, return a string
11654 if ($isAllowedForPreview) {
11655 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11656 $title = $langs->transnoentities("Preview");
11657 //$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().
11658 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
11659
11660 // 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,
11661 // 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.
11662 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
11663 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
11664 } else {
11665 return '';
11666 }
11667}
11668
11669
11678function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11679{
11680 global $langs;
11681 $out = '<script nonce="'.getNonce().'">
11682 jQuery(document).ready(function () {
11683 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11684 });
11685 </script>';
11686 if ($addlink) {
11687 if ($textonlink === 'image') {
11688 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11689 } else {
11690 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11691 }
11692 }
11693 return $out;
11694}
11695
11703function dolIsAllowedForPreview($file)
11704{
11705 // Check .noexe extension in filename
11706 if (preg_match('/\.noexe$/i', $file)) {
11707 return 0;
11708 }
11709
11710 // Check mime types
11711 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11712 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11713 $mime_preview[] = 'svg+xml';
11714 }
11715 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11716 //$mime_preview[]='archive';
11717 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11718 if ($num_mime !== false) {
11719 return 1;
11720 }
11721
11722 // By default, not allowed for preview
11723 return 0;
11724}
11725
11726
11736function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11737{
11738 $mime = $default;
11739 $imgmime = 'other.png';
11740 $famime = 'file-o';
11741 $srclang = '';
11742
11743 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11744
11745 // Plain text files
11746 if (preg_match('/\.txt$/i', $tmpfile)) {
11747 $mime = 'text/plain';
11748 $imgmime = 'text.png';
11749 $famime = 'file-alt';
11750 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11751 $mime = 'text/richtext';
11752 $imgmime = 'text.png';
11753 $famime = 'file-alt';
11754 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11755 $mime = 'text/csv';
11756 $imgmime = 'text.png';
11757 $famime = 'file-csv';
11758 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11759 $mime = 'text/tab-separated-values';
11760 $imgmime = 'text.png';
11761 $famime = 'file-alt';
11762 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11763 $mime = 'text/plain';
11764 $imgmime = 'text.png';
11765 $famime = 'file-alt';
11766 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11767 $mime = 'text/plain';
11768 $imgmime = 'text.png';
11769 $srclang = 'ini';
11770 $famime = 'file-alt';
11771 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11772 $mime = 'text/plain';
11773 $imgmime = 'text.png';
11774 $srclang = 'md';
11775 $famime = 'file-alt';
11776 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11777 $mime = 'text/css';
11778 $imgmime = 'css.png';
11779 $srclang = 'css';
11780 $famime = 'file-alt';
11781 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11782 $mime = 'text/plain';
11783 $imgmime = 'text.png';
11784 $srclang = 'lang';
11785 $famime = 'file-alt';
11786 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11787 $mime = 'text/plain';
11788 $imgmime = 'text.png';
11789 $famime = 'file-alt';
11790 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11791 $mime = 'text/html';
11792 $imgmime = 'html.png';
11793 $srclang = 'html';
11794 $famime = 'file-alt';
11795 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11796 $mime = 'text/xml';
11797 $imgmime = 'other.png';
11798 $srclang = 'xml';
11799 $famime = 'file-alt';
11800 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11801 $mime = 'text/xml';
11802 $imgmime = 'other.png';
11803 $srclang = 'xaml';
11804 $famime = 'file-alt';
11805 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11806 $mime = 'text/plain';
11807 $imgmime = 'text.png';
11808 $srclang = 'bas';
11809 $famime = 'file-code';
11810 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11811 $mime = 'text/plain';
11812 $imgmime = 'text.png';
11813 $srclang = 'c';
11814 $famime = 'file-code';
11815 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11816 $mime = 'text/plain';
11817 $imgmime = 'text.png';
11818 $srclang = 'cpp';
11819 $famime = 'file-code';
11820 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11821 $mime = 'text/plain';
11822 $imgmime = 'text.png';
11823 $srclang = 'cs';
11824 $famime = 'file-code';
11825 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11826 $mime = 'text/plain';
11827 $imgmime = 'text.png';
11828 $srclang = 'h';
11829 $famime = 'file-code';
11830 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11831 $mime = 'text/plain';
11832 $imgmime = 'text.png';
11833 $srclang = 'java';
11834 $famime = 'file-code';
11835 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11836 $mime = 'text/plain';
11837 $imgmime = 'php.png';
11838 $srclang = 'php';
11839 $famime = 'file-code';
11840 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
11841 $mime = 'text/plain';
11842 $imgmime = 'php.png';
11843 $srclang = 'php';
11844 $famime = 'file-code';
11845 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
11846 $mime = 'text/plain';
11847 $imgmime = 'pl.png';
11848 $srclang = 'perl';
11849 $famime = 'file-code';
11850 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
11851 $mime = 'text/plain';
11852 $imgmime = 'text.png';
11853 $srclang = 'sql';
11854 $famime = 'file-code';
11855 } elseif (preg_match('/\.js$/i', $tmpfile)) {
11856 $mime = 'text/x-javascript';
11857 $imgmime = 'jscript.png';
11858 $srclang = 'js';
11859 $famime = 'file-code';
11860 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
11861 $mime = 'application/vnd.oasis.opendocument.presentation';
11862 $imgmime = 'ooffice.png';
11863 $famime = 'file-powerpoint';
11864 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
11865 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
11866 $imgmime = 'ooffice.png';
11867 $famime = 'file-excel';
11868 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
11869 $mime = 'application/vnd.oasis.opendocument.text';
11870 $imgmime = 'ooffice.png';
11871 $famime = 'file-word';
11872 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
11873 $mime = 'application/msaccess';
11874 $imgmime = 'mdb.png';
11875 $famime = 'file';
11876 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
11877 $mime = 'application/msword';
11878 $imgmime = 'doc.png';
11879 $famime = 'file-word';
11880 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
11881 $mime = 'application/msword';
11882 $imgmime = 'doc.png';
11883 $famime = 'file-word';
11884 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
11885 $mime = 'application/vnd.ms-excel';
11886 $imgmime = 'xls.png';
11887 $famime = 'file-excel';
11888 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
11889 $mime = 'application/vnd.ms-excel';
11890 $imgmime = 'xls.png';
11891 $famime = 'file-excel';
11892 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
11893 $mime = 'application/vnd.ms-excel';
11894 $imgmime = 'xls.png';
11895 $famime = 'file-excel';
11896 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
11897 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
11898 $imgmime = 'xls.png';
11899 $famime = 'file-excel';
11900 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
11901 $mime = 'application/vnd.ms-powerpoint';
11902 $imgmime = 'ppt.png';
11903 $famime = 'file-powerpoint';
11904 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
11905 $mime = 'application/x-mspowerpoint';
11906 $imgmime = 'ppt.png';
11907 $famime = 'file-powerpoint';
11908 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
11909 $mime = 'application/pdf';
11910 $imgmime = 'pdf.png';
11911 $famime = 'file-pdf';
11912 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
11913 $mime = 'text/x-bat';
11914 $imgmime = 'script.png';
11915 $srclang = 'dos';
11916 $famime = 'file-code';
11917 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
11918 $mime = 'text/x-sh';
11919 $imgmime = 'script.png';
11920 $srclang = 'bash';
11921 $famime = 'file-code';
11922 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
11923 $mime = 'text/x-ksh';
11924 $imgmime = 'script.png';
11925 $srclang = 'bash';
11926 $famime = 'file-code';
11927 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
11928 $mime = 'text/x-bash';
11929 $imgmime = 'script.png';
11930 $srclang = 'bash';
11931 $famime = 'file-code';
11932 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
11933 $mime = 'image/x-icon';
11934 $imgmime = 'image.png';
11935 $famime = 'file-image';
11936 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
11937 $mime = 'image/jpeg';
11938 $imgmime = 'image.png';
11939 $famime = 'file-image';
11940 } elseif (preg_match('/\.png$/i', $tmpfile)) {
11941 $mime = 'image/png';
11942 $imgmime = 'image.png';
11943 $famime = 'file-image';
11944 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
11945 $mime = 'image/gif';
11946 $imgmime = 'image.png';
11947 $famime = 'file-image';
11948 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
11949 $mime = 'image/bmp';
11950 $imgmime = 'image.png';
11951 $famime = 'file-image';
11952 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
11953 $mime = 'image/tiff';
11954 $imgmime = 'image.png';
11955 $famime = 'file-image';
11956 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
11957 $mime = 'image/svg+xml';
11958 $imgmime = 'image.png';
11959 $famime = 'file-image';
11960 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
11961 $mime = 'image/webp';
11962 $imgmime = 'image.png';
11963 $famime = 'file-image';
11964 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
11965 $mime = 'text/calendar';
11966 $imgmime = 'other.png';
11967 $famime = 'file-alt';
11968 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
11969 $mime = 'text/calendar';
11970 $imgmime = 'other.png';
11971 $famime = 'file-alt';
11972 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
11973 $mime = 'application/x-bittorrent';
11974 $imgmime = 'other.png';
11975 $famime = 'file-o';
11976 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
11977 $mime = 'audio';
11978 $imgmime = 'audio.png';
11979 $famime = 'file-audio';
11980 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
11981 $mime = 'video/mp4';
11982 $imgmime = 'video.png';
11983 $famime = 'file-video';
11984 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
11985 $mime = 'video/ogg';
11986 $imgmime = 'video.png';
11987 $famime = 'file-video';
11988 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
11989 $mime = 'video/webm';
11990 $imgmime = 'video.png';
11991 $famime = 'file-video';
11992 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
11993 $mime = 'video/x-msvideo';
11994 $imgmime = 'video.png';
11995 $famime = 'file-video';
11996 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
11997 $mime = 'video/divx';
11998 $imgmime = 'video.png';
11999 $famime = 'file-video';
12000 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12001 $mime = 'video/xvid';
12002 $imgmime = 'video.png';
12003 $famime = 'file-video';
12004 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12005 $mime = 'video';
12006 $imgmime = 'video.png';
12007 $famime = 'file-video';
12008 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12009 // application/xxx where zzz is zip, ...
12010 $mime = 'archive';
12011 $imgmime = 'archive.png';
12012 $famime = 'file-archive';
12013 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12014 $mime = 'application/octet-stream';
12015 $imgmime = 'other.png';
12016 $famime = 'file-o';
12017 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12018 $mime = 'library';
12019 $imgmime = 'library.png';
12020 $famime = 'file-o';
12021 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12022 $mime = 'error';
12023 $imgmime = 'error.png';
12024 $famime = 'file-alt';
12025 }
12026
12027 // Return mimetype string
12028 switch ((int) $mode) {
12029 case 1:
12030 $tmp = explode('/', $mime);
12031 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12032 case 2:
12033 return $imgmime;
12034 case 3:
12035 return $srclang;
12036 case 4:
12037 return $famime;
12038 }
12039 return $mime;
12040}
12041
12053function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12054{
12055 global $conf, $db;
12056
12057 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12058
12059 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12060
12061 if (is_null($dictvalues)) {
12062 $dictvalues = array();
12063
12064 $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
12065 if ($checkentity) {
12066 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12067 }
12068
12069 $resql = $db->query($sql);
12070 if ($resql) {
12071 while ($obj = $db->fetch_object($resql)) {
12072 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12073 }
12074 } else {
12075 dol_print_error($db);
12076 }
12077
12078 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12079 }
12080
12081 if (!empty($dictvalues[$id])) {
12082 // Found
12083 $tmp = $dictvalues[$id];
12084 return (property_exists($tmp, $field) ? $tmp->$field : '');
12085 } else {
12086 // Not found
12087 return '';
12088 }
12089}
12090
12097function colorIsLight($stringcolor)
12098{
12099 $stringcolor = str_replace('#', '', $stringcolor);
12100 $res = -1;
12101 if (!empty($stringcolor)) {
12102 $res = 0;
12103 $tmp = explode(',', $stringcolor);
12104 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12105 $r = $tmp[0];
12106 $g = $tmp[1];
12107 $b = $tmp[2];
12108 } else {
12109 $hexr = $stringcolor[0].$stringcolor[1];
12110 $hexg = $stringcolor[2].$stringcolor[3];
12111 $hexb = $stringcolor[4].$stringcolor[5];
12112 $r = hexdec($hexr);
12113 $g = hexdec($hexg);
12114 $b = hexdec($hexb);
12115 }
12116 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12117 if ($bright > 0.6) {
12118 $res = 1;
12119 }
12120 }
12121 return $res;
12122}
12123
12132function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12133{
12134 global $conf;
12135
12136 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12137 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12138 if (empty($menuentry['enabled'])) {
12139 return 0; // Entry disabled by condition
12140 }
12141 if ($type_user && $menuentry['module']) {
12142 $tmploops = explode('|', $menuentry['module']);
12143 $found = 0;
12144 foreach ($tmploops as $tmploop) {
12145 if (in_array($tmploop, $listofmodulesforexternal)) {
12146 $found++;
12147 break;
12148 }
12149 }
12150 if (!$found) {
12151 return 0; // Entry is for menus all excluded to external users
12152 }
12153 }
12154 if (!$menuentry['perms'] && $type_user) {
12155 return 0; // No permissions and user is external
12156 }
12157 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12158 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12159 }
12160 if (!$menuentry['perms']) {
12161 return 2; // No permissions and user is external
12162 }
12163 return 1;
12164}
12165
12173function roundUpToNextMultiple($n, $x = 5)
12174{
12175 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12176 return (int) $result;
12177}
12178
12190function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12191{
12192 $csstouse = 'badge';
12193 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12194 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12195 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12196
12197 $attr = array(
12198 'class' => $csstouse
12199 );
12200
12201 if (empty($html)) {
12202 $html = $label;
12203 }
12204
12205 if (!empty($url)) {
12206 $attr['href'] = $url;
12207 }
12208
12209 if ($mode === 'dot') {
12210 $attr['class'] .= ' classfortooltip';
12211 $attr['title'] = $html;
12212 $attr['aria-label'] = $label;
12213 $html = '';
12214 }
12215
12216 // Override attr
12217 if (!empty($params['attr']) && is_array($params['attr'])) {
12218 foreach ($params['attr'] as $key => $value) {
12219 if ($key == 'class') {
12220 $attr['class'] .= ' '.$value;
12221 } elseif ($key == 'classOverride') {
12222 $attr['class'] = $value;
12223 } else {
12224 $attr[$key] = $value;
12225 }
12226 }
12227 }
12228
12229 // TODO: add hook
12230
12231 // escape all attribute
12232 $attr = array_map('dol_escape_htmltag', $attr);
12233
12234 $TCompiledAttr = array();
12235 foreach ($attr as $key => $value) {
12236 $TCompiledAttr[] = $key.'="'.$value.'"';
12237 }
12238
12239 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12240
12241 $tag = !empty($url) ? 'a' : 'span';
12242
12243 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12244}
12245
12246
12259function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12260{
12261 global $conf;
12262
12263 $return = '';
12264 $dolGetBadgeParams = array();
12265
12266 if (!empty($params['badgeParams'])) {
12267 $dolGetBadgeParams = $params['badgeParams'];
12268 }
12269
12270 // TODO : add a hook
12271 if ($displayMode == 0) {
12272 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12273 } elseif ($displayMode == 1) {
12274 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12275 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12276 // Use status with images (for backward compatibility)
12277 $return = '';
12278 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12279 $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>' : '');
12280
12281 // For small screen, we always use the short label instead of long label.
12282 if (!empty($conf->dol_optimize_smallscreen)) {
12283 if ($displayMode == 0) {
12284 $displayMode = 1;
12285 } elseif ($displayMode == 4) {
12286 $displayMode = 2;
12287 } elseif ($displayMode == 6) {
12288 $displayMode = 5;
12289 }
12290 }
12291
12292 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12293 $statusImg = array(
12294 'status0' => 'statut0',
12295 'status1' => 'statut1',
12296 'status2' => 'statut2',
12297 'status3' => 'statut3',
12298 'status4' => 'statut4',
12299 'status5' => 'statut5',
12300 'status6' => 'statut6',
12301 'status7' => 'statut7',
12302 'status8' => 'statut8',
12303 'status9' => 'statut9'
12304 );
12305
12306 if (!empty($statusImg[$statusType])) {
12307 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12308 } else {
12309 $htmlImg = img_picto($statusLabel, $statusType);
12310 }
12311
12312 if ($displayMode === 2) {
12313 $return = $htmlImg.' '.$htmlLabelShort;
12314 } elseif ($displayMode === 3) {
12315 $return = $htmlImg;
12316 } elseif ($displayMode === 4) {
12317 $return = $htmlImg.' '.$htmlLabel;
12318 } elseif ($displayMode === 5) {
12319 $return = $htmlLabelShort.' '.$htmlImg;
12320 } else { // $displayMode >= 6
12321 $return = $htmlLabel.' '.$htmlImg;
12322 }
12323 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12324 // Use new badge
12325 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12326
12327 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12328 if (empty($dolGetBadgeParams['attr']['title'])) {
12329 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12330 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12331 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12332 // And if we use tooltip, we can output title in HTML
12333 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12334 }
12335
12336 if ($displayMode == 3) {
12337 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12338 } elseif ($displayMode === 5) {
12339 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12340 } else {
12341 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12342 }
12343 }
12344
12345 return $return;
12346}
12347
12348
12387function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12388{
12389 global $hookmanager, $action, $object, $langs;
12390
12391 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12392 if (is_array($url)) {
12393 // Loop on $url array to remove entries of disabled modules
12394 foreach ($url as $key => $subbutton) {
12395 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12396 unset($url[$key]);
12397 }
12398 }
12399
12400 $out = '';
12401
12402 if (isset($params["areDropdownButtons"]) && $params["areDropdownButtons"] === false) {
12403 foreach ($url as $button) {
12404 if (!empty($button['lang'])) {
12405 $langs->load($button['lang']);
12406 }
12407 $label = $langs->trans($button['label']);
12408 $text = $button['text'] ?? '';
12409 $actionType = $button['actionType'] ?? '';
12410 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12411 $id = $button['id'] ?? '';
12412 $userRight = $button['perm'] ?? 1;
12413 $button['params'] = $button['params'] ?? [];
12414
12415 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12416 }
12417 return $out;
12418 }
12419
12420 if (count($url) > 1) {
12421 $out .= '<div class="dropdown inline-block dropdown-holder">';
12422 $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>';
12423 $out .= '<div class="dropdown-content">';
12424 foreach ($url as $subbutton) {
12425 if (!empty($subbutton['lang'])) {
12426 $langs->load($subbutton['lang']);
12427 }
12428
12429 if (!empty($subbutton['urlraw'])) {
12430 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12431 } else {
12432 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12433 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12434 }
12435
12436 $subbuttonparam = array();
12437 if (!empty($subbutton['attr'])) {
12438 $subbuttonparam['attr'] = $subbutton['attr'];
12439 }
12440 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown']??false) : $params['isDropDown']);
12441
12442 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12443 }
12444 $out .= "</div>";
12445 $out .= "</div>";
12446 } else {
12447 foreach ($url as $subbutton) { // Should loop on 1 record only
12448 if (!empty($subbutton['lang'])) {
12449 $langs->load($subbutton['lang']);
12450 }
12451
12452 if (!empty($subbutton['urlraw'])) {
12453 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12454 } else {
12455 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12456 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12457 }
12458
12459 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12460 }
12461 }
12462
12463 return $out;
12464 }
12465
12466 // Here, $url is a simple link
12467
12468 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12469 $class = "dropdown-item";
12470 } else {
12471 $class = 'butAction';
12472 if ($actionType == 'danger' || $actionType == 'delete') {
12473 $class = 'butActionDelete';
12474 if (!empty($url) && strpos($url, 'token=') === false) {
12475 $url .= '&token='.newToken();
12476 }
12477 }
12478 }
12479 $attr = array(
12480 'class' => $class,
12481 'href' => empty($url) ? '' : $url,
12482 'title' => $label
12483 );
12484
12485 if (empty($text)) {
12486 $text = $label;
12487 $attr['title'] = ''; // if html not set, leave label on title is redundant
12488 } else {
12489 $attr['title'] = $label;
12490 $attr['aria-label'] = $label;
12491 }
12492
12493 if (empty($userRight)) {
12494 $attr['class'] = 'butActionRefused';
12495 $attr['href'] = '';
12496 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12497 $attr['title'] = ($attr['title'] ? $attr['title'].'<br>' : '').$langs->trans('NotEnoughPermissions');
12498 }
12499
12500 if (!empty($id)) {
12501 $attr['id'] = $id;
12502 }
12503
12504 // Override attr
12505 if (!empty($params['attr']) && is_array($params['attr'])) {
12506 foreach ($params['attr'] as $key => $value) {
12507 if ($key == 'class') {
12508 $attr['class'] .= ' '.$value;
12509 } elseif ($key == 'classOverride') {
12510 $attr['class'] = $value;
12511 } else {
12512 $attr[$key] = $value;
12513 }
12514 }
12515 }
12516
12517 // automatic add tooltip when title is detected
12518 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12519 $attr['class'] .= ' classfortooltip';
12520 }
12521
12522 // Js Confirm button
12523 if ($userRight && !empty($params['confirm'])) {
12524 if (!is_array($params['confirm'])) {
12525 $params['confirm'] = array();
12526 }
12527
12528 if (empty($params['confirm']['url'])) {
12529 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12530 }
12531
12532 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12533 $attr['data-confirm-url'] = $params['confirm']['url'];
12534 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12535 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12536 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12537 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12538 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12539 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12540
12541 $attr['class'] .= ' butActionConfirm';
12542 }
12543
12544 if (isset($attr['href']) && empty($attr['href'])) {
12545 unset($attr['href']);
12546 }
12547
12548 // Escape all attributes
12549 if (!empty($params['use_unsecured_unescapedattr'])) { // Not recommended.
12550 if (is_array($params['use_unsecured_unescapedattr'])) {
12551 foreach ($attr as $attrK => $attrV) {
12552 if (in_array($attrK, $params['use_unsecured_unescapedattr'])) {
12553 $attr[$attrK] = dol_htmlentities($attrV, ENT_QUOTES | ENT_SUBSTITUTE);
12554 } else {
12555 $attr[$attrK] = dolPrintHTMLForAttribute($attrV);
12556 }
12557 }
12558 } else {
12559 $attr = array_map('dol_htmlentities', $attr);
12560 }
12561 } else {
12562 $attr = array_map('dolPrintHTMLForAttribute', $attr);
12563 }
12564
12565 $TCompiledAttr = array();
12566 foreach ($attr as $key => $value) {
12567 $TCompiledAttr[] = $key.'= "'.$value.'"';
12568 }
12569
12570 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12571
12572 $tag = !empty($attr['href']) ? 'a' : 'span';
12573
12574
12575 $parameters = array(
12576 'TCompiledAttr' => $TCompiledAttr, // array
12577 'compiledAttributes' => $compiledAttributes, // string
12578 'attr' => $attr,
12579 'tag' => $tag,
12580 'label' => $label,
12581 'html' => $text,
12582 'actionType' => $actionType,
12583 'url' => $url,
12584 'id' => $id,
12585 'userRight' => $userRight,
12586 'params' => $params
12587 );
12588
12589 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12590 if ($reshook < 0) {
12591 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12592 }
12593
12594 if (empty($reshook)) {
12595 if (dol_textishtml($text)) { // If content already HTML encoded
12596 return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
12597 } else {
12598 return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
12599 }
12600 } else {
12601 return $hookmanager->resPrint;
12602 }
12603}
12604
12605
12614function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
12615{
12616 if (empty($url)) {
12617 return '';
12618 }
12619
12620 $parsedUrl = parse_url($url);
12621 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
12622 return $url;
12623 }
12624
12625 if (!empty($parsedUrl['query'])) {
12626 // Use parse_str() function to parse the string passed via URL
12627 parse_str($parsedUrl['query'], $urlQuery);
12628 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
12629 $url.= '&amp;backtopage='.urlencode($params['backtopage']);
12630 }
12631 }
12632
12633 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
12634 $url = DOL_URL_ROOT.$url;
12635 }
12636
12637 return $url;
12638}
12639
12640
12647function dolGetButtonTitleSeparator($moreClass = "")
12648{
12649 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12650}
12651
12658function getFieldErrorIcon($fieldValidationErrorMsg)
12659{
12660 $out = '';
12661 if (!empty($fieldValidationErrorMsg)) {
12662 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12663 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12664 $out .= '</span>';
12665 }
12666
12667 return $out;
12668}
12669
12682function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12683{
12684 global $langs, $conf, $user;
12685
12686 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12687 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12688 return '';
12689 }
12690
12691 $class = 'btnTitle';
12692 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12693 $class .= ' btnTitlePlus';
12694 }
12695 $useclassfortooltip = 1;
12696
12697 if (!empty($params['morecss'])) {
12698 $class .= ' '.$params['morecss'];
12699 }
12700
12701 $attr = array(
12702 'class' => $class,
12703 'href' => empty($url) ? '' : $url
12704 );
12705
12706 if (!empty($helpText)) {
12707 $attr['title'] = dol_escape_htmltag($helpText);
12708 } elseif (empty($attr['title']) && $label) {
12709 $attr['title'] = $label;
12710 $useclassfortooltip = 0;
12711 }
12712
12713 if ($status == 2) {
12714 $attr['class'] .= ' btnTitleSelected';
12715 } elseif ($status <= 0) {
12716 $attr['class'] .= ' refused';
12717
12718 $attr['href'] = '';
12719
12720 if ($status == -1) { // disable
12721 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12722 } elseif ($status == 0) { // Not enough permissions
12723 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12724 }
12725 }
12726
12727 if (!empty($attr['title']) && $useclassfortooltip) {
12728 $attr['class'] .= ' classfortooltip';
12729 }
12730
12731 if (!empty($id)) {
12732 $attr['id'] = $id;
12733 }
12734
12735 // Override attr
12736 if (!empty($params['attr']) && is_array($params['attr'])) {
12737 foreach ($params['attr'] as $key => $value) {
12738 if ($key == 'class') {
12739 $attr['class'] .= ' '.$value;
12740 } elseif ($key == 'classOverride') {
12741 $attr['class'] = $value;
12742 } else {
12743 $attr[$key] = $value;
12744 }
12745 }
12746 }
12747
12748 if (isset($attr['href']) && empty($attr['href'])) {
12749 unset($attr['href']);
12750 }
12751
12752 // TODO : add a hook
12753
12754 // escape all attribute
12755 $attr = array_map('dol_escape_htmltag', $attr);
12756
12757 $TCompiledAttr = array();
12758 foreach ($attr as $key => $value) {
12759 $TCompiledAttr[] = $key.'="'.$value.'"';
12760 }
12761
12762 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12763
12764 $tag = (empty($attr['href']) ? 'span' : 'a');
12765
12766 $button = '<'.$tag.' '.$compiledAttributes.'>';
12767 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12768 if (!empty($params['forcenohideoftext'])) {
12769 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12770 }
12771 $button .= '</'.$tag.'>';
12772
12773 return $button;
12774}
12775
12785function getElementProperties($elementType)
12786{
12787 global $conf, $db, $hookmanager;
12788
12789 $regs = array();
12790
12791 //$element_type='facture';
12792
12793 $classfile = $classname = $classpath = $subdir = $dir_output = '';
12794
12795 // Parse element/subelement
12796 $module = $elementType;
12797 $element = $elementType;
12798 $subelement = $elementType;
12799 $table_element = $elementType;
12800
12801 // If we ask a resource form external module (instead of default path)
12802 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12803 $element = $subelement = $regs[1];
12804 $module = $regs[2];
12805 }
12806
12807 // If we ask a resource for a string with an element and a subelement
12808 // Example 'project_task'
12809 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12810 $module = $element = $regs[1];
12811 $subelement = $regs[2];
12812 }
12813
12814 // Object lines will use parent classpath and module ref
12815 if (substr($elementType, -3) == 'det') {
12816 $module = preg_replace('/det$/', '', $element);
12817 $subelement = preg_replace('/det$/', '', $subelement);
12818 $classpath = $module.'/class';
12819 $classfile = $module;
12820 $classname = preg_replace('/det$/', 'Line', $element);
12821 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12822 $classname = preg_replace('/det$/', 'Ligne', $element);
12823 }
12824 }
12825 // For compatibility and to work with non standard path
12826 if ($elementType == "action" || $elementType == "actioncomm") {
12827 $classpath = 'comm/action/class';
12828 $subelement = 'Actioncomm';
12829 $module = 'agenda';
12830 $table_element = 'actioncomm';
12831 } elseif ($elementType == 'cronjob') {
12832 $classpath = 'cron/class';
12833 $module = 'cron';
12834 $table_element = 'cron';
12835 } elseif ($elementType == 'adherent_type') {
12836 $classpath = 'adherents/class';
12837 $classfile = 'adherent_type';
12838 $module = 'adherent';
12839 $subelement = 'adherent_type';
12840 $classname = 'AdherentType';
12841 $table_element = 'adherent_type';
12842 } elseif ($elementType == 'bank_account') {
12843 $classpath = 'compta/bank/class';
12844 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12845 $classfile = 'account';
12846 $classname = 'Account';
12847 } elseif ($elementType == 'category') {
12848 $classpath = 'categories/class';
12849 $module = 'categorie';
12850 $subelement = 'categorie';
12851 $table_element = 'categorie';
12852 } elseif ($elementType == 'contact') {
12853 $classpath = 'contact/class';
12854 $classfile = 'contact';
12855 $module = 'societe';
12856 $subelement = 'contact';
12857 $table_element = 'socpeople';
12858 } elseif ($elementType == 'inventory') {
12859 $module = 'product';
12860 $classpath = 'product/inventory/class';
12861 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
12862 $module = 'stock';
12863 $classpath = 'product/stock/class';
12864 $classfile = 'entrepot';
12865 $classname = 'Entrepot';
12866 $table_element = 'entrepot';
12867 } elseif ($elementType == 'project') {
12868 $classpath = 'projet/class';
12869 $module = 'projet';
12870 $table_element = 'projet';
12871 } elseif ($elementType == 'project_task') {
12872 $classpath = 'projet/class';
12873 $module = 'projet';
12874 $subelement = 'task';
12875 $table_element = 'projet_task';
12876 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
12877 $classpath = 'compta/facture/class';
12878 $module = 'facture';
12879 $subelement = 'facture';
12880 $table_element = 'facture';
12881 } elseif ($elementType == 'facturerec') {
12882 $classpath = 'compta/facture/class';
12883 $module = 'facture';
12884 $classname = 'FactureRec';
12885 } elseif ($elementType == 'commande' || $elementType == 'order') {
12886 $classpath = 'commande/class';
12887 $module = 'commande';
12888 $subelement = 'commande';
12889 $table_element = 'commande';
12890 } elseif ($elementType == 'propal') {
12891 $classpath = 'comm/propal/class';
12892 $table_element = 'propal';
12893 } elseif ($elementType == 'shipping') {
12894 $classpath = 'expedition/class';
12895 $classfile = 'expedition';
12896 $classname = 'Expedition';
12897 $module = 'expedition';
12898 $table_element = 'expedition';
12899 } elseif ($elementType == 'delivery_note') {
12900 $classpath = 'delivery/class';
12901 $subelement = 'delivery';
12902 $module = 'expedition';
12903 } elseif ($elementType == 'delivery') {
12904 $classpath = 'delivery/class';
12905 $subelement = 'delivery';
12906 $module = 'expedition';
12907 } elseif ($elementType == 'supplier_proposal') {
12908 $classpath = 'supplier_proposal/class';
12909 $module = 'supplier_proposal';
12910 $element = 'supplierproposal';
12911 $classfile = 'supplier_proposal';
12912 $subelement = 'supplierproposal';
12913 } elseif ($elementType == 'contract') {
12914 $classpath = 'contrat/class';
12915 $module = 'contrat';
12916 $subelement = 'contrat';
12917 $table_element = 'contract';
12918 } elseif ($elementType == 'mailing') {
12919 $classpath = 'comm/mailing/class';
12920 $module = 'mailing';
12921 $classfile = 'mailing';
12922 $classname = 'Mailing';
12923 $subelement = '';
12924 } elseif ($elementType == 'member' || $elementType == 'adherent') {
12925 $classpath = 'adherents/class';
12926 $module = 'adherent';
12927 $subelement = 'adherent';
12928 $table_element = 'adherent';
12929 } elseif ($elementType == 'usergroup') {
12930 $classpath = 'user/class';
12931 $module = 'user';
12932 } elseif ($elementType == 'mo') {
12933 $classpath = 'mrp/class';
12934 $classfile = 'mo';
12935 $classname = 'Mo';
12936 $module = 'mrp';
12937 $subelement = '';
12938 $table_element = 'mrp_mo';
12939 } elseif ($elementType == 'cabinetmed_cons') {
12940 $classpath = 'cabinetmed/class';
12941 $module = 'cabinetmed';
12942 $subelement = 'cabinetmedcons';
12943 $table_element = 'cabinetmedcons';
12944 } elseif ($elementType == 'fichinter') {
12945 $classpath = 'fichinter/class';
12946 $module = 'ficheinter';
12947 $subelement = 'fichinter';
12948 $table_element = 'fichinter';
12949 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
12950 $classpath = 'resource/class';
12951 $module = 'resource';
12952 $subelement = 'dolresource';
12953 $table_element = 'resource';
12954 } elseif ($elementType == 'propaldet') {
12955 $classpath = 'comm/propal/class';
12956 $module = 'propal';
12957 $subelement = 'propaleligne';
12958 } elseif ($elementType == 'opensurvey_sondage') {
12959 $classpath = 'opensurvey/class';
12960 $module = 'opensurvey';
12961 $subelement = 'opensurveysondage';
12962 } elseif ($elementType == 'order_supplier') {
12963 $classpath = 'fourn/class';
12964 $module = 'fournisseur';
12965 $classfile = 'fournisseur.commande';
12966 $element = 'order_supplier';
12967 $subelement = '';
12968 $classname = 'CommandeFournisseur';
12969 $table_element = 'commande_fournisseur';
12970 } elseif ($elementType == 'commande_fournisseurdet') {
12971 $classpath = 'fourn/class';
12972 $module = 'fournisseur';
12973 $classfile = 'fournisseur.commande';
12974 $element = 'commande_fournisseurdet';
12975 $subelement = '';
12976 $classname = 'CommandeFournisseurLigne';
12977 $table_element = 'commande_fournisseurdet';
12978 } elseif ($elementType == 'invoice_supplier') {
12979 $classpath = 'fourn/class';
12980 $module = 'fournisseur';
12981 $classfile = 'fournisseur.facture';
12982 $element = 'invoice_supplier';
12983 $subelement = '';
12984 $classname = 'FactureFournisseur';
12985 $table_element = 'facture_fourn';
12986 } elseif ($elementType == "service") {
12987 $classpath = 'product/class';
12988 $subelement = 'product';
12989 $table_element = 'product';
12990 } elseif ($elementType == 'salary') {
12991 $classpath = 'salaries/class';
12992 $module = 'salaries';
12993 } elseif ($elementType == 'payment_salary') {
12994 $classpath = 'salaries/class';
12995 $classfile = 'paymentsalary';
12996 $classname = 'PaymentSalary';
12997 $module = 'salaries';
12998 } elseif ($elementType == 'productlot') {
12999 $module = 'productbatch';
13000 $classpath = 'product/stock/class';
13001 $classfile = 'productlot';
13002 $classname = 'Productlot';
13003 $element = 'productlot';
13004 $subelement = '';
13005 $table_element = 'product_lot';
13006 } elseif ($elementType == 'societeaccount') {
13007 $classpath = 'societe/class';
13008 $classfile = 'societeaccount';
13009 $classname = 'SocieteAccount';
13010 $module = 'societe';
13011 } elseif ($elementType == 'websitepage') {
13012 $classpath = 'website/class';
13013 $classfile = 'websitepage';
13014 $classname = 'Websitepage';
13015 $module = 'website';
13016 $subelement = 'websitepage';
13017 $table_element = 'website_page';
13018 } elseif ($elementType == 'fiscalyear') {
13019 $classpath = 'core/class';
13020 $module = 'accounting';
13021 $subelement = 'fiscalyear';
13022 } elseif ($elementType == 'chargesociales') {
13023 $classpath = 'compta/sociales/class';
13024 $module = 'tax';
13025 $table_element = 'chargesociales';
13026 } elseif ($elementType == 'tva') {
13027 $classpath = 'compta/tva/class';
13028 $module = 'tax';
13029 $subdir = '/vat';
13030 $table_element = 'tva';
13031 } elseif ($elementType == 'emailsenderprofile') {
13032 $module = '';
13033 $classpath = 'core/class';
13034 $classfile = 'emailsenderprofile';
13035 $classname = 'EmailSenderProfile';
13036 $table_element = 'c_email_senderprofile';
13037 $subelement = '';
13038 } elseif ($elementType == 'conferenceorboothattendee') {
13039 $classpath = 'eventorganization/class';
13040 $classfile = 'conferenceorboothattendee';
13041 $classname = 'ConferenceOrBoothAttendee';
13042 $module = 'eventorganization';
13043 } elseif ($elementType == 'conferenceorbooth') {
13044 $classpath = 'eventorganization/class';
13045 $classfile = 'conferenceorbooth';
13046 $classname = 'ConferenceOrBooth';
13047 $module = 'eventorganization';
13048 } elseif ($elementType == 'ccountry') {
13049 $module = '';
13050 $classpath = 'core/class';
13051 $classfile = 'ccountry';
13052 $classname = 'Ccountry';
13053 $table_element = 'c_country';
13054 $subelement = '';
13055 }
13056
13057 if (empty($classfile)) {
13058 $classfile = strtolower($subelement);
13059 }
13060 if (empty($classname)) {
13061 $classname = ucfirst($subelement);
13062 }
13063 if (empty($classpath)) {
13064 $classpath = $module.'/class';
13065 }
13066
13067 //print 'getElementProperties subdir='.$subdir;
13068
13069 // Set dir_output
13070 if ($module && isset($conf->$module)) { // The generic case
13071 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13072 $dir_output = $conf->$module->multidir_output[$conf->entity];
13073 } elseif (!empty($conf->$module->output[$conf->entity])) {
13074 $dir_output = $conf->$module->output[$conf->entity];
13075 } elseif (!empty($conf->$module->dir_output)) {
13076 $dir_output = $conf->$module->dir_output;
13077 }
13078 }
13079
13080 // Overwrite value for special cases
13081 if ($element == 'order_supplier') {
13082 $dir_output = $conf->fournisseur->commande->dir_output;
13083 } elseif ($element == 'invoice_supplier') {
13084 $dir_output = $conf->fournisseur->facture->dir_output;
13085 }
13086 $dir_output .= $subdir;
13087
13088 $elementProperties = array(
13089 'module' => $module,
13090 'element' => $element,
13091 'table_element' => $table_element,
13092 'subelement' => $subelement,
13093 'classpath' => $classpath,
13094 'classfile' => $classfile,
13095 'classname' => $classname,
13096 'dir_output' => $dir_output
13097 );
13098
13099
13100 // Add hook
13101 if (!is_object($hookmanager)) {
13102 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13103 $hookmanager = new HookManager($db);
13104 }
13105 $hookmanager->initHooks(array('elementproperties'));
13106
13107
13108 // Hook params
13109 $parameters = array(
13110 'elementType' => $elementType,
13111 'elementProperties' => $elementProperties
13112 );
13113
13114 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13115
13116 if ($reshook) {
13117 $elementProperties = $hookmanager->resArray;
13118 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13119 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13120 }
13121
13122 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13123 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13124 unset($hookmanager->contextarray[$key]);
13125 }
13126
13127 return $elementProperties;
13128}
13129
13142function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13143{
13144 global $db, $conf;
13145
13146 $ret = 0;
13147
13148 $element_prop = getElementProperties($element_type);
13149
13150 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13151 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13152 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13153 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13154 // of service and we will return properties of a product.
13155 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13156 } elseif ($element_prop['module'] == 'societeaccount') {
13157 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13158 } else {
13159 $ismodenabled = isModEnabled($element_prop['module']);
13160 }
13161 //var_dump('element_type='.$element_type);
13162 //var_dump($element_prop);
13163 //var_dump($element_prop['module'].' '.$ismodenabled);
13164 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13165 if ($useCache === 1
13166 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13167 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13168 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13169 ) {
13170 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13171 }
13172
13173 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13174
13175 if (class_exists($element_prop['classname'])) {
13176 $className = $element_prop['classname'];
13177 $objecttmp = new $className($db);
13178 '@phan-var-force CommonObject $objecttmp';
13179
13180 if ($element_id > 0 || !empty($element_ref)) {
13181 $ret = $objecttmp->fetch($element_id, $element_ref);
13182 if ($ret >= 0) {
13183 if (empty($objecttmp->module)) {
13184 $objecttmp->module = $element_prop['module'];
13185 }
13186
13187 if ($useCache > 0) {
13188 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13189 $conf->cache['fetchObjectByElement'][$element_type] = [];
13190 }
13191
13192 // Manage cache limit
13193 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13194 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13195 }
13196
13197 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13198 }
13199
13200 return $objecttmp;
13201 }
13202 } else {
13203 return $objecttmp; // returned an object without fetch
13204 }
13205 } else {
13206 return -1;
13207 }
13208 }
13209
13210 return $ret;
13211}
13212
13219function isAFileWithExecutableContent($filename)
13220{
13221 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)) {
13222 return true;
13223 }
13224
13225 return false;
13226}
13227
13235function newToken()
13236{
13237 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13238}
13239
13247function currentToken()
13248{
13249 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13250}
13251
13257function getNonce()
13258{
13259 global $conf;
13260
13261 if (empty($conf->cache['nonce'])) {
13262 $conf->cache['nonce'] = dolGetRandomBytes(8);
13263 }
13264
13265 return $conf->cache['nonce'];
13266}
13267
13268
13282function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13283{
13284 global $langs;
13285
13286 print '<div class="div-table-responsive-no-min">';
13287 print '<table class="noborder centpercent">';
13288 print '<tr class="liste_titre">';
13289
13290 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13291
13292 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13293
13294 if (!empty($link)) {
13295 if (!empty($arguments)) {
13296 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13297 } else {
13298 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13299 }
13300 }
13301
13302 if ($number > -1) {
13303 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13304 } elseif (!empty($link)) {
13305 print '<span class="badge marginleftonlyshort">...</span>';
13306 }
13307
13308 if (!empty($link)) {
13309 print '</a>';
13310 }
13311
13312 print '</th>';
13313
13314 if ($number < 0 && !empty($link)) {
13315 print '<th class="right">';
13316 print '</th>';
13317 }
13318
13319 print '</tr>';
13320}
13321
13330function finishSimpleTable($addLineBreak = false)
13331{
13332 print '</table>';
13333 print '</div>';
13334
13335 if ($addLineBreak) {
13336 print '<br>';
13337 }
13338}
13339
13351function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13352{
13353 global $langs;
13354
13355 if ($num === 0) {
13356 print '<tr class="oddeven">';
13357 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13358 print '</tr>';
13359 return;
13360 }
13361
13362 if ($nbofloop === 0) {
13363 // don't show a summary line
13364 return;
13365 }
13366
13367 if ($num === 0) {
13368 $colspan = $tableColumnCount;
13369 } elseif ($num > $nbofloop) {
13370 $colspan = $tableColumnCount;
13371 } else {
13372 $colspan = $tableColumnCount - 1;
13373 }
13374
13375 if ($extraRightColumn) {
13376 $colspan--;
13377 }
13378
13379 print '<tr class="liste_total">';
13380
13381 if ($nbofloop > 0 && $num > $nbofloop) {
13382 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13383 } else {
13384 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13385 print '<td class="right centpercent">'.price($total).'</td>';
13386 }
13387
13388 if ($extraRightColumn) {
13389 print '<td></td>';
13390 }
13391
13392 print '</tr>';
13393}
13394
13403function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13404{
13405 if ($method == -1) {
13406 $method = 0;
13407 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13408 $method = 1;
13409 }
13410 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13411 $method = 2;
13412 }
13413 }
13414
13415 // Be sure we don't have output buffering enabled to have readfile working correctly
13416 while (ob_get_level()) {
13417 ob_end_flush();
13418 }
13419
13420 // Solution 0
13421 if ($method == 0) {
13422 readfile($fullpath_original_file_osencoded);
13423 } elseif ($method == 1) {
13424 // Solution 1
13425 $handle = fopen($fullpath_original_file_osencoded, "rb");
13426 while (!feof($handle)) {
13427 print fread($handle, 8192);
13428 }
13429 fclose($handle);
13430 } elseif ($method == 2) {
13431 // Solution 2
13432 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13433 $handle2 = fopen("php://output", "wb");
13434 stream_copy_to_stream($handle1, $handle2);
13435 fclose($handle1);
13436 fclose($handle2);
13437 }
13438}
13439
13449function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13450{
13451 /*
13452 global $conf;
13453
13454 if (!empty($conf->dol_no_mouse_hover)) {
13455 $showonlyonhover = 0;
13456 }*/
13457
13458 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13459 if ($texttoshow === 'none') {
13460 $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>';
13461 } elseif ($texttoshow) {
13462 $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>';
13463 } else {
13464 $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>';
13465 }
13466
13467 return $result;
13468}
13469
13470
13477function jsonOrUnserialize($stringtodecode)
13478{
13479 $result = json_decode($stringtodecode);
13480 if ($result === null) {
13481 $result = unserialize($stringtodecode);
13482 }
13483
13484 return $result;
13485}
13486
13487
13504function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13505{
13506 global $db, $user;
13507
13508 if (is_null($filter) || !is_string($filter) || $filter === '') {
13509 return '';
13510 }
13511 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13512 $filter = '(' . $filter . ')';
13513 }
13514
13515 $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'
13516 $firstandlastparenthesis = 0;
13517
13518 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13519 if ($noerror) {
13520 return '1 = 2';
13521 } else {
13522 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13523 }
13524 }
13525
13526 // Test the filter syntax
13527 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13528 $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
13529 // If the string result contains something else than '()', the syntax was wrong
13530
13531 if (preg_match('/[^\‍(\‍)]/', $t)) {
13532 $tmperrorstr = 'Bad syntax of the search string';
13533 $errorstr = 'Bad syntax of the search string: '.$filter;
13534 if ($noerror) {
13535 return '1 = 2';
13536 } else {
13537 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
13538 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13539 }
13540 }
13541
13542 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
13543
13544 if (is_object($db)) {
13545 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
13546 }
13547 if (is_object($user)) {
13548 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13549 }
13550
13551 return $ret;
13552}
13553
13561function dolForgeExplodeAnd($sqlfilters)
13562{
13563 $arrayofandtags = array();
13564 $nbofchars = dol_strlen($sqlfilters);
13565
13566 $error = '';
13567 $parenthesislevel = 0;
13568 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13569 if (!$result) {
13570 return array();
13571 }
13572 if ($parenthesislevel >= 1) {
13573 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13574 }
13575
13576 $i = 0;
13577 $s = '';
13578 $countparenthesis = 0;
13579 while ($i < $nbofchars) {
13580 $char = dol_substr($sqlfilters, $i, 1);
13581
13582 if ($char == '(') {
13583 $countparenthesis++;
13584 } elseif ($char == ')') {
13585 $countparenthesis--;
13586 }
13587
13588 if ($countparenthesis == 0) {
13589 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13590 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13591 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13592 // We found a AND
13593 $s = trim($s);
13594 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13595 $s = '('.$s.')';
13596 }
13597 $arrayofandtags[] = $s;
13598 $s = '';
13599 $i += 2;
13600 } else {
13601 $s .= $char;
13602 }
13603 } else {
13604 $s .= $char;
13605 }
13606 $i++;
13607 }
13608 if ($s) {
13609 $s = trim($s);
13610 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13611 $s = '('.$s.')';
13612 }
13613 $arrayofandtags[] = $s;
13614 }
13615
13616 return $arrayofandtags;
13617}
13618
13628function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13629{
13630 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13631 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13632 $tmp = $sqlfilters;
13633
13634 $nb = dol_strlen($tmp);
13635 $counter = 0;
13636 $parenthesislevel = 0;
13637
13638 $error = '';
13639
13640 $i = 0;
13641 while ($i < $nb) {
13642 $char = dol_substr($tmp, $i, 1);
13643
13644 if ($char == '(') {
13645 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13646 // We open a parenthesis and it is the first char
13647 $parenthesislevel++;
13648 }
13649 $counter++;
13650 } elseif ($char == ')') {
13651 $nbcharremaining = ($nb - $i - 1);
13652 if ($nbcharremaining >= $counter) {
13653 $parenthesislevel = min($parenthesislevel, $counter - 1);
13654 }
13655 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13656 $parenthesislevel = $counter;
13657 }
13658 $counter--;
13659 }
13660
13661 if ($counter < 0) {
13662 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13663 $parenthesislevel = 0;
13664 dol_syslog($error, LOG_WARNING);
13665 return false;
13666 }
13667
13668 $i++;
13669 }
13670
13671 if ($counter > 0) {
13672 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13673 $parenthesislevel = 0;
13674 dol_syslog($error, LOG_WARNING);
13675 return false;
13676 }
13677
13678 return true;
13679}
13680
13688function dolForgeDummyCriteriaCallback($matches)
13689{
13690 //dol_syslog("Convert matches ".$matches[1]);
13691 if (empty($matches[1])) {
13692 return '';
13693 }
13694 $tmp = explode(':', $matches[1]);
13695 if (count($tmp) < 3) {
13696 return '';
13697 }
13698
13699 return '()'; // An empty criteria
13700}
13701
13710function dolForgeCriteriaCallback($matches)
13711{
13712 global $db;
13713
13714 //dol_syslog("Convert matches ".$matches[1]);
13715 if (empty($matches[1])) {
13716 return '';
13717 }
13718 $tmp = explode(':', $matches[1], 3);
13719 if (count($tmp) < 3) {
13720 return '';
13721 }
13722
13723 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13724
13725 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13726
13727 $realOperator = [
13728 'NOTLIKE' => 'NOT LIKE',
13729 'ISNOT' => 'IS NOT',
13730 'NOTIN' => 'NOT IN',
13731 '!=' => '<>',
13732 ];
13733
13734 if (array_key_exists($operator, $realOperator)) {
13735 $operator = $realOperator[$operator];
13736 }
13737
13738 $tmpescaped = $tmp[2];
13739
13740 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13741
13742 $regbis = array();
13743
13744 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13745 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13746 $tmpescaped2 = '(';
13747 // Explode and sanitize each element in list
13748 $tmpelemarray = explode(',', $tmpescaped);
13749 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13750 $reg = array();
13751 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13752 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13753 } else {
13754 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13755 }
13756 }
13757 $tmpescaped2 .= implode(',', $tmpelemarray);
13758 $tmpescaped2 .= ')';
13759
13760 $tmpescaped = $tmpescaped2;
13761 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
13762 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
13763 $tmpescaped = $regbis[1];
13764 }
13765 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
13766 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
13767 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
13768 // TODO Retrieve type of field for $operand field name.
13769 // So we can complete format. For example we could complete a year with month and day.
13770 $tmpescaped = "'".$db->escape($regbis[1])."'";
13771 } else {
13772 if (strtoupper($tmpescaped) == 'NULL') {
13773 $tmpescaped = 'NULL';
13774 } elseif (is_int($tmpescaped)) {
13775 $tmpescaped = (int) $tmpescaped;
13776 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
13777 $tmpescaped = (float) $tmpescaped;
13778 } else {
13779 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
13780 }
13781 }
13782
13783 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
13784}
13785
13786
13796function getTimelineIcon($actionstatic, &$histo, $key)
13797{
13798 global $langs;
13799
13800 $out = '<!-- timeline icon -->'."\n";
13801 $iconClass = 'fa fa-comments';
13802 $img_picto = '';
13803 $colorClass = '';
13804 $pictoTitle = '';
13805
13806 if ($histo[$key]['percent'] == -1) {
13807 $colorClass = 'timeline-icon-not-applicble';
13808 $pictoTitle = $langs->trans('StatusNotApplicable');
13809 } elseif ($histo[$key]['percent'] == 0) {
13810 $colorClass = 'timeline-icon-todo';
13811 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
13812 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
13813 $colorClass = 'timeline-icon-in-progress';
13814 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
13815 } elseif ($histo[$key]['percent'] >= 100) {
13816 $colorClass = 'timeline-icon-done';
13817 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
13818 }
13819
13820 if ($actionstatic->code == 'AC_TICKET_CREATE') {
13821 $iconClass = 'fa fa-ticket';
13822 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
13823 $iconClass = 'fa fa-pencilxxx';
13824 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
13825 $iconClass = 'fa fa-comments';
13826 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
13827 $iconClass = 'fa fa-mask';
13828 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13829 if ($actionstatic->type_picto) {
13830 $img_picto = img_picto('', $actionstatic->type_picto);
13831 } else {
13832 if ($actionstatic->type_code == 'AC_RDV') {
13833 $iconClass = 'fa fa-handshake';
13834 } elseif ($actionstatic->type_code == 'AC_TEL') {
13835 $iconClass = 'fa fa-phone';
13836 } elseif ($actionstatic->type_code == 'AC_FAX') {
13837 $iconClass = 'fa fa-fax';
13838 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
13839 $iconClass = 'fa fa-envelope';
13840 } elseif ($actionstatic->type_code == 'AC_INT') {
13841 $iconClass = 'fa fa-shipping-fast';
13842 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
13843 $iconClass = 'fa fa-robot';
13844 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
13845 $iconClass = 'fa fa-robot';
13846 }
13847 }
13848 }
13849
13850 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
13851 return $out;
13852}
13853
13861{
13862 global $conf, $db;
13863
13864 $documents = array();
13865
13866 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
13867 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
13868 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
13869 //$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
13870 $sql .= ' ORDER BY ecm.position ASC';
13871
13872 $resql = $db->query($sql);
13873 if ($resql) {
13874 if ($db->num_rows($resql)) {
13875 while ($obj = $db->fetch_object($resql)) {
13876 $documents[$obj->id] = $obj;
13877 }
13878 }
13879 }
13880
13881 return $documents;
13882}
13883
13884
13902function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
13903{
13904 global $user, $conf;
13905 global $form;
13906
13907 global $param, $massactionbutton;
13908
13909 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
13910
13911 // Check parameters
13912 if (!is_object($filterobj) && !is_object($objcon)) {
13913 dol_print_error(null, 'BadParameter');
13914 }
13915
13916 $histo = array();
13917 '@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';
13918
13919 $numaction = 0;
13920 $now = dol_now();
13921
13922 $sortfield_list = explode(',', $sortfield);
13923 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
13924 $sortfield_new_list = array();
13925 foreach ($sortfield_list as $sortfield_value) {
13926 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
13927 }
13928 $sortfield_new = implode(',', $sortfield_new_list);
13929
13930 $sql = null;
13931 $sql2 = null;
13932
13933 if (isModEnabled('agenda')) {
13934 // Search histo on actioncomm
13935 if (is_object($objcon) && $objcon->id > 0) {
13936 $sql = "SELECT DISTINCT a.id, a.label as label,";
13937 } else {
13938 $sql = "SELECT a.id, a.label as label,";
13939 }
13940 $sql .= " a.datep as dp,";
13941 $sql .= " a.note as message,";
13942 $sql .= " a.datep2 as dp2,";
13943 $sql .= " a.percent as percent, 'action' as type,";
13944 $sql .= " a.fk_element, a.elementtype,";
13945 $sql .= " a.fk_contact,";
13946 $sql .= " a.email_from as msg_from,";
13947 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
13948 $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";
13949 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13950 $sql .= ", sp.lastname, sp.firstname";
13951 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13952 $sql .= ", m.lastname, m.firstname";
13953 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13954 $sql .= ", o.ref";
13955 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13956 $sql .= ", o.ref";
13957 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13958 $sql .= ", o.ref";
13959 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13960 $sql .= ", o.ref";
13961 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13962 $sql .= ", o.ref";
13963 }
13964 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
13965 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
13966 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
13967
13968 $force_filter_contact = $filterobj instanceof User;
13969
13970 if (is_object($objcon) && $objcon->id > 0) {
13971 $force_filter_contact = true;
13972 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
13973 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
13974 }
13975
13976 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13977 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
13978 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
13979 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
13980 $sql .= " ON er.resource_type = 'dolresource'";
13981 $sql .= " AND er.element_id = a.id";
13982 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
13983 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13984 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
13985 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13986 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
13987 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13988 $sql .= ", ".MAIN_DB_PREFIX."product as o";
13989 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13990 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
13991 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13992 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
13993 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13994 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
13995 }
13996
13997 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
13998 if (!$force_filter_contact) {
13999 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14000 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14001 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14002 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14003 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14004 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14005 if ($filterobj->id) {
14006 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14007 }
14008 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14009 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14010 if ($filterobj->id) {
14011 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14012 }
14013 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14014 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14015 if ($filterobj->id) {
14016 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14017 }
14018 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14019 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14020 if ($filterobj->id) {
14021 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14022 }
14023 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14024 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14025 if ($filterobj->id) {
14026 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14027 }
14028 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14029 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14030 if ($filterobj->id) {
14031 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14032 }
14033 }
14034 } else {
14035 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14036 }
14037
14038 // Condition on actioncode
14039 if (!empty($actioncode)) {
14040 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14041 if ($actioncode == 'AC_NON_AUTO') {
14042 $sql .= " AND c.type != 'systemauto'";
14043 } elseif ($actioncode == 'AC_ALL_AUTO') {
14044 $sql .= " AND c.type = 'systemauto'";
14045 } else {
14046 if ($actioncode == 'AC_OTH') {
14047 $sql .= " AND c.type != 'systemauto'";
14048 } elseif ($actioncode == 'AC_OTH_AUTO') {
14049 $sql .= " AND c.type = 'systemauto'";
14050 }
14051 }
14052 } else {
14053 if ($actioncode == 'AC_NON_AUTO') {
14054 $sql .= " AND c.type != 'systemauto'";
14055 } elseif ($actioncode == 'AC_ALL_AUTO') {
14056 $sql .= " AND c.type = 'systemauto'";
14057 } else {
14058 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14059 }
14060 }
14061 }
14062 if ($donetodo == 'todo') {
14063 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14064 } elseif ($donetodo == 'done') {
14065 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14066 }
14067 if (is_array($filters) && $filters['search_agenda_label']) {
14068 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14069 }
14070 }
14071
14072 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14073 if (isModEnabled('mailing') && !empty($objcon->email)
14074 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14075 $langs->load("mails");
14076
14077 $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";
14078 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14079 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14080 $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
14081 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14082 $sql2 .= ", '' as lastname, '' as firstname";
14083 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14084 $sql2 .= ", '' as lastname, '' as firstname";
14085 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14086 $sql2 .= ", '' as ref";
14087 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14088 $sql2 .= ", '' as ref";
14089 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14090 $sql2 .= ", '' as ref";
14091 }
14092 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14093 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14094 $sql2 .= " AND mc.statut = 1";
14095 $sql2 .= " AND u.rowid = m.fk_user_valid";
14096 $sql2 .= " AND mc.fk_mailing=m.rowid";
14097 }
14098
14099 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14100 if (!empty($sql) && !empty($sql2)) {
14101 $sql = $sql." UNION ".$sql2;
14102 } elseif (empty($sql) && !empty($sql2)) {
14103 $sql = $sql2;
14104 }
14105
14106 //TODO Add navigation with this limits...
14107 $offset = 0;
14108 $limit = 1000;
14109
14110 // Complete request and execute it with limit
14111 $sql .= $db->order($sortfield_new, $sortorder);
14112 if ($limit) {
14113 $sql .= $db->plimit($limit + 1, $offset);
14114 }
14115
14116 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14117 $resql = $db->query($sql);
14118 if ($resql) {
14119 $i = 0;
14120 $num = $db->num_rows($resql);
14121
14122 $imaxinloop = ($limit ? min($num, $limit) : $num);
14123 while ($i < $imaxinloop) {
14124 $obj = $db->fetch_object($resql);
14125
14126 if ($obj->type == 'action') {
14127 $contactaction = new ActionComm($db);
14128 $contactaction->id = $obj->id;
14129 $result = $contactaction->fetchResources();
14130 if ($result < 0) {
14131 dol_print_error($db);
14132 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14133 }
14134
14135 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14136 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14137 $tododone = '';
14138 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14139 $tododone = 'todo';
14140 }
14141
14142 $histo[$numaction] = array(
14143 'type' => $obj->type,
14144 'tododone' => $tododone,
14145 'id' => $obj->id,
14146 'datestart' => $db->jdate($obj->dp),
14147 'dateend' => $db->jdate($obj->dp2),
14148 'note' => $obj->label,
14149 'message' => dol_htmlentitiesbr($obj->message),
14150 'percent' => $obj->percent,
14151
14152 'userid' => $obj->user_id,
14153 'login' => $obj->user_login,
14154 'userfirstname' => $obj->user_firstname,
14155 'userlastname' => $obj->user_lastname,
14156 'userphoto' => $obj->user_photo,
14157 'msg_from' => $obj->msg_from,
14158
14159 'contact_id' => $obj->fk_contact,
14160 'socpeopleassigned' => $contactaction->socpeopleassigned,
14161 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14162 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14163 'fk_element' => $obj->fk_element,
14164 'elementtype' => $obj->elementtype,
14165 // Type of event
14166 'acode' => $obj->acode,
14167 'alabel' => $obj->alabel,
14168 'libelle' => $obj->alabel, // deprecated
14169 'apicto' => $obj->apicto
14170 );
14171 } else {
14172 $histo[$numaction] = array(
14173 'type' => $obj->type,
14174 'tododone' => 'done',
14175 'id' => $obj->id,
14176 'datestart' => $db->jdate($obj->dp),
14177 'dateend' => $db->jdate($obj->dp2),
14178 'note' => $obj->label,
14179 'message' => dol_htmlentitiesbr($obj->message),
14180 'percent' => $obj->percent,
14181 'acode' => $obj->acode,
14182
14183 'userid' => $obj->user_id,
14184 'login' => $obj->user_login,
14185 'userfirstname' => $obj->user_firstname,
14186 'userlastname' => $obj->user_lastname,
14187 'userphoto' => $obj->user_photo
14188 );
14189 }
14190
14191 $numaction++;
14192 $i++;
14193 }
14194 } else {
14195 dol_print_error($db);
14196 }
14197 }
14198
14199 // Set $out to show events
14200 $out = '';
14201
14202 if (!isModEnabled('agenda')) {
14203 $langs->loadLangs(array("admin", "errors"));
14204 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14205 }
14206
14207 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14208 $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
14209
14210 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14211 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14212 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14213 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14214
14215 $formactions = new FormActions($db);
14216
14217 $actionstatic = new ActionComm($db);
14218 $userstatic = new User($db);
14219 $contactstatic = new Contact($db);
14220 $userGetNomUrlCache = array();
14221 $contactGetNomUrlCache = array();
14222
14223 $out .= '<div class="filters-container" >';
14224 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14225 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14226
14227 if ($objcon && get_class($objcon) == 'Contact' &&
14228 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14229 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14230 } else {
14231 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14232 }
14233 if (($filterobj && get_class($filterobj) == 'Societe')) {
14234 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14235 } else {
14236 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14237 }
14238
14239 $out .= "\n";
14240
14241 $out .= '<div class="div-table-responsive-no-min">';
14242 $out .= '<table class="noborder borderbottom centpercent">';
14243
14244 $out .= '<tr class="liste_titre">';
14245
14246 // Action column
14247 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14248 $out .= '<th class="liste_titre width50 middle">';
14249 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14250 $out .= $searchpicto;
14251 $out .= '</th>';
14252 }
14253
14254 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
14255
14256 $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14257 if ($donetodo) {
14258 $out .= '<th class="liste_titre"></th>';
14259 }
14260 $out .= '<th class="liste_titre">';
14261 $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14262 //$out .= img_picto($langs->trans("Type"), 'type');
14263 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', !getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : -1, 0, 0, 1, 'minwidth200imp');
14264 $out .= '</th>';
14265 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14266 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14267 $out .= '</th>';
14268
14269 // Action column
14270 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14271 $out .= '<th class="liste_titre width50 middle">';
14272 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14273 $out .= $searchpicto;
14274 $out .= '</th>';
14275 }
14276
14277 $out .= '</tr>';
14278
14279
14280 $out .= '</table>';
14281
14282 $out .= '</form>';
14283 $out .= '</div>';
14284
14285 $out .= "\n";
14286
14287 $out .= '<ul class="timeline">';
14288
14289 if ($donetodo) {
14290 $tmp = '';
14291 if ($filterobj instanceof Societe) {
14292 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14293 }
14294 if ($filterobj instanceof User) {
14295 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14296 }
14297 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14298 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14299 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14300 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14301 if ($filterobj instanceof Societe) {
14302 $tmp .= '</a>';
14303 }
14304 if ($filterobj instanceof User) {
14305 $tmp .= '</a>';
14306 }
14307 $out .= getTitleFieldOfList($tmp);
14308 }
14309
14310 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14311 $caction = new CActionComm($db);
14312 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14313
14314 $actualCycleDate = false;
14315
14316 // Loop on each event to show it
14317 foreach ($histo as $key => $value) {
14318 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14319
14320 $actionstatic->type_picto = $histo[$key]['apicto'];
14321 $actionstatic->type_code = $histo[$key]['acode'];
14322
14323 $labeltype = $actionstatic->type_code;
14324 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14325 $labeltype = 'AC_OTH';
14326 }
14327 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14328 $labeltype = $langs->trans("Message");
14329 } else {
14330 if (!empty($arraylist[$labeltype])) {
14331 $labeltype = $arraylist[$labeltype];
14332 }
14333 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14334 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14335 }
14336 }
14337
14338 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14339
14340 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14341
14342 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14343 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14344 $out .= '<!-- timeline time label -->';
14345 $out .= '<li class="time-label">';
14346 $out .= '<span class="timeline-badge-date">';
14347 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14348 $out .= '</span>';
14349 $out .= '</li>';
14350 $out .= '<!-- /.timeline-label -->';
14351 }
14352
14353
14354 $out .= '<!-- timeline item -->'."\n";
14355 $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
14356
14357 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14358 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14359 //$out .= $timelineicon;
14360 //var_dump($timelineicon);
14361 $out .= $typeicon;
14362
14363 $out .= '<div class="timeline-item">'."\n";
14364
14365 $out .= '<span class="time timeline-header-action2">';
14366
14367 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14368 $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").' ';
14369 $out .= $histo[$key]['id'];
14370 $out .= '</a> ';
14371 } else {
14372 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14373 }
14374
14375 if ($user->hasRight('agenda', 'allactions', 'create') ||
14376 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14377 $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).'">';
14378 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14379 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14380 $out .= '</a>';
14381 }
14382
14383 $out .= '</span>';
14384
14385 // Date
14386 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14387 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14388 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14389 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14390 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14391 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14392 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14393 } else {
14394 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14395 }
14396 }
14397 $late = 0;
14398 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14399 $late = 1;
14400 }
14401 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14402 $late = 1;
14403 }
14404 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14405 $late = 1;
14406 }
14407 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14408 $late = 1;
14409 }
14410 if ($late) {
14411 $out .= img_warning($langs->trans("Late")).' ';
14412 }
14413 $out .= "</span></span>\n";
14414
14415 // Ref
14416 $out .= '<h3 class="timeline-header">';
14417
14418 // Author of event
14419 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14420 if ($histo[$key]['userid'] > 0) {
14421 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14422 $userstatic->fetch($histo[$key]['userid']);
14423 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14424 }
14425 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14426 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14427 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14428 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14429 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14430 } else {
14431 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14432 }
14433 }
14434 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14435 }
14436 $out .= '</div>';
14437
14438 // Title
14439 $out .= ' <div class="messaging-title inline-block">';
14440 //$out .= $actionstatic->getTypePicto();
14441 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14442 $out .= $labeltype.' - ';
14443 }
14444
14445 $libelle = '';
14446 if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14447 $out .= $langs->trans('TicketNewMessage');
14448 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14449 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14450 } elseif (isset($histo[$key]['type'])) {
14451 if ($histo[$key]['type'] == 'action') {
14452 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14453 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14454 $libelle = $histo[$key]['note'];
14455 $actionstatic->id = $histo[$key]['id'];
14456 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14457 } elseif ($histo[$key]['type'] == 'mailing') {
14458 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14459 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14460 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14461 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14462 } else {
14463 $libelle .= $histo[$key]['note'];
14464 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14465 }
14466 }
14467
14468 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14469 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14470 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14471 } else {
14472 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14473 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14474 }
14475 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14476 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14477 }
14478 if ($link) {
14479 $out .= ' - '.$link;
14480 }
14481 }
14482
14483 $out .= '</div>';
14484
14485 $out .= '</h3>';
14486
14487 // Message
14488 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14489 && $actionstatic->code != 'AC_TICKET_CREATE'
14490 && $actionstatic->code != 'AC_TICKET_MODIFY'
14491 ) {
14492 $out .= '<div class="timeline-body wordbreak small">';
14493 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14494 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14495 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14496 $out .= '<div class="readmore-block --closed" >';
14497 $out .= ' <div class="readmore-block__excerpt">';
14498 $out .= dolPrintHTML($truncatedText);
14499 $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>';
14500 $out .= ' </div>';
14501 $out .= ' <div class="readmore-block__full-text" >';
14502 $out .= dolPrintHTML($histo[$key]['message']);
14503 $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>';
14504 $out .= ' </div>';
14505 $out .= '</div>';
14506 } else {
14507 $out .= dolPrintHTML($histo[$key]['message']);
14508 }
14509
14510 $out .= '</div>';
14511 }
14512
14513 // Timeline footer
14514 $footer = '';
14515
14516 // Contact for this action
14517 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14518 $contactList = '';
14519 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14520 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14521 $contact = new Contact($db);
14522 $contact->fetch($cid);
14523 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14524 } else {
14525 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14526 }
14527
14528 if ($contact) {
14529 $contactList .= !empty($contactList) ? ', ' : '';
14530 $contactList .= $contact->getNomUrl(1);
14531 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14532 if (!empty($contact->phone_pro)) {
14533 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14534 }
14535 }
14536 }
14537 }
14538
14539 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14540 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14541 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14542 $contact = new Contact($db);
14543 $result = $contact->fetch($histo[$key]['contact_id']);
14544 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14545 } else {
14546 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14547 $result = ($contact instanceof Contact) ? $contact->id : 0;
14548 }
14549
14550 if ($result > 0) {
14551 $footer .= $contact->getNomUrl(1);
14552 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14553 if (!empty($contact->phone_pro)) {
14554 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14555 }
14556 }
14557 }
14558 }
14559
14560 $documents = getActionCommEcmList($actionstatic);
14561 if (!empty($documents)) {
14562 $footer .= '<div class="timeline-documents-container">';
14563 foreach ($documents as $doc) {
14564 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14565 $footer .= ' data-id="'.$doc->id.'" ';
14566 $footer .= ' data-path="'.$doc->filepath.'"';
14567 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14568 $footer .= '>';
14569
14570 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14571 $mime = dol_mimetype($filePath);
14572 $file = $actionstatic->id.'/'.$doc->filename;
14573 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14574 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14575 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14576
14577 $mimeAttr = ' mime="'.$mime.'" ';
14578 $class = '';
14579 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14580 $class .= ' documentpreview';
14581 }
14582
14583 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14584 $footer .= img_mime($filePath).' '.$doc->filename;
14585 $footer .= '</a>';
14586
14587 $footer .= '</span>';
14588 }
14589 $footer .= '</div>';
14590 }
14591
14592 if (!empty($footer)) {
14593 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14594 }
14595
14596 $out .= '</div>'."\n"; // end timeline-item
14597
14598 $out .= '</li>';
14599 $out .= '<!-- END timeline item -->';
14600 }
14601
14602 $out .= "</ul>\n";
14603
14604 $out .= '<script>
14605 jQuery(document).ready(function () {
14606 $(document).on("click", "[data-read-more-action]", function(e){
14607 let readMoreBloc = $(this).closest(".readmore-block");
14608 if(readMoreBloc.length > 0){
14609 e.preventDefault();
14610 if($(this).attr("data-read-more-action") == "close"){
14611 readMoreBloc.addClass("--closed").removeClass("--open");
14612 $("html, body").animate({
14613 scrollTop: readMoreBloc.offset().top - 200
14614 }, 100);
14615 }else{
14616 readMoreBloc.addClass("--open").removeClass("--closed");
14617 }
14618 }
14619 });
14620 });
14621 </script>';
14622
14623
14624 if (empty($histo)) {
14625 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14626 }
14627 }
14628
14629 if ($noprint) {
14630 return $out;
14631 } else {
14632 print $out;
14633 }
14634}
14635
14646function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
14647{
14648 $m = array();
14649 if ($hourTime === 'getpost') {
14650 $hour = GETPOSTINT($prefix . 'hour');
14651 $minute = GETPOSTINT($prefix . 'min');
14652 $second = GETPOSTINT($prefix . 'sec');
14653 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
14654 $hour = intval($m[1]);
14655 $minute = intval($m[2]);
14656 $second = intval($m[3]);
14657 } else {
14658 $hour = $minute = $second = 0;
14659 }
14660 // normalize out of range values
14661 $hour = min($hour, 23);
14662 $minute = min($minute, 59);
14663 $second = min($second, 59);
14664 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
14665}
14666
14678function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14679{
14680 if ($timestamp === null) {
14681 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14682 }
14683 $TParam = array(
14684 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14685 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14686 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14687 );
14688 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14689 $TParam = array_merge($TParam, array(
14690 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14691 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
14692 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
14693 ));
14694 }
14695
14696 return '&' . http_build_query($TParam);
14697}
14698
14718function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14719{
14720 global $conf, $db, $langs, $hookmanager;
14721 global $action, $object;
14722
14723 if (!is_object($langs)) {
14724 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14725 $langs = new Translate('', $conf);
14726 $langs->setDefaultLang();
14727 }
14728
14729 $langs->load("errors");
14730
14731 if ($printheader) {
14732 if (function_exists("llxHeader")) {
14733 llxHeader('');
14734 } elseif (function_exists("llxHeaderVierge")) {
14735 llxHeaderVierge('');
14736 }
14737 }
14738
14739 print '<div class="error">';
14740 if (empty($message)) {
14741 print $langs->trans("ErrorRecordNotFound");
14742 } else {
14743 print $langs->trans($message);
14744 }
14745 print '</div>';
14746 print '<br>';
14747
14748 if (empty($showonlymessage)) {
14749 if (empty($hookmanager)) {
14750 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14751 $hookmanager = new HookManager($db);
14752 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
14753 $hookmanager->initHooks(array('main'));
14754 }
14755
14756 $parameters = array('message' => $message, 'params' => $params);
14757 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14758 print $hookmanager->resPrint;
14759 }
14760
14761 if ($printfooter && function_exists("llxFooter")) {
14762 llxFooter();
14763 }
14764 exit(0);
14765}
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:2043