dolibarr 21.0.0-alpha
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2022 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12 * Copyright (C) 2013-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2019-2023 Thibault Foucart <support@ptibogxiv.net>
18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21 * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
24 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 *
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 3 of the License, or
30 * (at your option) any later version.
31 *
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with this program. If not, see <https://www.gnu.org/licenses/>.
39 * or see https://www.gnu.org/
40 */
41
48//include_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
244function getDolUserString($key, $default = '', $tmpuser = null)
245{
246 if (empty($tmpuser)) {
247 global $user;
248 $tmpuser = $user;
249 }
250
251 return (string) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
252}
253
262function getDolUserInt($key, $default = 0, $tmpuser = null)
263{
264 if (empty($tmpuser)) {
265 global $user;
266 $tmpuser = $user;
267 }
268
269 return (int) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
270}
271
272
282define(
283 'MODULE_MAPPING',
284 array(
285 // Map deprecated names to new names
286 'adherent' => 'member', // Has new directory
287 'member_type' => 'adherent_type', // No directory, but file called adherent_type
288 'banque' => 'bank', // Has new directory
289 'contrat' => 'contract', // Has new directory
290 'entrepot' => 'stock', // Has new directory
291 'projet' => 'project', // Has new directory
292 'categorie' => 'category', // Has old directory
293 'commande' => 'order', // Has old directory
294 'expedition' => 'shipping', // Has old directory
295 'facture' => 'invoice', // Has old directory
296 'fichinter' => 'intervention', // Has old directory
297 'ficheinter' => 'intervention', // Backup for 'fichinter'
298 'propale' => 'propal', // Has old directory
299 'socpeople' => 'contact', // Has old directory
300 'fournisseur' => 'supplier', // Has old directory
301
302 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
303 'product_price' => 'productprice', // NO directory
304 'product_fournisseur_price' => 'productsupplierprice', // NO directory
305 )
306);
307
314function isModEnabled($module)
315{
316 global $conf;
317
318 // Fix old names (map to new names)
319 $arrayconv = MODULE_MAPPING;
320 $arrayconvbis = array_flip(MODULE_MAPPING);
321
322 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
323 // Special cases: both use the same module.
324 $arrayconv['supplier_order'] = 'fournisseur';
325 $arrayconv['supplier_invoice'] = 'fournisseur';
326 }
327 // Special case.
328 // @TODO Replace isModEnabled('delivery_note') with
329 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
330 if ($module == 'delivery_note') {
331 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
332 return false;
333 } else {
334 $module = 'shipping';
335 }
336 }
337
338 $module_alt = $module;
339 if (!empty($arrayconv[$module])) {
340 $module_alt = $arrayconv[$module];
341 }
342 $module_bis = $module;
343 if (!empty($arrayconvbis[$module])) {
344 $module_bis = $arrayconvbis[$module];
345 }
346
347 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
348 //return !empty($conf->$module->enabled);
349}
350
357function isDolTms($timestamp)
358{
359 if ($timestamp === '') {
360 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"], LOG_NOTICE);
361 return false;
362 }
363 if (is_null($timestamp) || !is_numeric($timestamp)) {
364 return false;
365 }
366
367 return true;
368}
369
381function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
382{
383 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
384
385 $class = 'DoliDB'.ucfirst($type);
386 $db = new $class($type, $host, $user, $pass, $name, $port);
387 return $db;
388}
389
407function getEntity($element, $shared = 1, $currentobject = null)
408{
409 global $conf, $mc, $hookmanager, $object, $action, $db;
410
411 if (!is_object($hookmanager)) {
412 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
413 $hookmanager = new HookManager($db);
414 }
415
416 // fix different element names (France to English)
417 switch ($element) {
418 case 'projet':
419 $element = 'project';
420 break;
421 case 'contrat':
422 $element = 'contract';
423 break; // "/contrat/class/contrat.class.php"
424 case 'order_supplier':
425 $element = 'supplier_order';
426 break; // "/fourn/class/fournisseur.commande.class.php"
427 case 'invoice_supplier':
428 $element = 'supplier_invoice';
429 break; // "/fourn/class/fournisseur.facture.class.php"
430 }
431
432 if (is_object($mc)) {
433 $out = $mc->getEntity($element, $shared, $currentobject);
434 } else {
435 $out = '';
436 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
437 if (in_array($element, $addzero)) {
438 $out .= '0,';
439 }
440 $out .= ((int) $conf->entity);
441 }
442
443 // Manipulate entities to query on the fly
444 $parameters = array(
445 'element' => $element,
446 'shared' => $shared,
447 'object' => $object,
448 'currentobject' => $currentobject,
449 'out' => $out
450 );
451 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
452
453 if (is_numeric($reshook)) {
454 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
455 $out .= ','.$hookmanager->resPrint; // add
456 } elseif ($reshook == 1) {
457 $out = $hookmanager->resPrint; // replace
458 }
459 }
460
461 return $out;
462}
463
470function setEntity($currentobject)
471{
472 global $conf, $mc;
473
474 if (is_object($mc) && method_exists($mc, 'setEntity')) {
475 return $mc->setEntity($currentobject);
476 } else {
477 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
478 }
479}
480
487function isASecretKey($keyname)
488{
489 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
490}
491
492
499function num2Alpha($n)
500{
501 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
502 $r = chr($n % 26 + 0x41) . $r;
503 }
504 return $r;
505}
506
507
524function getBrowserInfo($user_agent)
525{
526 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
527
528 $name = 'unknown';
529 $version = '';
530 $os = 'unknown';
531 $phone = '';
532
533 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
534
535 $detectmobile = new Mobile_Detect(null, $user_agent);
536 $tablet = $detectmobile->isTablet();
537
538 if ($detectmobile->isMobile()) {
539 $phone = 'unknown';
540
541 // If phone/smartphone, we set phone os name.
542 if ($detectmobile->is('AndroidOS')) {
543 $os = $phone = 'android';
544 } elseif ($detectmobile->is('BlackBerryOS')) {
545 $os = $phone = 'blackberry';
546 } elseif ($detectmobile->is('iOS')) {
547 $os = 'ios';
548 $phone = 'iphone';
549 } elseif ($detectmobile->is('PalmOS')) {
550 $os = $phone = 'palm';
551 } elseif ($detectmobile->is('SymbianOS')) {
552 $os = 'symbian';
553 } elseif ($detectmobile->is('webOS')) {
554 $os = 'webos';
555 } elseif ($detectmobile->is('MaemoOS')) {
556 $os = 'maemo';
557 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
558 $os = 'windows';
559 }
560 }
561
562 // OS
563 if (preg_match('/linux/i', $user_agent)) {
564 $os = 'linux';
565 } elseif (preg_match('/macintosh/i', $user_agent)) {
566 $os = 'macintosh';
567 } elseif (preg_match('/windows/i', $user_agent)) {
568 $os = 'windows';
569 }
570
571 // Name
572 $reg = array();
573 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
574 $name = 'firefox';
575 $version = empty($reg[2]) ? '' : $reg[2];
576 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
577 $name = 'edge';
578 $version = empty($reg[2]) ? '' : $reg[2];
579 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
580 $name = 'chrome';
581 $version = empty($reg[2]) ? '' : $reg[2];
582 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
583 // we can have 'chrome (Mozilla...) chrome x.y' in one string
584 $name = 'chrome';
585 } elseif (preg_match('/iceweasel/i', $user_agent)) {
586 $name = 'iceweasel';
587 } elseif (preg_match('/epiphany/i', $user_agent)) {
588 $name = 'epiphany';
589 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
590 $name = 'safari';
591 $version = empty($reg[2]) ? '' : $reg[2];
592 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
593 // Safari is often present in string for mobile but its not.
594 $name = 'opera';
595 $version = empty($reg[2]) ? '' : $reg[2];
596 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
597 $name = 'ie';
598 $version = end($reg);
599 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
600 // MS products at end
601 $name = 'ie';
602 $version = end($reg);
603 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
604 // MS products at end
605 $name = 'lynxlinks';
606 $version = empty($reg[3]) ? '' : $reg[3];
607 }
608
609 if ($tablet) {
610 $layout = 'tablet';
611 } elseif ($phone) {
612 $layout = 'phone';
613 } else {
614 $layout = 'classic';
615 }
616
617 return array(
618 'browsername' => $name,
619 'browserversion' => $version,
620 'browseros' => $os,
621 'browserua' => $user_agent,
622 'layout' => $layout, // tablet, phone, classic
623 'phone' => $phone, // deprecated
624 'tablet' => $tablet // deprecated
625 );
626}
627
633function dol_shutdown()
634{
635 global $db;
636 $disconnectdone = false;
637 $depth = 0;
638 if (is_object($db) && !empty($db->connected)) {
639 $depth = $db->transaction_opened;
640 $disconnectdone = $db->close();
641 }
642 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));
643}
644
654function GETPOSTISSET($paramname)
655{
656 $isset = false;
657
658 $relativepathstring = $_SERVER["PHP_SELF"];
659 // Clean $relativepathstring
660 if (constant('DOL_URL_ROOT')) {
661 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
662 }
663 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
664 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
665 //var_dump($relativepathstring);
666 //var_dump($user->default_values);
667
668 // Code for search criteria persistence.
669 // Retrieve values if restore_lastsearch_values
670 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
671 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
672 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
673 if (is_array($tmp)) {
674 foreach ($tmp as $key => $val) {
675 if ($key == $paramname) { // We are on the requested parameter
676 $isset = true;
677 break;
678 }
679 }
680 }
681 }
682 // If there is saved contextpage, limit, page or mode
683 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
684 $isset = true;
685 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
686 $isset = true;
687 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
688 $isset = true;
689 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
690 $isset = true;
691 }
692 } else {
693 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
694 }
695
696 return $isset;
697}
698
707function GETPOSTISARRAY($paramname, $method = 0)
708{
709 // for $method test need return the same $val as GETPOST
710 if (empty($method)) {
711 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
712 } elseif ($method == 1) {
713 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
714 } elseif ($method == 2) {
715 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
716 } elseif ($method == 3) {
717 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
718 } else {
719 $val = 'BadFirstParameterForGETPOST';
720 }
721
722 return is_array($val);
723}
724
754function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
755{
756 global $mysoc, $user, $conf;
757
758 if (empty($paramname)) { // Explicit test for null for phan.
759 return 'BadFirstParameterForGETPOST';
760 }
761 if (empty($check)) {
762 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);
763 // Enable this line to know who call the GETPOST with '' $check parameter.
764 //var_dump(debug_backtrace()[0]);
765 }
766
767 if (empty($method)) {
768 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
769 } elseif ($method == 1) {
770 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
771 } elseif ($method == 2) {
772 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
773 } elseif ($method == 3) {
774 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
775 } else {
776 return 'BadThirdParameterForGETPOST';
777 }
778
779 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
780
781 if (empty($method) || $method == 3 || $method == 4) {
782 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
783 // Clean $relativepathstring
784 if (constant('DOL_URL_ROOT')) {
785 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
786 }
787 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
788 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
789 //var_dump($relativepathstring);
790 //var_dump($user->default_values);
791
792 // Code for search criteria persistence.
793 // Retrieve saved values if restore_lastsearch_values is set
794 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
795 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
796 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
797 if (is_array($tmp)) {
798 foreach ($tmp as $key => $val) {
799 if ($key == $paramname) { // We are on the requested parameter
800 $out = $val;
801 break;
802 }
803 }
804 }
805 }
806 // If there is saved contextpage, page or limit
807 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
808 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
809 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
810 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
811 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
812 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
813 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
814 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
815 }
816 } elseif (!isset($_GET['sortfield'])) {
817 // Else, retrieve default values if we are not doing a sort
818 // 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
819 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
820 // Search default value from $object->field
821 global $object;
822 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
823 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
824 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
825 $out = $object->fields[$paramname]['default'];
826 }
827 }
828 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
829 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
830 // Now search in setup to overwrite default values
831 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
832 if (isset($user->default_values[$relativepathstring]['createform'])) {
833 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
834 $qualified = 0;
835 if ($defkey != '_noquery_') {
836 $tmpqueryarraytohave = explode('&', $defkey);
837 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
838 $foundintru = 0;
839 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
840 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
841 $foundintru = 1;
842 }
843 }
844 if (!$foundintru) {
845 $qualified = 1;
846 }
847 //var_dump($defkey.'-'.$qualified);
848 } else {
849 $qualified = 1;
850 }
851
852 if ($qualified) {
853 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
854 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
855 break;
856 }
857 }
858 }
859 }
860 }
861 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
862 // Management of default search_filters and sort order
863 if (!empty($user->default_values)) {
864 // $user->default_values defined from menu 'Setup - Default values'
865 //var_dump($user->default_values[$relativepathstring]);
866 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
867 // Sorted on which fields ? ASC or DESC ?
868 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
869 // Even if paramname is sortfield, data are stored into ['sortorder...']
870 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
871 $qualified = 0;
872 if ($defkey != '_noquery_') {
873 $tmpqueryarraytohave = explode('&', $defkey);
874 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
875 $foundintru = 0;
876 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
877 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
878 $foundintru = 1;
879 }
880 }
881 if (!$foundintru) {
882 $qualified = 1;
883 }
884 //var_dump($defkey.'-'.$qualified);
885 } else {
886 $qualified = 1;
887 }
888
889 if ($qualified) {
890 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
891 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
892 if ($out) {
893 $out .= ', ';
894 }
895 if ($paramname == 'sortfield') {
896 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
897 }
898 if ($paramname == 'sortorder') {
899 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
900 }
901 }
902 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
903 }
904 }
905 }
906 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
907 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
908 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
909 continue;
910 }
911 $qualified = 0;
912 if ($defkey != '_noquery_') {
913 $tmpqueryarraytohave = explode('&', $defkey);
914 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
915 $foundintru = 0;
916 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
917 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
918 $foundintru = 1;
919 }
920 }
921 if (!$foundintru) {
922 $qualified = 1;
923 }
924 //var_dump($defkey.'-'.$qualified);
925 } else {
926 $qualified = 1;
927 }
928
929 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
930 // We must keep $_POST and $_GET here
931 if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
932 // We made a search from quick search menu, do we still use default filter ?
933 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
934 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
935 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
936 }
937 } else {
938 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
939 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
940 }
941 break;
942 }
943 }
944 }
945 }
946 }
947 }
948 }
949 }
950
951 // 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)
952 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
953 // 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.
954 '@phan-var-force string $paramname';
955 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
956 $reg = array();
957 $maxloop = 20;
958 $loopnb = 0; // Protection against infinite loop
959 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.
960 $loopnb++;
961 $newout = '';
962
963 if ($reg[1] == 'DAY') {
964 $tmp = dol_getdate(dol_now(), true);
965 $newout = $tmp['mday'];
966 } elseif ($reg[1] == 'MONTH') {
967 $tmp = dol_getdate(dol_now(), true);
968 $newout = $tmp['mon'];
969 } elseif ($reg[1] == 'YEAR') {
970 $tmp = dol_getdate(dol_now(), true);
971 $newout = $tmp['year'];
972 } elseif ($reg[1] == 'PREVIOUS_DAY') {
973 $tmp = dol_getdate(dol_now(), true);
974 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
975 $newout = $tmp2['day'];
976 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
977 $tmp = dol_getdate(dol_now(), true);
978 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
979 $newout = $tmp2['month'];
980 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
981 $tmp = dol_getdate(dol_now(), true);
982 $newout = ($tmp['year'] - 1);
983 } elseif ($reg[1] == 'NEXT_DAY') {
984 $tmp = dol_getdate(dol_now(), true);
985 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
986 $newout = $tmp2['day'];
987 } elseif ($reg[1] == 'NEXT_MONTH') {
988 $tmp = dol_getdate(dol_now(), true);
989 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
990 $newout = $tmp2['month'];
991 } elseif ($reg[1] == 'NEXT_YEAR') {
992 $tmp = dol_getdate(dol_now(), true);
993 $newout = ($tmp['year'] + 1);
994 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
995 $newout = $mysoc->country_id;
996 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
997 $newout = $user->id;
998 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
999 $newout = $user->fk_user;
1000 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1001 $newout = $conf->entity;
1002 } else {
1003 $newout = ''; // Key not found, we replace with empty string
1004 }
1005 //var_dump('__'.$reg[1].'__ -> '.$newout);
1006 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1007 }
1008 }
1009
1010 // Check type of variable and make sanitization according to this
1011 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1012 if (!is_array($out) || empty($out)) {
1013 $out = array();
1014 } else {
1015 $tmparray = explode(':', $check);
1016 if (!empty($tmparray[1])) {
1017 $tmpcheck = $tmparray[1];
1018 } else {
1019 $tmpcheck = 'alphanohtml';
1020 }
1021 foreach ($out as $outkey => $outval) {
1022 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1023 }
1024 }
1025 } else {
1026 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1027 // 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
1028 if (strpos($paramname, 'search_') === 0) {
1029 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1030 }
1031
1032 // @phan-suppress-next-line UnknownSanitizeType
1033 $out = sanitizeVal($out, $check, $filter, $options);
1034 }
1035
1036 // Sanitizing for special parameters.
1037 // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1038 if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
1039 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1040 $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.
1041 do {
1042 $oldstringtoclean = $out;
1043 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1044 $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'
1045 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1046 } while ($oldstringtoclean != $out);
1047 }
1048
1049 // Code for search criteria persistence.
1050 // Save data into session if key start with 'search_'
1051 if (empty($method) || $method == 3 || $method == 4) {
1052 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1053 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1054
1055 // We save search key only if $out not empty that means:
1056 // - posted value not empty, or
1057 // - 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).
1058
1059 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1060 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1061 }
1062 }
1063 }
1064
1065 return $out;
1066}
1067
1077function GETPOSTINT($paramname, $method = 0)
1078{
1079 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1080}
1081
1082
1091function GETPOSTFLOAT($paramname, $rounding = '')
1092{
1093 // 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.)
1094 return (float) price2num(GETPOST($paramname), $rounding, 2);
1095}
1096
1097
1108function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1109{
1110 $m = array();
1111 if ($hourTime === 'getpost') {
1112 $hour = GETPOSTINT($prefix . 'hour');
1113 $minute = GETPOSTINT($prefix . 'minute');
1114 $second = GETPOSTINT($prefix . 'second');
1115 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1116 $hour = intval($m[1]);
1117 $minute = intval($m[2]);
1118 $second = intval($m[3]);
1119 } else {
1120 $hour = $minute = $second = 0;
1121 }
1122 // normalize out of range values
1123 $hour = min($hour, 23);
1124 $minute = min($minute, 59);
1125 $second = min($second, 59);
1126 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1127}
1128
1129
1140function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1141{
1142 return sanitizeVal($out, $check, $filter, $options);
1143}
1144
1154function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1155{
1156 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1157 // Check is done after replacement
1158 switch ($check) {
1159 case 'none':
1160 case 'password':
1161 break;
1162 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1163 if (!is_numeric($out)) {
1164 $out = '';
1165 }
1166 break;
1167 case 'intcomma':
1168 if (is_array($out)) {
1169 $out = implode(',', $out);
1170 }
1171 if (preg_match('/[^0-9,-]+/i', $out)) {
1172 $out = '';
1173 }
1174 break;
1175 case 'san_alpha':
1176 $out = filter_var($out, FILTER_SANITIZE_STRING);
1177 break;
1178 case 'email':
1179 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1180 break;
1181 case 'aZ':
1182 if (!is_array($out)) {
1183 $out = trim($out);
1184 if (preg_match('/[^a-z]+/i', $out)) {
1185 $out = '';
1186 }
1187 }
1188 break;
1189 case 'aZ09':
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 'aZ09arobase': // great to sanitize $objecttype parameter
1198 if (!is_array($out)) {
1199 $out = trim($out);
1200 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1201 $out = '';
1202 }
1203 }
1204 break;
1205 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1206 if (!is_array($out)) {
1207 $out = trim($out);
1208 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1209 $out = '';
1210 }
1211 }
1212 break;
1213 case 'alpha': // No html and no ../ and "
1214 case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1215 if (!is_array($out)) {
1216 $out = trim($out);
1217 do {
1218 $oldstringtoclean = $out;
1219 // Remove html tags
1220 $out = dol_string_nohtmltag($out, 0);
1221 // 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).
1222 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1223 // Remove also other dangerous string sequences
1224 // '../' or '..\' is dangerous because it allows dir transversals
1225 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1226 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1227 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1228 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1229 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1230 } while ($oldstringtoclean != $out);
1231 // keep lines feed
1232 }
1233 break;
1234 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'
1235 if (!is_array($out)) {
1236 $out = trim($out);
1237 do {
1238 $oldstringtoclean = $out;
1239 // Decode html entities
1240 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1241 // 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).
1242 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1243 // Remove also other dangerous string sequences
1244 // '../' or '..\' is dangerous because it allows dir transversals
1245 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1246 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1247 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1248 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1249 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1250 } while ($oldstringtoclean != $out);
1251 }
1252 break;
1253 case 'nohtml': // No html
1254 $out = dol_string_nohtmltag($out, 0);
1255 break;
1256 case 'restricthtmlnolink':
1257 case 'restricthtml': // Recommended for most html textarea
1258 case 'restricthtmlallowclass':
1259 case 'restricthtmlallowunvalid':
1260 $out = dol_htmlwithnojs($out, 1, $check);
1261 break;
1262 case 'custom':
1263 if (!empty($out)) {
1264 if (empty($filter)) {
1265 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1266 }
1267 if (is_null($options)) {
1268 $options = 0;
1269 }
1270 $out = filter_var($out, $filter, $options);
1271 }
1272 break;
1273 default:
1274 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1275 $out = GETPOST($out, 'alphanohtml');
1276 break;
1277 }
1278
1279 return $out;
1280}
1281
1282
1283if (!function_exists('dol_getprefix')) {
1294 function dol_getprefix($mode = '')
1295 {
1296 // If prefix is for email (we need to have $conf already loaded for this case)
1297 if ($mode == 'email') {
1298 global $conf;
1299
1300 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1301 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1302 return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1303 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1304 return $_SERVER["SERVER_NAME"];
1305 }
1306 }
1307
1308 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1309 if (!empty($conf->file->instance_unique_id)) {
1310 return sha1('dolibarr'.$conf->file->instance_unique_id);
1311 }
1312
1313 // For backward compatibility when instance_unique_id is not set
1314 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1315 }
1316
1317 // If prefix is for session (no need to have $conf loaded)
1318 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1319 $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
1320
1321 // The recommended value (may be not defined for old versions)
1322 if (!empty($tmp_instance_unique_id)) {
1323 return sha1('dolibarr'.$tmp_instance_unique_id);
1324 }
1325
1326 // For backward compatibility when instance_unique_id is not set
1327 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1328 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1329 } else {
1330 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1331 }
1332 }
1333}
1334
1345function dol_include_once($relpath, $classname = '')
1346{
1347 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']
1348
1349 $fullpath = dol_buildpath($relpath);
1350
1351 if (!file_exists($fullpath)) {
1352 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1353 return false;
1354 }
1355
1356 if (!empty($classname) && !class_exists($classname)) {
1357 return include $fullpath;
1358 } else {
1359 return include_once $fullpath;
1360 }
1361}
1362
1363
1374function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1375{
1376 global $conf;
1377
1378 $path = preg_replace('/^\//', '', $path);
1379
1380 if (empty($type)) { // For a filesystem path
1381 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1382 if (is_array($conf->file->dol_document_root)) {
1383 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1384 if ($key == 'main') {
1385 continue;
1386 }
1387 // if (@file_exists($dirroot.'/'.$path)) {
1388 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1389 $res = $dirroot.'/'.$path;
1390 return $res;
1391 }
1392 }
1393 }
1394 if ($returnemptyifnotfound) {
1395 // Not found into alternate dir
1396 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1397 return '';
1398 }
1399 }
1400 } else {
1401 // For an url path
1402 // We try to get local path of file on filesystem from url
1403 // Note that trying to know if a file on disk exist by forging path on disk from url
1404 // works only for some web server and some setup. This is bugged when
1405 // using proxy, rewriting, virtual path, etc...
1406 $res = '';
1407 if ($type == 1) {
1408 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1409 }
1410 if ($type == 2) {
1411 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1412 }
1413 if ($type == 3) {
1414 $res = DOL_URL_ROOT.'/'.$path;
1415 }
1416
1417 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1418 if ($key == 'main') {
1419 if ($type == 3) {
1420 /*global $dolibarr_main_url_root;*/
1421
1422 // Define $urlwithroot
1423 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1424 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1425 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1426
1427 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1428 }
1429 continue;
1430 }
1431 $regs = array();
1432 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1433 if (!empty($regs[1])) {
1434 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1435 //if (file_exists($dirroot.'/'.$regs[1])) {
1436 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1437 if ($type == 1) {
1438 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1439 }
1440 if ($type == 2) {
1441 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1442 }
1443 if ($type == 3) {
1444 /*global $dolibarr_main_url_root;*/
1445
1446 // Define $urlwithroot
1447 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1448 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1449 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1450
1451 $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
1452 }
1453 break;
1454 }
1455 }
1456 }
1457 }
1458
1459 return $res;
1460}
1461
1472function dol_get_object_properties($obj, $properties = [])
1473{
1474 // Get real properties using get_object_vars() if $properties is empty
1475 if (empty($properties)) {
1476 return get_object_vars($obj);
1477 }
1478
1479 $existingProperties = [];
1480 $realProperties = get_object_vars($obj);
1481
1482 // Get the real or magic property values
1483 foreach ($properties as $property) {
1484 if (array_key_exists($property, $realProperties)) {
1485 // Real property, add the value
1486 $existingProperties[$property] = $obj->{$property};
1487 } elseif (property_exists($obj, $property)) {
1488 // Magic property
1489 $existingProperties[$property] = $obj->{$property};
1490 }
1491 }
1492
1493 return $existingProperties;
1494}
1495
1496
1511function dol_clone($object, $native = 0)
1512{
1513 if ($native == 0) {
1514 // deprecated method, use the method with native = 2 instead
1515 $tmpsavdb = null;
1516 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1517 $tmpsavdb = $object->db;
1518 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1519 }
1520
1521 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1522
1523 if (!empty($tmpsavdb)) {
1524 $object->db = $tmpsavdb;
1525 }
1526 } elseif ($native == 2) {
1527 // recommended method to have a full isolated cloned object
1528 $myclone = new stdClass();
1529 $tmparray = get_object_vars($object); // return only public properties
1530
1531 if (is_array($tmparray)) {
1532 foreach ($tmparray as $propertykey => $propertyval) {
1533 if (is_scalar($propertyval) || is_array($propertyval)) {
1534 $myclone->$propertykey = $propertyval;
1535 }
1536 }
1537 }
1538 } else {
1539 $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)
1540 }
1541
1542 return $myclone;
1543}
1544
1554function dol_size($size, $type = '')
1555{
1556 global $conf;
1557 if (empty($conf->dol_optimize_smallscreen)) {
1558 return $size;
1559 }
1560 if ($type == 'width' && $size > 250) {
1561 return 250;
1562 } else {
1563 return 10;
1564 }
1565}
1566
1567
1579function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1580{
1581 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1582 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1583 // Char '/' and '\' are file delimiters.
1584 // 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
1585 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1586 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1587 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1588 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1589 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1590 $tmp = str_replace('..', '', $tmp);
1591 return $tmp;
1592}
1593
1594
1606function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1607{
1608 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1609 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1610 // 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
1611 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1612 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1613 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1614 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1615 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1616 $tmp = str_replace('..', '', $tmp);
1617 return $tmp;
1618}
1619
1627function dol_sanitizeUrl($stringtoclean, $type = 1)
1628{
1629 // 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)
1630 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1631 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1632 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1633 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1634
1635 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1636 if ($type == 1) {
1637 // removing : should disable links to external url like http:aaa)
1638 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1639 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1640 }
1641
1642 do {
1643 $oldstringtoclean = $stringtoclean;
1644 // removing '&colon' should disable links to external url like http:aaa)
1645 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1646 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1647 } while ($oldstringtoclean != $stringtoclean);
1648
1649 if ($type == 1) {
1650 // removing '//' should disable links to external url like //aaa or http//)
1651 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1652 }
1653
1654 return $stringtoclean;
1655}
1656
1663function dol_sanitizeEmail($stringtoclean)
1664{
1665 do {
1666 $oldstringtoclean = $stringtoclean;
1667 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1668 } while ($oldstringtoclean != $stringtoclean);
1669
1670 return $stringtoclean;
1671}
1672
1681function dol_string_unaccent($str)
1682{
1683 global $conf;
1684
1685 if (is_null($str)) {
1686 return '';
1687 }
1688
1689 if (utf8_check($str)) {
1690 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1691 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1692 return $transliterator->transliterate($str);
1693 }
1694 // See http://www.utf8-chartable.de/
1695 $string = rawurlencode($str);
1696 $replacements = array(
1697 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1698 '%C3%87' => 'C',
1699 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1700 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1701 '%C3%91' => 'N',
1702 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1703 '%C5%A0' => 'S',
1704 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1705 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1706 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1707 '%C3%A7' => 'c',
1708 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1709 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1710 '%C3%B1' => 'n',
1711 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1712 '%C5%A1' => 's',
1713 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1714 '%C3%BD' => 'y', '%C3%BF' => 'y'
1715 );
1716 $string = strtr($string, $replacements);
1717 return rawurldecode($string);
1718 } else {
1719 // See http://www.ascii-code.com/
1720 $string = strtr(
1721 $str,
1722 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1723 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1724 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1725 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1726 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1727 \xF9\xFA\xFB\xFC\xFD\xFF",
1728 "AAAAAAC
1729 EEEEIIIIDN
1730 OOOOOUUUY
1731 aaaaaaceeee
1732 iiiidnooooo
1733 uuuuyy"
1734 );
1735 $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"));
1736 return $string;
1737 }
1738}
1739
1753function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1754{
1755 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1756 if (empty($keepspaces)) {
1757 $forbidden_chars_to_replace[] = " ";
1758 }
1759 $forbidden_chars_to_remove = array();
1760 //$forbidden_chars_to_remove=array("(",")");
1761
1762 if (is_array($badcharstoreplace)) {
1763 $forbidden_chars_to_replace = $badcharstoreplace;
1764 }
1765 if (is_array($badcharstoremove)) {
1766 $forbidden_chars_to_remove = $badcharstoremove;
1767 }
1768
1769 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1770 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1771}
1772
1773
1787function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1788{
1789 if ($removetabcrlf) {
1790 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1791 } else {
1792 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
1793 }
1794}
1795
1804function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1805{
1806 if (is_null($stringtoescape)) {
1807 return '';
1808 }
1809
1810 // escape quotes and backslashes, newlines, etc.
1811 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1812 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1813 if (empty($noescapebackslashn)) {
1814 $substitjs["\n"] = '\\n';
1815 $substitjs['\\'] = '\\\\';
1816 }
1817 if (empty($mode)) {
1818 $substitjs["'"] = "\\'";
1819 $substitjs['"'] = "\\'";
1820 } elseif ($mode == 1) {
1821 $substitjs["'"] = "\\'";
1822 } elseif ($mode == 2) {
1823 $substitjs['"'] = '\\"';
1824 } elseif ($mode == 3) {
1825 $substitjs["'"] = "\\'";
1826 $substitjs['"'] = "\\\"";
1827 }
1828 return strtr($stringtoescape, $substitjs);
1829}
1830
1837function dol_escape_json($stringtoescape)
1838{
1839 return str_replace('"', '\"', $stringtoescape);
1840}
1841
1849function dol_escape_php($stringtoescape, $stringforquotes = 2)
1850{
1851 if (is_null($stringtoescape)) {
1852 return '';
1853 }
1854
1855 if ($stringforquotes == 2) {
1856 return str_replace('"', "'", $stringtoescape);
1857 } elseif ($stringforquotes == 1) {
1858 // We remove the \ char.
1859 // If we allow the \ char, we can have $stringtoescape =
1860 // abc\';phpcodedanger; so the escapement will become
1861 // abc\\';phpcodedanger; and injecting this into
1862 // $a='...' will give $ac='abc\\';phpcodedanger;
1863 $stringtoescape = str_replace('\\', '', $stringtoescape);
1864 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1865 }
1866
1867 return 'Bad parameter for stringforquotes in dol_escape_php';
1868}
1869
1876function dol_escape_xml($stringtoescape)
1877{
1878 return $stringtoescape;
1879}
1880
1888function dolPrintLabel($s)
1889{
1890 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1891}
1892
1900function dolPrintText($s)
1901{
1902 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
1903}
1904
1914function dolPrintHTML($s, $allowiframe = 0)
1915{
1916 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1917}
1918
1926function dolPrintHTMLForAttribute($s)
1927{
1928 // The dol_htmlentitiesbr will convert simple text into html
1929 // The dol_escape_htmltag will escape html chars.
1930 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
1931}
1932
1942function dolPrintHTMLForTextArea($s, $allowiframe = 0)
1943{
1944 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
1945}
1946
1953function dolPrintPassword($s)
1954{
1955 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
1956}
1957
1958
1974function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1975{
1976 if ($noescapetags == 'common') {
1977 $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';
1978 // Add also html5 tags
1979 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
1980 }
1981 if ($cleanalsojavascript) {
1982 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1983 }
1984
1985 // escape quotes and backslashes, newlines, etc.
1986 if ($escapeonlyhtmltags) {
1987 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1988 } else {
1989 $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1990 }
1991 if (!$keepb) {
1992 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
1993 }
1994 if (!$keepn) {
1995 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1996 } elseif ($keepn == -1) {
1997 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
1998 }
1999
2000 if ($escapeonlyhtmltags) {
2001 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2002 } else {
2003 // Escape tags to keep
2004 $tmparrayoftags = array();
2005 if ($noescapetags) {
2006 $tmparrayoftags = explode(',', $noescapetags);
2007 }
2008 if (count($tmparrayoftags)) {
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 $reg = array();
2018 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
2019 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must never have [ ] inside the attribute string
2020 $tmpattributes = str_ireplace('href="http:', '__HREFHTTPA', $tmpattributes);
2021 $tmpattributes = str_ireplace('href="https:', '__HREFHTTPSA', $tmpattributes);
2022 $tmpattributes = str_ireplace('src="http:', '__SRCHTTPIMG', $tmpattributes);
2023 $tmpattributes = str_ireplace('src="https:', '__SRCHTTPSIMG', $tmpattributes);
2024 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2025 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.-]/i', '', $tmpattributes);
2026 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2027 }
2028 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)> \/>/', $tmp, $reg)) {
2029 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
2030 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE', $tmpattributes);
2031 $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&]/i', '', $tmpattributes);
2032 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+) \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2033 }
2034 }
2035 }
2036
2037 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2038
2039 if (count($tmparrayoftags)) {
2040 foreach ($tmparrayoftags as $tagtoreplace) {
2041 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2042 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[(.*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2043 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2044 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2045 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[(.*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2046 }
2047
2048 $result = str_ireplace('__HREFHTTPA', 'href="http:', $result);
2049 $result = str_ireplace('__HREFHTTPSA', 'href="https:', $result);
2050 $result = str_ireplace('__SRCHTTPIMG', 'src="http:', $result);
2051 $result = str_ireplace('__SRCHTTPSIMG', 'src="https:', $result);
2052 $result = str_ireplace('__DOUBLEQUOTE', '"', $result);
2053 }
2054
2055 return $result;
2056 }
2057}
2058
2066function dol_strtolower($string, $encoding = "UTF-8")
2067{
2068 if (function_exists('mb_strtolower')) {
2069 return mb_strtolower($string, $encoding);
2070 } else {
2071 return strtolower($string);
2072 }
2073}
2074
2083function dol_strtoupper($string, $encoding = "UTF-8")
2084{
2085 if (function_exists('mb_strtoupper')) {
2086 return mb_strtoupper($string, $encoding);
2087 } else {
2088 return strtoupper($string);
2089 }
2090}
2091
2100function dol_ucfirst($string, $encoding = "UTF-8")
2101{
2102 if (function_exists('mb_substr')) {
2103 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2104 } else {
2105 return ucfirst($string);
2106 }
2107}
2108
2117function dol_ucwords($string, $encoding = "UTF-8")
2118{
2119 if (function_exists('mb_convert_case')) {
2120 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2121 } else {
2122 return ucwords($string);
2123 }
2124}
2125
2148function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2149{
2150 global $conf, $user, $debugbar;
2151
2152 // If syslog module enabled
2153 if (!isModEnabled('syslog')) {
2154 return;
2155 }
2156
2157 // Check if we are into execution of code of a website
2158 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2159 global $website, $websitekey;
2160 if (is_object($website) && !empty($website->ref)) {
2161 $suffixinfilename .= '_website_'.$website->ref;
2162 } elseif (!empty($websitekey)) {
2163 $suffixinfilename .= '_website_'.$websitekey;
2164 }
2165 }
2166
2167 // Check if we have a forced suffix
2168 if (defined('USESUFFIXINLOG')) {
2169 $suffixinfilename .= constant('USESUFFIXINLOG');
2170 }
2171
2172 if ($ident < 0) {
2173 foreach ($conf->loghandlers as $loghandlerinstance) {
2174 $loghandlerinstance->setIdent($ident);
2175 }
2176 }
2177
2178 if (!empty($message)) {
2179 // Test log level
2180 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2181 $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');
2182 if (!array_key_exists($level, $logLevels)) {
2183 throw new Exception('Incorrect log level');
2184 }
2185 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2186 return;
2187 }
2188
2189 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2190 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2191 }
2192
2193 // If adding log inside HTML page is required
2194 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2195 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2196 $ospid = sprintf("%7s", dol_trunc(getmypid(), 7, 'right', 'UTF-8', 1));
2197 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2198
2199 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2200 }
2201
2202 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2203 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2204 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2205 print "\n\n<!-- Log start\n";
2206 print dol_escape_htmltag($message)."\n";
2207 print "Log end -->\n";
2208 }
2209
2210 $data = array(
2211 'message' => $message,
2212 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2213 'level' => $level,
2214 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2215 'ip' => false,
2216 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2217 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2218 );
2219
2220 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2221 if (!empty($remoteip)) {
2222 $data['ip'] = $remoteip;
2223 // This is when server run behind a reverse proxy
2224 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2225 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2226 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2227 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2228 }
2229 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2230 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2231 $data['ip'] = $_SERVER['SERVER_ADDR'];
2232 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2233 // 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).
2234 $data['ip'] = $_SERVER['COMPUTERNAME'];
2235 } else {
2236 $data['ip'] = '???';
2237 }
2238
2239 if (!empty($_SERVER['USERNAME'])) {
2240 // 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).
2241 $data['osuser'] = $_SERVER['USERNAME'];
2242 } elseif (!empty($_SERVER['LOGNAME'])) {
2243 // 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).
2244 $data['osuser'] = $_SERVER['LOGNAME'];
2245 }
2246
2247 // Loop on each log handler and send output
2248 foreach ($conf->loghandlers as $loghandlerinstance) {
2249 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2250 continue;
2251 }
2252 $loghandlerinstance->export($data, $suffixinfilename);
2253 }
2254 unset($data);
2255 }
2256
2257 if ($ident > 0) {
2258 foreach ($conf->loghandlers as $loghandlerinstance) {
2259 $loghandlerinstance->setIdent($ident);
2260 }
2261 }
2262}
2263
2275function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2276{
2277 global $langs, $db;
2278
2279 $form = new Form($db);
2280
2281 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2282 if (empty($templatenameforexport)) {
2283 $templatenameforexport = 'website_'.$website->ref;
2284 }
2285
2286 $out = '';
2287 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2288
2289 // for generate popup
2290 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2291 $out .= 'jQuery(document).ready(function () {';
2292 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2293 $out .= ' var dialogHtml = \'';
2294
2295 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2296 $dialogcontent .= ' <div style="margin-top: 20px;">';
2297 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2298 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2299 $dialogcontent .= ' </div>';
2300 $dialogcontent .= ' <br>';
2301 $dialogcontent .= ' <div style="margin-top: 20px;">';
2302 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2303 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2304 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2305 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2306 $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>';
2307 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2308 $dialogcontent .= ' </form>';
2309 $dialogcontent .= ' </div>';
2310 $dialogcontent .= ' </div>';
2311
2312 $out .= dol_escape_js($dialogcontent);
2313
2314 $out .= '\';';
2315
2316
2317 // Add the content of the dialog to the body of the page
2318 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2319 $out .= ' if ($dialog.length > 0) {
2320 $dialog.remove();
2321 }
2322 jQuery("body").append(dialogHtml);';
2323
2324 // Configuration of popup
2325 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2326 $out .= ' autoOpen: false,';
2327 $out .= ' modal: true,';
2328 $out .= ' height: 290,';
2329 $out .= ' width: "40%",';
2330 $out .= ' title: "' . dol_escape_js($label) . '",';
2331 $out .= ' });';
2332
2333 // Simulate a click on the original "submit" input to export the site.
2334 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2335 $out .= ' console.log("Clic on exportsite.");';
2336 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2337 $out .= ' console.log("element founded:", target.length > 0);';
2338 $out .= ' if (target.length > 0) { target.click(); }';
2339 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2340 $out .= ' });';
2341
2342 // open popup
2343 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2344 $out .= ' return false;';
2345 $out .= ' });';
2346 $out .= '});';
2347 $out .= '</script>';
2348
2349 return $out;
2350}
2351
2352
2369function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2370{
2371 global $conf;
2372
2373 if (strpos($url, '?') > 0) {
2374 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2375 } else {
2376 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2377 }
2378
2379 $out = '';
2380
2381 $backtopagejsfieldsid = '';
2382 $backtopagejsfieldslabel = '';
2383 if ($backtopagejsfields) {
2384 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2385 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2386 $backtopagejsfields = $name.":".$backtopagejsfields;
2387 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2388 } else {
2389 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2390 }
2391 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2392 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2393 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2394 }
2395
2396 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2397 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2398 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2399 if (empty($conf->use_javascript_ajax)) {
2400 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2401 } elseif ($jsonopen) {
2402 $out .= ' href="#" onclick="'.$jsonopen.'"';
2403 } else {
2404 $out .= ' href="#"';
2405 }
2406 $out .= '>'.$buttonstring.'</a>';
2407
2408 if (!empty($conf->use_javascript_ajax)) {
2409 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2410 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2411 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2412 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2413 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2414
2415 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2416 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2417 jQuery(document).ready(function () {
2418 jQuery(".button_'.$name.'").click(function () {
2419 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2420 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2421 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2422 $tmpdialog.dialog({
2423 autoOpen: false,
2424 modal: true,
2425 height: (window.innerHeight - 150),
2426 width: \'80%\',
2427 title: \''.dol_escape_js($label).'\',
2428 open: function (event, ui) {
2429 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2430 },
2431 close: function (event, ui) {
2432 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2433 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2434 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2435 if (returnedid != "" && returnedid != "div for returned id") {
2436 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2437 }
2438 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2439 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2440 }
2441 }
2442 });
2443
2444 $tmpdialog.dialog(\'open\');
2445 return false;
2446 });
2447 });
2448 </script>';
2449 }
2450 return $out;
2451}
2452
2469function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2470{
2471 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2472}
2473
2490function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2491{
2492 global $conf, $langs, $hookmanager;
2493
2494 // Show title
2495 $showtitle = 1;
2496 if (!empty($conf->dol_optimize_smallscreen)) {
2497 $showtitle = 0;
2498 }
2499
2500 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2501
2502 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2503 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2504 }
2505
2506 // Show right part
2507 if ($morehtmlright) {
2508 $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.
2509 }
2510
2511 // Show title
2512 if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2513 $limittitle = 30;
2514 $out .= '<a class="tabTitle">';
2515 if ($picto) {
2516 $noprefix = $pictoisfullpath;
2517 if (strpos($picto, 'fontawesome_') !== false) {
2518 $noprefix = 1;
2519 }
2520 $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2521 }
2522 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2523 $out .= '</a>';
2524 }
2525
2526 // Show tabs
2527
2528 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2529 $maxkey = -1;
2530 if (is_array($links) && !empty($links)) {
2531 $keys = array_keys($links);
2532 if (count($keys)) {
2533 $maxkey = max($keys);
2534 }
2535 }
2536
2537 // Show tabs
2538 // if =0 we don't use the feature
2539 if (empty($limittoshow)) {
2540 $limittoshow = (!getDolGlobalString('MAIN_MAXTABS_IN_CARD') ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2541 }
2542 if (!empty($conf->dol_optimize_smallscreen)) {
2543 $limittoshow = 2;
2544 }
2545
2546 $displaytab = 0;
2547 $nbintab = 0;
2548 $popuptab = 0;
2549 $outmore = '';
2550 for ($i = 0; $i <= $maxkey; $i++) {
2551 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2552 // If active tab is already present
2553 if ($i >= $limittoshow) {
2554 $limittoshow--;
2555 }
2556 }
2557 }
2558
2559 for ($i = 0; $i <= $maxkey; $i++) {
2560 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2561 $isactive = true;
2562 } else {
2563 $isactive = false;
2564 }
2565
2566 if ($i < $limittoshow || $isactive) {
2567 // Output entry with a visible tab
2568 $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])).' -->';
2569
2570 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2571 if (!empty($links[$i][0])) {
2572 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2573 } else {
2574 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2575 }
2576 } elseif (!empty($links[$i][1])) {
2577 //print "x $i $active ".$links[$i][2]." z";
2578 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2579 if (!empty($links[$i][0])) {
2580 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2581 $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).'">';
2582 }
2583 $out .= $links[$i][1];
2584 if (!empty($links[$i][0])) {
2585 $out .= '</a>'."\n";
2586 }
2587 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2588 $out .= '</div>';
2589 }
2590
2591 $out .= '</div>';
2592 } else {
2593 // Add entry into the combo popup with the other tabs
2594 if (!$popuptab) {
2595 $popuptab = 1;
2596 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2597 }
2598 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2599 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2600 if (!empty($links[$i][0])) {
2601 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2602 } else {
2603 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2604 }
2605 } elseif (!empty($links[$i][1])) {
2606 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2607 $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.
2608 $outmore .= '</a>'."\n";
2609 }
2610 $outmore .= '</div>';
2611
2612 $nbintab++;
2613 }
2614 $displaytab = $i;
2615 }
2616 if ($popuptab) {
2617 $outmore .= '</div>';
2618 }
2619
2620 if ($popuptab) { // If there is some tabs not shown
2621 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2622 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2623 $widthofpopup = 200;
2624
2625 $tabsname = $moretabssuffix;
2626 if (empty($tabsname)) {
2627 $tabsname = str_replace("@", "", $picto);
2628 }
2629 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2630 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2631 $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".
2632 }
2633 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2634 $out .= $outmore;
2635 $out .= '</div>';
2636 $out .= '<div></div>';
2637 $out .= "</div>\n";
2638
2639 $out .= '<script nonce="'.getNonce().'">';
2640 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2641 var x = this.offsetLeft, y = this.offsetTop;
2642 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2643 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2644 $('#moretabsList".$tabsname."').css('".$right."','8px');
2645 }
2646 $('#moretabsList".$tabsname."').css('".$left."','auto');
2647 });
2648 ";
2649 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2650 $out .= "</script>";
2651 }
2652
2653 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2654 $out .= "</div>\n";
2655 }
2656
2657 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2658 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom')));
2659 $out .= '">'."\n";
2660 }
2661 if (!empty($dragdropfile)) {
2662 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2663 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2664 }
2665 $parameters = array('tabname' => $active, 'out' => $out);
2666 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2667 if ($reshook > 0) {
2668 $out = $hookmanager->resPrint;
2669 }
2670
2671 return $out;
2672}
2673
2681function dol_fiche_end($notab = 0)
2682{
2683 print dol_get_fiche_end($notab);
2684}
2685
2692function dol_get_fiche_end($notab = 0)
2693{
2694 if (!$notab || $notab == -1) {
2695 return "\n</div>\n";
2696 } else {
2697 return '';
2698 }
2699}
2700
2720function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2721{
2722 global $conf, $form, $user, $langs, $hookmanager, $action;
2723
2724 $error = 0;
2725
2726 $maxvisiblephotos = 1;
2727 $showimage = 1;
2728 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2729 // @phan-suppress-next-line PhanUndeclaredMethod
2730 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2731 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2732 $showbarcode = 0;
2733 }
2734 $modulepart = 'unknown';
2735
2736 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2737 $modulepart = $object->element;
2738 } elseif ($object->element == 'member') {
2739 $modulepart = 'memberphoto';
2740 } elseif ($object->element == 'user') {
2741 $modulepart = 'userphoto';
2742 }
2743
2744 if (class_exists("Imagick")) {
2745 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2746 $modulepart = $object->element;
2747 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2748 $modulepart = 'ficheinter';
2749 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2750 $modulepart = 'contract';
2751 } elseif ($object->element == 'order_supplier') {
2752 $modulepart = 'supplier_order';
2753 } elseif ($object->element == 'invoice_supplier') {
2754 $modulepart = 'supplier_invoice';
2755 }
2756 }
2757
2758 if ($object->element == 'product') {
2760 '@phan-var-force Product $object';
2761 $width = 80;
2762 $cssclass = 'photowithmargin photoref';
2763 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2764 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2765 if ($conf->browser->layout == 'phone') {
2766 $maxvisiblephotos = 1;
2767 }
2768 if ($showimage) {
2769 $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>';
2770 } else {
2771 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2772 $nophoto = '';
2773 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2774 } else { // Show no photo link
2775 $nophoto = '/public/theme/common/nophoto.png';
2776 $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>';
2777 }
2778 }
2779 } elseif ($object->element == 'category') {
2781 '@phan-var-force Categorie $object';
2782 $width = 80;
2783 $cssclass = 'photowithmargin photoref';
2784 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2785 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2786 if ($conf->browser->layout == 'phone') {
2787 $maxvisiblephotos = 1;
2788 }
2789 if ($showimage) {
2790 $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>';
2791 } else {
2792 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2793 $nophoto = '';
2794 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2795 } else { // Show no photo link
2796 $nophoto = '/public/theme/common/nophoto.png';
2797 $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>';
2798 }
2799 }
2800 } elseif ($object->element == 'bom') {
2802 '@phan-var-force Bom $object';
2803 $width = 80;
2804 $cssclass = 'photowithmargin photoref';
2805 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2806 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2807 if ($conf->browser->layout == 'phone') {
2808 $maxvisiblephotos = 1;
2809 }
2810 if ($showimage) {
2811 $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>';
2812 } else {
2813 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2814 $nophoto = '';
2815 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2816 } else { // Show no photo link
2817 $nophoto = '/public/theme/common/nophoto.png';
2818 $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>';
2819 }
2820 }
2821 } elseif ($object->element == 'ticket') {
2822 $width = 80;
2823 $cssclass = 'photoref';
2825 '@phan-var-force Ticket $object';
2826 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2827 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2828 if ($conf->browser->layout == 'phone') {
2829 $maxvisiblephotos = 1;
2830 }
2831
2832 if ($showimage) {
2833 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2834 if ($object->nbphoto > 0) {
2835 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2836 } else {
2837 $showimage = 0;
2838 }
2839 }
2840 if (!$showimage) {
2841 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2842 $nophoto = '';
2843 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2844 } else { // Show no photo link
2845 $nophoto = img_picto('No photo', 'object_ticket');
2846 $morehtmlleft .= '<!-- No photo to show -->';
2847 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2848 $morehtmlleft .= $nophoto;
2849 $morehtmlleft .= '</div></div>';
2850 }
2851 }
2852 } else {
2853 if ($showimage) {
2854 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2855 $phototoshow = '';
2856 // Check if a preview file is available
2857 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2858 $objectref = dol_sanitizeFileName($object->ref);
2859 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2860 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2861 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2862 $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
2863 } else {
2864 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2865 }
2866 if (empty($subdir)) {
2867 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2868 }
2869
2870 $filepath = $dir_output.$subdir."/";
2871
2872 $filepdf = $filepath.$objectref.".pdf";
2873 $relativepath = $subdir.'/'.$objectref.'.pdf';
2874
2875 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2876 $fileimage = $filepdf.'_preview.png';
2877 $relativepathimage = $relativepath.'_preview.png';
2878
2879 $pdfexists = file_exists($filepdf);
2880
2881 // If PDF file exists
2882 if ($pdfexists) {
2883 // Conversion du PDF en image png si fichier png non existent
2884 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2885 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2886 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2887 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2888 if ($ret < 0) {
2889 $error++;
2890 }
2891 }
2892 }
2893 }
2894
2895 if ($pdfexists && !$error) {
2896 $heightforphotref = 80;
2897 if (!empty($conf->dol_optimize_smallscreen)) {
2898 $heightforphotref = 60;
2899 }
2900 // If the preview file is found
2901 if (file_exists($fileimage)) {
2902 $phototoshow = '<div class="photoref">';
2903 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2904 $phototoshow .= '</div>';
2905 }
2906 }
2907 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
2908 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2909 }
2910
2911 if ($phototoshow) {
2912 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2913 $morehtmlleft .= $phototoshow;
2914 $morehtmlleft .= '</div>';
2915 }
2916 }
2917
2918 if (empty($phototoshow)) { // Show No photo link (picto of object)
2919 if ($object->element == 'action') {
2920 $width = 80;
2921 $cssclass = 'photorefcenter';
2922 $nophoto = img_picto('No photo', 'title_agenda');
2923 } else {
2924 $width = 14;
2925 $cssclass = 'photorefcenter';
2926 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
2927 $prefix = 'object_';
2928 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
2929 $picto = 'project'; // instead of projectpub
2930 }
2931 if (strpos($picto, 'fontawesome_') !== false) {
2932 $prefix = '';
2933 }
2934 $nophoto = img_picto('No photo', $prefix.$picto);
2935 }
2936 $morehtmlleft .= '<!-- No photo to show -->';
2937 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2938 $morehtmlleft .= $nophoto;
2939 $morehtmlleft .= '</div></div>';
2940 }
2941 }
2942 }
2943
2944 // Show barcode
2945 if ($showbarcode) {
2946 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2947 }
2948
2949 if ($object->element == 'societe') {
2950 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2951 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2952 } else {
2953 $morehtmlstatus .= $object->getLibStatut(6);
2954 }
2955 } elseif ($object->element == 'product') {
2956 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2957 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2958 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2959 } else {
2960 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2961 }
2962 $morehtmlstatus .= ' &nbsp; ';
2963 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2964 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2965 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2966 } else {
2967 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2968 }
2969 } elseif (in_array($object->element, array('salary'))) {
2970 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
2971 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2972 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
2973 }
2974 $morehtmlstatus .= $tmptxt;
2975 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
2976 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2977 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2978 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2979 }
2980 $morehtmlstatus .= $tmptxt;
2981 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2982 if ($object->statut == 0) {
2983 $morehtmlstatus .= $object->getLibStatut(5);
2984 } else {
2985 $morehtmlstatus .= $object->getLibStatut(4);
2986 }
2987 } elseif ($object->element == 'facturerec') {
2988 '@phan-var-force FactureRec $object';
2989 if ($object->frequency == 0) {
2990 $morehtmlstatus .= $object->getLibStatut(2);
2991 } else {
2992 $morehtmlstatus .= $object->getLibStatut(5);
2993 }
2994 } elseif ($object->element == 'project_task') {
2995 $object->fk_statut = 1;
2996 if ($object->progress > 0) {
2997 $object->fk_statut = 2;
2998 }
2999 if ($object->progress >= 100) {
3000 $object->fk_statut = 3;
3001 }
3002 $tmptxt = $object->getLibStatut(5);
3003 $morehtmlstatus .= $tmptxt; // No status on task
3004 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3005 $tmptxt = $object->getLibStatut(6);
3006 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3007 $tmptxt = $object->getLibStatut(5);
3008 }
3009 $morehtmlstatus .= $tmptxt;
3010 }
3011
3012 // Add if object was dispatched "into accountancy"
3013 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3014 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3015 if (method_exists($object, 'getVentilExportCompta')) {
3016 $accounted = $object->getVentilExportCompta();
3017 $langs->load("accountancy");
3018 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3019 }
3020 }
3021
3022 // Add alias for thirdparty
3023 if (!empty($object->name_alias)) {
3024 '@phan-var-force Societe $object';
3025 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3026 }
3027
3028 // Add label
3029 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3030 if (!empty($object->label)) {
3031 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3032 }
3033 }
3034 // Show address and email
3035 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3036 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3037 if ($moreaddress) {
3038 $morehtmlref .= '<div class="refidno refaddress">';
3039 $morehtmlref .= $moreaddress;
3040 $morehtmlref .= '</div>';
3041 }
3042 }
3043 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)) {
3044 $morehtmlref .= '<div style="clear: both;"></div>';
3045 $morehtmlref .= '<div class="refidno opacitymedium">';
3046 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3047 $morehtmlref .= '</div>';
3048 }
3049
3050 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3051 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3052 if ($reshook < 0) {
3053 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3054 } elseif (empty($reshook)) {
3055 $morehtmlref .= $hookmanager->resPrint;
3056 } elseif ($reshook > 0) {
3057 $morehtmlref = $hookmanager->resPrint;
3058 }
3059
3060
3061 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3062 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3063 print '</div>';
3064 print '<div class="underrefbanner clearboth"></div>';
3065}
3066
3076function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3077{
3078 global $langs;
3079 $ret = '';
3080 if ($fieldrequired) {
3081 $ret .= '<span class="fieldrequired">';
3082 }
3083 $ret .= '<label for="'.$fieldkey.'">';
3084 $ret .= $langs->trans($langkey);
3085 $ret .= '</label>';
3086 if ($fieldrequired) {
3087 $ret .= '</span>';
3088 }
3089 return $ret;
3090}
3091
3099function dol_bc($var, $moreclass = '')
3100{
3101 global $bc;
3102 $ret = ' '.$bc[$var];
3103 if ($moreclass) {
3104 $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
3105 }
3106 return $ret;
3107}
3108
3122function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3123{
3124 global $langs, $hookmanager;
3125
3126 $ret = '';
3127 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3128
3129 // See format of addresses on https://en.wikipedia.org/wiki/Address
3130 // Address
3131 if (empty($mode)) {
3132 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3133 }
3134 // Zip/Town/State
3135 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3136 // US: title firstname name \n address lines \n town, state, zip \n country
3137 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3138 $ret .= (($ret && $town) ? $sep : '').$town;
3139
3140 if (!empty($object->state)) {
3141 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3142 }
3143 if (!empty($object->zip)) {
3144 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3145 }
3146 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3147 // UK: title firstname name \n address lines \n town state \n zip \n country
3148 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3149 $ret .= ($ret ? $sep : '').$town;
3150 if (!empty($object->state)) {
3151 $ret .= ($ret ? ", " : '').$object->state;
3152 }
3153 if (!empty($object->zip)) {
3154 $ret .= ($ret ? $sep : '').$object->zip;
3155 }
3156 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3157 // ES: title firstname name \n address lines \n zip town \n state \n country
3158 $ret .= ($ret ? $sep : '').$object->zip;
3159 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3160 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3161 if (!empty($object->state)) {
3162 $ret .= $sep.$object->state;
3163 }
3164 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3165 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3166 // See https://www.sljfaq.org/afaq/addresses.html
3167 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3168 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3169 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3170 // IT: title firstname name\n address lines \n zip town state_code \n country
3171 $ret .= ($ret ? $sep : '').$object->zip;
3172 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3173 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3174 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3175 } else {
3176 // Other: title firstname name \n address lines \n zip town[, state] \n country
3177 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3178 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3179 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3180 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3181 $ret .= ($ret ? ", " : '').$object->state;
3182 }
3183 }
3184
3185 if (!is_object($outputlangs)) {
3186 $outputlangs = $langs;
3187 }
3188 if ($withcountry) {
3189 $langs->load("dict");
3190 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3191 }
3192 if ($hookmanager) {
3193 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3194 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3195 if ($reshook > 0) {
3196 $ret = '';
3197 }
3198 $ret .= $hookmanager->resPrint;
3199 }
3200
3201 return $ret;
3202}
3203
3204
3205
3215function dol_strftime($fmt, $ts = false, $is_gmt = false)
3216{
3217 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3218 return dol_print_date($ts, $fmt, $is_gmt);
3219 } else {
3220 return 'Error date outside supported range';
3221 }
3222}
3223
3245function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3246{
3247 global $conf, $langs;
3248
3249 // If date undefined or "", we return ""
3250 if (dol_strlen($time) == 0) {
3251 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3252 }
3253
3254 if ($tzoutput === 'auto') {
3255 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3256 }
3257
3258 // Clean parameters
3259 $to_gmt = false;
3260 $offsettz = $offsetdst = 0;
3261 if ($tzoutput) {
3262 $to_gmt = true; // For backward compatibility
3263 if (is_string($tzoutput)) {
3264 if ($tzoutput == 'tzserver') {
3265 $to_gmt = false;
3266 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3267 // @phan-suppress-next-line PhanPluginRedundantAssignment
3268 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3269 // @phan-suppress-next-line PhanPluginRedundantAssignment
3270 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3271 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3272 $to_gmt = true;
3273 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3274
3275 if (class_exists('DateTimeZone')) {
3276 $user_date_tz = new DateTimeZone($offsettzstring);
3277 $user_dt = new DateTime();
3278 $user_dt->setTimezone($user_date_tz);
3279 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3280 $offsettz = $user_dt->getOffset(); // should include dst ?
3281 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3282 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3283 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3284 }
3285 }
3286 }
3287 }
3288 if (!is_object($outputlangs)) {
3289 $outputlangs = $langs;
3290 }
3291 if (!$format) {
3292 $format = 'daytextshort';
3293 }
3294
3295 // Do we have to reduce the length of date (year on 2 chars) to save space.
3296 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3297 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3298 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3299 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3300 if ($formatwithoutreduce != $format) {
3301 $format = $formatwithoutreduce;
3302 $reduceformat = 1;
3303 } // so format 'dayreduceformat' is processed like day
3304
3305 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3306 // TODO Add format daysmallyear and dayhoursmallyear
3307 if ($format == 'day') {
3308 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3309 } elseif ($format == 'hour') {
3310 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3311 } elseif ($format == 'hourduration') {
3312 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3313 } elseif ($format == 'daytext') {
3314 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3315 } elseif ($format == 'daytextshort') {
3316 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3317 } elseif ($format == 'dayhour') {
3318 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3319 } elseif ($format == 'dayhoursec') {
3320 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3321 } elseif ($format == 'dayhourtext') {
3322 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3323 } elseif ($format == 'dayhourtextshort') {
3324 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3325 } elseif ($format == 'dayhourlog') {
3326 // Format not sensitive to language
3327 $format = '%Y%m%d%H%M%S';
3328 } elseif ($format == 'dayhourlogsmall') {
3329 // Format not sensitive to language
3330 $format = '%y%m%d%H%M';
3331 } elseif ($format == 'dayhourldap') {
3332 $format = '%Y%m%d%H%M%SZ';
3333 } elseif ($format == 'dayhourxcard') {
3334 $format = '%Y%m%dT%H%M%SZ';
3335 } elseif ($format == 'dayxcard') {
3336 $format = '%Y%m%d';
3337 } elseif ($format == 'dayrfc') {
3338 $format = '%Y-%m-%d'; // DATE_RFC3339
3339 } elseif ($format == 'dayhourrfc') {
3340 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3341 } elseif ($format == 'standard') {
3342 $format = '%Y-%m-%d %H:%M:%S';
3343 }
3344
3345 if ($reduceformat) {
3346 $format = str_replace('%Y', '%y', $format);
3347 $format = str_replace('yyyy', 'yy', $format);
3348 }
3349
3350 // Clean format
3351 if (preg_match('/%b/i', $format)) { // There is some text to translate
3352 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3353 $format = str_replace('%b', '__b__', $format);
3354 $format = str_replace('%B', '__B__', $format);
3355 }
3356 if (preg_match('/%a/i', $format)) { // There is some text to translate
3357 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3358 $format = str_replace('%a', '__a__', $format);
3359 $format = str_replace('%A', '__A__', $format);
3360 }
3361
3362 // Analyze date
3363 $reg = array();
3364 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
3365 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"]));
3366 return '';
3367 } 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
3368 // This part of code should not be used anymore.
3369 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);
3370 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3371 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3372 $syear = (!empty($reg[1]) ? $reg[1] : '');
3373 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3374 $sday = (!empty($reg[3]) ? $reg[3] : '');
3375 $shour = (!empty($reg[4]) ? $reg[4] : '');
3376 $smin = (!empty($reg[5]) ? $reg[5] : '');
3377 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3378
3379 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
3380
3381 if ($to_gmt) {
3382 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3383 } else {
3384 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3385 }
3386 $dtts = new DateTime();
3387 $dtts->setTimestamp($time);
3388 $dtts->setTimezone($tzo);
3389 $newformat = str_replace(
3390 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3391 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3392 $format
3393 );
3394 $ret = $dtts->format($newformat);
3395 $ret = str_replace(
3396 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3397 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3398 $ret
3399 );
3400 } else {
3401 // Date is a timestamps
3402 if ($time < 100000000000) { // Protection against bad date values
3403 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3404
3405 if ($to_gmt) {
3406 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3407 } else {
3408 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3409 }
3410 $dtts = new DateTime();
3411 $dtts->setTimestamp($timetouse);
3412 $dtts->setTimezone($tzo);
3413 $newformat = str_replace(
3414 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3415 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3416 $format
3417 );
3418 $ret = $dtts->format($newformat);
3419 $ret = str_replace(
3420 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3421 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3422 $ret
3423 );
3424 //var_dump($ret);exit;
3425 } else {
3426 $ret = 'Bad value '.$time.' for date';
3427 }
3428 }
3429
3430 if (preg_match('/__b__/i', $format)) {
3431 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3432
3433 if ($to_gmt) {
3434 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3435 } else {
3436 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3437 }
3438 $dtts = new DateTime();
3439 $dtts->setTimestamp($timetouse);
3440 $dtts->setTimezone($tzo);
3441 $month = (int) $dtts->format("m");
3442 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3443 if ($encodetooutput) {
3444 $monthtext = $outputlangs->transnoentities('Month'.$month);
3445 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3446 } else {
3447 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3448 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3449 }
3450 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3451 $ret = str_replace('__b__', $monthtextshort, $ret);
3452 $ret = str_replace('__B__', $monthtext, $ret);
3453 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3454 //return $ret;
3455 }
3456 if (preg_match('/__a__/i', $format)) {
3457 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3458 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3459
3460 if ($to_gmt) {
3461 $tzo = new DateTimeZone('UTC');
3462 } else {
3463 $tzo = new DateTimeZone(date_default_timezone_get());
3464 }
3465 $dtts = new DateTime();
3466 $dtts->setTimestamp($timetouse);
3467 $dtts->setTimezone($tzo);
3468 $w = $dtts->format("w");
3469 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3470
3471 $ret = str_replace('__A__', $dayweek, $ret);
3472 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3473 }
3474
3475 return $ret;
3476}
3477
3478
3499function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3500{
3501 if ($timestamp === '') {
3502 return array();
3503 }
3504
3505 $datetimeobj = new DateTime();
3506 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3507 if ($forcetimezone) {
3508 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3509 }
3510 $arrayinfo = array(
3511 'year' => ((int) date_format($datetimeobj, 'Y')),
3512 'mon' => ((int) date_format($datetimeobj, 'm')),
3513 'mday' => ((int) date_format($datetimeobj, 'd')),
3514 'wday' => ((int) date_format($datetimeobj, 'w')),
3515 'yday' => ((int) date_format($datetimeobj, 'z')),
3516 'hours' => ((int) date_format($datetimeobj, 'H')),
3517 'minutes' => ((int) date_format($datetimeobj, 'i')),
3518 'seconds' => ((int) date_format($datetimeobj, 's')),
3519 '0' => $timestamp
3520 );
3521
3522 return $arrayinfo;
3523}
3524
3546function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3547{
3548 global $conf;
3549 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3550
3551 if ($gm === 'auto') {
3552 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3553 }
3554 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3555
3556 // Clean parameters
3557 if ($hour == -1 || empty($hour)) {
3558 $hour = 0;
3559 }
3560 if ($minute == -1 || empty($minute)) {
3561 $minute = 0;
3562 }
3563 if ($second == -1 || empty($second)) {
3564 $second = 0;
3565 }
3566
3567 // Check parameters
3568 if ($check) {
3569 if (!$month || !$day) {
3570 return '';
3571 }
3572 if ($day > 31) {
3573 return '';
3574 }
3575 if ($month > 12) {
3576 return '';
3577 }
3578 if ($hour < 0 || $hour > 24) {
3579 return '';
3580 }
3581 if ($minute < 0 || $minute > 60) {
3582 return '';
3583 }
3584 if ($second < 0 || $second > 60) {
3585 return '';
3586 }
3587 }
3588
3589 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3590 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3591 $localtz = new DateTimeZone($default_timezone);
3592 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3593 // We use dol_tz_string first because it is more reliable.
3594 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3595 try {
3596 $localtz = new DateTimeZone($default_timezone);
3597 } catch (Exception $e) {
3598 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3599 $default_timezone = @date_default_timezone_get();
3600 }
3601 } elseif (strrpos($gm, "tz,") !== false) {
3602 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3603 try {
3604 $localtz = new DateTimeZone($timezone);
3605 } catch (Exception $e) {
3606 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3607 }
3608 }
3609
3610 if (empty($localtz)) {
3611 $localtz = new DateTimeZone('UTC');
3612 }
3613 //var_dump($localtz);
3614 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3615 $dt = new DateTime('now', $localtz);
3616 $dt->setDate((int) $year, (int) $month, (int) $day);
3617 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3618 $date = $dt->getTimestamp(); // should include daylight saving time
3619 //var_dump($date);
3620 return $date;
3621}
3622
3623
3634function dol_now($mode = 'auto')
3635{
3636 $ret = 0;
3637
3638 if ($mode === 'auto') {
3639 $mode = 'gmt';
3640 }
3641
3642 if ($mode == 'gmt') {
3643 $ret = time(); // Time for now at greenwich.
3644 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3645 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3646 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3647 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3648 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3649 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3650 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3651 // $ret=dol_now('gmt')+($tzsecond*3600);
3652 //}
3653 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3654 // Time for now with user timezone added
3655 //print 'time: '.time();
3656 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3657 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3658 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3659 }
3660
3661 return $ret;
3662}
3663
3664
3673function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3674{
3675 global $conf, $langs;
3676 $level = 1024;
3677
3678 if (!empty($conf->dol_optimize_smallscreen)) {
3679 $shortunit = 1;
3680 }
3681
3682 // Set value text
3683 if (empty($shortvalue) || $size < ($level * 10)) {
3684 $ret = $size;
3685 $textunitshort = $langs->trans("b");
3686 $textunitlong = $langs->trans("Bytes");
3687 } else {
3688 $ret = round($size / $level, 0);
3689 $textunitshort = $langs->trans("Kb");
3690 $textunitlong = $langs->trans("KiloBytes");
3691 }
3692 // Use long or short text unit
3693 if (empty($shortunit)) {
3694 $ret .= ' '.$textunitlong;
3695 } else {
3696 $ret .= ' '.$textunitshort;
3697 }
3698
3699 return $ret;
3700}
3701
3712function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3713{
3714 global $langs;
3715
3716 if (empty($url)) {
3717 return '';
3718 }
3719
3720 $linkstart = '<a href="';
3721 if (!preg_match('/^http/i', $url)) {
3722 $linkstart .= 'http://';
3723 }
3724 $linkstart .= $url;
3725 $linkstart .= '"';
3726 if ($target) {
3727 $linkstart .= ' target="'.$target.'"';
3728 }
3729 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3730 $linkstart .= '>';
3731
3732 $link = '';
3733 if (!preg_match('/^http/i', $url)) {
3734 $link .= 'http://';
3735 }
3736 $link .= dol_trunc($url, $max);
3737
3738 $linkend = '</a>';
3739
3740 if ($morecss == 'float') { // deprecated
3741 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3742 } else {
3743 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3744 }
3745}
3746
3759function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3760{
3761 global $user, $langs, $hookmanager;
3762
3763 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3764 //$showinvalid = 1; $email = 'rrrrr';
3765
3766 $newemail = dol_escape_htmltag($email);
3767
3768 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3769 $withpicto = 0;
3770 }
3771
3772 if (empty($email)) {
3773 return '&nbsp;';
3774 }
3775
3776 if ($addlink == 1) {
3777 $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="';
3778 if (!preg_match('/^mailto:/i', $email)) {
3779 $newemail .= 'mailto:';
3780 }
3781 $newemail .= $email;
3782 $newemail .= '">';
3783
3784 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3785
3786 if ($max > 0) {
3787 $newemail .= dol_trunc($email, $max);
3788 }
3789 $newemail .= '</a>';
3790 if ($showinvalid && !isValidEmail($email)) {
3791 $langs->load("errors");
3792 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3793 }
3794
3795 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3796 $type = 'AC_EMAIL';
3797 $linktoaddaction = '';
3798 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3799 $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>';
3800 }
3801 if ($linktoaddaction) {
3802 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3803 }
3804 }
3805 } elseif ($addlink == 'thirdparty') {
3806 $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
3807 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3808 $newemail .= '</a>';
3809 } else {
3810 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3811
3812 if ($showinvalid && !isValidEmail($email)) {
3813 $langs->load("errors");
3814 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3815 }
3816 }
3817
3818 //$rep = '<div class="nospan" style="margin-right: 10px">';
3819 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3820 //$rep .= '</div>';
3821 $rep = $newemail;
3822
3823 if ($hookmanager) {
3824 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3825
3826 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3827 if ($reshook > 0) {
3828 $rep = '';
3829 }
3830 $rep .= $hookmanager->resPrint;
3831 }
3832
3833 return $rep;
3834}
3835
3841function getArrayOfSocialNetworks()
3842{
3843 global $conf, $db;
3844
3845 $socialnetworks = array();
3846 // Enable caching of array
3847 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3848 $cachekey = 'socialnetworks_' . $conf->entity;
3849 $dataretrieved = dol_getcache($cachekey);
3850 if (!is_null($dataretrieved)) {
3851 $socialnetworks = $dataretrieved;
3852 } else {
3853 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3854 $sql .= " WHERE entity=".$conf->entity;
3855 $resql = $db->query($sql);
3856 if ($resql) {
3857 while ($obj = $db->fetch_object($resql)) {
3858 $socialnetworks[$obj->code] = array(
3859 'rowid' => $obj->rowid,
3860 'label' => $obj->label,
3861 'url' => $obj->url,
3862 'icon' => $obj->icon,
3863 'active' => $obj->active,
3864 );
3865 }
3866 }
3867 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3868 }
3869 return $socialnetworks;
3870}
3871
3882function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3883{
3884 global $user, $langs;
3885
3886 $htmllink = $value;
3887
3888 if (empty($value)) {
3889 return '&nbsp;';
3890 }
3891
3892 if (!empty($type)) {
3893 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3894 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3895 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3896 if ($type == 'skype') {
3897 $htmllink .= dol_escape_htmltag($value);
3898 $htmllink .= '&nbsp; <a href="skype:';
3899 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3900 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3901 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3902 $htmllink .= '</a><a href="skype:';
3903 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3904 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3905 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3906 $htmllink .= '</a>';
3907 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3908 $addlink = 'AC_SKYPE';
3909 $link = '';
3910 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
3911 $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>';
3912 }
3913 $htmllink .= ($link ? ' '.$link : '');
3914 }
3915 } else {
3916 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
3917 if (getDolGlobalString($networkconstname)) {
3918 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
3919 if (preg_match('/^https?:\/\//i', $link)) {
3920 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3921 } else {
3922 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3923 }
3924 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
3925 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3926 if ($tmpvirginurl) {
3927 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3928 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3929
3930 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3931 if ($tmpvirginurl3) {
3932 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3933 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3934 }
3935
3936 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3937 if ($tmpvirginurl2) {
3938 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3939 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3940 }
3941 }
3942 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3943 if (preg_match('/^https?:\/\//i', $link)) {
3944 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3945 } else {
3946 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3947 }
3948 } else {
3949 $htmllink .= dol_escape_htmltag($value);
3950 }
3951 }
3952 $htmllink .= '</div>';
3953 } else {
3954 $langs->load("errors");
3955 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3956 }
3957 return $htmllink;
3958}
3959
3969function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
3970{
3971 global $mysoc;
3972
3973 if (empty($profID) || empty($profIDtype)) {
3974 return '';
3975 }
3976 if (empty($countrycode)) {
3977 $countrycode = $mysoc->country_code;
3978 }
3979 $newProfID = $profID;
3980 $id = substr($profIDtype, -1);
3981 $ret = '';
3982 if (strtoupper($countrycode) == 'FR') {
3983 // France
3984 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
3985
3986 if ($id == 1 && dol_strlen($newProfID) == 9) {
3987 // SIREN (ex: 123 123 123)
3988 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
3989 }
3990 if ($id == 2 && dol_strlen($newProfID) == 14) {
3991 // SIRET (ex: 123 123 123 12345)
3992 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
3993 }
3994 if ($id == 3 && dol_strlen($newProfID) == 5) {
3995 // NAF/APE (ex: 69.20Z)
3996 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
3997 }
3998 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
3999 // TVA intracommunautaire (ex: FR12 123 123 123)
4000 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4001 }
4002 }
4003 if (!empty($addcpButton)) {
4004 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4005 } else {
4006 $ret = $newProfID;
4007 }
4008 return $ret;
4009}
4010
4026function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = '')
4027{
4028 global $conf, $user, $langs, $mysoc, $hookmanager;
4029
4030 // Clean phone parameter
4031 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4032 if (empty($phone)) {
4033 return '';
4034 }
4035 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4036 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4037 }
4038 if (empty($countrycode) && is_object($mysoc)) {
4039 $countrycode = $mysoc->country_code;
4040 }
4041
4042 // Short format for small screens
4043 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4044 $separ = '';
4045 }
4046
4047 $newphone = $phone;
4048 $newphonewa = $phone;
4049 if (strtoupper($countrycode) == "FR") {
4050 // France
4051 if (dol_strlen($phone) == 10) {
4052 $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);
4053 } elseif (dol_strlen($phone) == 7) {
4054 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4055 } elseif (dol_strlen($phone) == 9) {
4056 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4057 } elseif (dol_strlen($phone) == 11) {
4058 $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);
4059 } elseif (dol_strlen($phone) == 12) {
4060 $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);
4061 } elseif (dol_strlen($phone) == 13) {
4062 $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);
4063 }
4064 } elseif (strtoupper($countrycode) == "CA") {
4065 if (dol_strlen($phone) == 10) {
4066 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4067 }
4068 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4069 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4070 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4071 }
4072 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4073 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4074 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4075 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4076 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4077 }
4078 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4079 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4080 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4081 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4082 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4083 }
4084 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4085 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4086 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4087 }
4088 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4089 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4090 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4091 }
4092 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4093 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4094 $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);
4095 }
4096 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4097 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4098 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4099 }
4100 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4101 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4102 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4103 }
4104 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4105 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4106 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4107 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4108 $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);
4109 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4110 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4111 }
4112 } elseif (strtoupper($countrycode) == "ML") {//Mali
4113 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4114 $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);
4115 }
4116 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4117 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4118 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4119 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4120 $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);
4121 }
4122 } elseif (strtoupper($countrycode) == "MU") {
4123 //Maurice
4124 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4125 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4126 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4127 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4128 }
4129 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4130 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4131 $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);
4132 }
4133 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4134 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4135 $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);
4136 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
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, 3);
4138 }
4139 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4140 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4141 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4142 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4143 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4144 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4145 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4146 }
4147 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4148 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4149 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4150 }
4151 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4152 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4153 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4154 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4155 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4156 }
4157 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4158 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4159 $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);
4160 }
4161 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4162 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4163 $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);
4164 }
4165 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4166 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4167 $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);
4168 }
4169 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4170 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4171 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4172 }
4173 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4174 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4175 $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);
4176 }
4177 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4178 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4179 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4180 }
4181 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4182 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4183 $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);
4184 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4185 $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);
4186 }
4187 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4188 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4189 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4190 }
4191 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4192 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4193 $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);
4194 }
4195 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4196 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4197 $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);
4198 }
4199 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4200 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4201 $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);
4202 }
4203 } elseif (strtoupper($countrycode) == "IT") {//Italie
4204 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4205 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4206 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4207 $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);
4208 }
4209 } elseif (strtoupper($countrycode) == "AU") {
4210 //Australie
4211 if (dol_strlen($phone) == 12) {
4212 //ex: +61_A_BCDE_FGHI
4213 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4214 }
4215 } elseif (strtoupper($countrycode) == "LU") {
4216 // Luxembourg
4217 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4218 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4219 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4220 $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);
4221 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
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, 2);
4223 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4224 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2).$separ.substr($newphone, 11, 2);
4225 }
4226 } elseif (strtoupper($countrycode) == "PE") {
4227 // Peru
4228 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4229 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4230 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4231 $newphonewa = '+51'.$newphone;
4232 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4233 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4234 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4235 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4236 $newphonewa = $newphone;
4237 $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);
4238 }
4239 }
4240
4241 $newphoneastart = $newphoneaend = '';
4242 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4243 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
4244 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4245 $newphoneaend .= '</a>';
4246 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4247 if (empty($user->clicktodial_loaded)) {
4248 $user->fetch_clicktodial();
4249 }
4250
4251 // Define urlmask
4252 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4253 if (!empty($user->clicktodial_url)) {
4254 $urlmask = $user->clicktodial_url;
4255 }
4256
4257 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4258 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4259 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4260 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4261 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4262 // Those lines are for substitution
4263 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4264 '__PHONETO__' => urlencode($phone),
4265 '__LOGIN__' => $clicktodial_login,
4266 '__PASS__' => $clicktodial_password);
4267 $url = make_substitutions($url, $substitarray);
4268 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4269 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4270 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4271 $newphoneaend = '</a>';
4272 } else {
4273 // Old method
4274 $newphoneastart = '<a href="'.$url.'"';
4275 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4276 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4277 }
4278 $newphoneastart .= '>';
4279 $newphoneaend .= '</a>';
4280 }
4281 }
4282
4283 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4284 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4285 $type = 'AC_TEL';
4286 $addlinktoagenda = '';
4287 if ($addlink == 'AC_FAX') {
4288 $type = 'AC_FAX';
4289 }
4290 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4291 $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>';
4292 }
4293 if ($addlinktoagenda) {
4294 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4295 }
4296 }
4297 }
4298
4299 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4300 // Link to Whatsapp
4301 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4302 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4303 }
4304
4305 if (empty($titlealt)) {
4306 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4307 }
4308 $rep = '';
4309
4310 if ($hookmanager) {
4311 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4312 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4313 $rep .= $hookmanager->resPrint;
4314 }
4315 if (empty($reshook)) {
4316 $picto = '';
4317 if ($withpicto) {
4318 if ($withpicto == 'fax') {
4319 $picto = 'phoning_fax';
4320 } elseif ($withpicto == 'phone') {
4321 $picto = 'phoning';
4322 } elseif ($withpicto == 'mobile') {
4323 $picto = 'phoning_mobile';
4324 } else {
4325 $picto = '';
4326 }
4327 }
4328 if ($adddivfloat == 1) {
4329 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">';
4330 } elseif (empty($adddivfloat)) {
4331 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').' style="margin-right: 10px;">';
4332 }
4333
4334 $rep .= $newphoneastart;
4335 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4336 if ($separ != 'hidenum') {
4337 $rep .= ($withpicto ? ' ' : '').$newphone;
4338 }
4339 $rep .= $newphoneaend;
4340
4341 if ($adddivfloat == 1) {
4342 $rep .= '</div>';
4343 } elseif (empty($adddivfloat)) {
4344 $rep .= '</span>';
4345 }
4346 }
4347
4348 return $rep;
4349}
4350
4358function dol_print_ip($ip, $mode = 0)
4359{
4360 global $langs;
4361
4362 $ret = '';
4363
4364 if (empty($mode)) {
4365 $ret .= $ip;
4366 }
4367
4368 if ($mode != 2) {
4369 $countrycode = dolGetCountryCodeFromIp($ip);
4370 if ($countrycode) { // If success, countrycode is us, fr, ...
4371 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4372 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4373 } else {
4374 $ret .= ' ('.$countrycode.')';
4375 }
4376 } else {
4377 // Nothing
4378 }
4379 }
4380
4381 return $ret;
4382}
4383
4392function getUserRemoteIP()
4393{
4394 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4395 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4396 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4397 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4398 } else {
4399 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4400 }
4401 } else {
4402 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4403 }
4404 } else {
4405 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4406 }
4407 return $ip;
4408}
4409
4418function isHTTPS()
4419{
4420 $isSecure = false;
4421 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4422 $isSecure = true;
4423 } 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') {
4424 $isSecure = true;
4425 }
4426 return $isSecure;
4427}
4428
4435function dolGetCountryCodeFromIp($ip)
4436{
4437 global $conf;
4438
4439 $countrycode = '';
4440
4441 if (!empty($conf->geoipmaxmind->enabled)) {
4442 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4443 //$ip='24.24.24.24';
4444 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4445 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4446 $geoip = new DolGeoIP('country', $datafile);
4447 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4448 $countrycode = $geoip->getCountryCodeFromIP($ip);
4449 }
4450
4451 return $countrycode;
4452}
4453
4454
4461function dol_user_country()
4462{
4463 global $conf, $langs, $user;
4464
4465 //$ret=$user->xxx;
4466 $ret = '';
4467 if (!empty($conf->geoipmaxmind->enabled)) {
4468 $ip = getUserRemoteIP();
4469 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4470 //$ip='24.24.24.24';
4471 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4472 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4473 $geoip = new DolGeoIP('country', $datafile);
4474 $countrycode = $geoip->getCountryCodeFromIP($ip);
4475 $ret = $countrycode;
4476 }
4477 return $ret;
4478}
4479
4492function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4493{
4494 global $conf, $user, $langs, $hookmanager;
4495
4496 $out = '';
4497
4498 if ($address) {
4499 if ($hookmanager) {
4500 $parameters = array('element' => $element, 'id' => $id);
4501 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4502 $out .= $hookmanager->resPrint;
4503 }
4504 if (empty($reshook)) {
4505 if (empty($charfornl)) {
4506 $out .= nl2br($address);
4507 } else {
4508 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4509 }
4510
4511 // TODO Remove this block, we can add this using the hook now
4512 $showgmap = $showomap = 0;
4513 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4514 $showgmap = 1;
4515 }
4516 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4517 $showgmap = 1;
4518 }
4519 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4520 $showgmap = 1;
4521 }
4522 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4523 $showgmap = 1;
4524 }
4525 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4526 $showomap = 1;
4527 }
4528 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4529 $showomap = 1;
4530 }
4531 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4532 $showomap = 1;
4533 }
4534 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4535 $showomap = 1;
4536 }
4537 if ($showgmap) {
4538 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4539 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4540 }
4541 if ($showomap) {
4542 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4543 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4544 }
4545 }
4546 }
4547 if ($noprint) {
4548 return $out;
4549 } else {
4550 print $out;
4551 }
4552}
4553
4554
4564function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4565{
4566 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4567 return true;
4568 }
4569 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4570 return true;
4571 }
4572 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4573 return true;
4574 }
4575
4576 return false;
4577}
4578
4588function isValidMXRecord($domain)
4589{
4590 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4591 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4592 return 0;
4593 }
4594 if (function_exists('getmxrr')) {
4595 $mxhosts = array();
4596 $weight = array();
4597 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4598 if (count($mxhosts) > 1) {
4599 return 1;
4600 }
4601 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4602 return 1;
4603 }
4604
4605 return 0;
4606 }
4607 }
4608
4609 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4610 return -1;
4611}
4612
4620function isValidPhone($phone)
4621{
4622 return true;
4623}
4624
4625
4635function dolGetFirstLetters($s, $nbofchar = 1)
4636{
4637 $ret = '';
4638 $tmparray = explode(' ', $s);
4639 foreach ($tmparray as $tmps) {
4640 $ret .= dol_substr($tmps, 0, $nbofchar);
4641 }
4642
4643 return $ret;
4644}
4645
4646
4654function dol_strlen($string, $stringencoding = 'UTF-8')
4655{
4656 if (is_null($string)) {
4657 return 0;
4658 }
4659
4660 if (function_exists('mb_strlen')) {
4661 return mb_strlen($string, $stringencoding);
4662 } else {
4663 return strlen($string);
4664 }
4665}
4666
4677function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4678{
4679 global $langs;
4680
4681 if (empty($stringencoding)) {
4682 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4683 }
4684
4685 $ret = '';
4686 if (empty($trunconbytes)) {
4687 if (function_exists('mb_substr')) {
4688 $ret = mb_substr($string, $start, $length, $stringencoding);
4689 } else {
4690 $ret = substr($string, $start, $length);
4691 }
4692 } else {
4693 if (function_exists('mb_strcut')) {
4694 $ret = mb_strcut($string, $start, $length, $stringencoding);
4695 } else {
4696 $ret = substr($string, $start, $length);
4697 }
4698 }
4699 return $ret;
4700}
4701
4702
4716function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4717{
4718 global $conf;
4719
4720 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4721 return $string;
4722 }
4723
4724 if (empty($stringencoding)) {
4725 $stringencoding = 'UTF-8';
4726 }
4727 // reduce for small screen
4728 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4729 $size = round($size / 3);
4730 }
4731
4732 // We go always here
4733 if ($trunc == 'right') {
4734 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4735 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4736 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4737 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4738 } else {
4739 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4740 return $string;
4741 }
4742 } elseif ($trunc == 'middle') {
4743 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4744 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4745 $size1 = round($size / 2);
4746 $size2 = round($size / 2);
4747 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4748 } else {
4749 return $string;
4750 }
4751 } elseif ($trunc == 'left') {
4752 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4753 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4754 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4755 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4756 } else {
4757 return $string;
4758 }
4759 } elseif ($trunc == 'wrap') {
4760 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4761 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4762 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4763 } else {
4764 return $string;
4765 }
4766 } else {
4767 return 'BadParam3CallingDolTrunc';
4768 }
4769}
4770
4778function getPictoForType($key, $morecss = '')
4779{
4780 // Set array with type -> picto
4781 $type2picto = array(
4782 'varchar' => 'font',
4783 'text' => 'font',
4784 'html' => 'code',
4785 'int' => 'sort-numeric-down',
4786 'double' => 'sort-numeric-down',
4787 'price' => 'currency',
4788 'pricecy' => 'multicurrency',
4789 'password' => 'key',
4790 'boolean' => 'check-square',
4791 'date' => 'calendar',
4792 'datetime' => 'calendar',
4793 'phone' => 'phone',
4794 'mail' => 'email',
4795 'url' => 'url',
4796 'ip' => 'country',
4797 'select' => 'list',
4798 'sellist' => 'list',
4799 'radio' => 'check-circle',
4800 'checkbox' => 'check-square',
4801 'chkbxlst' => 'check-square',
4802 'link' => 'link',
4803 'icon' => "question",
4804 'point' => "country",
4805 'multipts' => 'country',
4806 'linestrg' => "country",
4807 'polygon' => "country",
4808 'separate' => 'minus'
4809 );
4810
4811 if (!empty($type2picto[$key])) {
4812 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4813 }
4814
4815 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4816}
4817
4818
4840function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4841{
4842 global $conf;
4843
4844 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4845 $url = DOL_URL_ROOT;
4846 $theme = isset($conf->theme) ? $conf->theme : null;
4847 $path = 'theme/'.$theme;
4848 if (empty($picto)) {
4849 $picto = 'generic';
4850 }
4851
4852 // Define fullpathpicto to use into src
4853 if ($pictoisfullpath) {
4854 // Clean parameters
4855 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4856 $picto .= '.png';
4857 }
4858 $fullpathpicto = $picto;
4859 $reg = array();
4860 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4861 $morecss .= ($morecss ? ' ' : '').$reg[1];
4862 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4863 }
4864 } else {
4865 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
4866 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4867 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4868
4869 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
4870 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
4871 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4872 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4873
4874 // Compatibility with old fontawesome versions
4875 if ($pictowithouttext == 'file-o') {
4876 $pictowithouttext = 'file';
4877 }
4878
4879 $pictowithouttextarray = explode('_', $pictowithouttext);
4880 $marginleftonlyshort = 0;
4881
4882 if (!empty($pictowithouttextarray[1])) {
4883 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4884 $fakey = 'fa-'.$pictowithouttextarray[0];
4885 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
4886 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4887 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4888 } else {
4889 $fakey = 'fa-'.$pictowithouttext;
4890 $faprefix = 'fas';
4891 $facolor = '';
4892 $fasize = '';
4893 }
4894
4895 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4896 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4897 $morestyle = '';
4898 $reg = array();
4899 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4900 $morecss .= ($morecss ? ' ' : '').$reg[1];
4901 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4902 }
4903 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4904 $morestyle = $reg[1];
4905 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4906 }
4907 $moreatt = trim($moreatt);
4908
4909 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4910 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4911 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4912 $enabledisablehtml .= $titlealt;
4913 }*/
4914 $enabledisablehtml .= '</span>';
4915
4916 return $enabledisablehtml;
4917 }
4918
4919 if (empty($srconly) && in_array($pictowithouttext, array(
4920 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4921 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
4922 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
4923 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4924 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4925 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
4926 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
4927 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
4928 'commercial', 'companies',
4929 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4930 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4931 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
4932 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4933 'hands-helping', 'help', 'holiday',
4934 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4935 'key', 'knowledgemanagement',
4936 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4937 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4938 'off', 'on', 'order',
4939 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4940 'stock', 'resize', 'service', 'stats',
4941 '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',
4942 'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4943 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4944 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4945 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4946 'technic', 'ticket',
4947 'error', 'warning',
4948 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4949 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4950 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
4951 'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4952 'conferenceorbooth', 'eventorganization',
4953 'stamp', 'signature',
4954 'webportal'
4955 ))) {
4956 $fakey = $pictowithouttext;
4957 $facolor = '';
4958 $fasize = '';
4959 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
4960 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'))) {
4961 $fa = 'far';
4962 }
4963 if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4964 $fa = 'fab';
4965 }
4966
4967 $arrayconvpictotofa = array(
4968 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
4969 'asset' => 'money-check-alt', 'autofill' => 'fill',
4970 'bank_account' => 'university',
4971 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
4972 'bookcal' => 'calendar-check',
4973 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
4974 'bom' => 'shapes',
4975 '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',
4976 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
4977 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
4978 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
4979 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
4980 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
4981 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
4982 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
4983 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
4984 'generic' => 'file', 'holiday' => 'umbrella-beach',
4985 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
4986 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
4987 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
4988 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
4989 'sign-out' => 'sign-out-alt',
4990 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
4991 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
4992 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
4993 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
4994 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
4995 'other' => 'square',
4996 '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',
4997 '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',
4998 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
4999 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5000 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5001 'service' => 'concierge-bell',
5002 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5003 'status' => 'stop-circle',
5004 'stripe' => 'stripe-s', 'supplier' => 'building',
5005 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5006 'title_agenda' => 'calendar-alt',
5007 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5008 'jabber' => 'comment-o',
5009 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5010 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5011 'webportal' => 'door-open'
5012 );
5013 if ($conf->currency == 'EUR') {
5014 $arrayconvpictotofa['currency'] = 'euro-sign';
5015 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5016 } else {
5017 $arrayconvpictotofa['currency'] = 'dollar-sign';
5018 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5019 }
5020 if ($pictowithouttext == 'off') {
5021 $fakey = 'fa-square';
5022 $fasize = '1.3em';
5023 } elseif ($pictowithouttext == 'on') {
5024 $fakey = 'fa-check-square';
5025 $fasize = '1.3em';
5026 } elseif ($pictowithouttext == 'listlight') {
5027 $fakey = 'fa-download';
5028 $marginleftonlyshort = 1;
5029 } elseif ($pictowithouttext == 'printer') {
5030 $fakey = 'fa-print';
5031 $fasize = '1.2em';
5032 } elseif ($pictowithouttext == 'note') {
5033 $fakey = 'fa-sticky-note';
5034 $marginleftonlyshort = 1;
5035 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5036 $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');
5037 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5038 if (preg_match('/selected/', $pictowithouttext)) {
5039 $facolor = '#888';
5040 }
5041 $marginleftonlyshort = 1;
5042 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5043 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5044 } else {
5045 $fakey = 'fa-'.$pictowithouttext;
5046 }
5047
5048 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5049 $morecss .= ' em092';
5050 }
5051 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5052 $morecss .= ' em088';
5053 }
5054 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5055 $morecss .= ' em080';
5056 }
5057
5058 // Define $marginleftonlyshort
5059 $arrayconvpictotomarginleftonly = array(
5060 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5061 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
5062 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5063 );
5064 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
5065 $marginleftonlyshort = 0;
5066 }
5067
5068 // Add CSS
5069 $arrayconvpictotomorcess = array(
5070 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5071 'bank_account' => 'infobox-bank_account',
5072 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5073 'bookcal' => 'infobox-action',
5074 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5075 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5076 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5077 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5078 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5079 'incoterm' => 'infobox-supplier_proposal',
5080 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5081 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5082 'order' => 'infobox-commande',
5083 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5084 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5085 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5086 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5087 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5088 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5089 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5090 'resource' => 'infobox-action',
5091 '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',
5092 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5093 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5094 'vat' => 'infobox-bank_account',
5095 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5096 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5097 );
5098 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5099 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5100 }
5101
5102 // Define $color
5103 $arrayconvpictotocolor = array(
5104 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5105 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5106 'dynamicprice' => '#a69944',
5107 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5108 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5109 'lock' => '#ddd', 'lot' => '#a69944',
5110 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5111 'other' => '#ddd', 'world' => '#986c6a',
5112 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5113 //'shipment'=>'#a69944',
5114 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5115 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5116 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5117 'website' => '#304', 'workstation' => '#a69944'
5118 );
5119 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5120 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5121 }
5122
5123 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5124 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5125 $morestyle = '';
5126 $reg = array();
5127 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5128 $morecss .= ($morecss ? ' ' : '').$reg[1];
5129 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5130 }
5131 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5132 $morestyle = $reg[1];
5133 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5134 }
5135 $moreatt = trim($moreatt);
5136
5137 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5138 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5139 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5140 $enabledisablehtml .= $titlealt;
5141 }*/
5142 $enabledisablehtml .= '</span>';
5143
5144 return $enabledisablehtml;
5145 }
5146
5147 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5148 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5149 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5150 $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
5151 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5152 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5153 }
5154
5155 // If we ask an image into $url/$mymodule/img (instead of default path)
5156 $regs = array();
5157 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5158 $picto = $regs[1];
5159 $path = $regs[2]; // $path is $mymodule
5160 }
5161
5162 // Clean parameters
5163 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5164 $picto .= '.png';
5165 }
5166 // If alt path are defined, define url where img file is, according to physical path
5167 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5168 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5169 if ($type == 'main') {
5170 continue;
5171 }
5172 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5173 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5174 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5175 break;
5176 }
5177 }
5178
5179 // $url is '' or '/custom', $path is current theme or
5180 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5181 }
5182
5183 if ($srconly) {
5184 return $fullpathpicto;
5185 }
5186
5187 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5188 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
5189}
5190
5204function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5205{
5206 if (strpos($picto, '^') === 0) {
5207 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5208 } else {
5209 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5210 }
5211}
5212
5224function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5225{
5226 global $conf;
5227
5228 if (is_numeric($picto)) {
5229 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5230 //$picto = $leveltopicto[$picto];
5231 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5232 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5233 $picto .= '.png';
5234 }
5235
5236 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5237
5238 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5239}
5240
5252function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5253{
5254 global $conf;
5255
5256 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5257 $picto .= '.png';
5258 }
5259
5260 if ($pictoisfullpath) {
5261 $path = $picto;
5262 } else {
5263 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5264
5265 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5266 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5267
5268 if (file_exists($themepath)) {
5269 $path = $themepath;
5270 }
5271 }
5272 }
5273
5274 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5275}
5276
5290function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5291{
5292 global $langs;
5293
5294 if (empty($titlealt) || $titlealt == 'default') {
5295 if ($numaction == '-1' || $numaction == 'ST_NO') {
5296 $numaction = -1;
5297 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5298 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5299 $numaction = 0;
5300 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5301 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5302 $numaction = 1;
5303 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5304 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5305 $numaction = 2;
5306 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5307 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5308 $numaction = 3;
5309 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5310 } else {
5311 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5312 $numaction = 0;
5313 }
5314 }
5315 if (!is_numeric($numaction)) {
5316 $numaction = 0;
5317 }
5318
5319 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5320}
5321
5329function img_pdf($titlealt = 'default', $size = 3)
5330{
5331 global $langs;
5332
5333 if ($titlealt == 'default') {
5334 $titlealt = $langs->trans('Show');
5335 }
5336
5337 return img_picto($titlealt, 'pdf'.$size.'.png');
5338}
5339
5347function img_edit_add($titlealt = 'default', $other = '')
5348{
5349 global $langs;
5350
5351 if ($titlealt == 'default') {
5352 $titlealt = $langs->trans('Add');
5353 }
5354
5355 return img_picto($titlealt, 'edit_add.png', $other);
5356}
5364function img_edit_remove($titlealt = 'default', $other = '')
5365{
5366 global $langs;
5367
5368 if ($titlealt == 'default') {
5369 $titlealt = $langs->trans('Remove');
5370 }
5371
5372 return img_picto($titlealt, 'edit_remove.png', $other);
5373}
5374
5383function img_edit($titlealt = 'default', $float = 0, $other = '')
5384{
5385 global $langs;
5386
5387 if ($titlealt == 'default') {
5388 $titlealt = $langs->trans('Modify');
5389 }
5390
5391 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5392}
5393
5402function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5403{
5404 global $langs;
5405
5406 if ($titlealt == 'default') {
5407 $titlealt = $langs->trans('View');
5408 }
5409
5410 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5411
5412 return img_picto($titlealt, 'eye', $moreatt);
5413}
5414
5423function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5424{
5425 global $langs;
5426
5427 if ($titlealt == 'default') {
5428 $titlealt = $langs->trans('Delete');
5429 }
5430
5431 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5432}
5433
5441function img_printer($titlealt = "default", $other = '')
5442{
5443 global $langs;
5444 if ($titlealt == "default") {
5445 $titlealt = $langs->trans("Print");
5446 }
5447 return img_picto($titlealt, 'printer.png', $other);
5448}
5449
5457function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5458{
5459 global $langs;
5460
5461 if ($titlealt == 'default') {
5462 $titlealt = $langs->trans('Split');
5463 }
5464
5465 return img_picto($titlealt, 'split.png', $other);
5466}
5467
5475function img_help($usehelpcursor = 1, $usealttitle = 1)
5476{
5477 global $langs;
5478
5479 if ($usealttitle) {
5480 if (is_string($usealttitle)) {
5481 $usealttitle = dol_escape_htmltag($usealttitle);
5482 } else {
5483 $usealttitle = $langs->trans('Info');
5484 }
5485 }
5486
5487 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5488}
5489
5496function img_info($titlealt = 'default')
5497{
5498 global $langs;
5499
5500 if ($titlealt == 'default') {
5501 $titlealt = $langs->trans('Informations');
5502 }
5503
5504 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5505}
5506
5515function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5516{
5517 global $langs;
5518
5519 if ($titlealt == 'default') {
5520 $titlealt = $langs->trans('Warning');
5521 }
5522
5523 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5524 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5525}
5526
5533function img_error($titlealt = 'default')
5534{
5535 global $langs;
5536
5537 if ($titlealt == 'default') {
5538 $titlealt = $langs->trans('Error');
5539 }
5540
5541 return img_picto($titlealt, 'error.png');
5542}
5543
5551function img_next($titlealt = 'default', $moreatt = '')
5552{
5553 global $langs;
5554
5555 if ($titlealt == 'default') {
5556 $titlealt = $langs->trans('Next');
5557 }
5558
5559 //return img_picto($titlealt, 'next.png', $moreatt);
5560 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5561}
5562
5570function img_previous($titlealt = 'default', $moreatt = '')
5571{
5572 global $langs;
5573
5574 if ($titlealt == 'default') {
5575 $titlealt = $langs->trans('Previous');
5576 }
5577
5578 //return img_picto($titlealt, 'previous.png', $moreatt);
5579 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5580}
5581
5590function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5591{
5592 global $langs;
5593
5594 if ($titlealt == 'default') {
5595 $titlealt = $langs->trans('Down');
5596 }
5597
5598 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5599}
5600
5609function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5610{
5611 global $langs;
5612
5613 if ($titlealt == 'default') {
5614 $titlealt = $langs->trans('Up');
5615 }
5616
5617 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5618}
5619
5628function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5629{
5630 global $langs;
5631
5632 if ($titlealt == 'default') {
5633 $titlealt = $langs->trans('Left');
5634 }
5635
5636 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5637}
5638
5647function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5648{
5649 global $langs;
5650
5651 if ($titlealt == 'default') {
5652 $titlealt = $langs->trans('Right');
5653 }
5654
5655 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5656}
5657
5665function img_allow($allow, $titlealt = 'default')
5666{
5667 global $langs;
5668
5669 if ($titlealt == 'default') {
5670 $titlealt = $langs->trans('Active');
5671 }
5672
5673 if ($allow == 1) {
5674 return img_picto($titlealt, 'tick.png');
5675 }
5676
5677 return '-';
5678}
5679
5687function img_credit_card($brand, $morecss = null)
5688{
5689 if (is_null($morecss)) {
5690 $morecss = 'fa-2x';
5691 }
5692
5693 if ($brand == 'visa' || $brand == 'Visa') {
5694 $brand = 'cc-visa';
5695 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5696 $brand = 'cc-mastercard';
5697 } elseif ($brand == 'amex' || $brand == 'American Express') {
5698 $brand = 'cc-amex';
5699 } elseif ($brand == 'discover' || $brand == 'Discover') {
5700 $brand = 'cc-discover';
5701 } elseif ($brand == 'jcb' || $brand == 'JCB') {
5702 $brand = 'cc-jcb';
5703 } elseif ($brand == 'diners' || $brand == 'Diners club') {
5704 $brand = 'cc-diners-club';
5705 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5706 $brand = 'credit-card';
5707 }
5708
5709 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5710}
5711
5720function img_mime($file, $titlealt = '', $morecss = '')
5721{
5722 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5723
5724 $mimetype = dol_mimetype($file, '', 1);
5725 $mimeimg = dol_mimetype($file, '', 2);
5726 $mimefa = dol_mimetype($file, '', 4);
5727
5728 if (empty($titlealt)) {
5729 $titlealt = 'Mime type: '.$mimetype;
5730 }
5731
5732 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5733 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5734}
5735
5736
5744function img_search($titlealt = 'default', $other = '')
5745{
5746 global $langs;
5747
5748 if ($titlealt == 'default') {
5749 $titlealt = $langs->trans('Search');
5750 }
5751
5752 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5753
5754 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5755 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5756
5757 return $input;
5758}
5759
5767function img_searchclear($titlealt = 'default', $other = '')
5768{
5769 global $langs;
5770
5771 if ($titlealt == 'default') {
5772 $titlealt = $langs->trans('Search');
5773 }
5774
5775 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5776
5777 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5778 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5779
5780 return $input;
5781}
5782
5795function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
5796{
5797 global $conf, $langs;
5798
5799 if ($infoonimgalt) {
5800 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5801 } else {
5802 if (empty($conf->use_javascript_ajax)) {
5803 $textfordropdown = '';
5804 }
5805
5806 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5807 $fa = 'info-circle';
5808 if ($picto == 'warning') {
5809 $fa = 'exclamation-triangle';
5810 }
5811 $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> ';
5812 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5813 $result .= ($nodiv ? '' : '</div>');
5814
5815 if ($textfordropdown) {
5816 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5817 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5818 jQuery(document).ready(function() {
5819 jQuery(".'.$class.'text").click(function() {
5820 console.log("toggle text");
5821 jQuery(".'.$class.'").toggle();
5822 });
5823 });
5824 </script>';
5825
5826 $result = $tmpresult.$result;
5827 }
5828 }
5829
5830 return $result;
5831}
5832
5833
5845function dol_print_error($db = null, $error = '', $errors = null)
5846{
5847 global $conf, $langs, $argv;
5848 global $dolibarr_main_prod;
5849
5850 $out = '';
5851 $syslog = '';
5852
5853 // If error occurs before the $lang object was loaded
5854 if (!$langs) {
5855 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5856 $langs = new Translate('', $conf);
5857 $langs->load("main");
5858 }
5859
5860 // Load translation files required by the error messages
5861 $langs->loadLangs(array('main', 'errors'));
5862
5863 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5864 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5865 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5866 $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";
5867 }
5868 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5869
5870 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5871 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5872 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5873 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5874 }
5875 if (function_exists("phpversion")) {
5876 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5877 }
5878 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5879 if (function_exists("php_uname")) {
5880 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5881 }
5882 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5883 $out .= "<br>\n";
5884 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5885 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5886 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5887 $out .= "<br>\n";
5888 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5889 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5890 } else { // Mode CLI
5891 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5892 $syslog .= "pid=".dol_getmypid();
5893 }
5894
5895 if (!empty($conf->modules)) {
5896 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
5897 }
5898
5899 if (is_object($db)) {
5900 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5901 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5902 $lastqueryerror = $db->lastqueryerror();
5903 if (!utf8_check($lastqueryerror)) {
5904 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5905 }
5906 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5907 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5908 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5909 $out .= "<br>\n";
5910 } else { // Mode CLI
5911 // No dol_escape_htmltag for output, we are in CLI mode
5912 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5913 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5914 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5915 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5916 }
5917 $syslog .= ", sql=".$db->lastquery();
5918 $syslog .= ", db_error=".$db->lasterror();
5919 }
5920
5921 if ($error || $errors) {
5922 // Merge all into $errors array
5923 if (is_array($error) && is_array($errors)) {
5924 $errors = array_merge($error, $errors);
5925 } elseif (is_array($error)) { // deprecated, use second parameters
5926 $errors = $error;
5927 } elseif (is_array($errors) && !empty($error)) {
5928 $errors = array_merge(array($error), $errors);
5929 } elseif (!empty($error)) {
5930 $errors = array_merge(array($error), array($errors));
5931 }
5932
5933 $langs->load("errors");
5934
5935 foreach ($errors as $msg) {
5936 if (empty($msg)) {
5937 continue;
5938 }
5939 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5940 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5941 } else { // Mode CLI
5942 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5943 }
5944 $syslog .= ", msg=".$msg;
5945 }
5946 }
5947 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5948 xdebug_print_function_stack();
5949 $out .= '<b>XDebug information:</b>'."<br>\n";
5950 $out .= 'File: '.xdebug_call_file()."<br>\n";
5951 $out .= 'Line: '.xdebug_call_line()."<br>\n";
5952 $out .= 'Function: '.xdebug_call_function()."<br>\n";
5953 $out .= "<br>\n";
5954 }
5955
5956 // Return a http header with error code if possible
5957 if (!headers_sent()) {
5958 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5959 top_httphead();
5960 }
5961 //http_response_code(500); // If we use 500, message is not output with some command line tools
5962 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
5963 }
5964
5965 if (empty($dolibarr_main_prod)) {
5966 print $out;
5967 } else {
5968 if (empty($langs->defaultlang)) {
5969 $langs->setDefaultLang();
5970 }
5971 $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.
5972 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5973 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";
5974 print $langs->trans("DolibarrHasDetectedError").'. ';
5975 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5976 if (!defined("MAIN_CORE_ERROR")) {
5977 define("MAIN_CORE_ERROR", 1);
5978 }
5979 }
5980
5981 dol_syslog("Error ".$syslog, LOG_ERR);
5982}
5983
5994function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5995{
5996 global $langs;
5997
5998 if (empty($email)) {
5999 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6000 }
6001
6002 $langs->load("errors");
6003 $now = dol_now();
6004
6005 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6006 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6007 if ($errormessage) {
6008 print '<br><br>'.$errormessage;
6009 }
6010 if (is_array($errormessages) && count($errormessages)) {
6011 foreach ($errormessages as $mesgtoshow) {
6012 print '<br><br>'.$mesgtoshow;
6013 }
6014 }
6015 print '</div></div>';
6016}
6017
6034function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6035{
6036 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6037}
6038
6057function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6058{
6059 global $langs, $form;
6060 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6061
6062 if ($moreattrib == 'class="right"') {
6063 $prefix .= 'right '; // For backward compatibility
6064 }
6065
6066 $sortorder = strtoupper($sortorder);
6067 $out = '';
6068 $sortimg = '';
6069
6070 $tag = 'th';
6071 if ($thead == 2) {
6072 $tag = 'div';
6073 }
6074
6075 $tmpsortfield = explode(',', $sortfield);
6076 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6077 $tmpfield = explode(',', $field);
6078 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6079
6080 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6081 $prefix = 'wrapcolumntitle '.$prefix;
6082 }
6083
6084 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6085 // If field is used as sort criteria we use a specific css class liste_titre_sel
6086 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6087 $liste_titre = 'liste_titre';
6088 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6089 $liste_titre = 'liste_titre_sel';
6090 }
6091
6092 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6093 //$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)).'"' : '');
6094 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6095 $tagstart .= '>';
6096
6097 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6098 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6099 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6100 $options = preg_replace('/&+/i', '&', $options);
6101 if (!preg_match('/^&/', $options)) {
6102 $options = '&'.$options;
6103 }
6104
6105 $sortordertouseinlink = '';
6106 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6107 if (preg_match('/^DESC/i', $sortorder)) {
6108 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6109 } else { // We reverse the var $sortordertouseinlink
6110 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6111 }
6112 } else { // We are on field that is the first current sorting criteria
6113 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6114 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6115 } else {
6116 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6117 }
6118 }
6119 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6120 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6121 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6122 $out .= '>';
6123 }
6124 if ($tooltip) {
6125 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6126 if (preg_match('/:\w+$/', $tooltip)) {
6127 $tmptooltip = explode(':', $tooltip);
6128 } else {
6129 $tmptooltip = array($tooltip);
6130 }
6131 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6132 } else {
6133 $out .= $langs->trans($name);
6134 }
6135
6136 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6137 $out .= '</a>';
6138 }
6139
6140 if (empty($thead) && $field) { // If this is a sort field
6141 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6142 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6143 $options = preg_replace('/&+/i', '&', $options);
6144 if (!preg_match('/^&/', $options)) {
6145 $options = '&'.$options;
6146 }
6147
6148 if (!$sortorder || ($field1 != $sortfield1)) {
6149 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6150 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6151 } else {
6152 if (preg_match('/^DESC/', $sortorder)) {
6153 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6154 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6155 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6156 }
6157 if (preg_match('/^ASC/', $sortorder)) {
6158 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6159 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6160 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6161 }
6162 }
6163 }
6164
6165 $tagend = '</'.$tag.'>';
6166
6167 $out = $tagstart.$sortimg.$out.$tagend;
6168
6169 return $out;
6170}
6171
6180function print_titre($title)
6181{
6182 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6183
6184 print '<div class="titre">'.$title.'</div>';
6185}
6186
6198function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6199{
6200 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6201}
6202
6216function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6217{
6218 $return = '';
6219
6220 if ($picto == 'setup') {
6221 $picto = 'generic';
6222 }
6223
6224 $return .= "\n";
6225 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
6226 $return .= '<tr class="titre">';
6227 if ($picto) {
6228 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6229 }
6230 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6231 $return .= '<div class="titre inline-block">';
6232 $return .= $title; // $title is already HTML sanitized content
6233 $return .= '</div>';
6234 $return .= '</td>';
6235 if (dol_strlen($morehtmlcenter)) {
6236 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6237 }
6238 if (dol_strlen($morehtmlright)) {
6239 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6240 }
6241 $return .= '</tr></table>'."\n";
6242
6243 return $return;
6244}
6245
6269function 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 = '')
6270{
6271 global $conf, $langs;
6272
6273 $savlimit = $limit;
6274 $savtotalnboflines = $totalnboflines;
6275 if (is_numeric($totalnboflines)) {
6276 $totalnboflines = abs($totalnboflines);
6277 }
6278
6279 $page = (int) $page;
6280
6281 if ($picto == 'setup') {
6282 $picto = 'title_setup.png';
6283 }
6284 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6285 $picto = 'title.gif';
6286 }
6287 if ($limit < 0) {
6288 $limit = $conf->liste_limit;
6289 }
6290
6291 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6292 $nextpage = 1;
6293 } else {
6294 $nextpage = 0;
6295 }
6296 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
6297
6298 print "\n";
6299 print "<!-- Begin title -->\n";
6300 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
6301
6302 // Left
6303
6304 if ($picto && $title) {
6305 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
6306 }
6307
6308 print '<td class="nobordernopadding valignmiddle col-title">';
6309 print '<div class="titre inline-block">';
6310 print $title; // $title may contains HTML
6311 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
6312 print '<span class="opacitymedium colorblack marginleftonly" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6313 }
6314 print '</div></td>';
6315
6316 // Center
6317 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6318 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6319 }
6320
6321 // Right
6322 print '<td class="nobordernopadding valignmiddle right col-right">';
6323 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6324 if ($sortfield) {
6325 $options .= "&sortfield=".urlencode($sortfield);
6326 }
6327 if ($sortorder) {
6328 $options .= "&sortorder=".urlencode($sortorder);
6329 }
6330 // Show navigation bar
6331 $pagelist = '';
6332 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6333 if ($totalnboflines) { // If we know total nb of lines
6334 // Define nb of extra page links before and after selected page + ... + first or last
6335 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6336
6337 if ($limit > 0) {
6338 $nbpages = ceil($totalnboflines / $limit);
6339 } else {
6340 $nbpages = 1;
6341 }
6342 $cpt = ($page - $maxnbofpage);
6343 if ($cpt < 0) {
6344 $cpt = 0;
6345 }
6346
6347 if ($cpt >= 1) {
6348 if (empty($pagenavastextinput)) {
6349 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6350 if ($cpt > 2) {
6351 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6352 } elseif ($cpt == 2) {
6353 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6354 }
6355 }
6356 }
6357
6358 do {
6359 if ($pagenavastextinput) {
6360 if ($cpt == $page) {
6361 $pagelist .= '<li class="pagination"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6362 $pagelist .= '/';
6363 }
6364 } else {
6365 if ($cpt == $page) {
6366 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6367 } else {
6368 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6369 }
6370 }
6371 $cpt++;
6372 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6373
6374 if (empty($pagenavastextinput)) {
6375 if ($cpt < $nbpages) {
6376 if ($cpt < $nbpages - 2) {
6377 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6378 } elseif ($cpt == $nbpages - 2) {
6379 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6380 }
6381 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6382 }
6383 } else {
6384 //var_dump($page.' '.$cpt.' '.$nbpages);
6385 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6386 }
6387 } else {
6388 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6389 }
6390 }
6391
6392 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6393 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
6394 }
6395
6396 // js to autoselect page field on focus
6397 if ($pagenavastextinput) {
6398 print ajax_autoselect('.pageplusone');
6399 }
6400
6401 print '</td>';
6402 print '</tr>';
6403
6404 print '</table>'."\n";
6405
6406 // Center
6407 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6408 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6409 }
6410
6411 print "<!-- End title -->\n\n";
6412}
6413
6430function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
6431{
6432 global $conf, $langs;
6433
6434 print '<div class="pagination"><ul>';
6435 if ($beforearrows) {
6436 print '<li class="paginationbeforearrows">';
6437 print $beforearrows;
6438 print '</li>';
6439 }
6440
6441 if (empty($hidenavigation)) {
6442 if ((int) $limit > 0 && empty($hideselectlimit)) {
6443 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6444 $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
6445 //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
6446 //$pagesizechoices.=',2:2';
6447 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6448 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6449 }
6450
6451 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6452 print '<li class="pagination">';
6453 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.'">';
6454 print '<datalist id="limitlist">';
6455 } else {
6456 print '<li class="paginationxxx valignmiddle">';
6457 print '<select id="limit" class="flat selectlimit nopadding maxwidth75 center" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6458 }
6459 $tmpchoice = explode(',', $pagesizechoices);
6460 $tmpkey = $limit.':'.$limit;
6461 if (!in_array($tmpkey, $tmpchoice)) {
6462 $tmpchoice[] = $tmpkey;
6463 }
6464 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6465 if (!in_array($tmpkey, $tmpchoice)) {
6466 $tmpchoice[] = $tmpkey;
6467 }
6468 asort($tmpchoice, SORT_NUMERIC);
6469 foreach ($tmpchoice as $val) {
6470 $selected = '';
6471 $tmp = explode(':', $val);
6472 $key = $tmp[0];
6473 $val = $tmp[1];
6474 if ($key != '' && $val != '') {
6475 if ((int) $key == (int) $limit) {
6476 $selected = ' selected="selected"';
6477 }
6478 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6479 }
6480 }
6481 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6482 print '</datalist>';
6483 } else {
6484 print '</select>';
6485 print ajax_combobox("limit", array(), 0, 0, 'resolve', -1, 'limit');
6486 //print ajax_combobox("limit");
6487 }
6488
6489 if ($conf->use_javascript_ajax) {
6490 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6491 <script>
6492 jQuery(document).ready(function () {
6493 jQuery(".selectlimit").change(function() {
6494 console.log("Change limit. Send submit");
6495 $(this).parents(\'form:first\').submit();
6496 });
6497 });
6498 </script>
6499 ';
6500 }
6501 print '</li>';
6502 }
6503 if ($page > 0) {
6504 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>';
6505 }
6506 if ($betweenarrows) {
6507 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6508 print $betweenarrows;
6509 print '<!--</div>-->';
6510 }
6511 if ($nextpage > 0) {
6512 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>';
6513 }
6514 if ($afterarrows) {
6515 print '<li class="paginationafterarrows">';
6516 print $afterarrows;
6517 print '</li>';
6518 }
6519 }
6520 print '</ul></div>'."\n";
6521}
6522
6523
6535function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6536{
6537 $morelabel = '';
6538
6539 if (preg_match('/%/', $rate)) {
6540 $rate = str_replace('%', '', $rate);
6541 $addpercent = true;
6542 }
6543 $reg = array();
6544 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6545 $morelabel = ' ('.$reg[1].')';
6546 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6547 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6548 }
6549 if (preg_match('/\*/', $rate)) {
6550 $rate = str_replace('*', '', $rate);
6551 $info_bits |= 1;
6552 }
6553
6554 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6555 if (!preg_match('/\//', $rate)) {
6556 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6557 } else {
6558 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6559 $ret = $rate.($addpercent ? '%' : '');
6560 }
6561 if (($info_bits & 1) && $usestarfornpr >= 0) {
6562 $ret .= ' *';
6563 }
6564 $ret .= $morelabel;
6565 return $ret;
6566}
6567
6568
6584function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6585{
6586 global $langs, $conf;
6587
6588 // Clean parameters
6589 if (empty($amount)) {
6590 $amount = 0; // To have a numeric value if amount not defined or = ''
6591 }
6592 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6593 if ($rounding == -1) {
6594 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6595 }
6596 $nbdecimal = $rounding;
6597
6598 if ($outlangs === 'none') {
6599 // Use international separators
6600 $dec = '.';
6601 $thousand = '';
6602 } else {
6603 // Output separators by default (french)
6604 $dec = ',';
6605 $thousand = ' ';
6606
6607 // If $outlangs not forced, we use use language
6608 if (!($outlangs instanceof Translate)) {
6609 $outlangs = $langs;
6610 }
6611
6612 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6613 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6614 }
6615 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6616 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6617 }
6618 if ($thousand == 'None') {
6619 $thousand = '';
6620 } elseif ($thousand == 'Space') {
6621 $thousand = ' ';
6622 }
6623 }
6624 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6625
6626 //print "amount=".$amount."-";
6627 $amount = str_replace(',', '.', $amount); // should be useless
6628 //print $amount."-";
6629 $data = explode('.', $amount);
6630 $decpart = isset($data[1]) ? $data[1] : '';
6631 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6632 //print "decpart=".$decpart."<br>";
6633 $end = '';
6634
6635 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6636 if (dol_strlen($decpart) > $nbdecimal) {
6637 $nbdecimal = dol_strlen($decpart);
6638 }
6639 // Si on depasse max
6640 $max_nbdecimal = getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN');
6641 if ($trunc && $nbdecimal > (int) $max_nbdecimal) {
6642 $nbdecimal = $max_nbdecimal;
6643 if (preg_match('/\.\.\./i', $nbdecimal)) {
6644 // Si un affichage est tronque, on montre des ...
6645 $end = '...';
6646 }
6647 }
6648
6649 // If force rounding
6650 if ((string) $forcerounding != '-1') {
6651 if ($forcerounding === 'MU') {
6652 $nbdecimal = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6653 } elseif ($forcerounding === 'MT') {
6654 $nbdecimal = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6655 } elseif ($forcerounding >= 0) {
6656 $nbdecimal = $forcerounding;
6657 }
6658 }
6659
6660 // Format number
6661 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6662 if ($form) {
6663 $output = preg_replace('/\s/', '&nbsp;', $output);
6664 $output = preg_replace('/\'/', '&#039;', $output);
6665 }
6666 // Add symbol of currency if requested
6667 $cursymbolbefore = $cursymbolafter = '';
6668 if ($currency_code && is_object($outlangs)) {
6669 if ($currency_code == 'auto') {
6670 $currency_code = $conf->currency;
6671 }
6672
6673 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6674 $listoflanguagesbefore = array('nl_NL');
6675 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6676 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6677 } else {
6678 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6679 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6680 }
6681 }
6682 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6683
6684 return $output;
6685}
6686
6711function price2num($amount, $rounding = '', $option = 0)
6712{
6713 global $langs, $conf;
6714
6715 // Clean parameters
6716 if (is_null($amount)) {
6717 $amount = '';
6718 }
6719
6720 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6721 // Numbers must be '1234.56'
6722 // Decimal delimiter for PHP and database SQL requests must be '.'
6723 $dec = ',';
6724 $thousand = ' ';
6725 if (is_null($langs)) { // $langs is not defined, we use english values.
6726 $dec = '.';
6727 $thousand = ',';
6728 } else {
6729 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6730 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6731 }
6732 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6733 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6734 }
6735 }
6736 if ($thousand == 'None') {
6737 $thousand = '';
6738 } elseif ($thousand == 'Space') {
6739 $thousand = ' ';
6740 }
6741 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6742
6743 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6744 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6745 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6746 if (!is_numeric($amount)) {
6747 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6748 }
6749
6750 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
6751 $amount = str_replace($thousand, '', $amount);
6752 }
6753
6754 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6755 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6756 // So if number was already a good number, it is converted into local Dolibarr setup.
6757 if (is_numeric($amount)) {
6758 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6759 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6760 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6761 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6762 $amount = number_format($amount, $nbofdec, $dec, $thousand);
6763 }
6764 //print "QQ".$amount."<br>\n";
6765
6766 // Now make replace (the main goal of function)
6767 if ($thousand != ',' && $thousand != '.') {
6768 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6769 }
6770
6771 $amount = str_replace(' ', '', $amount); // To avoid spaces
6772 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6773 $amount = str_replace($dec, '.', $amount);
6774
6775 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6776 }
6777 //print ' XX'.$amount.' '.$rounding;
6778
6779 // Now, $amount is a real PHP float number. We make a rounding if required.
6780 if ($rounding) {
6781 $nbofdectoround = '';
6782 if ($rounding == 'MU') {
6783 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6784 } elseif ($rounding == 'MT') {
6785 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6786 } elseif ($rounding == 'MS') {
6787 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6788 } elseif ($rounding == 'CU') {
6789 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6790 } elseif ($rounding == 'CT') {
6791 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
6792 } elseif (is_numeric($rounding)) {
6793 $nbofdectoround = (int) $rounding;
6794 }
6795
6796 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6797 if (dol_strlen($nbofdectoround)) {
6798 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6799 } else {
6800 return 'ErrorBadParameterProvidedToFunction';
6801 }
6802 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6803
6804 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6805 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6806 if (is_numeric($amount)) {
6807 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6808 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6809 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6810 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6811 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6812 }
6813 //print "TT".$amount.'<br>';
6814
6815 // Always make replace because each math function (like round) replace
6816 // with local values and we want a number that has a SQL string format x.y
6817 if ($thousand != ',' && $thousand != '.') {
6818 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6819 }
6820
6821 $amount = str_replace(' ', '', $amount); // To avoid spaces
6822 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6823 $amount = str_replace($dec, '.', $amount);
6824
6825 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6826 }
6827
6828 return $amount;
6829}
6830
6843function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6844{
6845 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6846
6847 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6848 $dimension *= 1000000;
6849 $unit -= 6;
6850 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6851 $dimension *= 1000;
6852 $unit -= 3;
6853 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6854 $dimension /= 1000000;
6855 $unit += 6;
6856 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6857 $dimension /= 1000;
6858 $unit += 3;
6859 }
6860 // Special case when we want output unit into pound or ounce
6861 /* TODO
6862 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6863 {
6864 $dimension = // convert dimension from standard unit into ounce or pound
6865 $unit = $forceunitoutput;
6866 }
6867 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6868 {
6869 $dimension = // convert dimension from standard unit into ounce or pound
6870 $unit = $forceunitoutput;
6871 }*/
6872
6873 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6874 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
6875 $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6876
6877 return $ret;
6878}
6879
6880
6893function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
6894{
6895 global $db, $conf, $mysoc;
6896
6897 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6898 $thirdparty_seller = $mysoc;
6899 }
6900
6901 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);
6902
6903 $vatratecleaned = $vatrate;
6904 $reg = array();
6905 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
6906 $vatratecleaned = trim($reg[1]);
6907 $vatratecode = $reg[2];
6908 }
6909
6910 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6911 {
6912 return 0;
6913 }*/
6914
6915 // Some test to guess with no need to make database access
6916 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6917 if ($local == 1) {
6918 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6919 return 0;
6920 }
6921 if ($thirdparty_seller->id == $mysoc->id) {
6922 if (!$thirdparty_buyer->localtax1_assuj) {
6923 return 0;
6924 }
6925 } else {
6926 if (!$thirdparty_seller->localtax1_assuj) {
6927 return 0;
6928 }
6929 }
6930 }
6931
6932 if ($local == 2) {
6933 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6934 if (!$mysoc->localtax2_assuj) {
6935 return 0; // If main vat is 0, IRPF may be different than 0.
6936 }
6937 if ($thirdparty_seller->id == $mysoc->id) {
6938 if (!$thirdparty_buyer->localtax2_assuj) {
6939 return 0;
6940 }
6941 } else {
6942 if (!$thirdparty_seller->localtax2_assuj) {
6943 return 0;
6944 }
6945 }
6946 }
6947 } else {
6948 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6949 return 0;
6950 }
6951 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6952 return 0;
6953 }
6954 }
6955
6956 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6957 if (in_array($mysoc->country_code, array('ES'))) {
6958 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6959 }
6960
6961 // Search local taxes
6962 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
6963 if ($local == 1) {
6964 if ($thirdparty_seller != $mysoc) {
6965 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6966 return $thirdparty_seller->localtax1_value;
6967 }
6968 } else { // i am the seller
6969 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6970 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6971 }
6972 }
6973 }
6974 if ($local == 2) {
6975 if ($thirdparty_seller != $mysoc) {
6976 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6977 // TODO We should also return value defined on thirdparty only if defined
6978 return $thirdparty_seller->localtax2_value;
6979 }
6980 } else { // i am the seller
6981 if (in_array($mysoc->country_code, array('ES'))) {
6982 return $thirdparty_buyer->localtax2_value;
6983 } else {
6984 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6985 }
6986 }
6987 }
6988 }
6989
6990 // By default, search value of local tax on line of common tax
6991 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6992 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6993 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6994 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6995 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
6996 if (!empty($vatratecode)) {
6997 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6998 } else {
6999 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7000 }
7001
7002 $resql = $db->query($sql);
7003
7004 if ($resql) {
7005 $obj = $db->fetch_object($resql);
7006 if ($obj) {
7007 if ($local == 1) {
7008 return $obj->localtax1;
7009 } elseif ($local == 2) {
7010 return $obj->localtax2;
7011 }
7012 }
7013 }
7014
7015 return 0;
7016}
7017
7018
7027function isOnlyOneLocalTax($local)
7028{
7029 $tax = get_localtax_by_third($local);
7030
7031 $valors = explode(":", $tax);
7032
7033 if (count($valors) > 1) {
7034 return false;
7035 } else {
7036 return true;
7037 }
7038}
7039
7046function get_localtax_by_third($local)
7047{
7048 global $db, $mysoc;
7049
7050 $sql = " SELECT t.localtax".$local." as localtax";
7051 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7052 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7053 $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";
7054 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7055 $sql .= " AND t.localtax".$local."_type <> '0'";
7056 $sql .= " ORDER BY t.rowid DESC";
7057
7058 $resql = $db->query($sql);
7059 if ($resql) {
7060 $obj = $db->fetch_object($resql);
7061 if ($obj) {
7062 return $obj->localtax;
7063 } else {
7064 return '0';
7065 }
7066 }
7067
7068 return 'Error';
7069}
7070
7071
7083function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7084{
7085 global $db;
7086
7087 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7088
7089 // Search local taxes
7090 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7091 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7092 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7093 if ($firstparamisid) {
7094 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7095 } else {
7096 $vatratecleaned = $vatrate;
7097 $vatratecode = '';
7098 $reg = array();
7099 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7100 $vatratecleaned = $reg[1];
7101 $vatratecode = $reg[2];
7102 }
7103
7104 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7105 /*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 ??
7106 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7107 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
7108 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7109 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7110 if ($vatratecode) {
7111 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7112 }
7113 }
7114
7115 $resql = $db->query($sql);
7116 if ($resql) {
7117 $obj = $db->fetch_object($resql);
7118 if ($obj) {
7119 return array(
7120 'rowid' => $obj->rowid,
7121 'code' => $obj->code,
7122 'rate' => $obj->rate,
7123 'localtax1' => $obj->localtax1,
7124 'localtax1_type' => $obj->localtax1_type,
7125 'localtax2' => $obj->localtax2,
7126 'localtax2_type' => $obj->localtax2_type,
7127 'npr' => $obj->npr,
7128 'accountancy_code_sell' => $obj->accountancy_code_sell,
7129 'accountancy_code_buy' => $obj->accountancy_code_buy
7130 );
7131 } else {
7132 return array();
7133 }
7134 } else {
7135 dol_print_error($db);
7136 }
7137
7138 return array();
7139}
7140
7157function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7158{
7159 global $db, $mysoc;
7160
7161 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7162
7163 // Search local taxes
7164 $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";
7165 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7166 if ($firstparamisid) {
7167 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7168 } else {
7169 $vatratecleaned = $vatrate;
7170 $vatratecode = '';
7171 $reg = array();
7172 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7173 $vatratecleaned = $reg[1];
7174 $vatratecode = $reg[2];
7175 }
7176
7177 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7178 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7179 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7180 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7181 } else {
7182 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7183 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7184 }
7185 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7186 if ($vatratecode) {
7187 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7188 }
7189 }
7190
7191 $resql = $db->query($sql);
7192 if ($resql) {
7193 $obj = $db->fetch_object($resql);
7194
7195 if ($obj) {
7196 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7197
7198 if ($local == 1) {
7199 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7200 } elseif ($local == 2) {
7201 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7202 } else {
7203 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);
7204 }
7205 }
7206 }
7207
7208 return array();
7209}
7210
7221function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7222{
7223 global $db, $mysoc;
7224
7225 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7226
7227 $ret = 0;
7228 $found = 0;
7229
7230 if ($idprod > 0) {
7231 // Load product
7232 $product = new Product($db);
7233 $product->fetch($idprod);
7234
7235 if (($mysoc->country_code == $thirdpartytouse->country_code)
7236 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7237 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7238 ) {
7239 // If country of thirdparty to consider is ours
7240 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7241 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7242 if ($result > 0) {
7243 $ret = $product->vatrate_supplier;
7244 if ($product->default_vat_code_supplier) {
7245 $ret .= ' ('.$product->default_vat_code_supplier.')';
7246 }
7247 $found = 1;
7248 }
7249 }
7250 if (!$found) {
7251 $ret = $product->tva_tx; // Default sales vat of product
7252 if ($product->default_vat_code) {
7253 $ret .= ' ('.$product->default_vat_code.')';
7254 }
7255 $found = 1;
7256 }
7257 } else {
7258 // TODO Read default product vat according to product and an other countrycode.
7259 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7260 }
7261 }
7262
7263 if (!$found) {
7264 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7265 // 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).
7266 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7267 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7268 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7269 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7270 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7271 $sql .= $db->plimit(1);
7272
7273 $resql = $db->query($sql);
7274 if ($resql) {
7275 $obj = $db->fetch_object($resql);
7276 if ($obj) {
7277 $ret = $obj->vat_rate;
7278 if ($obj->default_vat_code) {
7279 $ret .= ' ('.$obj->default_vat_code.')';
7280 }
7281 }
7282 $db->free($resql);
7283 } else {
7284 dol_print_error($db);
7285 }
7286 } else {
7287 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7288 // '1.23'
7289 // or '1.23 (CODE)'
7290 $defaulttx = '';
7291 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7292 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7293 }
7294 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7295 $defaultcode = $reg[1];
7296 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7297 }*/
7298
7299 $ret = $defaulttx;
7300 }
7301 }
7302
7303 dol_syslog("get_product_vat_for_country: ret=".$ret);
7304 return $ret;
7305}
7306
7316function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7317{
7318 global $db, $mysoc;
7319
7320 if (!class_exists('Product')) {
7321 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7322 }
7323
7324 $ret = 0;
7325 $found = 0;
7326
7327 if ($idprod > 0) {
7328 // Load product
7329 $product = new Product($db);
7330 $result = $product->fetch($idprod);
7331
7332 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7333 /* Not defined yet, so we don't use this
7334 if ($local==1) $ret=$product->localtax1_tx;
7335 elseif ($local==2) $ret=$product->localtax2_tx;
7336 $found=1;
7337 */
7338 } else {
7339 // TODO Read default product vat according to product and another countrycode.
7340 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7341 }
7342 }
7343
7344 if (!$found) {
7345 // If vat of product for the country not found or not defined, we return higher vat of country.
7346 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7347 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7348 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7349 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7350 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7351 $sql .= $db->plimit(1);
7352
7353 $resql = $db->query($sql);
7354 if ($resql) {
7355 $obj = $db->fetch_object($resql);
7356 if ($obj) {
7357 if ($local == 1) {
7358 $ret = $obj->localtax1;
7359 } elseif ($local == 2) {
7360 $ret = $obj->localtax2;
7361 }
7362 }
7363 } else {
7364 dol_print_error($db);
7365 }
7366 }
7367
7368 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7369 return $ret;
7370}
7371
7388function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7389{
7390 global $conf;
7391
7392 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7393
7394 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7395 $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;
7396
7397 $seller_country_code = $thirdparty_seller->country_code;
7398 $seller_in_cee = isInEEC($thirdparty_seller);
7399
7400 $buyer_country_code = $thirdparty_buyer->country_code;
7401 $buyer_in_cee = isInEEC($thirdparty_buyer);
7402
7403 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 : ''));
7404
7405 // 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)
7406 // we use the buyer VAT.
7407 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7408 if ($seller_in_cee && $buyer_in_cee) {
7409 $isacompany = $thirdparty_buyer->isACompany();
7410 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7411 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7412 if (!isValidVATID($thirdparty_buyer)) {
7413 $isacompany = 0;
7414 }
7415 }
7416
7417 if (!$isacompany) {
7418 //print 'VATRULE 0';
7419 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7420 }
7421 }
7422 }
7423
7424 // If seller does not use VAT, default VAT is 0. End of rule.
7425 if (!$seller_use_vat) {
7426 //print 'VATRULE 1';
7427 return 0;
7428 }
7429
7430 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7431 if (($seller_country_code == $buyer_country_code)
7432 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7433 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7434 ) { // Warning ->country_code not always defined
7435 //print 'VATRULE 2';
7436 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7437
7438 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7439 // Special case for india.
7440 //print 'VATRULE 2b';
7441 $reg = array();
7442 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7443 // we must revert the C+S into I
7444 $tmpvat = str_replace("C+S", "I", $tmpvat);
7445 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7446 // we must revert the I into C+S
7447 $tmpvat = str_replace("I", "C+S", $tmpvat);
7448 }
7449 }
7450
7451 return $tmpvat;
7452 }
7453
7454 // 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.
7455 // 'VATRULE 3' - Not supported
7456
7457 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7458 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7459 if (($seller_in_cee && $buyer_in_cee)) {
7460 $isacompany = $thirdparty_buyer->isACompany();
7461 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7462 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7463 if (!isValidVATID($thirdparty_buyer)) {
7464 $isacompany = 0;
7465 }
7466 }
7467
7468 if (!$isacompany) {
7469 //print 'VATRULE 4';
7470 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7471 } else {
7472 //print 'VATRULE 5';
7473 return 0;
7474 }
7475 }
7476
7477 // 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
7478 // I don't see any use case that need this rule.
7479 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7480 $isacompany = $thirdparty_buyer->isACompany();
7481 if (!$isacompany) {
7482 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7483 //print 'VATRULE extra';
7484 }
7485 }
7486
7487 // Otherwise the VAT proposed by default=0. End of rule.
7488 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7489 //print 'VATRULE 6';
7490 return 0;
7491}
7492
7493
7504function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7505{
7506 global $db;
7507
7508 if ($idprodfournprice > 0) {
7509 if (!class_exists('ProductFournisseur')) {
7510 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7511 }
7512 $prodprice = new ProductFournisseur($db);
7513 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7514 return $prodprice->fourn_tva_npr;
7515 } elseif ($idprod > 0) {
7516 if (!class_exists('Product')) {
7517 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7518 }
7519 $prod = new Product($db);
7520 $prod->fetch($idprod);
7521 return $prod->tva_npr;
7522 }
7523
7524 return 0;
7525}
7526
7540function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7541{
7542 global $mysoc;
7543
7544 if (!is_object($thirdparty_seller)) {
7545 return -1;
7546 }
7547 if (!is_object($thirdparty_buyer)) {
7548 return -1;
7549 }
7550
7551 if ($local == 1) { // Localtax 1
7552 if ($mysoc->country_code == 'ES') {
7553 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7554 return 0;
7555 }
7556 } else {
7557 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7558 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7559 return 0;
7560 }
7561 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7562 return 0;
7563 }
7564 }
7565 } elseif ($local == 2) { //I Localtax 2
7566 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7567 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7568 return 0;
7569 }
7570 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7571 return 0;
7572 }
7573 }
7574
7575 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7576 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7577 }
7578
7579 return 0;
7580}
7581
7590function yn($yesno, $case = 1, $color = 0)
7591{
7592 global $langs;
7593
7594 $result = 'unknown';
7595 $classname = '';
7596 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7597 $result = $langs->trans('yes');
7598 if ($case == 1 || $case == 3) {
7599 $result = $langs->trans("Yes");
7600 }
7601 if ($case == 2) {
7602 $result = '<input type="checkbox" value="1" checked disabled>';
7603 }
7604 if ($case == 3) {
7605 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7606 }
7607 if ($case == 4) {
7608 $result = img_picto('check', 'check');
7609 }
7610
7611 $classname = 'ok';
7612 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7613 $result = $langs->trans("no");
7614 if ($case == 1 || $case == 3) {
7615 $result = $langs->trans("No");
7616 }
7617 if ($case == 2) {
7618 $result = '<input type="checkbox" value="0" disabled>';
7619 }
7620 if ($case == 3) {
7621 $result = '<input type="checkbox" value="0" disabled> '.$result;
7622 }
7623 if ($case == 4) {
7624 $result = img_picto('uncheck', 'uncheck');
7625 }
7626
7627 if ($color == 2) {
7628 $classname = 'ok';
7629 } else {
7630 $classname = 'error';
7631 }
7632 }
7633 if ($color) {
7634 return '<span class="'.$classname.'">'.$result.'</span>';
7635 }
7636 return $result;
7637}
7638
7657function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
7658{
7659 if (empty($modulepart) && is_object($object)) {
7660 if (!empty($object->module)) {
7661 $modulepart = $object->module;
7662 } elseif (!empty($object->element)) {
7663 $modulepart = $object->element;
7664 }
7665 }
7666
7667 $path = '';
7668
7669 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
7670 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
7671 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
7672 $arrayforoldpath['product'] = 2;
7673 }
7674
7675 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7676 $level = $arrayforoldpath[$modulepart];
7677 }
7678
7679 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
7680 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
7681 if (empty($num) && is_object($object)) {
7682 $num = $object->id;
7683 }
7684 if (empty($alpha)) {
7685 $num = preg_replace('/([^0-9])/i', '', $num);
7686 } else {
7687 $num = preg_replace('/^.*\-/i', '', $num);
7688 }
7689 $num = substr("000".$num, -$level);
7690 if ($level == 1) {
7691 $path = substr($num, 0, 1);
7692 }
7693 if ($level == 2) {
7694 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
7695 }
7696 if ($level == 3) {
7697 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
7698 }
7699 } else {
7700 // We will enhance here a common way of forging path for document storage.
7701 // In a future, we may distribute directories on several levels depending on setup and object.
7702 // Here, $object->id, $object->ref and $modulepart are required.
7703 //var_dump($modulepart);
7704 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
7705 }
7706
7707 if (empty($withoutslash) && !empty($path)) {
7708 $path .= '/';
7709 }
7710
7711 return $path;
7712}
7713
7722function dol_mkdir($dir, $dataroot = '', $newmask = '')
7723{
7724 global $conf;
7725
7726 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
7727
7728 $dir_osencoded = dol_osencode($dir);
7729 if (@is_dir($dir_osencoded)) {
7730 return 0;
7731 }
7732
7733 $nberr = 0;
7734 $nbcreated = 0;
7735
7736 $ccdir = '';
7737 if (!empty($dataroot)) {
7738 // Remove data root from loop
7739 $dir = str_replace($dataroot.'/', '', $dir);
7740 $ccdir = $dataroot.'/';
7741 }
7742
7743 $cdir = explode("/", $dir);
7744 $num = count($cdir);
7745 for ($i = 0; $i < $num; $i++) {
7746 if ($i > 0) {
7747 $ccdir .= '/'.$cdir[$i];
7748 } else {
7749 $ccdir .= $cdir[$i];
7750 }
7751 $regs = array();
7752 if (preg_match("/^.:$/", $ccdir, $regs)) {
7753 continue; // If the Windows path is incomplete, continue with next directory
7754 }
7755
7756 // Attention, is_dir() can fail event if the directory exists
7757 // (i.e. according the open_basedir configuration)
7758 if ($ccdir) {
7759 $ccdir_osencoded = dol_osencode($ccdir);
7760 if (!@is_dir($ccdir_osencoded)) {
7761 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
7762
7763 umask(0);
7764 $dirmaskdec = octdec((string) $newmask);
7765 if (empty($newmask)) {
7766 $dirmaskdec = !getDolGlobalString('MAIN_UMASK') ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
7767 }
7768 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
7769 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
7770 // If the is_dir has returned a false information, we arrive here
7771 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
7772 $nberr++;
7773 } else {
7774 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
7775 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7776 $nbcreated++;
7777 }
7778 } else {
7779 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
7780 }
7781 }
7782 }
7783 return ($nberr ? -$nberr : $nbcreated);
7784}
7785
7786
7794function dolChmod($filepath, $newmask = '')
7795{
7796 global $conf;
7797
7798 if (!empty($newmask)) {
7799 @chmod($filepath, octdec($newmask));
7800 } elseif (getDolGlobalString('MAIN_UMASK')) {
7801 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
7802 }
7803}
7804
7805
7811function picto_required()
7812{
7813 return '<span class="fieldrequired">*</span>';
7814}
7815
7816
7833function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
7834{
7835 if (is_null($stringtoclean)) {
7836 return '';
7837 }
7838
7839 if ($removelinefeed == 2) {
7840 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
7841 }
7842 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
7843
7844 // 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)
7845 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7846
7847 $temp = str_replace('< ', '__ltspace__', $temp);
7848 $temp = str_replace('<:', '__lttwopoints__', $temp);
7849
7850 if ($strip_tags) {
7851 $temp = strip_tags($temp);
7852 } else {
7853 // 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).
7854 $pattern = "/<[^<>]+>/";
7855 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
7856 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
7857 // pass 2 - $temp after pass 2: 0000-021
7858 $tempbis = $temp;
7859 do {
7860 $temp = $tempbis;
7861 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
7862 $tempbis = preg_replace($pattern, '', $tempbis);
7863 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
7864 } while ($tempbis != $temp);
7865
7866 $temp = $tempbis;
7867
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 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
7870 }
7871
7872 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
7873
7874 // Remove also carriage returns
7875 if ($removelinefeed == 1) {
7876 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
7877 }
7878
7879 // And double quotes
7880 if ($removedoublespaces) {
7881 while (strpos($temp, " ")) {
7882 $temp = str_replace(" ", " ", $temp);
7883 }
7884 }
7885
7886 $temp = str_replace('__ltspace__', '< ', $temp);
7887 $temp = str_replace('__lttwopoints__', '<:', $temp);
7888
7889 return trim($temp);
7890}
7891
7907function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0)
7908{
7909 if (empty($allowed_tags)) {
7910 $allowed_tags = array(
7911 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
7912 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
7913 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
7914 );
7915 }
7916 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
7917 if ($allowiframe) {
7918 if (!in_array('iframe', $allowed_tags)) {
7919 $allowed_tags[] = "iframe";
7920 }
7921 }
7922 if ($allowlink) {
7923 if (!in_array('link', $allowed_tags)) {
7924 $allowed_tags[] = "link";
7925 }
7926 }
7927
7928 $allowed_tags_string = implode("><", $allowed_tags);
7929 $allowed_tags_string = '<'.$allowed_tags_string.'>';
7930
7931 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
7932
7933 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
7934
7935 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
7936 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
7937
7938 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
7939 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
7940
7941 $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
7942
7943 if ($cleanalsosomestyles) { // Clean for remaining html tags
7944 $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
7945 }
7946 if ($removeclassattribute) { // Clean for remaining html tags
7947 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
7948 }
7949
7950 // Remove 'javascript:' that we should not find into a text with
7951 // 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)).
7952 if ($cleanalsojavascript) {
7953 $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);
7954 }
7955
7956 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
7957
7958 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
7959
7960
7961 return $temp;
7962}
7963
7964
7977function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
7978{
7979 if (is_null($allowed_attributes)) {
7980 $allowed_attributes = array(
7981 "allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width",
7982 // HTML5
7983 "header", "footer", "nav", "section", "menu", "menuitem"
7984 );
7985 }
7986
7987 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
7988 $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
7989
7990 // Warning: loadHTML does not support HTML5 on old libxml versions.
7991 $dom = new DOMDocument('', 'UTF-8');
7992 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
7993 $savwarning = error_reporting();
7994 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
7995 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
7996 error_reporting($savwarning);
7997
7998 if ($dom instanceof DOMDocument) {
7999 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8000 $el = $els->item($i);
8001 if (!$el instanceof DOMElement) {
8002 continue;
8003 }
8004 $attrs = $el->attributes;
8005 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8006 //var_dump($attrs->item($ii));
8007 if (!empty($attrs->item($ii)->name)) {
8008 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8009 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8010 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8011 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8012 // If attribute is 'style'
8013 $valuetoclean = $attrs->item($ii)->value;
8014
8015 if (isset($valuetoclean)) {
8016 do {
8017 $oldvaluetoclean = $valuetoclean;
8018 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8019 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8020 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8021 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8022 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8023 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8024 }
8025
8026 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8027 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8028 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8029 } while ($oldvaluetoclean != $valuetoclean);
8030 }
8031
8032 $attrs->item($ii)->value = $valuetoclean;
8033 }
8034 }
8035 }
8036 }
8037 }
8038
8039 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8040 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8041
8042 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8043 $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
8044 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
8045 return trim($return);
8046 } else {
8047 return $stringtoclean;
8048 }
8049}
8050
8062function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8063{
8064 $temp = $stringtoclean;
8065 foreach ($disallowed_tags as $tagtoremove) {
8066 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8067 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8068 }
8069
8070 if ($cleanalsosomestyles) {
8071 $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
8072 }
8073
8074 return $temp;
8075}
8076
8077
8087function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8088{
8089 if ($nboflines == 1) {
8090 if (dol_textishtml($text)) {
8091 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8092 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8093 } else {
8094 if (isset($text)) {
8095 $firstline = preg_replace('/[\n\r].*/', '', $text);
8096 } else {
8097 $firstline = '';
8098 }
8099 }
8100 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8101 } else {
8102 $ishtml = 0;
8103 if (dol_textishtml($text)) {
8104 $text = preg_replace('/\n/', '', $text);
8105 $ishtml = 1;
8106 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8107 } else {
8108 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8109 }
8110
8111 $text = strtr($text, $repTable);
8112 if ($charset == 'UTF-8') {
8113 $pattern = '/(<br[^>]*>)/Uu';
8114 } else {
8115 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8116 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8117 }
8118 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8119
8120 $firstline = '';
8121 $i = 0;
8122 $countline = 0;
8123 $lastaddediscontent = 1;
8124 while ($countline < $nboflines && isset($a[$i])) {
8125 if (preg_match('/<br[^>]*>/', $a[$i])) {
8126 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8127 $firstline .= ($ishtml ? "<br>\n" : "\n");
8128 // Is it a br for a new line of after a printed line ?
8129 if (!$lastaddediscontent) {
8130 $countline++;
8131 }
8132 $lastaddediscontent = 0;
8133 }
8134 } else {
8135 $firstline .= $a[$i];
8136 $lastaddediscontent = 1;
8137 $countline++;
8138 }
8139 $i++;
8140 }
8141
8142 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8143 //unset($a);
8144 $ret = $firstline.($adddots ? '...' : '');
8145 //exit;
8146 return $ret;
8147 }
8148}
8149
8150
8162function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8163{
8164 if (is_null($stringtoencode)) {
8165 return '';
8166 }
8167
8168 if (!$nl2brmode) {
8169 return nl2br($stringtoencode, $forxml);
8170 } else {
8171 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8172 return $ret;
8173 }
8174}
8175
8185function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8186{
8187 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8188 // TODO using sandbox on inline html content is not possible yet with current browsers
8189 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8190 //$s .= $stringtoencode;
8191 //$s .= '</body></html></iframe>';
8192 return $stringtoencode;
8193 } else {
8194 $out = $stringtoencode;
8195
8196 do {
8197 $oldstringtoclean = $out;
8198
8199 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8200 try {
8201 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8202 if (LIBXML_VERSION < 20900) {
8203 // Avoid load of external entities (security problem).
8204 // Required only if LIBXML_VERSION < 20900
8205 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8206 libxml_disable_entity_loader(true);
8207 }
8208
8209 $dom = new DOMDocument();
8210 // Add a trick to solve pb with text without parent tag
8211 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8212 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8213
8214 if (dol_textishtml($out)) {
8215 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.$out.'</div>';
8216 } else {
8217 $out = '<?xml encoding="UTF-8"><div class="tricktoremove">'.dol_nl2br($out).'</div>';
8218 }
8219
8220 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8221 $out = trim($dom->saveHTML());
8222
8223 // Remove the trick added to solve pb with text without parent tag
8224 $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8225 $out = preg_replace('/<\/div>$/', '', $out);
8226 } catch (Exception $e) {
8227 // If error, invalid HTML string with no way to clean it
8228 //print $e->getMessage();
8229 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8230 }
8231 }
8232
8233 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && $check != 'restricthtmlallowunvalid') {
8234 try {
8235 // Try cleaning using tidy
8236 if (extension_loaded('tidy') && class_exists("tidy")) {
8237 //print "aaa".$out."\n";
8238
8239 // See options at https://tidy.sourceforge.net/docs/quickref.html
8240 $config = array(
8241 'clean' => false,
8242 'quote-marks' => false, // do not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8243 'doctype' => 'strict',
8244 'show-body-only' => true,
8245 "indent-attributes" => false,
8246 "vertical-space" => false,
8247 //'ident' => false, // Not always supported
8248 "wrap" => 0
8249 // HTML5 tags
8250 //'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',
8251 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8252 //'new-empty-tags' => 'command embed keygen source track wbr',
8253 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8254 );
8255
8256 // Tidy
8257 $tidy = new tidy();
8258 $out = $tidy->repairString($out, $config, 'utf8');
8259
8260 //print "xxx".$out;exit;
8261 }
8262 } catch (Exception $e) {
8263 // If error, invalid HTML string with no way to clean it
8264 //print $e->getMessage();
8265 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8266 }
8267 }
8268
8269 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8270 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8271
8272 // Clean some html entities that are useless so text is cleaner
8273 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8274
8275 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8276 // encoded using text entities) so we can then exclude all numeric entities.
8277 $out = preg_replace('/&#39;/i', '&apos;', $out);
8278
8279 // 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).
8280 // 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
8281 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8282 $out = preg_replace_callback(
8283 '/&#(x?[0-9][0-9a-f]+;?)/i',
8288 static function ($m) {
8289 return realCharForNumericEntities($m);
8290 },
8291 $out
8292 );
8293
8294 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8295 $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'.
8296
8297 // Keep only some html tags and remove also some 'javascript:' strings
8298 $out = dol_string_onlythesehtmltags($out, 0, ($check == 'restricthtmlallowclass' ? 0 : 1), 1);
8299
8300 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8301 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8303 }
8304
8305 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8306 $out = preg_replace('/&apos;/i', "&#39;", $out);
8307 } while ($oldstringtoclean != $out);
8308
8309 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8310 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8311 // 'url(' to avoid inline style like background: url(http...
8312 // '<link' to avoid <link href="http...">
8313 $reg = array();
8314 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8315 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8316 $nblinks = count($reg[0]);
8317 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8318 $out = 'ErrorTooManyLinksIntoHTMLString';
8319 }
8320
8321 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8322 if ($nblinks > 0) {
8323 $out = 'ErrorHTMLLinksNotAllowed';
8324 }
8325 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8326 $nblinks = 0;
8327 // Loop on each url in src= and url(
8328 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8329
8330 $matches = array();
8331 if (preg_match_all($pattern, $out, $matches)) {
8332 // URLs are into $matches[1]
8333 $urls = $matches[1];
8334
8335 // Affiche les URLs
8336 foreach ($urls as $url) {
8337 $nblinks++;
8338 echo "Found url = ".$url . "\n";
8339 }
8340 if ($nblinks > 0) {
8341 $out = 'ErrorHTMLExternalLinksNotAllowed';
8342 }
8343 }
8344 }
8345
8346 return $out;
8347 }
8348}
8349
8370function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8371{
8372 if (is_null($stringtoencode)) {
8373 return '';
8374 }
8375
8376 $newstring = $stringtoencode;
8377 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8378 $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.
8379 if ($removelasteolbr) {
8380 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8381 }
8382 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8383 $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
8384 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8385 $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
8386 } else {
8387 if ($removelasteolbr) {
8388 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8389 }
8390 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8391 }
8392 // Other substitutions that htmlentities does not do
8393 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8394 return $newstring;
8395}
8396
8404function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8405{
8406 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8407 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8408 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8409 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8410 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8411 return $ret;
8412}
8413
8420function dol_htmlcleanlastbr($stringtodecode)
8421{
8422 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8423 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8424 return $ret;
8425}
8426
8436function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8437{
8438 $newstring = $a;
8439 if ($keepsomeentities) {
8440 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8441 }
8442 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8443 if ($keepsomeentities) {
8444 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8445 }
8446 return $newstring;
8447}
8448
8460function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8461{
8462 return htmlentities($string, $flags, $encoding, $double_encode);
8463}
8464
8476function dol_string_is_good_iso($s, $clean = 0)
8477{
8478 $len = dol_strlen($s);
8479 $out = '';
8480 $ok = 1;
8481 for ($scursor = 0; $scursor < $len; $scursor++) {
8482 $ordchar = ord($s[$scursor]);
8483 //print $scursor.'-'.$ordchar.'<br>';
8484 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8485 $ok = 0;
8486 break;
8487 } elseif ($ordchar > 126 && $ordchar < 160) {
8488 $ok = 0;
8489 break;
8490 } elseif ($clean) {
8491 $out .= $s[$scursor];
8492 }
8493 }
8494 if ($clean) {
8495 return $out;
8496 }
8497 return $ok;
8498}
8499
8508function dol_nboflines($s, $maxchar = 0)
8509{
8510 if ($s == '') {
8511 return 0;
8512 }
8513 $arraystring = explode("\n", $s);
8514 $nb = count($arraystring);
8515
8516 return $nb;
8517}
8518
8519
8529function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8530{
8531 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8532 if (dol_textishtml($text)) {
8533 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8534 }
8535
8536 $text = strtr($text, $repTable);
8537 if ($charset == 'UTF-8') {
8538 $pattern = '/(<br[^>]*>)/Uu';
8539 } else {
8540 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8541 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8542 }
8543 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8544
8545 $nblines = (int) floor((count($a) + 1) / 2);
8546 // count possible auto line breaks
8547 if ($maxlinesize) {
8548 foreach ($a as $line) {
8549 if (dol_strlen($line) > $maxlinesize) {
8550 //$line_dec = html_entity_decode(strip_tags($line));
8551 $line_dec = html_entity_decode($line);
8552 if (dol_strlen($line_dec) > $maxlinesize) {
8553 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8554 $nblines += substr_count($line_dec, '\n');
8555 }
8556 }
8557 }
8558 }
8559
8560 unset($a);
8561 return $nblines;
8562}
8563
8572function dol_textishtml($msg, $option = 0)
8573{
8574 if (is_null($msg)) {
8575 return false;
8576 }
8577
8578 if ($option == 1) {
8579 if (preg_match('/<html/i', $msg)) {
8580 return true;
8581 } elseif (preg_match('/<body/i', $msg)) {
8582 return true;
8583 } elseif (preg_match('/<\/textarea/i', $msg)) {
8584 return true;
8585 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8586 return true;
8587 } elseif (preg_match('/<br/i', $msg)) {
8588 return true;
8589 }
8590 return false;
8591 } else {
8592 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
8593 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
8594 if (preg_match('/<html/i', $msg)) {
8595 return true;
8596 } elseif (preg_match('/<body/i', $msg)) {
8597 return true;
8598 } elseif (preg_match('/<\/textarea/i', $msg)) {
8599 return true;
8600 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
8601 return true;
8602 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
8603 return true;
8604 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
8605 return true;
8606 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
8607 return true;
8608 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
8609 return true; // must accept <img src="http://example.com/aaa.png" />
8610 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
8611 return true; // must accept <a href="http://example.com/aaa.png" />
8612 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
8613 return true;
8614 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
8615 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
8616 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
8617 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
8618 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
8619 }
8620
8621 return false;
8622 }
8623}
8624
8639function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
8640{
8641 if (!empty($invert)) {
8642 $tmp = $text1;
8643 $text1 = $text2;
8644 $text2 = $tmp;
8645 }
8646
8647 $ret = '';
8648 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
8649 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
8650 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
8651 return $ret;
8652}
8653
8654
8655
8669function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
8670{
8671 global $db, $conf, $mysoc, $user, $extrafields;
8672
8673 $substitutionarray = array();
8674
8675 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include))) {
8676 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
8677 // this will include signature content first and then replace var found into content of signature
8678 //var_dump($onlykey);
8679 $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()
8680 $usersignature = $user->signature;
8681 $substitutionarray = array_merge($substitutionarray, array(
8682 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
8683 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
8684 ));
8685
8686 if (is_object($user) && ($user instanceof User)) {
8687 $substitutionarray = array_merge($substitutionarray, array(
8688 '__USER_ID__' => (string) $user->id,
8689 '__USER_LOGIN__' => (string) $user->login,
8690 '__USER_EMAIL__' => (string) $user->email,
8691 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
8692 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
8693 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
8694 '__USER_FAX__' => (string) $user->office_fax,
8695 '__USER_LASTNAME__' => (string) $user->lastname,
8696 '__USER_FIRSTNAME__' => (string) $user->firstname,
8697 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
8698 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
8699 '__USER_JOB__' => (string) $user->job,
8700 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
8701 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
8702 ));
8703 }
8704 }
8705 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
8706 $substitutionarray = array_merge($substitutionarray, array(
8707 '__MYCOMPANY_NAME__' => $mysoc->name,
8708 '__MYCOMPANY_EMAIL__' => $mysoc->email,
8709 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
8710 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
8711 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
8712 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
8713 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
8714 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
8715 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
8716 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
8717 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
8718 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
8719 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
8720 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
8721 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
8722 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
8723 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
8724 '__MYCOMPANY_ZIP__' => $mysoc->zip,
8725 '__MYCOMPANY_TOWN__' => $mysoc->town,
8726 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
8727 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
8728 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
8729 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
8730 ));
8731 }
8732
8733 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
8734 if ($onlykey) {
8735 $substitutionarray['__ID__'] = '__ID__';
8736 $substitutionarray['__REF__'] = '__REF__';
8737 $substitutionarray['__NEWREF__'] = '__NEWREF__';
8738 $substitutionarray['__LABEL__'] = '__LABEL__';
8739 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
8740 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
8741 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
8742 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
8743 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
8744
8745 if (isModEnabled("societe")) { // Most objects are concerned
8746 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
8747 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
8748 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
8749 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
8750 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
8751 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
8752 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
8753 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
8754 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
8755 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
8756 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
8757 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
8758 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
8759 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
8760 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
8761 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
8762 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
8763 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
8764 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
8765 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
8766 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
8767 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
8768 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
8769 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
8770 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
8771 }
8772 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
8773 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
8774 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
8775 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
8776 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
8777 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
8778 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
8779 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
8780 }
8781 // add substitution variables for ticket
8782 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
8783 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
8784 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
8785 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
8786 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
8787 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
8788 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
8789 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
8790 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
8791 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
8792 }
8793
8794 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
8795 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
8796 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
8797 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
8798 }
8799 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
8800 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
8801 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
8802 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
8803 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
8804 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
8805 }
8806 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
8807 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
8808 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
8809 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
8810 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
8811 }
8812 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
8813 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
8814 }
8815 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
8816 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
8817 }
8818 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
8819 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
8820 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
8821 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
8822 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
8823 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
8824 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
8825
8826 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
8827 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
8828 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
8829 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
8830 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
8831
8832 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
8833 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
8834 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
8835 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
8836 }
8837 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
8838 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
8839 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
8840 }
8841 } else {
8842 '@phan-var-force Adherent|Delivery $object';
8843 $substitutionarray['__ID__'] = $object->id;
8844 $substitutionarray['__REF__'] = $object->ref;
8845 $substitutionarray['__NEWREF__'] = $object->newref;
8846 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
8847 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8848 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8849 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
8850 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
8851 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', 0, $outputlangs) : '');
8852 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', 0, $outputlangs) : '');
8853 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', 0, $outputlangs) : '');
8854 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
8855 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%d") : '');
8856 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%A") : '');
8857 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%m") : '');
8858 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%b") : '');
8859 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%Y") : '');
8860 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%H") : '');
8861 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%M") : '');
8862 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, "%S") : '');
8863
8864 // For backward compatibility (deprecated)
8865 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
8866 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
8867 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->delivery_date) ? dol_print_date($object->delivery_date, 'day', 0, $outputlangs) : '');
8868 $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 : '')) : '');
8869 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
8870
8871 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
8872 '@phan-var-force Adherent $object';
8873 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
8874
8875 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
8876 if (method_exists($object, 'getCivilityLabel')) {
8877 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
8878 }
8879 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
8880 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
8881 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
8882 if (method_exists($object, 'getFullName')) {
8883 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
8884 }
8885 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
8886 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
8887 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
8888 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
8889 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
8890 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
8891 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
8892 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
8893 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
8894 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
8895 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
8896 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
8897 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
8898 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
8899 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
8900
8901 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
8902 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
8903 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
8904 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
8905 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
8906 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
8907 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
8908 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
8909 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
8910 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
8911 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
8912 }
8913
8914 if (is_object($object) && $object->element == 'societe') {
8915 '@phan-var-force Societe $object';
8916 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
8917 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
8918 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
8919 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
8920 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
8921 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
8922 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
8923 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
8924 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
8925 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
8926 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
8927 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
8928 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
8929 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
8930 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
8931 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
8932 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
8933 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
8934 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
8935 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
8936 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
8937 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
8938 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
8939 } elseif (is_object($object->thirdparty)) {
8940 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
8941 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
8942 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
8943 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
8944 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
8945 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
8946 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
8947 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
8948 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
8949 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
8950 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
8951 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
8952 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
8953 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
8954 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
8955 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
8956 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
8957 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
8958 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
8959 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
8960 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
8961 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
8962 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
8963 }
8964
8965 if (is_object($object) && $object->element == 'recruitmentcandidature') {
8966 '@phan-var-force RecruitmentCandidature $object';
8967 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
8968 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
8969 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
8970 }
8971 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
8972 '@phan-var-force ConferenceOrBoothAttendee $object';
8973 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
8974 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
8975 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
8976 }
8977
8978 if (is_object($object) && $object->element == 'project') {
8979 '@phan-var-force Project $object';
8980 $substitutionarray['__PROJECT_ID__'] = $object->id;
8981 $substitutionarray['__PROJECT_REF__'] = $object->ref;
8982 $substitutionarray['__PROJECT_NAME__'] = $object->title;
8983 } elseif (is_object($object)) {
8984 $project = null;
8985 if (!empty($object->project)) {
8986 $project = $object->project;
8987 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
8988 $project = $object->projet;
8989 }
8990 if (!is_null($project) && is_object($project)) {
8991 $substitutionarray['__PROJECT_ID__'] = $project->id;
8992 $substitutionarray['__PROJECT_REF__'] = $project->ref;
8993 $substitutionarray['__PROJECT_NAME__'] = $project->title;
8994 } else {
8995 // can substitute variables for project : uses lazy load in "make_substitutions" method
8996 $project_id = 0;
8997 if (!empty($object->fk_project) && $object->fk_project > 0) {
8998 $project_id = $object->fk_project;
8999 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9000 $project_id = $object->fk_project;
9001 }
9002 if ($project_id > 0) {
9003 // path:class:method:id
9004 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9005 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9006 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9007 }
9008 }
9009 }
9010
9011 if (is_object($object) && $object->element == 'facture') {
9012 '@phan-var-force Facture $object';
9013 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9014 }
9015 if (is_object($object) && $object->element == 'shipping') {
9016 '@phan-var-force Expedition $object';
9017 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9018 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9019 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9020 }
9021 if (is_object($object) && $object->element == 'reception') {
9022 '@phan-var-force Reception $object';
9023 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9024 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9025 }
9026
9027 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9028 '@phan-var-force Contrat $object';
9029 $dateplannedstart = '';
9030 $datenextexpiration = '';
9031 foreach ($object->lines as $line) {
9032 if ($line->date_start > $dateplannedstart) {
9033 $dateplannedstart = $line->date_start;
9034 }
9035 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9036 $datenextexpiration = $line->date_end;
9037 }
9038 }
9039 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9040 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9041 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9042
9043 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9044 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9045 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9046 }
9047 // add substitution variables for ticket
9048 if (is_object($object) && $object->element == 'ticket') {
9049 '@phan-var-force Ticket $object';
9050 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9051 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9052 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9053 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9054 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9055 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9056 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9057 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9058 $userstat = new User($db);
9059 if ($object->fk_user_assign > 0) {
9060 $userstat->fetch($object->fk_user_assign);
9061 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9062 }
9063
9064 if ($object->fk_user_create > 0) {
9065 $userstat->fetch($object->fk_user_create);
9066 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9067 }
9068 }
9069
9070 // Create dynamic tags for __EXTRAFIELD_FIELD__
9071 if ($object->table_element && $object->id > 0) {
9072 if (!is_object($extrafields)) {
9073 $extrafields = new ExtraFields($db);
9074 }
9075 $extrafields->fetch_name_optionals_label($object->table_element, true);
9076
9077 if ($object->fetch_optionals() > 0) {
9078 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9079 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9080 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9081 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9082 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9083 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9084 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9085 $datetime = $object->array_options['options_'.$key];
9086 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9087 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9088 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9089 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9090 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9091 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9092 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9093 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9094 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9095 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9096 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9097 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9098 }
9099 }
9100 }
9101 }
9102 }
9103
9104 // Complete substitution array with the url to make online payment
9105 if (empty($substitutionarray['__REF__'])) {
9106 $paymenturl = '';
9107 } else {
9108 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9109 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9110 $outputlangs->loadLangs(array('paypal', 'other'));
9111
9112 $amounttouse = 0;
9113 $typeforonlinepayment = 'free';
9114 if (is_object($object) && $object->element == 'commande') {
9115 $typeforonlinepayment = 'order';
9116 }
9117 if (is_object($object) && $object->element == 'facture') {
9118 $typeforonlinepayment = 'invoice';
9119 }
9120 if (is_object($object) && $object->element == 'member') {
9121 $typeforonlinepayment = 'member';
9122 if (!empty($object->last_subscription_amount)) {
9123 $amounttouse = $object->last_subscription_amount;
9124 }
9125 }
9126 if (is_object($object) && $object->element == 'contrat') {
9127 $typeforonlinepayment = 'contract';
9128 }
9129 if (is_object($object) && $object->element == 'fichinter') {
9130 $typeforonlinepayment = 'ficheinter';
9131 }
9132
9133 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9134 $paymenturl = $url;
9135 }
9136
9137 if ($object->id > 0) {
9138 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9139 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9140
9141 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9142 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9143 } else {
9144 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9145 }
9146 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9147 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9148 } else {
9149 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9150 }
9151 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9152 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9153 } else {
9154 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9155 }
9156 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9157 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9158 } else {
9159 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9160 }
9161 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9162 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9163 } else {
9164 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9165 }
9166 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9167 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9168 } else {
9169 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9170 }
9171
9172 if (is_object($object) && $object->element == 'propal') {
9173 '@phan-var-force Propal $object';
9174 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9175 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9176 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9177 }
9178 if (is_object($object) && $object->element == 'commande') {
9179 '@phan-var-force Commande $object';
9180 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9181 }
9182 if (is_object($object) && $object->element == 'facture') {
9183 '@phan-var-force Facture $object';
9184 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9185 }
9186 if (is_object($object) && $object->element == 'contrat') {
9187 '@phan-var-force Contrat $object';
9188 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9189 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9190 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9191 }
9192 if (is_object($object) && $object->element == 'fichinter') {
9193 '@phan-var-force Fichinter $object';
9194 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9195 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9196 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9197 }
9198 if (is_object($object) && $object->element == 'supplier_proposal') {
9199 '@phan-var-force SupplierProposal $object';
9200 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9201 }
9202 if (is_object($object) && $object->element == 'invoice_supplier') {
9203 '@phan-var-force FactureFournisseur $object';
9204 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9205 }
9206 if (is_object($object) && $object->element == 'shipping') {
9207 '@phan-var-force Expedition $object';
9208 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9209 }
9210 }
9211
9212 if (is_object($object) && $object->element == 'action') {
9213 '@phan-var-force ActionComm $object';
9214 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9215 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9216 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9217 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9218 }
9219 }
9220 }
9221 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9222 '@phan-var-force Facture|FactureRec $object';
9223 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9224
9225 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
9226 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
9227 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', 0, $outputlangs) : null) : '';
9228 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', 0, $outputlangs) : null) : '';
9229
9230 $already_payed_all = 0;
9231 if (is_object($object) && ($object instanceof Facture)) {
9232 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9233 }
9234
9235 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9236 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9237 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9238
9239 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9240 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9241 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9242
9243 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9244
9245 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9246 $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)) : '';
9247 $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)) : '';
9248
9249 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9250 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9251 }
9252 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9253 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9254 }
9255
9256 // Amount keys formatted in a currency
9257 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9258 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9259 $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) : '';
9260 $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)) : '';
9261 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9262 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9263 }
9264 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9265 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9266 }
9267 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9268 if ($onlykey != 2) {
9269 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9270 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9271 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9272 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9273 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9274 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9275 }
9276 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9277 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9278 }
9279 }
9280
9281 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9282 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9283 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9284 // TODO Add other keys for foreign multicurrency
9285
9286 // For backward compatibility
9287 if ($onlykey != 2) {
9288 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9289 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9290 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9291 }
9292 }
9293
9294
9295 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9296 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9297
9298 $now = dol_now();
9299
9300 $tmp = dol_getdate($now, true);
9301 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9302 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9303 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9304 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9305
9306 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9307
9308 $substitutionarray = array_merge($substitutionarray, array(
9309 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9310 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9311 '__DAY__' => (string) $tmp['mday'],
9312 '__DAY_TEXT__' => $daytext, // Monday
9313 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9314 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9315 '__MONTH__' => (string) $tmp['mon'],
9316 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9317 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9318 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9319 '__YEAR__' => (string) $tmp['year'],
9320 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9321 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9322 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9323 '__NEXT_DAY__' => (string) $tmp4['day'],
9324 '__NEXT_MONTH__' => (string) $tmp5['month'],
9325 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9326 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9327 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9328 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9329 ));
9330 }
9331
9332 if (isModEnabled('multicompany')) {
9333 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9334 }
9335 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9336 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9337 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9338 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9339 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9340 }
9341
9342 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9343
9344 return $substitutionarray;
9345}
9346
9363function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9364{
9365 global $conf, $db, $langs;
9366
9367 if (!is_array($substitutionarray)) {
9368 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9369 }
9370
9371 if (empty($outputlangs)) {
9372 $outputlangs = $langs;
9373 }
9374
9375 // Is initial text HTML or simple text ?
9376 $msgishtml = 0;
9377 if (dol_textishtml($text, 1)) {
9378 $msgishtml = 1;
9379 }
9380
9381 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9382 if (is_object($outputlangs)) {
9383 $reg = array();
9384 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9385 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9386 $tmp = explode('|', $reg[1]);
9387 if (!empty($tmp[1])) {
9388 $outputlangs->load($tmp[1]);
9389 }
9390
9391 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9392
9393 if (empty($converttextinhtmlifnecessary)) {
9394 // convert $newval into HTML is necessary
9395 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9396 } else {
9397 if (! $msgishtml) {
9398 $valueishtml = dol_textishtml($value, 1);
9399 //var_dump("valueishtml=".$valueishtml);
9400
9401 if ($valueishtml) {
9402 $text = dol_htmlentitiesbr($text);
9403 $msgishtml = 1;
9404 }
9405 } else {
9406 $value = dol_nl2br("$value");
9407 }
9408
9409 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9410 }
9411 }
9412 }
9413
9414 // Make substitution for constant keys.
9415 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9416 $reg = array();
9417 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9418 $keyfound = $reg[1];
9419 if (isASecretKey($keyfound)) {
9420 $value = '*****forbidden*****';
9421 } else {
9422 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9423 }
9424
9425 if (empty($converttextinhtmlifnecessary)) {
9426 // convert $newval into HTML is necessary
9427 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9428 } else {
9429 if (! $msgishtml) {
9430 $valueishtml = dol_textishtml($value, 1);
9431
9432 if ($valueishtml) {
9433 $text = dol_htmlentitiesbr($text);
9434 $msgishtml = 1;
9435 }
9436 } else {
9437 $value = dol_nl2br("$value");
9438 }
9439
9440 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9441 }
9442 }
9443
9444 // Make substitution for array $substitutionarray
9445 foreach ($substitutionarray as $key => $value) {
9446 if (!isset($value)) {
9447 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9448 }
9449
9450 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9451 $value = ''; // Protection
9452 }
9453
9454 if (empty($converttextinhtmlifnecessary)) {
9455 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9456 } else {
9457 if (! $msgishtml) {
9458 $valueishtml = dol_textishtml($value, 1);
9459
9460 if ($valueishtml) {
9461 $text = dol_htmlentitiesbr($text);
9462 $msgishtml = 1;
9463 }
9464 } else {
9465 $value = dol_nl2br("$value");
9466 }
9467 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9468 }
9469 }
9470
9471 // TODO Implement the lazyload substitution
9472 /*
9473 add a loop to scan $substitutionarray:
9474 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.
9475 If no, we don't need to make replacement, so we do nothing.
9476 If yes, we can make the substitution:
9477
9478 include_once $path;
9479 $tmpobj = new $class($db);
9480 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9481 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9482 */
9483 $memory_object_list = array();
9484 foreach ($substitutionarray as $key => $value) {
9485 $lazy_load_arr = array();
9486 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9487 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9488 $key_to_substitute = $lazy_load_arr[1];
9489 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9490 $param_arr = explode(':', $value);
9491 // path:class:method:id
9492 if (count($param_arr) == 4) {
9493 $path = $param_arr[0];
9494 $class = $param_arr[1];
9495 $method = $param_arr[2];
9496 $id = (int) $param_arr[3];
9497
9498 // load class file and init object list in memory
9499 if (!isset($memory_object_list[$class])) {
9500 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9501 require_once DOL_DOCUMENT_ROOT . $path;
9502 if (class_exists($class)) {
9503 $memory_object_list[$class] = array(
9504 'list' => array(),
9505 );
9506 }
9507 }
9508 }
9509
9510 // fetch object and set substitution
9511 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9512 if (method_exists($class, $method)) {
9513 if (!isset($memory_object_list[$class]['list'][$id])) {
9514 $tmpobj = new $class($db);
9515 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9516 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9517 $memory_object_list[$class]['list'][$id] = $tmpobj;
9518 } else {
9519 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9520 $tmpobj = $memory_object_list[$class]['list'][$id];
9521 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9522 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9523 }
9524
9525 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9526 }
9527 }
9528 }
9529 }
9530 }
9531 }
9532 }
9533
9534 return $text;
9535}
9536
9549function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
9550{
9551 global $conf, $user;
9552
9553 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9554
9555 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
9556
9557 // Check if there is external substitution to do, requested by plugins
9558 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
9559
9560 foreach ($dirsubstitutions as $reldir) {
9561 $dir = dol_buildpath($reldir, 0);
9562
9563 // Check if directory exists
9564 if (!dol_is_dir($dir)) {
9565 continue;
9566 }
9567
9568 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
9569 foreach ($substitfiles as $substitfile) {
9570 $reg = array();
9571 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
9572 $module = $reg[1];
9573
9574 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
9575 // Include the user's functions file
9576 require_once $dir.$substitfile['name'];
9577 // Call the user's function, and only if it is defined
9578 $function_name = $module."_".$callfunc;
9579 if (function_exists($function_name)) {
9580 $function_name($substitutionarray, $outputlangs, $object, $parameters);
9581 }
9582 }
9583 }
9584 }
9585 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
9586 // to list all tags in odt template
9587 $tags = '';
9588 foreach ($substitutionarray as $key => $value) {
9589 $tags .= '{'.$key.'} => '.$value."\n";
9590 }
9591 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
9592 }
9593}
9594
9604function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
9605{
9606 print get_date_range($date_start, $date_end, $format, $outputlangs);
9607}
9608
9619function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
9620{
9621 global $langs;
9622
9623 $out = '';
9624
9625 if (!is_object($outputlangs)) {
9626 $outputlangs = $langs;
9627 }
9628
9629 if ($date_start && $date_end) {
9630 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9631 }
9632 if ($date_start && !$date_end) {
9633 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9634 }
9635 if (!$date_start && $date_end) {
9636 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
9637 }
9638
9639 return $out;
9640}
9641
9650function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
9651{
9652 global $conf;
9653
9654 $ret = '';
9655 // If order not defined, we use the setup
9656 if ($nameorder < 0) {
9657 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
9658 }
9659 if ($nameorder == 1) {
9660 $ret .= $firstname;
9661 if ($firstname && $lastname) {
9662 $ret .= ' ';
9663 }
9664 $ret .= $lastname;
9665 } elseif ($nameorder == 2 || $nameorder == 3) {
9666 $ret .= $firstname;
9667 if (empty($ret) && $nameorder == 3) {
9668 $ret .= $lastname;
9669 }
9670 } else { // 0, 4 or 5
9671 $ret .= $lastname;
9672 if (empty($ret) && $nameorder == 5) {
9673 $ret .= $firstname;
9674 }
9675 if ($nameorder == 0) {
9676 if ($firstname && $lastname) {
9677 $ret .= ' ';
9678 }
9679 $ret .= $firstname;
9680 }
9681 }
9682 return $ret;
9683}
9684
9685
9697function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0)
9698{
9699 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
9700 if (!is_array($mesgs)) {
9701 $mesgs = trim((string) $mesgs);
9702 // If mesgs is a not an empty string
9703 if ($mesgs) {
9704 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
9705 return;
9706 }
9707 $_SESSION['dol_events'][$style][] = $mesgs;
9708 }
9709 } else {
9710 // If mesgs is an array
9711 foreach ($mesgs as $mesg) {
9712 $mesg = trim((string) $mesg);
9713 if ($mesg) {
9714 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
9715 return;
9716 }
9717 $_SESSION['dol_events'][$style][] = $mesg;
9718 }
9719 }
9720 }
9721}
9722
9735function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0)
9736{
9737 if (empty($mesg) && empty($mesgs)) {
9738 dol_syslog("Try to add a message in stack, but value to add is empty message", LOG_WARNING);
9739 } else {
9740 if ($messagekey) {
9741 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
9742 // TODO
9743 $mesg .= '';
9744 }
9745 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
9746 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
9747 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
9748 }
9749 if (empty($mesgs)) {
9750 setEventMessage($mesg, $style, $noduplicate);
9751 } else {
9752 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
9753 setEventMessage($mesg, $style, $noduplicate); // Add message string if not already into array
9754 }
9755 setEventMessage($mesgs, $style, $noduplicate);
9756 }
9757 }
9758 }
9759}
9760
9770function dol_htmloutput_events($disabledoutputofmessages = 0)
9771{
9772 // Show mesgs
9773 if (isset($_SESSION['dol_events']['mesgs'])) {
9774 if (empty($disabledoutputofmessages)) {
9775 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
9776 }
9777 unset($_SESSION['dol_events']['mesgs']);
9778 }
9779 // Show errors
9780 if (isset($_SESSION['dol_events']['errors'])) {
9781 if (empty($disabledoutputofmessages)) {
9782 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
9783 }
9784 unset($_SESSION['dol_events']['errors']);
9785 }
9786
9787 // Show warnings
9788 if (isset($_SESSION['dol_events']['warnings'])) {
9789 if (empty($disabledoutputofmessages)) {
9790 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
9791 }
9792 unset($_SESSION['dol_events']['warnings']);
9793 }
9794}
9795
9810function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
9811{
9812 global $conf, $langs;
9813
9814 $ret = 0;
9815 $return = '';
9816 $out = '';
9817 $divstart = $divend = '';
9818
9819 // If inline message with no format, we add it.
9820 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
9821 $divstart = '<div class="'.$style.' clearboth">';
9822 $divend = '</div>';
9823 }
9824
9825 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
9826 $langs->load("errors");
9827 $out .= $divstart;
9828 if (is_array($mesgarray) && count($mesgarray)) {
9829 foreach ($mesgarray as $message) {
9830 $ret++;
9831 $out .= $langs->trans($message);
9832 if ($ret < count($mesgarray)) {
9833 $out .= "<br>\n";
9834 }
9835 }
9836 }
9837 if ($mesgstring) {
9838 $ret++;
9839 $out .= $langs->trans($mesgstring);
9840 }
9841 $out .= $divend;
9842 }
9843
9844 if ($out) {
9845 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
9846 $return = '<script nonce="'.getNonce().'">
9847 $(document).ready(function() {
9848 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
9849 if (block) {
9850 $.dolEventValid("","'.dol_escape_js($out).'");
9851 } else {
9852 /* jnotify(message, preset of message type, keepmessage) */
9853 $.jnotify("'.dol_escape_js($out).'",
9854 "'.($style == "ok" ? 3000 : $style).'",
9855 '.($style == "ok" ? "false" : "true").',
9856 { remove: function (){} } );
9857 }
9858 });
9859 </script>';
9860 } else {
9861 $return = $out;
9862 }
9863 }
9864
9865 return $return;
9866}
9867
9879function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
9880{
9881 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
9882}
9883
9897function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
9898{
9899 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
9900 return;
9901 }
9902
9903 $iserror = 0;
9904 $iswarning = 0;
9905 if (is_array($mesgarray)) {
9906 foreach ($mesgarray as $val) {
9907 if ($val && preg_match('/class="error"/i', $val)) {
9908 $iserror++;
9909 break;
9910 }
9911 if ($val && preg_match('/class="warning"/i', $val)) {
9912 $iswarning++;
9913 break;
9914 }
9915 }
9916 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
9917 $iserror++;
9918 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
9919 $iswarning++;
9920 }
9921 if ($style == 'error') {
9922 $iserror++;
9923 }
9924 if ($style == 'warning') {
9925 $iswarning++;
9926 }
9927
9928 if ($iserror || $iswarning) {
9929 // Remove div from texts
9930 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
9931 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
9932 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
9933 // Remove div from texts array
9934 if (is_array($mesgarray)) {
9935 $newmesgarray = array();
9936 foreach ($mesgarray as $val) {
9937 if (is_string($val)) {
9938 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
9939 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
9940 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
9941 $newmesgarray[] = $tmpmesgstring;
9942 } else {
9943 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
9944 }
9945 }
9946 $mesgarray = $newmesgarray;
9947 }
9948 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
9949 } else {
9950 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
9951 }
9952}
9953
9965function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
9966{
9967 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
9968}
9969
9983function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
9984{
9985 // Clean parameters
9986 $order = strtolower($order);
9987
9988 if (is_array($array)) {
9989 $sizearray = count($array);
9990 if ($sizearray > 0) {
9991 $temp = array();
9992 foreach (array_keys($array) as $key) {
9993 if (is_object($array[$key])) {
9994 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
9995 } else {
9996 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9997 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
9998 }
9999 if ($natsort == -1) {
10000 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10001 }
10002 }
10003
10004 if (empty($natsort) || $natsort == -1) {
10005 if ($order == 'asc') {
10006 asort($temp);
10007 } else {
10008 arsort($temp);
10009 }
10010 } else {
10011 if ($case_sensitive) {
10012 natsort($temp);
10013 } else {
10014 natcasesort($temp); // natecasesort is not sensible to case
10015 }
10016 if ($order != 'asc') {
10017 $temp = array_reverse($temp, true);
10018 }
10019 }
10020
10021 $sorted = array();
10022
10023 foreach (array_keys($temp) as $key) {
10024 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10025 }
10026
10027 return $sorted;
10028 }
10029 }
10030 return $array;
10031}
10032
10033
10041function utf8_check($str)
10042{
10043 $str = (string) $str; // Sometimes string is an int.
10044
10045 // We must use here a binary strlen function (so not dol_strlen)
10046 $strLength = strlen($str);
10047 for ($i = 0; $i < $strLength; $i++) {
10048 if (ord($str[$i]) < 0x80) {
10049 continue; // 0bbbbbbb
10050 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10051 $n = 1; // 110bbbbb
10052 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10053 $n = 2; // 1110bbbb
10054 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10055 $n = 3; // 11110bbb
10056 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10057 $n = 4; // 111110bb
10058 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10059 $n = 5; // 1111110b
10060 } else {
10061 return false; // Does not match any model
10062 }
10063 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10064 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10065 return false;
10066 }
10067 }
10068 }
10069 return true;
10070}
10071
10079function utf8_valid($str)
10080{
10081 /* 2 other methods to test if string is utf8
10082 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10083 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10084 */
10085 return preg_match('//u', $str) ? true : false;
10086}
10087
10088
10095function ascii_check($str)
10096{
10097 if (function_exists('mb_check_encoding')) {
10098 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10099 if (!mb_check_encoding($str, 'ASCII')) {
10100 return false;
10101 }
10102 } else {
10103 if (preg_match('/[^\x00-\x7f]/', $str)) {
10104 return false; // Contains a byte > 7f
10105 }
10106 }
10107
10108 return true;
10109}
10110
10111
10119function dol_osencode($str)
10120{
10121 $tmp = ini_get("unicode.filesystem_encoding");
10122 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10123 $tmp = 'iso-8859-1'; // By default for windows
10124 }
10125 if (empty($tmp)) {
10126 $tmp = 'utf-8'; // By default for other
10127 }
10128 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10129 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10130 }
10131
10132 if ($tmp == 'iso-8859-1') {
10133 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10134 }
10135 return $str;
10136}
10137
10138
10153function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
10154{
10155 global $conf;
10156
10157 // If key empty
10158 if ($key == '') {
10159 return 0;
10160 }
10161
10162 // Check in cache
10163 if (isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10164 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10165 }
10166
10167 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10168
10169 $sql = "SELECT ".$fieldid." as valuetoget";
10170 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10171 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10172 if (!empty($entityfilter)) {
10173 $sql .= " AND entity IN (".getEntity($tablename).")";
10174 }
10175 if ($filters) {
10176 $sql .= $filters;
10177 }
10178
10179 $resql = $db->query($sql);
10180 if ($resql) {
10181 $obj = $db->fetch_object($resql);
10182 if ($obj) {
10183 $conf->cache['codeid'][$tablename][$key][$fieldid] = $obj->valuetoget;
10184 } else {
10185 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10186 }
10187 $db->free($resql);
10188
10189 return $conf->cache['codeid'][$tablename][$key][$fieldid];
10190 } else {
10191 return -1;
10192 }
10193}
10194
10204function isStringVarMatching($var, $regextext, $matchrule = 1)
10205{
10206 if ($matchrule == 1) {
10207 if ($var == 'mainmenu') {
10208 global $mainmenu;
10209 return (preg_match('/^'.$regextext.'/', $mainmenu));
10210 } elseif ($var == 'leftmenu') {
10211 global $leftmenu;
10212 return (preg_match('/^'.$regextext.'/', $leftmenu));
10213 } else {
10214 return 'This variable is not accessible with dol_eval';
10215 }
10216 } else {
10217 return 'This value for matchrule is not implemented';
10218 }
10219}
10220
10221
10231function verifCond($strToEvaluate, $onlysimplestring = '1')
10232{
10233 //print $strToEvaluate."<br>\n";
10234 $rights = true;
10235 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10236 //var_dump($strToEvaluate);
10237 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10238 $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
10239 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10240 //var_dump($rights);
10241 }
10242 return $rights;
10243}
10244
10259function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10260{
10261 // Only this global variables can be read by eval function and returned to caller
10262 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10263 global $db, $langs, $user, $website, $websitepage;
10264 global $action, $mainmenu, $leftmenu;
10265 global $mysoc;
10266 global $objectoffield; // To allow the use of $objectoffield in computed fields
10267
10268 // Old variables used
10269 global $object;
10270 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10271
10272 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10273 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10274 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10275 }
10276
10277 try {
10278 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10279 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10280 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10281 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10282 // 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"
10283 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10284 if ($onlysimplestring == '2') {
10285 $specialcharsallowed .= '[]';
10286 }
10287 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10288 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10289 }
10290 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10291 if ($returnvalue) {
10292 return 'Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s;
10293 } else {
10294 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple clean eval string): '.$s);
10295 return '';
10296 }
10297 }
10298 $savescheck = '';
10299 $scheck = $s;
10300 while ($scheck && $savescheck != $scheck) {
10301 $savescheck = $scheck;
10302 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10303 $scheck = preg_replace('/^\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10304 $scheck = preg_replace('/\s\‍(/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '... ('. Must replace with __PARENTHESIS__ with a space after to allow following substitutions
10305 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10306 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10307 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10308 }
10309 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10310 if (strpos($scheck, '(') !== false) {
10311 if ($returnvalue) {
10312 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10313 } else {
10314 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);
10315 return '';
10316 }
10317 }
10318 // TODO
10319 // We can exclude $ char that are not:
10320 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10321 }
10322 if (is_array($s) || $s === 'Array') {
10323 return 'Bad string syntax to evaluate (value is Array) '.var_export($s, true);
10324 }
10325 if (strpos($s, '::') !== false) {
10326 if ($returnvalue) {
10327 return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
10328 } else {
10329 dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
10330 return '';
10331 }
10332 }
10333 if (strpos($s, '`') !== false) {
10334 if ($returnvalue) {
10335 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10336 } else {
10337 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
10338 return '';
10339 }
10340 }
10341 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10342 if ($returnvalue) {
10343 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10344 } else {
10345 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
10346 return '';
10347 }
10348 }
10349
10350 // We block use of php exec or php file functions
10351 $forbiddenphpstrings = array('$$');
10352 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10353
10354 $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen");
10355 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10356 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64_decode", "rawurldecode", "urldecode", "str_rot13", "hex2bin")); // decode string functions used to obfuscated function name
10357 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10358 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10359 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
10360 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10361 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
10362 $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
10363
10364 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10365
10366 $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
10367
10368 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10369
10370 do {
10371 $oldstringtoclean = $s;
10372 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10373 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10374 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10375 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10376 } while ($oldstringtoclean != $s);
10377
10378 if (strpos($s, '__forbiddenstring__') !== false) {
10379 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10380 if ($returnvalue) {
10381 return 'Bad string syntax to evaluate: '.$s;
10382 } else {
10383 dol_syslog('Bad string syntax to evaluate: '.$s);
10384 return '';
10385 }
10386 }
10387
10388 //print $s."<br>\n";
10389 if ($returnvalue) {
10390 if ($hideerrors) {
10391 ob_start(); // An evaluation has no reason to output data
10392 $isObBufferActive = true;
10393 $tmps = @eval('return '.$s.';');
10394 $tmpo = ob_get_clean();
10395 $isObBufferActive = false;
10396 if ($tmpo) {
10397 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10398 }
10399 return $tmps;
10400 } else {
10401 ob_start(); // An evaluation has no reason to output data
10402 $isObBufferActive = true;
10403 $tmps = eval('return '.$s.';');
10404 $tmpo = ob_get_clean();
10405 $isObBufferActive = false;
10406 if ($tmpo) {
10407 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: '.$s;
10408 }
10409 return $tmps;
10410 }
10411 } else {
10412 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10413 if ($hideerrors) {
10414 @eval($s);
10415 } else {
10416 eval($s);
10417 }
10418 return '';
10419 }
10420 } catch (Error $e) {
10421 if ($isObBufferActive) {
10422 // Clean up buffer which was left behind due to exception.
10423 $tmpo = ob_get_clean();
10424 $isObBufferActive = false;
10425 }
10426 $error = 'dol_eval try/catch error : ';
10427 $error .= $e->getMessage();
10428 dol_syslog($error, LOG_WARNING);
10429 if ($returnvalue) {
10430 return 'Exception during evaluation: '.$s;
10431 } else {
10432 return '';
10433 }
10434 }
10435}
10436
10444function dol_validElement($element)
10445{
10446 return (trim($element) != '');
10447}
10448
10457function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10458{
10459 if (empty($codelang)) {
10460 return '';
10461 }
10462
10463 if ($codelang == 'auto') {
10464 return '<span class="fa fa-language"></span>';
10465 }
10466
10467 $langtocountryflag = array(
10468 'ar_AR' => '',
10469 'ca_ES' => 'catalonia',
10470 'da_DA' => 'dk',
10471 'fr_CA' => 'mq',
10472 'sv_SV' => 'se',
10473 'sw_SW' => 'unknown',
10474 'AQ' => 'unknown',
10475 'CW' => 'unknown',
10476 'IM' => 'unknown',
10477 'JE' => 'unknown',
10478 'MF' => 'unknown',
10479 'BL' => 'unknown',
10480 'SX' => 'unknown'
10481 );
10482
10483 if (isset($langtocountryflag[$codelang])) {
10484 $flagImage = $langtocountryflag[$codelang];
10485 } else {
10486 $tmparray = explode('_', $codelang);
10487 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
10488 }
10489
10490 $morecss = '';
10491 $reg = array();
10492 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
10493 $morecss = $reg[1];
10494 $moreatt = "";
10495 }
10496
10497 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
10498 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
10499}
10500
10508function getLanguageCodeFromCountryCode($countrycode)
10509{
10510 global $mysoc;
10511
10512 if (empty($countrycode)) {
10513 return null;
10514 }
10515
10516 if (strtoupper($countrycode) == 'MQ') {
10517 return 'fr_CA';
10518 }
10519 if (strtoupper($countrycode) == 'SE') {
10520 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
10521 }
10522 if (strtoupper($countrycode) == 'CH') {
10523 if ($mysoc->country_code == 'FR') {
10524 return 'fr_CH';
10525 }
10526 if ($mysoc->country_code == 'DE') {
10527 return 'de_CH';
10528 }
10529 if ($mysoc->country_code == 'IT') {
10530 return 'it_CH';
10531 }
10532 }
10533
10534 // Locale list taken from:
10535 // http://stackoverflow.com/questions/3191664/
10536 // list-of-all-locales-and-their-short-codes
10537 $locales = array(
10538 'af-ZA',
10539 'am-ET',
10540 'ar-AE',
10541 'ar-BH',
10542 'ar-DZ',
10543 'ar-EG',
10544 'ar-IQ',
10545 'ar-JO',
10546 'ar-KW',
10547 'ar-LB',
10548 'ar-LY',
10549 'ar-MA',
10550 'ar-OM',
10551 'ar-QA',
10552 'ar-SA',
10553 'ar-SY',
10554 'ar-TN',
10555 'ar-YE',
10556 //'as-IN', // Moved after en-IN
10557 'ba-RU',
10558 'be-BY',
10559 'bg-BG',
10560 'bn-BD',
10561 //'bn-IN', // Moved after en-IN
10562 'bo-CN',
10563 'br-FR',
10564 'ca-ES',
10565 'co-FR',
10566 'cs-CZ',
10567 'cy-GB',
10568 'da-DK',
10569 'de-AT',
10570 'de-CH',
10571 'de-DE',
10572 'de-LI',
10573 'de-LU',
10574 'dv-MV',
10575 'el-GR',
10576 'en-AU',
10577 'en-BZ',
10578 'en-CA',
10579 'en-GB',
10580 'en-IE',
10581 'en-IN',
10582 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
10583 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
10584 'en-JM',
10585 'en-MY',
10586 'en-NZ',
10587 'en-PH',
10588 'en-SG',
10589 'en-TT',
10590 'en-US',
10591 'en-ZA',
10592 'en-ZW',
10593 'es-AR',
10594 'es-BO',
10595 'es-CL',
10596 'es-CO',
10597 'es-CR',
10598 'es-DO',
10599 'es-EC',
10600 'es-ES',
10601 'es-GT',
10602 'es-HN',
10603 'es-MX',
10604 'es-NI',
10605 'es-PA',
10606 'es-PE',
10607 'es-PR',
10608 'es-PY',
10609 'es-SV',
10610 'es-US',
10611 'es-UY',
10612 'es-VE',
10613 'et-EE',
10614 'eu-ES',
10615 'fa-IR',
10616 'fi-FI',
10617 'fo-FO',
10618 'fr-BE',
10619 'fr-CA',
10620 'fr-CH',
10621 'fr-FR',
10622 'fr-LU',
10623 'fr-MC',
10624 'fy-NL',
10625 'ga-IE',
10626 'gd-GB',
10627 'gl-ES',
10628 'gu-IN',
10629 'he-IL',
10630 'hi-IN',
10631 'hr-BA',
10632 'hr-HR',
10633 'hu-HU',
10634 'hy-AM',
10635 'id-ID',
10636 'ig-NG',
10637 'ii-CN',
10638 'is-IS',
10639 'it-CH',
10640 'it-IT',
10641 'ja-JP',
10642 'ka-GE',
10643 'kk-KZ',
10644 'kl-GL',
10645 'km-KH',
10646 'kn-IN',
10647 'ko-KR',
10648 'ky-KG',
10649 'lb-LU',
10650 'lo-LA',
10651 'lt-LT',
10652 'lv-LV',
10653 'mi-NZ',
10654 'mk-MK',
10655 'ml-IN',
10656 'mn-MN',
10657 'mr-IN',
10658 'ms-BN',
10659 'ms-MY',
10660 'mt-MT',
10661 'nb-NO',
10662 'ne-NP',
10663 'nl-BE',
10664 'nl-NL',
10665 'nn-NO',
10666 'oc-FR',
10667 'or-IN',
10668 'pa-IN',
10669 'pl-PL',
10670 'ps-AF',
10671 'pt-BR',
10672 'pt-PT',
10673 'rm-CH',
10674 'ro-MD',
10675 'ro-RO',
10676 'ru-RU',
10677 'rw-RW',
10678 'sa-IN',
10679 'se-FI',
10680 'se-NO',
10681 'se-SE',
10682 'si-LK',
10683 'sk-SK',
10684 'sl-SI',
10685 'sq-AL',
10686 'sv-FI',
10687 'sv-SE',
10688 'sw-KE',
10689 'ta-IN',
10690 'te-IN',
10691 'th-TH',
10692 'tk-TM',
10693 'tn-ZA',
10694 'tr-TR',
10695 'tt-RU',
10696 'ug-CN',
10697 'uk-UA',
10698 'ur-PK',
10699 'vi-VN',
10700 'wo-SN',
10701 'xh-ZA',
10702 'yo-NG',
10703 'zh-CN',
10704 'zh-HK',
10705 'zh-MO',
10706 'zh-SG',
10707 'zh-TW',
10708 'zu-ZA',
10709 );
10710
10711 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
10712 if (in_array($buildprimarykeytotest, $locales)) {
10713 return strtolower($countrycode).'_'.strtoupper($countrycode);
10714 }
10715
10716 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
10717 foreach ($locales as $locale) {
10718 $locale_language = locale_get_primary_language($locale);
10719 $locale_region = locale_get_region($locale);
10720 if (strtoupper($countrycode) == $locale_region) {
10721 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
10722 return strtolower($locale_language).'_'.strtoupper($locale_region);
10723 }
10724 }
10725 } else {
10726 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
10727 }
10728
10729 return null;
10730}
10731
10762function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
10763{
10764 global $hookmanager, $db;
10765
10766 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
10767 foreach ($conf->modules_parts['tabs'][$type] as $value) {
10768 $values = explode(':', $value);
10769
10770 $reg = array();
10771 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
10772 $newtab = array();
10773 $postab = $h;
10774 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
10775 $str = $values[1];
10776 $posstart = strpos($str, '(');
10777 if ($posstart > 0) {
10778 $posend = strpos($str, ')');
10779 if ($posstart > 0) {
10780 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
10781 if (is_numeric($res1)) {
10782 $postab = (int) $res1;
10783 $values[1] = '+' . substr($str, $posend + 1);
10784 }
10785 }
10786 }
10787 if (count($values) == 6) {
10788 // new declaration with permissions:
10789 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10790 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
10791 if ($values[0] != $type) {
10792 continue;
10793 }
10794
10795 if (verifCond($values[4], '2')) {
10796 if ($values[3]) {
10797 if ($filterorigmodule) { // If a filter of module origin has been requested
10798 if (strpos($values[3], '@')) { // This is an external module
10799 if ($filterorigmodule != 'external') {
10800 continue;
10801 }
10802 } else { // This looks a core module
10803 if ($filterorigmodule != 'core') {
10804 continue;
10805 }
10806 }
10807 }
10808 $langs->load($values[3]);
10809 }
10810 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10811 // If label is "SUBSTITUION_..."
10812 $substitutionarray = array();
10813 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10814 $label = make_substitutions($reg[1], $substitutionarray);
10815 } else {
10816 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
10817 $labeltemp = explode(',', $values[2]);
10818 $label = $langs->trans($labeltemp[0]);
10819
10820 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
10821 dol_include_once($labeltemp[2]);
10822 $classtoload = $labeltemp[1];
10823 if (class_exists($classtoload)) {
10824 $obj = new $classtoload($db);
10825 $function = $labeltemp[3];
10826 if ($obj && $function && method_exists($obj, $function)) {
10827 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
10828 $nbrec = $obj->$function($object->id, $obj);
10829 if (!empty($nbrec)) {
10830 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
10831 }
10832 }
10833 }
10834 }
10835 }
10836
10837 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
10838 $newtab[1] = $label;
10839 $newtab[2] = str_replace('+', '', $values[1]);
10840 $h++;
10841 } else {
10842 continue;
10843 }
10844 } elseif (count($values) == 5) { // case deprecated
10845 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
10846
10847 if ($values[0] != $type) {
10848 continue;
10849 }
10850 if ($values[3]) {
10851 if ($filterorigmodule) { // If a filter of module origin has been requested
10852 if (strpos($values[3], '@')) { // This is an external module
10853 if ($filterorigmodule != 'external') {
10854 continue;
10855 }
10856 } else { // This looks a core module
10857 if ($filterorigmodule != 'core') {
10858 continue;
10859 }
10860 }
10861 }
10862 $langs->load($values[3]);
10863 }
10864 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
10865 $substitutionarray = array();
10866 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
10867 $label = make_substitutions($reg[1], $substitutionarray);
10868 } else {
10869 $label = $langs->trans($values[2]);
10870 }
10871
10872 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
10873 $newtab[1] = $label;
10874 $newtab[2] = str_replace('+', '', $values[1]);
10875 $h++;
10876 }
10877 // set tab at its position
10878 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
10879 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
10880 if ($values[0] != $type) {
10881 continue;
10882 }
10883 $tabname = str_replace('-', '', $values[1]);
10884 foreach ($head as $key => $val) {
10885 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
10886 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
10887 if ($head[$key][2] == $tabname && $condition) {
10888 unset($head[$key]);
10889 break;
10890 }
10891 }
10892 }
10893 }
10894 }
10895
10896 // No need to make a return $head. Var is modified as a reference
10897 if (!empty($hookmanager)) {
10898 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
10899 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
10900 if ($reshook > 0) { // Hook ask to replace completely the array
10901 $head = $hookmanager->resArray;
10902 } else { // Hook
10903 $head = array_merge($head, $hookmanager->resArray);
10904 }
10905 $h = count($head);
10906 }
10907}
10908
10920function printCommonFooter($zone = 'private')
10921{
10922 global $conf, $hookmanager, $user, $debugbar;
10923 global $action;
10924 global $micro_start_time;
10925
10926 if ($zone == 'private') {
10927 print "\n".'<!-- Common footer for private page -->'."\n";
10928 } else {
10929 print "\n".'<!-- Common footer for public page -->'."\n";
10930 }
10931
10932 // A div to store page_y POST parameter so we can read it using javascript
10933 print "\n<!-- A div to store page_y POST parameter -->\n";
10934 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
10935
10936 $parameters = array();
10937 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
10938 if (empty($reshook)) {
10939 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
10940 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
10941 }
10942
10943 print "\n";
10944 if (!empty($conf->use_javascript_ajax)) {
10945 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
10946 print '<script>'."\n";
10947 print 'jQuery(document).ready(function() {'."\n";
10948
10949 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
10950 print "\n";
10951 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
10952 print 'jQuery("li.menuhider").click(function(event) {';
10953 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
10954 print ' console.log("We click on .menuhider");'."\n";
10955 print ' $("body").toggleClass("sidebar-collapse")'."\n";
10956 print '});'."\n";
10957 }
10958
10959 // Management of focus and mandatory for fields
10960 if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
10961 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
10962 $relativepathstring = $_SERVER["PHP_SELF"];
10963 // Clean $relativepathstring
10964 if (constant('DOL_URL_ROOT')) {
10965 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
10966 }
10967 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
10968 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
10969 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
10970 if (!empty($user->default_values[$relativepathstring]['focus'])) {
10971 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
10972 $qualified = 0;
10973 if ($defkey != '_noquery_') {
10974 $tmpqueryarraytohave = explode('&', $defkey);
10975 $foundintru = 0;
10976 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
10977 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
10978 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
10979 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
10980 $foundintru = 1;
10981 }
10982 }
10983 if (!$foundintru) {
10984 $qualified = 1;
10985 }
10986 //var_dump($defkey.'-'.$qualified);
10987 } else {
10988 $qualified = 1;
10989 }
10990
10991 if ($qualified) {
10992 foreach ($defval as $paramkey => $paramval) {
10993 // Set focus on field
10994 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
10995 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
10996 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
10997 }
10998 }
10999 }
11000 }
11001 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11002 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11003 $qualified = 0;
11004 if ($defkey != '_noquery_') {
11005 $tmpqueryarraytohave = explode('&', $defkey);
11006 $foundintru = 0;
11007 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11008 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11009 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11010 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11011 $foundintru = 1;
11012 }
11013 }
11014 if (!$foundintru) {
11015 $qualified = 1;
11016 }
11017 //var_dump($defkey.'-'.$qualified);
11018 } else {
11019 $qualified = 1;
11020 }
11021
11022 if ($qualified) {
11023 foreach ($defval as $paramkey => $paramval) {
11024 // Add property 'required' on input
11025 print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11026 print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11027 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";
11028 print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11029 print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11030 print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11031
11032 // Add 'field required' class on closest td for all input elements : input, textarea and select
11033 print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
11034 }
11035 // If we submit the cancel button we remove the required attributes
11036 print 'jQuery("input[name=\'cancel\']").click(function() {
11037 console.log("We click on cancel button so removed all required attribute");
11038 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11039 });'."\n";
11040 }
11041 }
11042 }
11043 }
11044
11045 print '});'."\n";
11046
11047 // End of tuning
11048 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11049 print "\n";
11050 print "/* JS CODE TO ENABLE to add memory info */\n";
11051 print 'window.console && console.log("';
11052 if (getDolGlobalString('MEMCACHED_SERVER')) {
11053 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11054 }
11055 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11056 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11057 $micro_end_time = microtime(true);
11058 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11059 }
11060
11061 if (function_exists("memory_get_usage")) {
11062 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11063 }
11064 if (function_exists("memory_get_peak_usage")) {
11065 print ' - Real mem peak: '.memory_get_peak_usage(true);
11066 }
11067 if (function_exists("zend_loader_file_encoded")) {
11068 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11069 }
11070 print '");'."\n";
11071 }
11072
11073 print "\n".'</script>'."\n";
11074
11075 // Google Analytics
11076 // TODO Add a hook here
11077 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11078 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11079 foreach ($tmptagarray as $tmptag) {
11080 print "\n";
11081 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11082 print '
11083 <!-- Global site tag (gtag.js) - Google Analytics -->
11084 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11085 <script>
11086 window.dataLayer = window.dataLayer || [];
11087 function gtag(){dataLayer.push(arguments);}
11088 gtag(\'js\', new Date());
11089
11090 gtag(\'config\', \''.trim($tmptag).'\');
11091 </script>';
11092 print "\n";
11093 }
11094 }
11095 }
11096
11097 // Add Xdebug coverage of code
11098 if (defined('XDEBUGCOVERAGE')) {
11099 print_r(xdebug_get_code_coverage());
11100 }
11101
11102 // Add DebugBar data
11103 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11104 if (isset($debugbar['time'])) {
11105 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11106 $debugbar['time']->stopMeasure('pageaftermaster');
11107 }
11108 print '<!-- Output debugbar data -->'."\n";
11109 $renderer = $debugbar->getJavascriptRenderer();
11110 print $renderer->render();
11111 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11112 print "\n";
11113 print "<!-- Start of log output\n";
11114 //print '<div class="hidden">'."\n";
11115 foreach ($conf->logbuffer as $logline) {
11116 print $logline."<br>\n";
11117 }
11118 //print '</div>'."\n";
11119 print "End of log output -->\n";
11120 }
11121 }
11122}
11123
11133function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11134{
11135 if (is_null($string)) {
11136 return array();
11137 }
11138
11139 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11140 // This is a regex string
11141 $newdelimiter = $delimiter;
11142 } else {
11143 // This is a simple string
11144 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11145 $newdelimiter = preg_quote($delimiter, '/');
11146 }
11147
11148 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11149 $ka = array();
11150 foreach ($a as $s) { // each part
11151 if ($s) {
11152 if ($pos = strpos($s, $kv)) { // key/value delimiter
11153 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11154 } else { // key delimiter not found
11155 $ka[] = trim($s);
11156 }
11157 }
11158 }
11159 return $ka;
11160 }
11161
11162 return array();
11163}
11164
11165
11172function dol_set_focus($selector)
11173{
11174 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11175 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11176}
11177
11178
11186function dol_getmypid()
11187{
11188 if (!function_exists('getmypid')) {
11189 return mt_rand(99900000, 99965535);
11190 } else {
11191 return getmypid(); // May be a number on 64 bits (depending on OS)
11192 }
11193}
11194
11195
11217function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11218{
11219 global $db, $langs;
11220
11221 $value = trim($value);
11222
11223 if ($mode == 0) {
11224 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11225 }
11226 if ($mode == 1) {
11227 $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
11228 }
11229
11230 $value = preg_replace('/\s*\|\s*/', '|', $value);
11231
11232 $crits = explode(' ', $value);
11233 $res = '';
11234 if (!is_array($fields)) {
11235 $fields = array($fields);
11236 }
11237
11238 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
11239 foreach ($crits as $crit) { // Loop on each AND criteria
11240 $crit = trim($crit);
11241 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11242 $newres = '';
11243 foreach ($fields as $field) {
11244 if ($mode == 1) {
11245 $tmpcrits = explode('|', $crit);
11246 $i3 = 0; // count the nb of valid criteria added for this current field
11247 foreach ($tmpcrits as $tmpcrit) {
11248 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11249 continue;
11250 }
11251 $tmpcrit = trim($tmpcrit);
11252
11253 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11254
11255 $operator = '=';
11256 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11257
11258 $reg = array();
11259 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11260 if (!empty($reg[1])) {
11261 $operator = $reg[1];
11262 }
11263 if ($newcrit != '') {
11264 $numnewcrit = price2num($newcrit);
11265 if (is_numeric($numnewcrit)) {
11266 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11267 } else {
11268 $newres .= '1 = 2'; // force false, we received a corrupted data
11269 }
11270 $i3++; // a criteria was added to string
11271 }
11272 }
11273 $i2++; // a criteria for 1 more field was added to string
11274 } elseif ($mode == 2 || $mode == -2) {
11275 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11276 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11277 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11278 if ($mode == -2) {
11279 $newres .= ' OR '.$field.' IS NULL';
11280 }
11281 $i2++; // a criteria for 1 more field was added to string
11282 } elseif ($mode == 3 || $mode == -3) {
11283 $tmparray = explode(',', $crit);
11284 if (count($tmparray)) {
11285 $listofcodes = '';
11286 foreach ($tmparray as $val) {
11287 $val = trim($val);
11288 if ($val) {
11289 $listofcodes .= ($listofcodes ? ',' : '');
11290 $listofcodes .= "'".$db->escape($val)."'";
11291 }
11292 }
11293 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
11294 $i2++; // a criteria for 1 more field was added to string
11295 }
11296 if ($mode == -3) {
11297 $newres .= ' OR '.$field.' IS NULL';
11298 }
11299 } elseif ($mode == 4) {
11300 $tmparray = explode(',', $crit);
11301 if (count($tmparray)) {
11302 $listofcodes = '';
11303 foreach ($tmparray as $val) {
11304 $val = trim($val);
11305 if ($val) {
11306 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11307 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11308 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11309 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11310 $newres .= ')';
11311 $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)
11312 }
11313 }
11314 }
11315 } else { // $mode=0
11316 $tmpcrits = explode('|', $crit);
11317 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11318 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11319 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11320 continue;
11321 }
11322 $tmpcrit = trim($tmpcrit);
11323
11324 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11325 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11326 } else {
11327 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11328 }
11329
11330 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11331 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11332 } else {
11333 $tmpcrit2 = $tmpcrit;
11334 $tmpbefore = '%';
11335 $tmpafter = '%';
11336 $tmps = '';
11337
11338 if (preg_match('/^!/', $tmpcrit)) {
11339 $tmps .= $field." NOT LIKE "; // ! as exclude character
11340 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11341 } else {
11342 $tmps .= $field." LIKE ";
11343 }
11344 $tmps .= "'";
11345
11346 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11347 $tmpbefore = '';
11348 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11349 }
11350 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11351 $tmpafter = '';
11352 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11353 }
11354
11355 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11356 $tmps = "(".$tmps;
11357 }
11358 $newres .= $tmps;
11359 $newres .= $tmpbefore;
11360 $newres .= $db->escape($tmpcrit2);
11361 $newres .= $tmpafter;
11362 $newres .= "'";
11363 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11364 $newres .= " OR ".$field." IS NULL)";
11365 }
11366 }
11367
11368 $i3++;
11369 }
11370
11371 $i2++; // a criteria for 1 more field was added to string
11372 }
11373 }
11374
11375 if ($newres) {
11376 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11377 }
11378 $i1++;
11379 }
11380 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11381
11382 return $res;
11383}
11384
11391function showDirectDownloadLink($object)
11392{
11393 global $conf, $langs;
11394
11395 $out = '';
11396 $url = $object->getLastMainDocLink($object->element);
11397
11398 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11399 if ($url) {
11400 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
11401 $out .= ajax_autoselect("directdownloadlink", 0);
11402 } else {
11403 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
11404 }
11405
11406 return $out;
11407}
11408
11417function getImageFileNameForSize($file, $extName, $extImgTarget = '')
11418{
11419 $dirName = dirname($file);
11420 if ($dirName == '.') {
11421 $dirName = '';
11422 }
11423
11424 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
11425 $fileName = basename($fileName);
11426
11427 if (empty($extImgTarget)) {
11428 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
11429 }
11430 if (empty($extImgTarget)) {
11431 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
11432 }
11433 if (empty($extImgTarget)) {
11434 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
11435 }
11436 if (empty($extImgTarget)) {
11437 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
11438 }
11439 if (empty($extImgTarget)) {
11440 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
11441 }
11442 if (empty($extImgTarget)) {
11443 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
11444 }
11445
11446 if (!$extImgTarget) {
11447 return $file;
11448 }
11449
11450 $subdir = '';
11451 if ($extName) {
11452 $subdir = 'thumbs/';
11453 }
11454
11455 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
11456}
11457
11458
11468function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
11469{
11470 global $conf, $langs;
11471
11472 if (empty($conf->use_javascript_ajax)) {
11473 return '';
11474 }
11475
11476 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
11477
11478 if ($alldata == 1) {
11479 if ($isAllowedForPreview) {
11480 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));
11481 } else {
11482 return array();
11483 }
11484 }
11485
11486 // old behavior, return a string
11487 if ($isAllowedForPreview) {
11488 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
11489 $title = $langs->transnoentities("Preview");
11490 //$title = '%27-alert(document.domain)-%27';
11491 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg");
11492
11493 // We need to urlencode the parameter after the dol_escape_js($tmpurl) because $tmpurl may contain n url with param file=abc%27def if file has a ' inside.
11494 // and when we click on href with this javascript string, a urlcode is done by browser, converted the %27 of file param
11495 return 'javascript:document_preview(\''.urlencode(dol_escape_js($tmpurl)).'\', \''.urlencode(dol_mimetype($relativepath)).'\', \''.urlencode(dol_escape_js($title)).'\')';
11496 } else {
11497 return '';
11498 }
11499}
11500
11501
11510function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
11511{
11512 global $langs;
11513 $out = '<script nonce="'.getNonce().'">
11514 jQuery(document).ready(function () {
11515 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
11516 });
11517 </script>';
11518 if ($addlink) {
11519 if ($textonlink === 'image') {
11520 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
11521 } else {
11522 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
11523 }
11524 }
11525 return $out;
11526}
11527
11535function dolIsAllowedForPreview($file)
11536{
11537 // Check .noexe extension in filename
11538 if (preg_match('/\.noexe$/i', $file)) {
11539 return 0;
11540 }
11541
11542 // Check mime types
11543 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
11544 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
11545 $mime_preview[] = 'svg+xml';
11546 }
11547 //$mime_preview[]='vnd.oasis.opendocument.presentation';
11548 //$mime_preview[]='archive';
11549 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
11550 if ($num_mime !== false) {
11551 return 1;
11552 }
11553
11554 // By default, not allowed for preview
11555 return 0;
11556}
11557
11558
11568function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
11569{
11570 $mime = $default;
11571 $imgmime = 'other.png';
11572 $famime = 'file-o';
11573 $srclang = '';
11574
11575 $tmpfile = preg_replace('/\.noexe$/', '', $file);
11576
11577 // Plain text files
11578 if (preg_match('/\.txt$/i', $tmpfile)) {
11579 $mime = 'text/plain';
11580 $imgmime = 'text.png';
11581 $famime = 'file-alt';
11582 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
11583 $mime = 'text/richtext';
11584 $imgmime = 'text.png';
11585 $famime = 'file-alt';
11586 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
11587 $mime = 'text/csv';
11588 $imgmime = 'text.png';
11589 $famime = 'file-csv';
11590 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
11591 $mime = 'text/tab-separated-values';
11592 $imgmime = 'text.png';
11593 $famime = 'file-alt';
11594 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
11595 $mime = 'text/plain';
11596 $imgmime = 'text.png';
11597 $famime = 'file-alt';
11598 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
11599 $mime = 'text/plain';
11600 $imgmime = 'text.png';
11601 $srclang = 'ini';
11602 $famime = 'file-alt';
11603 } elseif (preg_match('/\.md$/i', $tmpfile)) {
11604 $mime = 'text/plain';
11605 $imgmime = 'text.png';
11606 $srclang = 'md';
11607 $famime = 'file-alt';
11608 } elseif (preg_match('/\.css$/i', $tmpfile)) {
11609 $mime = 'text/css';
11610 $imgmime = 'css.png';
11611 $srclang = 'css';
11612 $famime = 'file-alt';
11613 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
11614 $mime = 'text/plain';
11615 $imgmime = 'text.png';
11616 $srclang = 'lang';
11617 $famime = 'file-alt';
11618 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
11619 $mime = 'text/plain';
11620 $imgmime = 'text.png';
11621 $famime = 'file-alt';
11622 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
11623 $mime = 'text/html';
11624 $imgmime = 'html.png';
11625 $srclang = 'html';
11626 $famime = 'file-alt';
11627 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
11628 $mime = 'text/xml';
11629 $imgmime = 'other.png';
11630 $srclang = 'xml';
11631 $famime = 'file-alt';
11632 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
11633 $mime = 'text/xml';
11634 $imgmime = 'other.png';
11635 $srclang = 'xaml';
11636 $famime = 'file-alt';
11637 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
11638 $mime = 'text/plain';
11639 $imgmime = 'text.png';
11640 $srclang = 'bas';
11641 $famime = 'file-code';
11642 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
11643 $mime = 'text/plain';
11644 $imgmime = 'text.png';
11645 $srclang = 'c';
11646 $famime = 'file-code';
11647 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
11648 $mime = 'text/plain';
11649 $imgmime = 'text.png';
11650 $srclang = 'cpp';
11651 $famime = 'file-code';
11652 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
11653 $mime = 'text/plain';
11654 $imgmime = 'text.png';
11655 $srclang = 'cs';
11656 $famime = 'file-code';
11657 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
11658 $mime = 'text/plain';
11659 $imgmime = 'text.png';
11660 $srclang = 'h';
11661 $famime = 'file-code';
11662 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
11663 $mime = 'text/plain';
11664 $imgmime = 'text.png';
11665 $srclang = 'java';
11666 $famime = 'file-code';
11667 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
11668 $mime = 'text/plain';
11669 $imgmime = 'php.png';
11670 $srclang = 'php';
11671 $famime = 'file-code';
11672 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
11673 $mime = 'text/plain';
11674 $imgmime = 'php.png';
11675 $srclang = 'php';
11676 $famime = 'file-code';
11677 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
11678 $mime = 'text/plain';
11679 $imgmime = 'pl.png';
11680 $srclang = 'perl';
11681 $famime = 'file-code';
11682 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
11683 $mime = 'text/plain';
11684 $imgmime = 'text.png';
11685 $srclang = 'sql';
11686 $famime = 'file-code';
11687 } elseif (preg_match('/\.js$/i', $tmpfile)) {
11688 $mime = 'text/x-javascript';
11689 $imgmime = 'jscript.png';
11690 $srclang = 'js';
11691 $famime = 'file-code';
11692 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
11693 $mime = 'application/vnd.oasis.opendocument.presentation';
11694 $imgmime = 'ooffice.png';
11695 $famime = 'file-powerpoint';
11696 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
11697 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
11698 $imgmime = 'ooffice.png';
11699 $famime = 'file-excel';
11700 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
11701 $mime = 'application/vnd.oasis.opendocument.text';
11702 $imgmime = 'ooffice.png';
11703 $famime = 'file-word';
11704 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
11705 $mime = 'application/msaccess';
11706 $imgmime = 'mdb.png';
11707 $famime = 'file';
11708 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
11709 $mime = 'application/msword';
11710 $imgmime = 'doc.png';
11711 $famime = 'file-word';
11712 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
11713 $mime = 'application/msword';
11714 $imgmime = 'doc.png';
11715 $famime = 'file-word';
11716 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
11717 $mime = 'application/vnd.ms-excel';
11718 $imgmime = 'xls.png';
11719 $famime = 'file-excel';
11720 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
11721 $mime = 'application/vnd.ms-excel';
11722 $imgmime = 'xls.png';
11723 $famime = 'file-excel';
11724 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
11725 $mime = 'application/vnd.ms-excel';
11726 $imgmime = 'xls.png';
11727 $famime = 'file-excel';
11728 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
11729 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
11730 $imgmime = 'xls.png';
11731 $famime = 'file-excel';
11732 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
11733 $mime = 'application/vnd.ms-powerpoint';
11734 $imgmime = 'ppt.png';
11735 $famime = 'file-powerpoint';
11736 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
11737 $mime = 'application/x-mspowerpoint';
11738 $imgmime = 'ppt.png';
11739 $famime = 'file-powerpoint';
11740 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
11741 $mime = 'application/pdf';
11742 $imgmime = 'pdf.png';
11743 $famime = 'file-pdf';
11744 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
11745 $mime = 'text/x-bat';
11746 $imgmime = 'script.png';
11747 $srclang = 'dos';
11748 $famime = 'file-code';
11749 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
11750 $mime = 'text/x-sh';
11751 $imgmime = 'script.png';
11752 $srclang = 'bash';
11753 $famime = 'file-code';
11754 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
11755 $mime = 'text/x-ksh';
11756 $imgmime = 'script.png';
11757 $srclang = 'bash';
11758 $famime = 'file-code';
11759 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
11760 $mime = 'text/x-bash';
11761 $imgmime = 'script.png';
11762 $srclang = 'bash';
11763 $famime = 'file-code';
11764 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
11765 $mime = 'image/x-icon';
11766 $imgmime = 'image.png';
11767 $famime = 'file-image';
11768 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
11769 $mime = 'image/jpeg';
11770 $imgmime = 'image.png';
11771 $famime = 'file-image';
11772 } elseif (preg_match('/\.png$/i', $tmpfile)) {
11773 $mime = 'image/png';
11774 $imgmime = 'image.png';
11775 $famime = 'file-image';
11776 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
11777 $mime = 'image/gif';
11778 $imgmime = 'image.png';
11779 $famime = 'file-image';
11780 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
11781 $mime = 'image/bmp';
11782 $imgmime = 'image.png';
11783 $famime = 'file-image';
11784 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
11785 $mime = 'image/tiff';
11786 $imgmime = 'image.png';
11787 $famime = 'file-image';
11788 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
11789 $mime = 'image/svg+xml';
11790 $imgmime = 'image.png';
11791 $famime = 'file-image';
11792 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
11793 $mime = 'image/webp';
11794 $imgmime = 'image.png';
11795 $famime = 'file-image';
11796 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
11797 $mime = 'text/calendar';
11798 $imgmime = 'other.png';
11799 $famime = 'file-alt';
11800 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
11801 $mime = 'text/calendar';
11802 $imgmime = 'other.png';
11803 $famime = 'file-alt';
11804 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
11805 $mime = 'application/x-bittorrent';
11806 $imgmime = 'other.png';
11807 $famime = 'file-o';
11808 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
11809 $mime = 'audio';
11810 $imgmime = 'audio.png';
11811 $famime = 'file-audio';
11812 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
11813 $mime = 'video/mp4';
11814 $imgmime = 'video.png';
11815 $famime = 'file-video';
11816 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
11817 $mime = 'video/ogg';
11818 $imgmime = 'video.png';
11819 $famime = 'file-video';
11820 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
11821 $mime = 'video/webm';
11822 $imgmime = 'video.png';
11823 $famime = 'file-video';
11824 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
11825 $mime = 'video/x-msvideo';
11826 $imgmime = 'video.png';
11827 $famime = 'file-video';
11828 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
11829 $mime = 'video/divx';
11830 $imgmime = 'video.png';
11831 $famime = 'file-video';
11832 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
11833 $mime = 'video/xvid';
11834 $imgmime = 'video.png';
11835 $famime = 'file-video';
11836 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
11837 $mime = 'video';
11838 $imgmime = 'video.png';
11839 $famime = 'file-video';
11840 } elseif (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
11841 // application/xxx where zzz is zip, ...
11842 $mime = 'archive';
11843 $imgmime = 'archive.png';
11844 $famime = 'file-archive';
11845 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
11846 $mime = 'application/octet-stream';
11847 $imgmime = 'other.png';
11848 $famime = 'file-o';
11849 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
11850 $mime = 'library';
11851 $imgmime = 'library.png';
11852 $famime = 'file-o';
11853 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
11854 $mime = 'error';
11855 $imgmime = 'error.png';
11856 $famime = 'file-alt';
11857 }
11858
11859 // Return mimetype string
11860 switch ((int) $mode) {
11861 case 1:
11862 $tmp = explode('/', $mime);
11863 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
11864 case 2:
11865 return $imgmime;
11866 case 3:
11867 return $srclang;
11868 case 4:
11869 return $famime;
11870 }
11871 return $mime;
11872}
11873
11885function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
11886{
11887 global $conf, $db;
11888
11889 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
11890
11891 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
11892
11893 if (is_null($dictvalues)) {
11894 $dictvalues = array();
11895
11896 $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
11897 if ($checkentity) {
11898 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
11899 }
11900
11901 $resql = $db->query($sql);
11902 if ($resql) {
11903 while ($obj = $db->fetch_object($resql)) {
11904 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
11905 }
11906 } else {
11907 dol_print_error($db);
11908 }
11909
11910 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
11911 }
11912
11913 if (!empty($dictvalues[$id])) {
11914 // Found
11915 $tmp = $dictvalues[$id];
11916 return (property_exists($tmp, $field) ? $tmp->$field : '');
11917 } else {
11918 // Not found
11919 return '';
11920 }
11921}
11922
11929function colorIsLight($stringcolor)
11930{
11931 $stringcolor = str_replace('#', '', $stringcolor);
11932 $res = -1;
11933 if (!empty($stringcolor)) {
11934 $res = 0;
11935 $tmp = explode(',', $stringcolor);
11936 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
11937 $r = $tmp[0];
11938 $g = $tmp[1];
11939 $b = $tmp[2];
11940 } else {
11941 $hexr = $stringcolor[0].$stringcolor[1];
11942 $hexg = $stringcolor[2].$stringcolor[3];
11943 $hexb = $stringcolor[4].$stringcolor[5];
11944 $r = hexdec($hexr);
11945 $g = hexdec($hexg);
11946 $b = hexdec($hexb);
11947 }
11948 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
11949 if ($bright > 0.6) {
11950 $res = 1;
11951 }
11952 }
11953 return $res;
11954}
11955
11964function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
11965{
11966 global $conf;
11967
11968 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
11969 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
11970 if (empty($menuentry['enabled'])) {
11971 return 0; // Entry disabled by condition
11972 }
11973 if ($type_user && $menuentry['module']) {
11974 $tmploops = explode('|', $menuentry['module']);
11975 $found = 0;
11976 foreach ($tmploops as $tmploop) {
11977 if (in_array($tmploop, $listofmodulesforexternal)) {
11978 $found++;
11979 break;
11980 }
11981 }
11982 if (!$found) {
11983 return 0; // Entry is for menus all excluded to external users
11984 }
11985 }
11986 if (!$menuentry['perms'] && $type_user) {
11987 return 0; // No permissions and user is external
11988 }
11989 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
11990 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
11991 }
11992 if (!$menuentry['perms']) {
11993 return 2; // No permissions and user is external
11994 }
11995 return 1;
11996}
11997
12005function roundUpToNextMultiple($n, $x = 5)
12006{
12007 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12008 return (int) $result;
12009}
12010
12022function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12023{
12024 $csstouse = 'badge';
12025 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12026 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12027 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12028
12029 $attr = array(
12030 'class' => $csstouse
12031 );
12032
12033 if (empty($html)) {
12034 $html = $label;
12035 }
12036
12037 if (!empty($url)) {
12038 $attr['href'] = $url;
12039 }
12040
12041 if ($mode === 'dot') {
12042 $attr['class'] .= ' classfortooltip';
12043 $attr['title'] = $html;
12044 $attr['aria-label'] = $label;
12045 $html = '';
12046 }
12047
12048 // Override attr
12049 if (!empty($params['attr']) && is_array($params['attr'])) {
12050 foreach ($params['attr'] as $key => $value) {
12051 if ($key == 'class') {
12052 $attr['class'] .= ' '.$value;
12053 } elseif ($key == 'classOverride') {
12054 $attr['class'] = $value;
12055 } else {
12056 $attr[$key] = $value;
12057 }
12058 }
12059 }
12060
12061 // TODO: add hook
12062
12063 // escape all attribute
12064 $attr = array_map('dol_escape_htmltag', $attr);
12065
12066 $TCompiledAttr = array();
12067 foreach ($attr as $key => $value) {
12068 $TCompiledAttr[] = $key.'="'.$value.'"';
12069 }
12070
12071 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12072
12073 $tag = !empty($url) ? 'a' : 'span';
12074
12075 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12076}
12077
12078
12091function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12092{
12093 global $conf;
12094
12095 $return = '';
12096 $dolGetBadgeParams = array();
12097
12098 if (!empty($params['badgeParams'])) {
12099 $dolGetBadgeParams = $params['badgeParams'];
12100 }
12101
12102 // TODO : add a hook
12103 if ($displayMode == 0) {
12104 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12105 } elseif ($displayMode == 1) {
12106 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12107 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12108 // Use status with images (for backward compatibility)
12109 $return = '';
12110 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12111 $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>' : '');
12112
12113 // For small screen, we always use the short label instead of long label.
12114 if (!empty($conf->dol_optimize_smallscreen)) {
12115 if ($displayMode == 0) {
12116 $displayMode = 1;
12117 } elseif ($displayMode == 4) {
12118 $displayMode = 2;
12119 } elseif ($displayMode == 6) {
12120 $displayMode = 5;
12121 }
12122 }
12123
12124 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12125 $statusImg = array(
12126 'status0' => 'statut0',
12127 'status1' => 'statut1',
12128 'status2' => 'statut2',
12129 'status3' => 'statut3',
12130 'status4' => 'statut4',
12131 'status5' => 'statut5',
12132 'status6' => 'statut6',
12133 'status7' => 'statut7',
12134 'status8' => 'statut8',
12135 'status9' => 'statut9'
12136 );
12137
12138 if (!empty($statusImg[$statusType])) {
12139 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12140 } else {
12141 $htmlImg = img_picto($statusLabel, $statusType);
12142 }
12143
12144 if ($displayMode === 2) {
12145 $return = $htmlImg.' '.$htmlLabelShort;
12146 } elseif ($displayMode === 3) {
12147 $return = $htmlImg;
12148 } elseif ($displayMode === 4) {
12149 $return = $htmlImg.' '.$htmlLabel;
12150 } elseif ($displayMode === 5) {
12151 $return = $htmlLabelShort.' '.$htmlImg;
12152 } else { // $displayMode >= 6
12153 $return = $htmlLabel.' '.$htmlImg;
12154 }
12155 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12156 // Use new badge
12157 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12158
12159 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12160 if (empty($dolGetBadgeParams['attr']['title'])) {
12161 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12162 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12163 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12164 // And if we use tooltip, we can output title in HTML
12165 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12166 }
12167
12168 if ($displayMode == 3) {
12169 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12170 } elseif ($displayMode === 5) {
12171 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12172 } else {
12173 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12174 }
12175 }
12176
12177 return $return;
12178}
12179
12180
12217function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12218{
12219 global $hookmanager, $action, $object, $langs;
12220
12221 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12222 if (is_array($url)) {
12223 // Loop on $url array to remove entries of disabled modules
12224 foreach ($url as $key => $subbutton) {
12225 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12226 unset($url[$key]);
12227 }
12228 }
12229
12230 $out = '';
12231
12232 if (isset($params["areDropdownButtons"]) && $params["areDropdownButtons"] === false) {
12233 foreach ($url as $button) {
12234 if (!empty($button['lang'])) {
12235 $langs->load($button['lang']);
12236 }
12237 $label = $langs->trans($button['label']);
12238 $text = $button['text'] ?? '';
12239 $actionType = $button['actionType'] ?? '';
12240 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12241 $id = $button['$id'] ?? '';
12242 $userRight = $button['perm'] ?? 1;
12243 $params = $button['$params'] ?? [];
12244
12245 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $params);
12246 }
12247 return $out;
12248 }
12249
12250 if (count($url) > 1) {
12251 $out .= '<div class="dropdown inline-block dropdown-holder">';
12252 $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>';
12253 $out .= '<div class="dropdown-content">';
12254 foreach ($url as $subbutton) {
12255 if (!empty($subbutton['lang'])) {
12256 $langs->load($subbutton['lang']);
12257 }
12258 $tmpurl = DOL_URL_ROOT.$subbutton['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12259 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], array('isDropDown' => true));
12260 }
12261 $out .= "</div>";
12262 $out .= "</div>";
12263 } else {
12264 foreach ($url as $subbutton) { // Should loop on 1 record only
12265 if (!empty($subbutton['lang'])) {
12266 $langs->load($subbutton['lang']);
12267 }
12268 $tmpurl = DOL_URL_ROOT.$subbutton['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12269 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm']);
12270 }
12271 }
12272
12273 return $out;
12274 }
12275
12276 // Here, $url is a simple link
12277
12278 if (!empty($params['isDropdown'])) {
12279 $class = "dropdown-item";
12280 } else {
12281 $class = 'butAction';
12282 if ($actionType == 'danger' || $actionType == 'delete') {
12283 $class = 'butActionDelete';
12284 if (!empty($url) && strpos($url, 'token=') === false) {
12285 $url .= '&token='.newToken();
12286 }
12287 }
12288 }
12289 $attr = array(
12290 'class' => $class,
12291 'href' => empty($url) ? '' : $url,
12292 'title' => $label
12293 );
12294
12295 if (empty($text)) {
12296 $text = $label;
12297 $attr['title'] = ''; // if html not set, leave label on title is redundant
12298 } else {
12299 $attr['title'] = $label;
12300 $attr['aria-label'] = $label;
12301 }
12302
12303 if (empty($userRight)) {
12304 $attr['class'] = 'butActionRefused';
12305 $attr['href'] = '';
12306 $attr['title'] = (($label && $text && $label != $text) ? $label : $langs->trans('NotEnoughPermissions'));
12307 }
12308
12309 if (!empty($id)) {
12310 $attr['id'] = $id;
12311 }
12312
12313 // Override attr
12314 if (!empty($params['attr']) && is_array($params['attr'])) {
12315 foreach ($params['attr'] as $key => $value) {
12316 if ($key == 'class') {
12317 $attr['class'] .= ' '.$value;
12318 } elseif ($key == 'classOverride') {
12319 $attr['class'] = $value;
12320 } else {
12321 $attr[$key] = $value;
12322 }
12323 }
12324 }
12325
12326 // automatic add tooltip when title is detected
12327 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12328 $attr['class'] .= ' classfortooltip';
12329 }
12330
12331 // Js Confirm button
12332 if ($userRight && !empty($params['confirm'])) {
12333 if (!is_array($params['confirm'])) {
12334 $params['confirm'] = array();
12335 }
12336
12337 if (empty($params['confirm']['url'])) {
12338 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12339 }
12340
12341 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12342 $attr['data-confirm-url'] = $params['confirm']['url'];
12343 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12344 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12345 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12346 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12347 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12348 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
12349
12350 $attr['class'] .= ' butActionConfirm';
12351 }
12352
12353 if (isset($attr['href']) && empty($attr['href'])) {
12354 unset($attr['href']);
12355 }
12356
12357 // escape all attribute
12358 $attr = array_map('dol_escape_htmltag', $attr);
12359
12360 $TCompiledAttr = array();
12361 foreach ($attr as $key => $value) {
12362 $TCompiledAttr[] = $key.'= "'.$value.'"';
12363 }
12364
12365 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
12366
12367 $tag = !empty($attr['href']) ? 'a' : 'span';
12368
12369
12370 $parameters = array(
12371 'TCompiledAttr' => $TCompiledAttr, // array
12372 'compiledAttributes' => $compiledAttributes, // string
12373 'attr' => $attr,
12374 'tag' => $tag,
12375 'label' => $label,
12376 'html' => $text,
12377 'actionType' => $actionType,
12378 'url' => $url,
12379 'id' => $id,
12380 'userRight' => $userRight,
12381 'params' => $params
12382 );
12383
12384 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
12385 if ($reshook < 0) {
12386 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
12387 }
12388
12389 if (empty($reshook)) {
12390 if (dol_textishtml($text)) { // If content already HTML encoded
12391 return '<' . $tag . ' ' . $compiledAttributes . '>' . $text . '</' . $tag . '>';
12392 } else {
12393 return '<' . $tag . ' ' . $compiledAttributes . '>' . dol_escape_htmltag($text) . '</' . $tag . '>';
12394 }
12395 } else {
12396 return $hookmanager->resPrint;
12397 }
12398}
12399
12406function dolGetButtonTitleSeparator($moreClass = "")
12407{
12408 return '<span class="button-title-separator '.$moreClass.'" ></span>';
12409}
12410
12417function getFieldErrorIcon($fieldValidationErrorMsg)
12418{
12419 $out = '';
12420 if (!empty($fieldValidationErrorMsg)) {
12421 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
12422 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
12423 $out .= '</span>';
12424 }
12425
12426 return $out;
12427}
12428
12441function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
12442{
12443 global $langs, $conf, $user;
12444
12445 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
12446 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
12447 return '';
12448 }
12449
12450 $class = 'btnTitle';
12451 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
12452 $class .= ' btnTitlePlus';
12453 }
12454 $useclassfortooltip = 1;
12455
12456 if (!empty($params['morecss'])) {
12457 $class .= ' '.$params['morecss'];
12458 }
12459
12460 $attr = array(
12461 'class' => $class,
12462 'href' => empty($url) ? '' : $url
12463 );
12464
12465 if (!empty($helpText)) {
12466 $attr['title'] = dol_escape_htmltag($helpText);
12467 } elseif (empty($attr['title']) && $label) {
12468 $attr['title'] = $label;
12469 $useclassfortooltip = 0;
12470 }
12471
12472 if ($status == 2) {
12473 $attr['class'] .= ' btnTitleSelected';
12474 } elseif ($status <= 0) {
12475 $attr['class'] .= ' refused';
12476
12477 $attr['href'] = '';
12478
12479 if ($status == -1) { // disable
12480 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
12481 } elseif ($status == 0) { // Not enough permissions
12482 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
12483 }
12484 }
12485
12486 if (!empty($attr['title']) && $useclassfortooltip) {
12487 $attr['class'] .= ' classfortooltip';
12488 }
12489
12490 if (!empty($id)) {
12491 $attr['id'] = $id;
12492 }
12493
12494 // Override attr
12495 if (!empty($params['attr']) && is_array($params['attr'])) {
12496 foreach ($params['attr'] as $key => $value) {
12497 if ($key == 'class') {
12498 $attr['class'] .= ' '.$value;
12499 } elseif ($key == 'classOverride') {
12500 $attr['class'] = $value;
12501 } else {
12502 $attr[$key] = $value;
12503 }
12504 }
12505 }
12506
12507 if (isset($attr['href']) && empty($attr['href'])) {
12508 unset($attr['href']);
12509 }
12510
12511 // TODO : add a hook
12512
12513 // escape all attribute
12514 $attr = array_map('dol_escape_htmltag', $attr);
12515
12516 $TCompiledAttr = array();
12517 foreach ($attr as $key => $value) {
12518 $TCompiledAttr[] = $key.'="'.$value.'"';
12519 }
12520
12521 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
12522
12523 $tag = (empty($attr['href']) ? 'span' : 'a');
12524
12525 $button = '<'.$tag.' '.$compiledAttributes.'>';
12526 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
12527 if (!empty($params['forcenohideoftext'])) {
12528 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
12529 }
12530 $button .= '</'.$tag.'>';
12531
12532 return $button;
12533}
12534
12544function getElementProperties($elementType)
12545{
12546 global $conf, $db, $hookmanager;
12547
12548 $regs = array();
12549
12550 //$element_type='facture';
12551
12552 $classfile = $classname = $classpath = $subdir = $dir_output = '';
12553
12554 // Parse element/subelement
12555 $module = $elementType;
12556 $element = $elementType;
12557 $subelement = $elementType;
12558 $table_element = $elementType;
12559
12560 // If we ask a resource form external module (instead of default path)
12561 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
12562 $element = $subelement = $regs[1];
12563 $module = $regs[2];
12564 }
12565
12566 // If we ask a resource for a string with an element and a subelement
12567 // Example 'project_task'
12568 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
12569 $module = $element = $regs[1];
12570 $subelement = $regs[2];
12571 }
12572
12573 // Object lines will use parent classpath and module ref
12574 if (substr($elementType, -3) == 'det') {
12575 $module = preg_replace('/det$/', '', $element);
12576 $subelement = preg_replace('/det$/', '', $subelement);
12577 $classpath = $module.'/class';
12578 $classfile = $module;
12579 $classname = preg_replace('/det$/', 'Line', $element);
12580 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
12581 $classname = preg_replace('/det$/', 'Ligne', $element);
12582 }
12583 }
12584 // For compatibility and to work with non standard path
12585 if ($elementType == "action" || $elementType == "actioncomm") {
12586 $classpath = 'comm/action/class';
12587 $subelement = 'Actioncomm';
12588 $module = 'agenda';
12589 $table_element = 'actioncomm';
12590 } elseif ($elementType == 'cronjob') {
12591 $classpath = 'cron/class';
12592 $module = 'cron';
12593 $table_element = 'cron';
12594 } elseif ($elementType == 'adherent_type') {
12595 $classpath = 'adherents/class';
12596 $classfile = 'adherent_type';
12597 $module = 'adherent';
12598 $subelement = 'adherent_type';
12599 $classname = 'AdherentType';
12600 $table_element = 'adherent_type';
12601 } elseif ($elementType == 'bank_account') {
12602 $classpath = 'compta/bank/class';
12603 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
12604 $classfile = 'account';
12605 $classname = 'Account';
12606 } elseif ($elementType == 'category') {
12607 $classpath = 'categories/class';
12608 $module = 'categorie';
12609 $subelement = 'categorie';
12610 $table_element = 'categorie';
12611 } elseif ($elementType == 'contact') {
12612 $classpath = 'contact/class';
12613 $classfile = 'contact';
12614 $module = 'societe';
12615 $subelement = 'contact';
12616 $table_element = 'socpeople';
12617 } elseif ($elementType == 'inventory') {
12618 $module = 'product';
12619 $classpath = 'product/inventory/class';
12620 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
12621 $module = 'stock';
12622 $classpath = 'product/stock/class';
12623 $classfile = 'entrepot';
12624 $classname = 'Entrepot';
12625 $table_element = 'entrepot';
12626 } elseif ($elementType == 'project') {
12627 $classpath = 'projet/class';
12628 $module = 'projet';
12629 $table_element = 'projet';
12630 } elseif ($elementType == 'project_task') {
12631 $classpath = 'projet/class';
12632 $module = 'projet';
12633 $subelement = 'task';
12634 $table_element = 'projet_task';
12635 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
12636 $classpath = 'compta/facture/class';
12637 $module = 'facture';
12638 $subelement = 'facture';
12639 $table_element = 'facture';
12640 } elseif ($elementType == 'facturerec') {
12641 $classpath = 'compta/facture/class';
12642 $module = 'facture';
12643 $classname = 'FactureRec';
12644 } elseif ($elementType == 'commande' || $elementType == 'order') {
12645 $classpath = 'commande/class';
12646 $module = 'commande';
12647 $subelement = 'commande';
12648 $table_element = 'commande';
12649 } elseif ($elementType == 'propal') {
12650 $classpath = 'comm/propal/class';
12651 $table_element = 'propal';
12652 } elseif ($elementType == 'shipping') {
12653 $classpath = 'expedition/class';
12654 $classfile = 'expedition';
12655 $classname = 'Expedition';
12656 $module = 'expedition';
12657 $table_element = 'expedition';
12658 } elseif ($elementType == 'delivery_note') {
12659 $classpath = 'delivery/class';
12660 $subelement = 'delivery';
12661 $module = 'expedition';
12662 } elseif ($elementType == 'delivery') {
12663 $classpath = 'delivery/class';
12664 $subelement = 'delivery';
12665 $module = 'expedition';
12666 } elseif ($elementType == 'supplier_proposal') {
12667 $classpath = 'supplier_proposal/class';
12668 $module = 'supplier_proposal';
12669 $element = 'supplierproposal';
12670 $classfile = 'supplier_proposal';
12671 $subelement = 'supplierproposal';
12672 } elseif ($elementType == 'contract') {
12673 $classpath = 'contrat/class';
12674 $module = 'contrat';
12675 $subelement = 'contrat';
12676 $table_element = 'contract';
12677 } elseif ($elementType == 'mailing') {
12678 $classpath = 'comm/mailing/class';
12679 $module = 'mailing';
12680 $classfile = 'mailing';
12681 $classname = 'Mailing';
12682 $subelement = '';
12683 } elseif ($elementType == 'member') {
12684 $classpath = 'adherents/class';
12685 $module = 'adherent';
12686 $subelement = 'adherent';
12687 $table_element = 'adherent';
12688 } elseif ($elementType == 'usergroup') {
12689 $classpath = 'user/class';
12690 $module = 'user';
12691 } elseif ($elementType == 'mo') {
12692 $classpath = 'mrp/class';
12693 $classfile = 'mo';
12694 $classname = 'Mo';
12695 $module = 'mrp';
12696 $subelement = '';
12697 $table_element = 'mrp_mo';
12698 } elseif ($elementType == 'cabinetmed_cons') {
12699 $classpath = 'cabinetmed/class';
12700 $module = 'cabinetmed';
12701 $subelement = 'cabinetmedcons';
12702 $table_element = 'cabinetmedcons';
12703 } elseif ($elementType == 'fichinter') {
12704 $classpath = 'fichinter/class';
12705 $module = 'ficheinter';
12706 $subelement = 'fichinter';
12707 $table_element = 'fichinter';
12708 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
12709 $classpath = 'resource/class';
12710 $module = 'resource';
12711 $subelement = 'dolresource';
12712 $table_element = 'resource';
12713 } elseif ($elementType == 'propaldet') {
12714 $classpath = 'comm/propal/class';
12715 $module = 'propal';
12716 $subelement = 'propaleligne';
12717 } elseif ($elementType == 'opensurvey_sondage') {
12718 $classpath = 'opensurvey/class';
12719 $module = 'opensurvey';
12720 $subelement = 'opensurveysondage';
12721 } elseif ($elementType == 'order_supplier') {
12722 $classpath = 'fourn/class';
12723 $module = 'fournisseur';
12724 $classfile = 'fournisseur.commande';
12725 $element = 'order_supplier';
12726 $subelement = '';
12727 $classname = 'CommandeFournisseur';
12728 $table_element = 'commande_fournisseur';
12729 } elseif ($elementType == 'commande_fournisseurdet') {
12730 $classpath = 'fourn/class';
12731 $module = 'fournisseur';
12732 $classfile = 'fournisseur.commande';
12733 $element = 'commande_fournisseurdet';
12734 $subelement = '';
12735 $classname = 'CommandeFournisseurLigne';
12736 $table_element = 'commande_fournisseurdet';
12737 } elseif ($elementType == 'invoice_supplier') {
12738 $classpath = 'fourn/class';
12739 $module = 'fournisseur';
12740 $classfile = 'fournisseur.facture';
12741 $element = 'invoice_supplier';
12742 $subelement = '';
12743 $classname = 'FactureFournisseur';
12744 $table_element = 'facture_fourn';
12745 } elseif ($elementType == "service") {
12746 $classpath = 'product/class';
12747 $subelement = 'product';
12748 $table_element = 'product';
12749 } elseif ($elementType == 'salary') {
12750 $classpath = 'salaries/class';
12751 $module = 'salaries';
12752 } elseif ($elementType == 'payment_salary') {
12753 $classpath = 'salaries/class';
12754 $classfile = 'paymentsalary';
12755 $classname = 'PaymentSalary';
12756 $module = 'salaries';
12757 } elseif ($elementType == 'productlot') {
12758 $module = 'productbatch';
12759 $classpath = 'product/stock/class';
12760 $classfile = 'productlot';
12761 $classname = 'Productlot';
12762 $element = 'productlot';
12763 $subelement = '';
12764 $table_element = 'product_lot';
12765 } elseif ($elementType == 'societeaccount') {
12766 $classpath = 'societe/class';
12767 $classfile = 'societeaccount';
12768 $classname = 'SocieteAccount';
12769 $module = 'societe';
12770 } elseif ($elementType == 'websitepage') {
12771 $classpath = 'website/class';
12772 $classfile = 'websitepage';
12773 $classname = 'Websitepage';
12774 $module = 'website';
12775 $subelement = 'websitepage';
12776 $table_element = 'website_page';
12777 } elseif ($elementType == 'fiscalyear') {
12778 $classpath = 'core/class';
12779 $module = 'accounting';
12780 $subelement = 'fiscalyear';
12781 } elseif ($elementType == 'chargesociales') {
12782 $classpath = 'compta/sociales/class';
12783 $module = 'tax';
12784 $table_element = 'chargesociales';
12785 } elseif ($elementType == 'tva') {
12786 $classpath = 'compta/tva/class';
12787 $module = 'tax';
12788 $subdir = '/vat';
12789 $table_element = 'tva';
12790 } elseif ($elementType == 'emailsenderprofile') {
12791 $module = '';
12792 $classpath = 'core/class';
12793 $classfile = 'emailsenderprofile';
12794 $classname = 'EmailSenderProfile';
12795 $table_element = 'c_email_senderprofile';
12796 $subelement = '';
12797 } elseif ($elementType == 'conferenceorboothattendee') {
12798 $classpath = 'eventorganization/class';
12799 $classfile = 'conferenceorboothattendee';
12800 $classname = 'ConferenceOrBoothAttendee';
12801 $module = 'eventorganization';
12802 } elseif ($elementType == 'conferenceorbooth') {
12803 $classpath = 'eventorganization/class';
12804 $classfile = 'conferenceorbooth';
12805 $classname = 'ConferenceOrBooth';
12806 $module = 'eventorganization';
12807 } elseif ($elementType == 'ccountry') {
12808 $module = '';
12809 $classpath = 'core/class';
12810 $classfile = 'ccountry';
12811 $classname = 'Ccountry';
12812 $table_element = 'c_country';
12813 $subelement = '';
12814 } elseif ($elementType == 'knowledgerecord') {
12815 $module = '';
12816 $classpath = 'knowledgemanagement/class';
12817 $classfile = 'knowledgerecord';
12818 $classname = 'KnowledgeRecord';
12819 $table_element = 'knowledgemanagement_knowledgerecord';
12820 $subelement = '';
12821 }
12822
12823 if (empty($classfile)) {
12824 $classfile = strtolower($subelement);
12825 }
12826 if (empty($classname)) {
12827 $classname = ucfirst($subelement);
12828 }
12829 if (empty($classpath)) {
12830 $classpath = $module.'/class';
12831 }
12832
12833 //print 'getElementProperties subdir='.$subdir;
12834
12835 // Set dir_output
12836 if ($module && isset($conf->$module)) { // The generic case
12837 if (!empty($conf->$module->multidir_output[$conf->entity])) {
12838 $dir_output = $conf->$module->multidir_output[$conf->entity];
12839 } elseif (!empty($conf->$module->output[$conf->entity])) {
12840 $dir_output = $conf->$module->output[$conf->entity];
12841 } elseif (!empty($conf->$module->dir_output)) {
12842 $dir_output = $conf->$module->dir_output;
12843 }
12844 }
12845
12846 // Overwrite value for special cases
12847 if ($element == 'order_supplier') {
12848 $dir_output = $conf->fournisseur->commande->dir_output;
12849 } elseif ($element == 'invoice_supplier') {
12850 $dir_output = $conf->fournisseur->facture->dir_output;
12851 }
12852 $dir_output .= $subdir;
12853
12854 $elementProperties = array(
12855 'module' => $module,
12856 'element' => $element,
12857 'table_element' => $table_element,
12858 'subelement' => $subelement,
12859 'classpath' => $classpath,
12860 'classfile' => $classfile,
12861 'classname' => $classname,
12862 'dir_output' => $dir_output
12863 );
12864
12865
12866 // Add hook
12867 if (!is_object($hookmanager)) {
12868 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
12869 $hookmanager = new HookManager($db);
12870 }
12871 $hookmanager->initHooks(array('elementproperties'));
12872
12873
12874 // Hook params
12875 $parameters = array(
12876 'elementType' => $elementType,
12877 'elementProperties' => $elementProperties
12878 );
12879
12880 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
12881
12882 if ($reshook) {
12883 $elementProperties = $hookmanager->resArray;
12884 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
12885 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
12886 }
12887
12888 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
12889 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
12890 unset($hookmanager->contextarray[$key]);
12891 }
12892
12893 return $elementProperties;
12894}
12895
12908function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
12909{
12910 global $db, $globalCacheForGetObjectFromCache;
12911
12912 $ret = 0;
12913
12914 $element_prop = getElementProperties($element_type);
12915
12916 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
12917 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
12918 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
12919 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
12920 // of service and we will return properties of a product.
12921 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
12922 } elseif ($element_prop['module'] == 'societeaccount') {
12923 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
12924 } else {
12925 $ismodenabled = isModEnabled($element_prop['module']);
12926 }
12927 //var_dump('element_type='.$element_type);
12928 //var_dump($element_prop);
12929 //var_dump($element_prop['module'].' '.$ismodenabled);
12930 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
12931 if ($useCache === 1
12932 && !empty($globalCacheForGetObjectFromCache[$element_type])
12933 && !empty($globalCacheForGetObjectFromCache[$element_type][$element_id])
12934 && is_object($globalCacheForGetObjectFromCache[$element_type][$element_id])
12935 ) {
12936 return $globalCacheForGetObjectFromCache[$element_type][$element_id];
12937 }
12938
12939 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
12940
12941 if (class_exists($element_prop['classname'])) {
12942 $className = $element_prop['classname'];
12943 $objecttmp = new $className($db);
12944 '@phan-var-force CommonObject $objecttmp';
12945
12946 if ($element_id > 0 || !empty($element_ref)) {
12947 $ret = $objecttmp->fetch($element_id, $element_ref);
12948 if ($ret >= 0) {
12949 if (empty($objecttmp->module)) {
12950 $objecttmp->module = $element_prop['module'];
12951 }
12952
12953 if ($useCache > 0) {
12954 if (!isset($globalCacheForGetObjectFromCache[$element_type])) {
12955 $globalCacheForGetObjectFromCache[$element_type] = [];
12956 }
12957
12958 // Manage cache limit
12959 if (! empty($globalCacheForGetObjectFromCache[$element_type]) && is_array($globalCacheForGetObjectFromCache[$element_type]) && count($globalCacheForGetObjectFromCache[$element_type]) >= $maxCacheByType) {
12960 array_shift($globalCacheForGetObjectFromCache[$element_type]);
12961 }
12962
12963 $globalCacheForGetObjectFromCache[$element_type][$element_id] = $objecttmp;
12964 }
12965
12966 return $objecttmp;
12967 }
12968 } else {
12969 return $objecttmp; // returned an object without fetch
12970 }
12971 } else {
12972 return -1;
12973 }
12974 }
12975
12976 return $ret;
12977}
12978
12985function isAFileWithExecutableContent($filename)
12986{
12987 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)) {
12988 return true;
12989 }
12990
12991 return false;
12992}
12993
13001function newToken()
13002{
13003 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13004}
13005
13013function currentToken()
13014{
13015 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13016}
13017
13023function getNonce()
13024{
13025 global $conf;
13026
13027 if (empty($conf->cache['nonce'])) {
13028 $conf->cache['nonce'] = dolGetRandomBytes(8);
13029 }
13030
13031 return $conf->cache['nonce'];
13032}
13033
13034
13048function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13049{
13050 global $langs;
13051
13052 print '<div class="div-table-responsive-no-min">';
13053 print '<table class="noborder centpercent">';
13054 print '<tr class="liste_titre">';
13055
13056 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13057
13058 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13059
13060 if (!empty($link)) {
13061 if (!empty($arguments)) {
13062 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13063 } else {
13064 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13065 }
13066 }
13067
13068 if ($number > -1) {
13069 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13070 } elseif (!empty($link)) {
13071 print '<span class="badge marginleftonlyshort">...</span>';
13072 }
13073
13074 if (!empty($link)) {
13075 print '</a>';
13076 }
13077
13078 print '</th>';
13079
13080 if ($number < 0 && !empty($link)) {
13081 print '<th class="right">';
13082 print '</th>';
13083 }
13084
13085 print '</tr>';
13086}
13087
13096function finishSimpleTable($addLineBreak = false)
13097{
13098 print '</table>';
13099 print '</div>';
13100
13101 if ($addLineBreak) {
13102 print '<br>';
13103 }
13104}
13105
13117function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13118{
13119 global $langs;
13120
13121 if ($num === 0) {
13122 print '<tr class="oddeven">';
13123 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13124 print '</tr>';
13125 return;
13126 }
13127
13128 if ($nbofloop === 0) {
13129 // don't show a summary line
13130 return;
13131 }
13132
13133 if ($num === 0) {
13134 $colspan = $tableColumnCount;
13135 } elseif ($num > $nbofloop) {
13136 $colspan = $tableColumnCount;
13137 } else {
13138 $colspan = $tableColumnCount - 1;
13139 }
13140
13141 if ($extraRightColumn) {
13142 $colspan--;
13143 }
13144
13145 print '<tr class="liste_total">';
13146
13147 if ($nbofloop > 0 && $num > $nbofloop) {
13148 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13149 } else {
13150 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13151 print '<td class="right centpercent">'.price($total).'</td>';
13152 }
13153
13154 if ($extraRightColumn) {
13155 print '<td></td>';
13156 }
13157
13158 print '</tr>';
13159}
13160
13169function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13170{
13171 if ($method == -1) {
13172 $method = 0;
13173 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13174 $method = 1;
13175 }
13176 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13177 $method = 2;
13178 }
13179 }
13180
13181 // Be sure we don't have output buffering enabled to have readfile working correctly
13182 while (ob_get_level()) {
13183 ob_end_flush();
13184 }
13185
13186 // Solution 0
13187 if ($method == 0) {
13188 readfile($fullpath_original_file_osencoded);
13189 } elseif ($method == 1) {
13190 // Solution 1
13191 $handle = fopen($fullpath_original_file_osencoded, "rb");
13192 while (!feof($handle)) {
13193 print fread($handle, 8192);
13194 }
13195 fclose($handle);
13196 } elseif ($method == 2) {
13197 // Solution 2
13198 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13199 $handle2 = fopen("php://output", "wb");
13200 stream_copy_to_stream($handle1, $handle2);
13201 fclose($handle1);
13202 fclose($handle2);
13203 }
13204}
13205
13215function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13216{
13217 /*
13218 global $conf;
13219
13220 if (!empty($conf->dol_no_mouse_hover)) {
13221 $showonlyonhover = 0;
13222 }*/
13223
13224 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13225 if ($texttoshow === 'none') {
13226 $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>';
13227 } elseif ($texttoshow) {
13228 $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>';
13229 } else {
13230 $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>';
13231 }
13232
13233 return $result;
13234}
13235
13236
13243function jsonOrUnserialize($stringtodecode)
13244{
13245 $result = json_decode($stringtodecode);
13246 if ($result === null) {
13247 $result = unserialize($stringtodecode);
13248 }
13249
13250 return $result;
13251}
13252
13253
13270function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
13271{
13272 global $db, $user;
13273
13274 if ($filter === '') {
13275 return '';
13276 }
13277 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
13278 $filter = '(' . $filter . ')';
13279 }
13280
13281 $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'
13282 $firstandlastparenthesis = 0;
13283
13284 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
13285 if ($noerror) {
13286 return '1 = 2';
13287 } else {
13288 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
13289 }
13290 }
13291
13292 // Test the filter syntax
13293 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
13294 $t = str_replace(array('and','or','AND','OR',' '), '', $t); // Remove the only strings allowed between each () criteria
13295 // If the string result contains something else than '()', the syntax was wrong
13296
13297 if (preg_match('/[^\‍(\‍)]/', $t)) {
13298 $tmperrorstr = 'Bad syntax of the search string';
13299 $errorstr = 'Bad syntax of the search string: '.$filter;
13300 if ($noerror) {
13301 return '1 = 2';
13302 } else {
13303 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
13304 }
13305 }
13306
13307 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeCriteriaCallback', $filter).($nopar ? "" : ')');
13308
13309 if (is_object($db)) {
13310 $ret = str_replace('__NOW__', $db->idate(dol_now()), $ret);
13311 }
13312 if (is_object($user)) {
13313 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
13314 }
13315
13316 return $ret;
13317}
13318
13326function dolForgeExplodeAnd($sqlfilters)
13327{
13328 $arrayofandtags = array();
13329 $nbofchars = dol_strlen($sqlfilters);
13330
13331 $error = '';
13332 $parenthesislevel = 0;
13333 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
13334 if (!$result) {
13335 return array();
13336 }
13337 if ($parenthesislevel >= 1) {
13338 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
13339 }
13340
13341 $i = 0;
13342 $s = '';
13343 $countparenthesis = 0;
13344 while ($i < $nbofchars) {
13345 $char = dol_substr($sqlfilters, $i, 1);
13346
13347 if ($char == '(') {
13348 $countparenthesis++;
13349 } elseif ($char == ')') {
13350 $countparenthesis--;
13351 }
13352
13353 if ($countparenthesis == 0) {
13354 $char2 = dol_substr($sqlfilters, $i + 1, 1);
13355 $char3 = dol_substr($sqlfilters, $i + 2, 1);
13356 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
13357 // We found a AND
13358 $s = trim($s);
13359 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13360 $s = '('.$s.')';
13361 }
13362 $arrayofandtags[] = $s;
13363 $s = '';
13364 $i += 2;
13365 } else {
13366 $s .= $char;
13367 }
13368 } else {
13369 $s .= $char;
13370 }
13371 $i++;
13372 }
13373 if ($s) {
13374 $s = trim($s);
13375 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
13376 $s = '('.$s.')';
13377 }
13378 $arrayofandtags[] = $s;
13379 }
13380
13381 return $arrayofandtags;
13382}
13383
13393function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
13394{
13395 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
13396 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
13397 $tmp = $sqlfilters;
13398
13399 $nb = dol_strlen($tmp);
13400 $counter = 0;
13401 $parenthesislevel = 0;
13402
13403 $error = '';
13404
13405 $i = 0;
13406 while ($i < $nb) {
13407 $char = dol_substr($tmp, $i, 1);
13408
13409 if ($char == '(') {
13410 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
13411 // We open a parenthesis and it is the first char
13412 $parenthesislevel++;
13413 }
13414 $counter++;
13415 } elseif ($char == ')') {
13416 $nbcharremaining = ($nb - $i - 1);
13417 if ($nbcharremaining >= $counter) {
13418 $parenthesislevel = min($parenthesislevel, $counter - 1);
13419 }
13420 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
13421 $parenthesislevel = $counter;
13422 }
13423 $counter--;
13424 }
13425
13426 if ($counter < 0) {
13427 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13428 $parenthesislevel = 0;
13429 dol_syslog($error, LOG_WARNING);
13430 return false;
13431 }
13432
13433 $i++;
13434 }
13435
13436 if ($counter > 0) {
13437 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
13438 $parenthesislevel = 0;
13439 dol_syslog($error, LOG_WARNING);
13440 return false;
13441 }
13442
13443 return true;
13444}
13445
13453function dolForgeDummyCriteriaCallback($matches)
13454{
13455 //dol_syslog("Convert matches ".$matches[1]);
13456 if (empty($matches[1])) {
13457 return '';
13458 }
13459 $tmp = explode(':', $matches[1]);
13460 if (count($tmp) < 3) {
13461 return '';
13462 }
13463
13464 return '()'; // An empty criteria
13465}
13466
13475function dolForgeCriteriaCallback($matches)
13476{
13477 global $db;
13478
13479 //dol_syslog("Convert matches ".$matches[1]);
13480 if (empty($matches[1])) {
13481 return '';
13482 }
13483 $tmp = explode(':', $matches[1]);
13484 if (count($tmp) < 3) {
13485 return '';
13486 }
13487
13488 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
13489
13490 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
13491
13492 $realOperator = [
13493 'NOTLIKE' => 'NOT LIKE',
13494 'ISNOT' => 'IS NOT',
13495 'NOTIN' => 'NOT IN',
13496 '!=' => '<>',
13497 ];
13498
13499 if (array_key_exists($operator, $realOperator)) {
13500 $operator = $realOperator[$operator];
13501 }
13502
13503 $tmpescaped = $tmp[2];
13504
13505 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
13506
13507 $regbis = array();
13508
13509 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
13510 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
13511 $tmpescaped2 = '(';
13512 // Explode and sanitize each element in list
13513 $tmpelemarray = explode(',', $tmpescaped);
13514 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
13515 $reg = array();
13516 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
13517 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 1, 1, 1))."'";
13518 } else {
13519 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1));
13520 }
13521 }
13522 $tmpescaped2 .= implode(',', $tmpelemarray);
13523 $tmpescaped2 .= ')';
13524
13525 $tmpescaped = $tmpescaped2;
13526 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
13527 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
13528 $tmpescaped = $regbis[1];
13529 }
13530 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
13531 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
13532 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
13533 // TODO Retrieve type of field for $operand field name.
13534 // So we can complete format. For example we could complete a year with month and day.
13535 $tmpescaped = "'".$db->escape($regbis[1])."'";
13536 } else {
13537 if (strtoupper($tmpescaped) == 'NULL') {
13538 $tmpescaped = 'NULL';
13539 } elseif (is_int($tmpescaped)) {
13540 $tmpescaped = (int) $tmpescaped;
13541 } else {
13542 $tmpescaped = (float) $tmpescaped;
13543 }
13544 }
13545
13546 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
13547}
13548
13549
13559function getTimelineIcon($actionstatic, &$histo, $key)
13560{
13561 global $langs;
13562
13563 $out = '<!-- timeline icon -->'."\n";
13564 $iconClass = 'fa fa-comments';
13565 $img_picto = '';
13566 $colorClass = '';
13567 $pictoTitle = '';
13568
13569 if ($histo[$key]['percent'] == -1) {
13570 $colorClass = 'timeline-icon-not-applicble';
13571 $pictoTitle = $langs->trans('StatusNotApplicable');
13572 } elseif ($histo[$key]['percent'] == 0) {
13573 $colorClass = 'timeline-icon-todo';
13574 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
13575 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
13576 $colorClass = 'timeline-icon-in-progress';
13577 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
13578 } elseif ($histo[$key]['percent'] >= 100) {
13579 $colorClass = 'timeline-icon-done';
13580 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
13581 }
13582
13583 if ($actionstatic->code == 'AC_TICKET_CREATE') {
13584 $iconClass = 'fa fa-ticket';
13585 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
13586 $iconClass = 'fa fa-pencilxxx';
13587 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
13588 $iconClass = 'fa fa-comments';
13589 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
13590 $iconClass = 'fa fa-mask';
13591 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13592 if ($actionstatic->type_picto) {
13593 $img_picto = img_picto('', $actionstatic->type_picto);
13594 } else {
13595 if ($actionstatic->type_code == 'AC_RDV') {
13596 $iconClass = 'fa fa-handshake';
13597 } elseif ($actionstatic->type_code == 'AC_TEL') {
13598 $iconClass = 'fa fa-phone';
13599 } elseif ($actionstatic->type_code == 'AC_FAX') {
13600 $iconClass = 'fa fa-fax';
13601 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
13602 $iconClass = 'fa fa-envelope';
13603 } elseif ($actionstatic->type_code == 'AC_INT') {
13604 $iconClass = 'fa fa-shipping-fast';
13605 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
13606 $iconClass = 'fa fa-robot';
13607 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
13608 $iconClass = 'fa fa-robot';
13609 }
13610 }
13611 }
13612
13613 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
13614 return $out;
13615}
13616
13624{
13625 global $conf, $db;
13626
13627 $documents = array();
13628
13629 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
13630 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
13631 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
13632 //$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
13633 $sql .= ' ORDER BY ecm.position ASC';
13634
13635 $resql = $db->query($sql);
13636 if ($resql) {
13637 if ($db->num_rows($resql)) {
13638 while ($obj = $db->fetch_object($resql)) {
13639 $documents[$obj->id] = $obj;
13640 }
13641 }
13642 }
13643
13644 return $documents;
13645}
13646
13647
13665function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
13666{
13667 global $user, $conf;
13668 global $form;
13669
13670 global $param, $massactionbutton;
13671
13672 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
13673
13674 // Check parameters
13675 if (!is_object($filterobj) && !is_object($objcon)) {
13676 dol_print_error(null, 'BadParameter');
13677 }
13678
13679 $histo = array();
13680 '@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';
13681
13682 $numaction = 0;
13683 $now = dol_now();
13684
13685 $sortfield_list = explode(',', $sortfield);
13686 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
13687 $sortfield_new_list = array();
13688 foreach ($sortfield_list as $sortfield_value) {
13689 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
13690 }
13691 $sortfield_new = implode(',', $sortfield_new_list);
13692
13693 $sql = null;
13694 $sql2 = null;
13695
13696 if (isModEnabled('agenda')) {
13697 // Search histo on actioncomm
13698 if (is_object($objcon) && $objcon->id > 0) {
13699 $sql = "SELECT DISTINCT a.id, a.label as label,";
13700 } else {
13701 $sql = "SELECT a.id, a.label as label,";
13702 }
13703 $sql .= " a.datep as dp,";
13704 $sql .= " a.note as message,";
13705 $sql .= " a.datep2 as dp2,";
13706 $sql .= " a.percent as percent, 'action' as type,";
13707 $sql .= " a.fk_element, a.elementtype,";
13708 $sql .= " a.fk_contact,";
13709 $sql .= " a.email_from as msg_from,";
13710 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
13711 $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";
13712 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13713 $sql .= ", sp.lastname, sp.firstname";
13714 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13715 $sql .= ", m.lastname, m.firstname";
13716 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13717 $sql .= ", o.ref";
13718 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13719 $sql .= ", o.ref";
13720 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13721 $sql .= ", o.ref";
13722 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13723 $sql .= ", o.ref";
13724 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13725 $sql .= ", o.ref";
13726 }
13727 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
13728 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
13729 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
13730
13731 $force_filter_contact = $filterobj instanceof User;
13732
13733 if (is_object($objcon) && $objcon->id > 0) {
13734 $force_filter_contact = true;
13735 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
13736 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
13737 }
13738
13739 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13740 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
13741 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
13742 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
13743 $sql .= " ON er.resource_type = 'dolresource'";
13744 $sql .= " AND er.element_id = a.id";
13745 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
13746 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13747 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
13748 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13749 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
13750 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13751 $sql .= ", ".MAIN_DB_PREFIX."product as o";
13752 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13753 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
13754 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13755 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
13756 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13757 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
13758 }
13759
13760 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
13761 if (!$force_filter_contact) {
13762 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
13763 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
13764 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
13765 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
13766 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13767 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
13768 if ($filterobj->id) {
13769 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13770 }
13771 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13772 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
13773 if ($filterobj->id) {
13774 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13775 }
13776 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13777 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
13778 if ($filterobj->id) {
13779 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13780 }
13781 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13782 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
13783 if ($filterobj->id) {
13784 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13785 }
13786 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
13787 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
13788 if ($filterobj->id) {
13789 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13790 }
13791 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
13792 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
13793 if ($filterobj->id) {
13794 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
13795 }
13796 }
13797 } else {
13798 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
13799 }
13800
13801 // Condition on actioncode
13802 if (!empty($actioncode)) {
13803 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
13804 if ($actioncode == 'AC_NON_AUTO') {
13805 $sql .= " AND c.type != 'systemauto'";
13806 } elseif ($actioncode == 'AC_ALL_AUTO') {
13807 $sql .= " AND c.type = 'systemauto'";
13808 } else {
13809 if ($actioncode == 'AC_OTH') {
13810 $sql .= " AND c.type != 'systemauto'";
13811 } elseif ($actioncode == 'AC_OTH_AUTO') {
13812 $sql .= " AND c.type = 'systemauto'";
13813 }
13814 }
13815 } else {
13816 if ($actioncode == 'AC_NON_AUTO') {
13817 $sql .= " AND c.type != 'systemauto'";
13818 } elseif ($actioncode == 'AC_ALL_AUTO') {
13819 $sql .= " AND c.type = 'systemauto'";
13820 } else {
13821 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
13822 }
13823 }
13824 }
13825 if ($donetodo == 'todo') {
13826 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
13827 } elseif ($donetodo == 'done') {
13828 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
13829 }
13830 if (is_array($filters) && $filters['search_agenda_label']) {
13831 $sql .= natural_search('a.label', $filters['search_agenda_label']);
13832 }
13833 }
13834
13835 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
13836 if (isModEnabled('mailing') && !empty($objcon->email)
13837 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
13838 $langs->load("mails");
13839
13840 $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";
13841 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
13842 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
13843 $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
13844 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
13845 $sql2 .= ", '' as lastname, '' as firstname";
13846 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
13847 $sql2 .= ", '' as lastname, '' as firstname";
13848 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
13849 $sql2 .= ", '' as ref";
13850 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
13851 $sql2 .= ", '' as ref";
13852 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
13853 $sql2 .= ", '' as ref";
13854 }
13855 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
13856 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
13857 $sql2 .= " AND mc.statut = 1";
13858 $sql2 .= " AND u.rowid = m.fk_user_valid";
13859 $sql2 .= " AND mc.fk_mailing=m.rowid";
13860 }
13861
13862 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
13863 if (!empty($sql) && !empty($sql2)) {
13864 $sql = $sql." UNION ".$sql2;
13865 } elseif (empty($sql) && !empty($sql2)) {
13866 $sql = $sql2;
13867 }
13868
13869 //TODO Add navigation with this limits...
13870 $offset = 0;
13871 $limit = 1000;
13872
13873 // Complete request and execute it with limit
13874 $sql .= $db->order($sortfield_new, $sortorder);
13875 if ($limit) {
13876 $sql .= $db->plimit($limit + 1, $offset);
13877 }
13878
13879 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
13880 $resql = $db->query($sql);
13881 if ($resql) {
13882 $i = 0;
13883 $num = $db->num_rows($resql);
13884
13885 $imaxinloop = ($limit ? min($num, $limit) : $num);
13886 while ($i < $imaxinloop) {
13887 $obj = $db->fetch_object($resql);
13888
13889 if ($obj->type == 'action') {
13890 $contactaction = new ActionComm($db);
13891 $contactaction->id = $obj->id;
13892 $result = $contactaction->fetchResources();
13893 if ($result < 0) {
13894 dol_print_error($db);
13895 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
13896 }
13897
13898 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
13899 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
13900 $tododone = '';
13901 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
13902 $tododone = 'todo';
13903 }
13904
13905 $histo[$numaction] = array(
13906 'type' => $obj->type,
13907 'tododone' => $tododone,
13908 'id' => $obj->id,
13909 'datestart' => $db->jdate($obj->dp),
13910 'dateend' => $db->jdate($obj->dp2),
13911 'note' => $obj->label,
13912 'message' => dol_htmlentitiesbr($obj->message),
13913 'percent' => $obj->percent,
13914
13915 'userid' => $obj->user_id,
13916 'login' => $obj->user_login,
13917 'userfirstname' => $obj->user_firstname,
13918 'userlastname' => $obj->user_lastname,
13919 'userphoto' => $obj->user_photo,
13920 'msg_from' => $obj->msg_from,
13921
13922 'contact_id' => $obj->fk_contact,
13923 'socpeopleassigned' => $contactaction->socpeopleassigned,
13924 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
13925 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
13926 'fk_element' => $obj->fk_element,
13927 'elementtype' => $obj->elementtype,
13928 // Type of event
13929 'acode' => $obj->acode,
13930 'alabel' => $obj->alabel,
13931 'libelle' => $obj->alabel, // deprecated
13932 'apicto' => $obj->apicto
13933 );
13934 } else {
13935 $histo[$numaction] = array(
13936 'type' => $obj->type,
13937 'tododone' => 'done',
13938 'id' => $obj->id,
13939 'datestart' => $db->jdate($obj->dp),
13940 'dateend' => $db->jdate($obj->dp2),
13941 'note' => $obj->label,
13942 'message' => dol_htmlentitiesbr($obj->message),
13943 'percent' => $obj->percent,
13944 'acode' => $obj->acode,
13945
13946 'userid' => $obj->user_id,
13947 'login' => $obj->user_login,
13948 'userfirstname' => $obj->user_firstname,
13949 'userlastname' => $obj->user_lastname,
13950 'userphoto' => $obj->user_photo
13951 );
13952 }
13953
13954 $numaction++;
13955 $i++;
13956 }
13957 } else {
13958 dol_print_error($db);
13959 }
13960 }
13961
13962 // Set $out to show events
13963 $out = '';
13964
13965 if (!isModEnabled('agenda')) {
13966 $langs->loadLangs(array("admin", "errors"));
13967 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
13968 }
13969
13970 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
13971 $delay_warning = $conf->global->MAIN_DELAY_ACTIONS_TODO * 24 * 60 * 60;
13972
13973 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
13974 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
13975 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
13976 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
13977
13978 $formactions = new FormActions($db);
13979
13980 $actionstatic = new ActionComm($db);
13981 $userstatic = new User($db);
13982 $contactstatic = new Contact($db);
13983 $userGetNomUrlCache = array();
13984 $contactGetNomUrlCache = array();
13985
13986 $out .= '<div class="filters-container" >';
13987 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
13988 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
13989
13990 if ($objcon && get_class($objcon) == 'Contact' &&
13991 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
13992 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
13993 } else {
13994 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
13995 }
13996 if (($filterobj && get_class($filterobj) == 'Societe')) {
13997 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
13998 } else {
13999 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14000 }
14001
14002 $out .= "\n";
14003
14004 $out .= '<div class="div-table-responsive-no-min">';
14005 $out .= '<table class="noborder borderbottom centpercent">';
14006
14007 $out .= '<tr class="liste_titre">';
14008
14009 // Action column
14010 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14011 $out .= '<th class="liste_titre width50 middle">';
14012 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14013 $out .= $searchpicto;
14014 $out .= '</th>';
14015 }
14016
14017 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, '')."\n";
14018
14019 $out .= '<th class="liste_titre"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14020 if ($donetodo) {
14021 $out .= '<th class="liste_titre"></th>';
14022 }
14023 $out .= '<th class="liste_titre">';
14024 $out .= '<span class="fas fa-square inline-block fawidth30" style=" color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14025 //$out .= img_picto($langs->trans("Type"), 'type');
14026 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', !getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : -1, 0, 0, 1, 'minwidth200imp');
14027 $out .= '</th>';
14028 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14029 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14030 $out .= '</th>';
14031
14032 // Action column
14033 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14034 $out .= '<th class="liste_titre width50 middle">';
14035 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14036 $out .= $searchpicto;
14037 $out .= '</th>';
14038 }
14039
14040 $out .= '</tr>';
14041
14042
14043 $out .= '</table>';
14044
14045 $out .= '</form>';
14046 $out .= '</div>';
14047
14048 $out .= "\n";
14049
14050 $out .= '<ul class="timeline">';
14051
14052 if ($donetodo) {
14053 $tmp = '';
14054 if ($filterobj instanceof Societe) {
14055 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14056 }
14057 if ($filterobj instanceof User) {
14058 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14059 }
14060 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14061 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14062 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14063 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14064 if ($filterobj instanceof Societe) {
14065 $tmp .= '</a>';
14066 }
14067 if ($filterobj instanceof User) {
14068 $tmp .= '</a>';
14069 }
14070 $out .= getTitleFieldOfList($tmp);
14071 }
14072
14073 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14074 $caction = new CActionComm($db);
14075 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14076
14077 $actualCycleDate = false;
14078
14079 // Loop on each event to show it
14080 foreach ($histo as $key => $value) {
14081 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14082
14083 $actionstatic->type_picto = $histo[$key]['apicto'];
14084 $actionstatic->type_code = $histo[$key]['acode'];
14085
14086 $labeltype = $actionstatic->type_code;
14087 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14088 $labeltype = 'AC_OTH';
14089 }
14090 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14091 $labeltype = $langs->trans("Message");
14092 } else {
14093 if (!empty($arraylist[$labeltype])) {
14094 $labeltype = $arraylist[$labeltype];
14095 }
14096 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14097 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14098 }
14099 }
14100
14101 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14102
14103 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14104
14105 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14106 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14107 $out .= '<!-- timeline time label -->';
14108 $out .= '<li class="time-label">';
14109 $out .= '<span class="timeline-badge-date">';
14110 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14111 $out .= '</span>';
14112 $out .= '</li>';
14113 $out .= '<!-- /.timeline-label -->';
14114 }
14115
14116
14117 $out .= '<!-- timeline item -->'."\n";
14118 $out .= '<li class="timeline-code-'.strtolower($actionstatic->code).'">';
14119
14120 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14121 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14122 //$out .= $timelineicon;
14123 //var_dump($timelineicon);
14124 $out .= $typeicon;
14125
14126 $out .= '<div class="timeline-item">'."\n";
14127
14128 $out .= '<span class="time timeline-header-action2">';
14129
14130 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14131 $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").' ';
14132 $out .= $histo[$key]['id'];
14133 $out .= '</a> ';
14134 } else {
14135 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14136 }
14137
14138 if ($user->hasRight('agenda', 'allactions', 'create') ||
14139 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14140 $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).'">';
14141 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14142 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14143 $out .= '</a>';
14144 }
14145
14146 $out .= '</span>';
14147
14148 // Date
14149 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14150 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14151 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14152 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14153 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14154 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14155 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14156 } else {
14157 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14158 }
14159 }
14160 $late = 0;
14161 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14162 $late = 1;
14163 }
14164 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14165 $late = 1;
14166 }
14167 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14168 $late = 1;
14169 }
14170 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14171 $late = 1;
14172 }
14173 if ($late) {
14174 $out .= img_warning($langs->trans("Late")).' ';
14175 }
14176 $out .= "</span></span>\n";
14177
14178 // Ref
14179 $out .= '<h3 class="timeline-header">';
14180
14181 // Author of event
14182 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14183 if ($histo[$key]['userid'] > 0) {
14184 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14185 $userstatic->fetch($histo[$key]['userid']);
14186 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14187 }
14188 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14189 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14190 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14191 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14192 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14193 } else {
14194 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14195 }
14196 }
14197 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14198 }
14199 $out .= '</div>';
14200
14201 // Title
14202 $out .= ' <div class="messaging-title inline-block">';
14203 //$out .= $actionstatic->getTypePicto();
14204 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
14205 $out .= $labeltype.' - ';
14206 }
14207
14208 $libelle = '';
14209 if (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14210 $out .= $langs->trans('TicketNewMessage');
14211 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14212 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
14213 } elseif (isset($histo[$key]['type'])) {
14214 if ($histo[$key]['type'] == 'action') {
14215 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14216 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
14217 $libelle = $histo[$key]['note'];
14218 $actionstatic->id = $histo[$key]['id'];
14219 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14220 } elseif ($histo[$key]['type'] == 'mailing') {
14221 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
14222 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
14223 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
14224 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14225 } else {
14226 $libelle .= $histo[$key]['note'];
14227 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
14228 }
14229 }
14230
14231 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
14232 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
14233 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
14234 } else {
14235 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
14236 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
14237 }
14238 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
14239 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
14240 }
14241 if ($link) {
14242 $out .= ' - '.$link;
14243 }
14244 }
14245
14246 $out .= '</div>';
14247
14248 $out .= '</h3>';
14249
14250 // Message
14251 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
14252 && $actionstatic->code != 'AC_TICKET_CREATE'
14253 && $actionstatic->code != 'AC_TICKET_MODIFY'
14254 ) {
14255 $out .= '<div class="timeline-body wordbreak">';
14256 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
14257 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
14258 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
14259 $out .= '<div class="readmore-block --closed" >';
14260 $out .= ' <div class="readmore-block__excerpt" >';
14261 $out .= dolPrintHTML($truncatedText);
14262 $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>';
14263 $out .= ' </div>';
14264 $out .= ' <div class="readmore-block__full-text" >';
14265 $out .= dolPrintHTML($histo[$key]['message']);
14266 $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>';
14267 $out .= ' </div>';
14268 $out .= '</div>';
14269 } else {
14270 $out .= dolPrintHTML($histo[$key]['message']);
14271 }
14272
14273 $out .= '</div>';
14274 }
14275
14276 // Timeline footer
14277 $footer = '';
14278
14279 // Contact for this action
14280 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
14281 $contactList = '';
14282 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
14283 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14284 $contact = new Contact($db);
14285 $contact->fetch($cid);
14286 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14287 } else {
14288 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14289 }
14290
14291 if ($contact) {
14292 $contactList .= !empty($contactList) ? ', ' : '';
14293 $contactList .= $contact->getNomUrl(1);
14294 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14295 if (!empty($contact->phone_pro)) {
14296 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
14297 }
14298 }
14299 }
14300 }
14301
14302 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
14303 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
14304 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
14305 $contact = new Contact($db);
14306 $result = $contact->fetch($histo[$key]['contact_id']);
14307 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
14308 } else {
14309 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
14310 $result = ($contact instanceof Contact) ? $contact->id : 0;
14311 }
14312
14313 if ($result > 0) {
14314 $footer .= $contact->getNomUrl(1);
14315 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
14316 if (!empty($contact->phone_pro)) {
14317 $footer .= '('.dol_print_phone($contact->phone_pro).')';
14318 }
14319 }
14320 }
14321 }
14322
14323 $documents = getActionCommEcmList($actionstatic);
14324 if (!empty($documents)) {
14325 $footer .= '<div class="timeline-documents-container">';
14326 foreach ($documents as $doc) {
14327 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
14328 $footer .= ' data-id="'.$doc->id.'" ';
14329 $footer .= ' data-path="'.$doc->filepath.'"';
14330 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
14331 $footer .= '>';
14332
14333 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
14334 $mime = dol_mimetype($filePath);
14335 $file = $actionstatic->id.'/'.$doc->filename;
14336 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
14337 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
14338 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
14339
14340 $mimeAttr = ' mime="'.$mime.'" ';
14341 $class = '';
14342 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
14343 $class .= ' documentpreview';
14344 }
14345
14346 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
14347 $footer .= img_mime($filePath).' '.$doc->filename;
14348 $footer .= '</a>';
14349
14350 $footer .= '</span>';
14351 }
14352 $footer .= '</div>';
14353 }
14354
14355 if (!empty($footer)) {
14356 $out .= '<div class="timeline-footer">'.$footer.'</div>';
14357 }
14358
14359 $out .= '</div>'."\n"; // end timeline-item
14360
14361 $out .= '</li>';
14362 $out .= '<!-- END timeline item -->';
14363 }
14364
14365 $out .= "</ul>\n";
14366
14367 $out .= '<script>
14368 jQuery(document).ready(function () {
14369 $(document).on("click", "[data-read-more-action]", function(e){
14370 let readMoreBloc = $(this).closest(".readmore-block");
14371 if(readMoreBloc.length > 0){
14372 e.preventDefault();
14373 if($(this).attr("data-read-more-action") == "close"){
14374 readMoreBloc.addClass("--closed").removeClass("--open");
14375 $("html, body").animate({
14376 scrollTop: readMoreBloc.offset().top - 200
14377 }, 100);
14378 }else{
14379 readMoreBloc.addClass("--open").removeClass("--closed");
14380 }
14381 }
14382 });
14383 });
14384 </script>';
14385
14386
14387 if (empty($histo)) {
14388 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
14389 }
14390 }
14391
14392 if ($noprint) {
14393 return $out;
14394 } else {
14395 print $out;
14396 }
14397}
14398
14410function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
14411{
14412 if ($timestamp === null) {
14413 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
14414 }
14415 $TParam = array(
14416 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
14417 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
14418 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
14419 );
14420 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
14421 $TParam = array_merge($TParam, array(
14422 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
14423 $prefix . 'minute' => intval(dol_print_date($timestamp, '%M')),
14424 $prefix . 'second' => intval(dol_print_date($timestamp, '%S'))
14425 ));
14426 }
14427
14428 return '&' . http_build_query($TParam);
14429}
14430
14450function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
14451{
14452 global $conf, $db, $langs, $hookmanager;
14453 global $action, $object;
14454
14455 if (!is_object($langs)) {
14456 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
14457 $langs = new Translate('', $conf);
14458 $langs->setDefaultLang();
14459 }
14460
14461 $langs->load("errors");
14462
14463 if ($printheader) {
14464 if (function_exists("llxHeader")) {
14465 llxHeader('');
14466 } elseif (function_exists("llxHeaderVierge")) {
14467 llxHeaderVierge('');
14468 }
14469 }
14470
14471 print '<div class="error">';
14472 if (empty($message)) {
14473 print $langs->trans("ErrorRecordNotFound");
14474 } else {
14475 print $langs->trans($message);
14476 }
14477 print '</div>';
14478 print '<br>';
14479
14480 if (empty($showonlymessage)) {
14481 if (empty($hookmanager)) {
14482 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
14483 $hookmanager = new HookManager($db);
14484 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
14485 $hookmanager->initHooks(array('main'));
14486 }
14487
14488 $parameters = array('message' => $message, 'params' => $params);
14489 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
14490 print $hookmanager->resPrint;
14491 }
14492
14493 if ($printfooter && function_exists("llxFooter")) {
14494 llxFooter();
14495 }
14496 exit(0);
14497}
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)
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition ajax.lib.php:724
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:70
$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:513
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:498
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:85
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition date.lib.php:482
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:532
llxFooter()
Footer empty.
Definition document.php:107
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert an image file or a PDF into another image format.
dragAndDropFileUpload($htmlname)
Function to manage the drag and drop of a file.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_is_dir($folder)
Test if filename is a directory.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_fiche_end($notab=0)
Show tab footer of a card.
verifCond($strToEvaluate, $onlysimplestring='1')
Verify if condition in string is ok or not.
recordNotFound($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Displays an error page when a record is not found.
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formatted size.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) If ...
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
finishSimpleTable($addLineBreak=false)
Add the correct HTML close tags for "startSimpleTable(...)" (use after the last table line)
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon=null, $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
setEntity($currentobject)
Set entity id to use when to create an object.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='')
Format phone numbers according to country.
dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
img_right($titlealt='default', $selected=0, $moreatt='')
Show right arrow logo.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto='UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
dol_print_ip($ip, $mode=0)
Return an IP formatted to be shown on screen.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
Function to test if an entry is enabled or not.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs=null, $mode=0, $extralangcode='')
Return a formatted address (part address/zip/town/state) according to country rules.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dolPrintHTML($s, $allowiframe=0)
Return a string (that can be on several lines) ready to be output on a HTML page.
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
dolPrintHTMLForTextArea($s, $allowiframe=0)
Return a string ready to be output on input textarea.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formatted error messages to output (Used to show messages on html output).
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
roundUpToNextMultiple($n, $x=5)
Round to next multiple.
dol_user_country()
Return country code for current user.
dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes=null)
Clean a string from some undesirable HTML tags.
getMultidirTemp($object, $module='', $forobject=0)
Return the full path of the directory where a module (or an object of a module) stores its temporary ...
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
picto_required()
Return picto saying a field is required.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_action($titlealt, $numaction, $picto='', $moreatt='')
Show logo action.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0)
Set event message in dol_events session object.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
GETPOSTDATE($prefix, $hourTime='', $gm='auto')
Helper function that combines values of a dolibarr DatePicker (such as Form\selectDate) for year,...
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
checkVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dolPrintText($s)
Return a string label (possible on several lines and that should not contains any HTML) ready to be o...
utf8_valid($str)
Check if a string is in UTF8.
getPictoForType($key, $morecss='')
Return the picto for a data type.
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
Create a dialog with two buttons for export and overwrite of a website.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value.
dol_escape_php($stringtoescape, $stringforquotes=2)
Returns text escaped for inclusion into a php string, build with double quotes " or '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getElementProperties($elementType)
Get an array with properties of an element.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0)
Clean a string to keep only desirable HTML tags.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
dolPrintHTMLForAttribute($s)
Return a string ready to be output on an HTML attribute (alt, title, data-html, .....
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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.
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_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:139
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:142
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:1929