dolibarr 21.0.4
functions.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2024 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-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2019-2023 Thibault Foucart <support@ptibogxiv.net>
18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21 * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23 * Copyright (C) 2023-2024 Joachim Kueter <git-jk@bloxera.com>
24 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
25 * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26 * Copyright (C) 2024 Josep Lluís Amador Teruel <joseplluis@lliuretic.cat>
27 * Copyright (C) 2024 Benoît PASCAL <contact@p-ben.com>
28 *
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 3 of the License, or
32 * (at your option) any later version.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program. If not, see <https://www.gnu.org/licenses/>.
41 * or see https://www.gnu.org/
42 */
43
50//include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
51
52// Function for better PHP x compatibility
53if (!function_exists('utf8_encode')) {
61 function utf8_encode($elements)
62 {
63 return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
64 }
65}
66
67if (!function_exists('utf8_decode')) {
75 function utf8_decode($elements)
76 {
77 return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
78 }
79}
80if (!function_exists('str_starts_with')) {
89 function str_starts_with($haystack, $needle)
90 {
91 return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
92 }
93}
94if (!function_exists('str_ends_with')) {
103 function str_ends_with($haystack, $needle)
104 {
105 return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
106 }
107}
108if (!function_exists('str_contains')) {
117 function str_contains($haystack, $needle)
118 {
119 return $needle !== '' && mb_strpos($haystack, $needle) !== false;
120 }
121}
122
123
135function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
136{
137 global $conf;
138
139 if (!is_object($object) && empty($module)) {
140 return null;
141 }
142 if (empty($module) && !empty($object->element)) {
143 $module = $object->element;
144 }
145
146 // Special case for backward compatibility
147 if ($module == 'fichinter') {
148 $module = 'ficheinter';
149 } elseif ($module == 'invoice_supplier') {
150 $module = 'supplier_invoice';
151 } elseif ($module == 'order_supplier') {
152 $module = 'supplier_order';
153 }
154
155 // Get the relative path of directory
156 if ($mode == 'output' || $mode == 'outputrel' || $mode == 'version') {
157 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
158 $s = '';
159 if ($mode != 'outputrel') {
160 $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
161 }
162 if ($forobject && $object->id > 0) {
163 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
164 }
165 return $s;
166 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_output')) {
167 $s = '';
168 if ($mode != 'outputrel') {
169 $s = $conf->$module->dir_output;
170 }
171 if ($forobject && $object->id > 0) {
172 $s .= ($mode != 'outputrel' ? '/' : '').get_exdir(0, 0, 0, 0, $object);
173 }
174 return $s;
175 } else {
176 return 'error-diroutput-not-defined-for-this-object='.$module;
177 }
178 } elseif ($mode == 'temp') {
179 if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
180 return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
181 } elseif (isset($conf->$module) && property_exists($conf->$module, 'dir_temp')) {
182 return $conf->$module->dir_temp;
183 } else {
184 return 'error-dirtemp-not-defined-for-this-object='.$module;
185 }
186 } else {
187 return 'error-bad-value-for-mode';
188 }
189}
190
200function getMultidirTemp($object, $module = '', $forobject = 0)
201{
202 return getMultidirOutput($object, $module, $forobject, 'temp');
203}
204
214function getMultidirVersion($object, $module = '', $forobject = 0)
215{
216 return getMultidirOutput($object, $module, $forobject, 'version');
217}
218
219
228function getDolGlobalString($key, $default = '')
229{
230 global $conf;
231 return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
232}
233
243function getDolGlobalInt($key, $default = 0)
244{
245 global $conf;
246 return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
247}
248
258function getDolGlobalFloat($key, $default = 0)
259{
260 global $conf;
261 return (float) (isset($conf->global->$key) ? $conf->global->$key : $default);
262}
263
272function getDolGlobalBool($key, $default = false)
273{
274 global $conf;
275 return (bool) ($conf->global->$key ?? $default);
276}
277
287function getDolUserString($key, $default = '', $tmpuser = null)
288{
289 if (empty($tmpuser)) {
290 global $user;
291 $tmpuser = $user;
292 }
293
294 return (string) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
295}
296
305function getDolUserInt($key, $default = 0, $tmpuser = null)
306{
307 if (empty($tmpuser)) {
308 global $user;
309 $tmpuser = $user;
310 }
311
312 return (int) (isset($tmpuser->conf->$key) ? $tmpuser->conf->$key : $default);
313}
314
315
325define(
326 'MODULE_MAPPING',
327 array(
328 // Map deprecated names to new names
329 'adherent' => 'member', // Has new directory
330 'member_type' => 'adherent_type', // No directory, but file called adherent_type
331 'banque' => 'bank', // Has new directory
332 'contrat' => 'contract', // Has new directory
333 'entrepot' => 'stock', // Has new directory
334 'projet' => 'project', // Has new directory
335 'categorie' => 'category', // Has old directory
336 'commande' => 'order', // Has old directory
337 'expedition' => 'shipping', // Has old directory
338 'facture' => 'invoice', // Has old directory
339 'fichinter' => 'intervention', // Has old directory
340 'ficheinter' => 'intervention', // Backup for 'fichinter'
341 'propale' => 'propal', // Has old directory
342 'socpeople' => 'contact', // Has old directory
343 'fournisseur' => 'supplier', // Has old directory
344
345 'actioncomm' => 'agenda', // NO module directory (public dir agenda)
346 'product_price' => 'productprice', // NO directory
347 'product_fournisseur_price' => 'productsupplierprice', // NO directory
348 )
349);
350
357function isModEnabled($module)
358{
359 global $conf;
360
361 // Fix old names (map to new names)
362 $arrayconv = MODULE_MAPPING;
363 $arrayconvbis = array_flip(MODULE_MAPPING);
364
365 if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
366 // Special cases: both use the same module.
367 $arrayconv['supplier_order'] = 'fournisseur';
368 $arrayconv['supplier_invoice'] = 'fournisseur';
369 }
370 // Special case.
371 // @TODO Replace isModEnabled('delivery_note') with
372 // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
373 if ($module == 'delivery_note') {
374 if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
375 return false;
376 } else {
377 $module = 'shipping';
378 }
379 }
380
381 $module_alt = $module;
382 if (!empty($arrayconv[$module])) {
383 $module_alt = $arrayconv[$module];
384 }
385 $module_bis = $module;
386 if (!empty($arrayconvbis[$module])) {
387 $module_bis = $arrayconvbis[$module];
388 }
389
390 return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
391 //return !empty($conf->$module->enabled);
392}
393
400function isDolTms($timestamp)
401{
402 if ($timestamp === '') {
403 dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"] . getCallerInfoString(), LOG_NOTICE);
404 return false;
405 }
406 if (is_null($timestamp) || !is_numeric($timestamp)) {
407 return false;
408 }
409
410 return true;
411}
412
424function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
425{
426 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
427
428 $class = 'DoliDB'.ucfirst($type);
429 $db = new $class($type, $host, $user, $pass, $name, $port);
430 return $db;
431}
432
450function getEntity($element, $shared = 1, $currentobject = null)
451{
452 global $conf, $mc, $hookmanager, $object, $action, $db;
453
454 if (!is_object($hookmanager)) {
455 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
456 $hookmanager = new HookManager($db);
457 }
458
459 // fix different element names (France to English)
460 switch ($element) {
461 case 'projet':
462 $element = 'project';
463 break;
464 case 'contrat':
465 $element = 'contract';
466 break; // "/contrat/class/contrat.class.php"
467 case 'order_supplier':
468 $element = 'supplier_order';
469 break; // "/fourn/class/fournisseur.commande.class.php"
470 case 'invoice_supplier':
471 $element = 'supplier_invoice';
472 break; // "/fourn/class/fournisseur.facture.class.php"
473 }
474
475 if (is_object($mc)) {
476 $out = $mc->getEntity($element, $shared, $currentobject);
477 } else {
478 $out = '';
479 $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
480 if (getDolGlobalString('HOLIDAY_ALLOW_ZERO_IN_DIC')) { // this constant break the dictionary admin without Multicompany
481 $addzero[] = 'c_holiday_types';
482 }
483 if (in_array($element, $addzero)) {
484 $out .= '0,';
485 }
486 $out .= ((int) $conf->entity);
487 }
488
489 // Manipulate entities to query on the fly
490 $parameters = array(
491 'element' => $element,
492 'shared' => $shared,
493 'object' => $object,
494 'currentobject' => $currentobject,
495 'out' => $out
496 );
497 $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
498
499 if (is_numeric($reshook)) {
500 if ($reshook == 0 && !empty($hookmanager->resPrint)) {
501 $out .= ','.$hookmanager->resPrint; // add
502 } elseif ($reshook == 1) {
503 $out = $hookmanager->resPrint; // replace
504 }
505 }
506
507 return $out;
508}
509
516function setEntity($currentobject)
517{
518 global $conf, $mc;
519
520 if (is_object($mc) && method_exists($mc, 'setEntity')) {
521 return $mc->setEntity($currentobject);
522 } else {
523 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
524 }
525}
526
533function isASecretKey($keyname)
534{
535 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
536}
537
538
545function num2Alpha($n)
546{
547 for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
548 $r = chr($n % 26 + 0x41) . $r;
549 }
550 return $r;
551}
552
553
570function getBrowserInfo($user_agent)
571{
572 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
573
574 $name = 'unknown';
575 $version = '';
576 $os = 'unknown';
577 $phone = '';
578
579 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
580
581 $detectmobile = new Mobile_Detect(null, $user_agent);
582 $tablet = $detectmobile->isTablet();
583
584 if ($detectmobile->isMobile()) {
585 $phone = 'unknown';
586
587 // If phone/smartphone, we set phone os name.
588 if ($detectmobile->is('AndroidOS')) {
589 $os = $phone = 'android';
590 } elseif ($detectmobile->is('BlackBerryOS')) {
591 $os = $phone = 'blackberry';
592 } elseif ($detectmobile->is('iOS')) {
593 $os = 'ios';
594 $phone = 'iphone';
595 } elseif ($detectmobile->is('PalmOS')) {
596 $os = $phone = 'palm';
597 } elseif ($detectmobile->is('SymbianOS')) {
598 $os = 'symbian';
599 } elseif ($detectmobile->is('webOS')) {
600 $os = 'webos';
601 } elseif ($detectmobile->is('MaemoOS')) {
602 $os = 'maemo';
603 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
604 $os = 'windows';
605 }
606 }
607
608 // OS
609 if (preg_match('/linux/i', $user_agent)) {
610 $os = 'linux';
611 } elseif (preg_match('/macintosh/i', $user_agent)) {
612 $os = 'macintosh';
613 } elseif (preg_match('/windows/i', $user_agent)) {
614 $os = 'windows';
615 }
616
617 // Name
618 $reg = array();
619 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
620 $name = 'firefox';
621 $version = empty($reg[2]) ? '' : $reg[2];
622 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
623 $name = 'edge';
624 $version = empty($reg[2]) ? '' : $reg[2];
625 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
626 $name = 'chrome';
627 $version = empty($reg[2]) ? '' : $reg[2];
628 } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
629 // we can have 'chrome (Mozilla...) chrome x.y' in one string
630 $name = 'chrome';
631 } elseif (preg_match('/iceweasel/i', $user_agent)) {
632 $name = 'iceweasel';
633 } elseif (preg_match('/epiphany/i', $user_agent)) {
634 $name = 'epiphany';
635 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
636 $name = 'safari';
637 $version = empty($reg[2]) ? '' : $reg[2];
638 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
639 // Safari is often present in string for mobile but its not.
640 $name = 'opera';
641 $version = empty($reg[2]) ? '' : $reg[2];
642 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
643 $name = 'ie';
644 $version = end($reg);
645 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
646 // MS products at end
647 $name = 'ie';
648 $version = end($reg);
649 } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
650 // MS products at end
651 $name = 'textbrowser';
652 $version = empty($reg[3]) ? '' : $reg[3];
653 } elseif (preg_match('/w3m\/([\d\.]+)/i', $user_agent, $reg)) {
654 // MS products at end
655 $name = 'textbrowser';
656 $version = empty($reg[1]) ? '' : $reg[1];
657 }
658
659 if ($tablet) {
660 $layout = 'tablet';
661 } elseif ($phone) {
662 $layout = 'phone';
663 } else {
664 $layout = 'classic';
665 }
666
667 return array(
668 'browsername' => $name,
669 'browserversion' => $version,
670 'browseros' => $os,
671 'browserua' => $user_agent,
672 'layout' => $layout, // tablet, phone, classic
673 'phone' => $phone, // deprecated
674 'tablet' => $tablet // deprecated
675 );
676}
677
683function dol_shutdown()
684{
685 global $db;
686 $disconnectdone = false;
687 $depth = 0;
688 if (is_object($db) && !empty($db->connected)) {
689 $depth = $db->transaction_opened;
690 $disconnectdone = $db->close();
691 }
692 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));
693}
694
704function GETPOSTISSET($paramname)
705{
706 $isset = false;
707
708 $relativepathstring = $_SERVER["PHP_SELF"];
709 // Clean $relativepathstring
710 if (constant('DOL_URL_ROOT')) {
711 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
712 }
713 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
714 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
715 //var_dump($relativepathstring);
716 //var_dump($user->default_values);
717
718 // Code for search criteria persistence.
719 // Retrieve values if restore_lastsearch_values
720 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
721 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
722 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
723 if (is_array($tmp)) {
724 foreach ($tmp as $key => $val) {
725 if ($key == $paramname) { // We are on the requested parameter
726 $isset = true;
727 break;
728 }
729 }
730 }
731 }
732 // If there is saved contextpage, limit, page or mode
733 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
734 $isset = true;
735 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
736 $isset = true;
737 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
738 $isset = true;
739 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
740 $isset = true;
741 }
742 } else {
743 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
744 }
745
746 return $isset;
747}
748
757function GETPOSTISARRAY($paramname, $method = 0)
758{
759 // for $method test need return the same $val as GETPOST
760 if (empty($method)) {
761 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
762 } elseif ($method == 1) {
763 $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
764 } elseif ($method == 2) {
765 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
766 } elseif ($method == 3) {
767 $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
768 } else {
769 $val = 'BadFirstParameterForGETPOST';
770 }
771
772 return is_array($val);
773}
774
804function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
805{
806 global $mysoc, $user, $conf;
807
808 if (empty($paramname)) { // Explicit test for null for phan.
809 return 'BadFirstParameterForGETPOST';
810 }
811 if (empty($check)) {
812 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);
813 // Enable this line to know who call the GETPOST with '' $check parameter.
814 //var_dump(getCallerInfoString());
815 }
816
817 if (empty($method)) {
818 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
819 } elseif ($method == 1) {
820 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
821 } elseif ($method == 2) {
822 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
823 } elseif ($method == 3) {
824 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
825 } else {
826 return 'BadThirdParameterForGETPOST';
827 }
828
829 $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
830
831 if (empty($method) || $method == 3 || $method == 4) {
832 $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
833 // Clean $relativepathstring
834 if (constant('DOL_URL_ROOT')) {
835 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
836 }
837 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
838 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
839 //var_dump($relativepathstring);
840 //var_dump($user->default_values);
841
842 // Code for search criteria persistence.
843 // Retrieve saved values if restore_lastsearch_values is set
844 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
845 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
846 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
847 if (is_array($tmp)) {
848 foreach ($tmp as $key => $val) {
849 if ($key == $paramname) { // We are on the requested parameter
850 $out = $val;
851 break;
852 }
853 }
854 }
855 }
856 // If there is saved contextpage, page or limit
857 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
858 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
859 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
860 $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
861 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
862 $out = $_SESSION['lastsearch_page_'.$relativepathstring];
863 } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
864 $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
865 }
866 } elseif (!isset($_GET['sortfield'])) {
867 // Else, retrieve default values if we are not doing a sort
868 // 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
869 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
870 // Search default value from $object->field
871 global $object;
872 '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
873 if (is_object($object) && isset($object->fields[$paramname]['default'])) {
874 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
875 $out = $object->fields[$paramname]['default'];
876 }
877 }
878 if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
879 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
880 // Now search in setup to overwrite default values
881 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
882 if (isset($user->default_values[$relativepathstring]['createform'])) {
883 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
884 $qualified = 0;
885 if ($defkey != '_noquery_') {
886 $tmpqueryarraytohave = explode('&', $defkey);
887 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
888 $foundintru = 0;
889 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
890 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
891 $foundintru = 1;
892 }
893 }
894 if (!$foundintru) {
895 $qualified = 1;
896 }
897 //var_dump($defkey.'-'.$qualified);
898 } else {
899 $qualified = 1;
900 }
901
902 if ($qualified) {
903 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
904 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
905 break;
906 }
907 }
908 }
909 }
910 }
911 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
912 // Management of default search_filters and sort order
913 if (!empty($user->default_values)) {
914 // $user->default_values defined from menu 'Setup - Default values'
915 //var_dump($user->default_values[$relativepathstring]);
916 if ($paramname == 'sortfield' || $paramname == 'sortorder') {
917 // Sorted on which fields ? ASC or DESC ?
918 if (isset($user->default_values[$relativepathstring]['sortorder'])) {
919 // Even if paramname is sortfield, data are stored into ['sortorder...']
920 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
921 $qualified = 0;
922 if ($defkey != '_noquery_') {
923 $tmpqueryarraytohave = explode('&', $defkey);
924 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
925 $foundintru = 0;
926 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
927 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
928 $foundintru = 1;
929 }
930 }
931 if (!$foundintru) {
932 $qualified = 1;
933 }
934 //var_dump($defkey.'-'.$qualified);
935 } else {
936 $qualified = 1;
937 }
938
939 if ($qualified) {
940 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
941 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
942 if ($out) {
943 $out .= ', ';
944 }
945 if ($paramname == 'sortfield') {
946 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
947 }
948 if ($paramname == 'sortorder') {
949 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
950 }
951 }
952 //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
953 }
954 }
955 }
956 } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
957 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
958 if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
959 continue;
960 }
961 $qualified = 0;
962 if ($defkey != '_noquery_') {
963 $tmpqueryarraytohave = explode('&', $defkey);
964 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
965 $foundintru = 0;
966 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
967 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
968 $foundintru = 1;
969 }
970 }
971 if (!$foundintru) {
972 $qualified = 1;
973 }
974 //var_dump($defkey.'-'.$qualified);
975 } else {
976 $qualified = 1;
977 }
978
979 if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
980 // We must keep $_POST and $_GET here
981 if (isset($_POST['search_all']) || isset($_GET['search_all'])) {
982 // We made a search from quick search menu, do we still use default filter ?
983 if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
984 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
985 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
986 }
987 } else {
988 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
989 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
990 }
991 break;
992 }
993 }
994 }
995 }
996 }
997 }
998 }
999 }
1000
1001 // 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)
1002 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
1003 // 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.
1004 '@phan-var-force string $paramname';
1005 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
1006 $reg = array();
1007 $regreplace = array();
1008 $maxloop = 20;
1009 $loopnb = 0; // Protection against infinite loop
1010
1011 while (preg_match('/__([A-Z0-9]+(?:_[A-Z0-9]+){0,3})__/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.
1012 $loopnb++;
1013 $newout = '';
1014
1015 if ($reg[1] == 'DAY') {
1016 $tmp = dol_getdate(dol_now(), true);
1017 $newout = $tmp['mday'];
1018 } elseif ($reg[1] == 'MONTH') {
1019 $tmp = dol_getdate(dol_now(), true);
1020 $newout = $tmp['mon'];
1021 } elseif ($reg[1] == 'YEAR') {
1022 $tmp = dol_getdate(dol_now(), true);
1023 $newout = $tmp['year'];
1024 } elseif ($reg[1] == 'PREVIOUS_DAY') {
1025 $tmp = dol_getdate(dol_now(), true);
1026 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1027 $newout = $tmp2['day'];
1028 } elseif ($reg[1] == 'PREVIOUS_MONTH') {
1029 $tmp = dol_getdate(dol_now(), true);
1030 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
1031 $newout = $tmp2['month'];
1032 } elseif ($reg[1] == 'PREVIOUS_YEAR') {
1033 $tmp = dol_getdate(dol_now(), true);
1034 $newout = ($tmp['year'] - 1);
1035 } elseif ($reg[1] == 'NEXT_DAY') {
1036 $tmp = dol_getdate(dol_now(), true);
1037 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
1038 $newout = $tmp2['day'];
1039 } elseif ($reg[1] == 'NEXT_MONTH') {
1040 $tmp = dol_getdate(dol_now(), true);
1041 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
1042 $newout = $tmp2['month'];
1043 } elseif ($reg[1] == 'NEXT_YEAR') {
1044 $tmp = dol_getdate(dol_now(), true);
1045 $newout = ($tmp['year'] + 1);
1046 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
1047 $newout = $mysoc->country_id;
1048 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
1049 $newout = $user->id;
1050 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
1051 $newout = $user->fk_user;
1052 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
1053 $newout = $conf->entity;
1054 } elseif ($reg[1] == 'ID') {
1055 $newout = '__ID__'; // We keep __ID__ we find into backtopage url
1056 } else {
1057 $newout = 'REGREPLACE_'.$loopnb; // Key not found, we replace with temporary string to reload later
1058 $regreplace[$loopnb] = $reg[0];
1059 }
1060 //var_dump('__'.$reg[1].'__ -> '.$newout);
1061 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1062 }
1063 if (!empty($regreplace)) {
1064 foreach ($regreplace as $key => $value) {
1065 $out = preg_replace('/REGREPLACE_'.$key.'/', $value, $out);
1066 }
1067 }
1068 }
1069
1070 // Check type of variable and make sanitization according to this
1071 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1072 if (!is_array($out) || empty($out)) {
1073 $out = array();
1074 } else {
1075 $tmparray = explode(':', $check);
1076 if (!empty($tmparray[1])) {
1077 $tmpcheck = $tmparray[1];
1078 } else {
1079 $tmpcheck = 'alphanohtml';
1080 }
1081 foreach ($out as $outkey => $outval) {
1082 $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1083 }
1084 }
1085 } else {
1086 // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1087 // 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
1088 if (strpos($paramname, 'search_') === 0) {
1089 $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1090 }
1091
1092 // @phan-suppress-next-line UnknownSanitizeType
1093 $out = sanitizeVal($out, $check, $filter, $options);
1094 }
1095
1096 // Sanitizing for special parameters.
1097 // Note: There is no reason to allow the backtopage/backtopageforcancel/backtopagejs, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1098 // @TODO Merge backtopage with backtourl
1099 // @TODO Rename backtolist into backtopagelist
1100 if (preg_match('/^backto/i', $paramname)) {
1101 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1102 $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.
1103 do {
1104 $oldstringtoclean = $out;
1105 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1106 $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'
1107 $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1108 } while ($oldstringtoclean != $out);
1109 }
1110
1111 // Code for search criteria persistence.
1112 // Save data into session if key start with 'search_'
1113 if (empty($method) || $method == 3 || $method == 4) {
1114 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1115 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1116
1117 // We save search key only if $out not empty that means:
1118 // - posted value not empty, or
1119 // - 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).
1120
1121 if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1122 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1123 }
1124 }
1125 }
1126
1127 return $out;
1128}
1129
1139function GETPOSTINT($paramname, $method = 0)
1140{
1141 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1142}
1143
1152function GETPOSTFLOAT($paramname, $rounding = '')
1153{
1154 // 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.)
1155 return (float) price2num(GETPOST($paramname), $rounding, 2);
1156}
1157
1171function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1172{
1173 $m = array();
1174 if ($hourTime === 'getpost') {
1175 $hour = GETPOSTINT($prefix . 'hour');
1176 $minute = GETPOSTINT($prefix . 'min');
1177 $second = GETPOSTINT($prefix . 'sec');
1178 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1179 $hour = intval($m[1]);
1180 $minute = intval($m[2]);
1181 $second = intval($m[3]);
1182 } else {
1183 $hour = $minute = $second = 0;
1184 }
1185 // normalize out of range values
1186 $hour = (int) min($hour, 23);
1187 $minute = (int) min($minute, 59);
1188 $second = (int) min($second, 59);
1189
1190 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1191}
1192
1193
1204function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1205{
1206 return sanitizeVal($out, $check, $filter, $options);
1207}
1208
1218function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1219{
1220 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1221 // Check is done after replacement
1222 switch ($check) {
1223 case 'none':
1224 case 'password':
1225 break;
1226 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1227 if (!is_numeric($out)) {
1228 $out = '';
1229 }
1230 break;
1231 case 'intcomma':
1232 if (is_array($out)) {
1233 $out = implode(',', $out);
1234 }
1235 if (preg_match('/[^0-9,-]+/i', $out)) {
1236 $out = '';
1237 }
1238 break;
1239 case 'san_alpha':
1240 $out = filter_var($out, FILTER_SANITIZE_STRING);
1241 break;
1242 case 'email':
1243 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1244 break;
1245 case 'aZ':
1246 if (!is_array($out)) {
1247 $out = trim($out);
1248 if (preg_match('/[^a-z]+/i', $out)) {
1249 $out = '';
1250 }
1251 }
1252 break;
1253 case 'aZ09':
1254 if (!is_array($out)) {
1255 $out = trim($out);
1256 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1257 $out = '';
1258 }
1259 }
1260 break;
1261 case 'aZ09arobase': // great to sanitize $objecttype parameter
1262 if (!is_array($out)) {
1263 $out = trim($out);
1264 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1265 $out = '';
1266 }
1267 }
1268 break;
1269 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1270 if (!is_array($out)) {
1271 $out = trim($out);
1272 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1273 $out = '';
1274 }
1275 }
1276 break;
1277 case 'alpha': // No html and no ../ and "
1278 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1279 if (!is_array($out)) {
1280 $out = trim($out);
1281 do {
1282 $oldstringtoclean = $out;
1283 // Remove html tags
1284 $out = dol_string_nohtmltag($out, 0);
1285 // 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).
1286 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1287 // Remove also other dangerous string sequences
1288 // '../' or '..\' is dangerous because it allows dir transversals
1289 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1290 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1291 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1292 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1293 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1294 } while ($oldstringtoclean != $out);
1295 // keep lines feed
1296 }
1297 break;
1298 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email@domain.com>". Less secured than 'alphanohtml'
1299 if (!is_array($out)) {
1300 $out = trim($out);
1301 do {
1302 $oldstringtoclean = $out;
1303 // Decode html entities
1304 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1305 // 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).
1306 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1307 // Remove also other dangerous string sequences
1308 // '../' or '..\' is dangerous because it allows dir transversals
1309 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1310 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1311 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1312 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1313 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1314 } while ($oldstringtoclean != $out);
1315 }
1316 break;
1317 case 'nohtml': // No html. Valid for JSON strings.
1318 $out = dol_string_nohtmltag($out, 0);
1319 break;
1320 case 'restricthtmlnolink':
1321 case 'restricthtml': // Recommended for most html textarea
1322 case 'restricthtmlallowclass':
1323 case 'restricthtmlallowiframe':
1324 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1325 case 'restricthtmlallowunvalid':
1326 $out = dol_htmlwithnojs($out, 1, $check);
1327 break;
1328 case 'custom':
1329 if (!empty($out)) {
1330 if (empty($filter)) {
1331 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1332 }
1333 if (is_null($options)) {
1334 $options = 0;
1335 }
1336 $out = filter_var($out, $filter, $options);
1337 }
1338 break;
1339 default:
1340 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1341 $out = GETPOST($out, 'alphanohtml');
1342 break;
1343 }
1344
1345 return $out;
1346}
1347
1348
1349if (!function_exists('dol_getprefix')) {
1360 function dol_getprefix($mode = '')
1361 {
1362 // If prefix is for email (we need to have $conf already loaded for this case)
1363 if ($mode == 'email') {
1364 global $conf;
1365
1366 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1367 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1368 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1369 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1370 return $_SERVER["SERVER_NAME"];
1371 }
1372 }
1373
1374 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1375 if (!empty($conf->file->instance_unique_id)) {
1376 return sha1('dolibarr'.$conf->file->instance_unique_id);
1377 }
1378
1379 // For backward compatibility when instance_unique_id is not set
1380 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1381 }
1382
1383 // If prefix is for session (no need to have $conf loaded)
1384 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1385 $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
1386
1387 // The recommended value (may be not defined for old versions)
1388 if (!empty($tmp_instance_unique_id)) {
1389 return sha1('dolibarr'.$tmp_instance_unique_id);
1390 }
1391
1392 // For backward compatibility when instance_unique_id is not set
1393 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1394 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1395 } else {
1396 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1397 }
1398 }
1399}
1400
1411function dol_include_once($relpath, $classname = '')
1412{
1413 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']
1414
1415 $fullpath = dol_buildpath($relpath);
1416
1417 if (!file_exists($fullpath)) {
1418 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1419 return false;
1420 }
1421
1422 if (!empty($classname) && !class_exists($classname)) {
1423 return include $fullpath;
1424 } else {
1425 return include_once $fullpath;
1426 }
1427}
1428
1429
1443function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1444{
1445 global $conf;
1446
1447 $path = preg_replace('/^\//', '', $path);
1448
1449 if (empty($type)) { // For a filesystem path
1450 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1451 if (is_array($conf->file->dol_document_root)) {
1452 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1453 if ($key == 'main') {
1454 continue;
1455 }
1456 // if (@file_exists($dirroot.'/'.$path)) {
1457 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1458 $res = $dirroot.'/'.$path;
1459 return $res;
1460 }
1461 }
1462 }
1463 if ($returnemptyifnotfound) {
1464 // Not found into alternate dir
1465 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1466 return '';
1467 }
1468 }
1469 } else {
1470 // For an url path
1471 // We try to get local path of file on filesystem from url
1472 // Note that trying to know if a file on disk exist by forging path on disk from url
1473 // works only for some web server and some setup. This is bugged when
1474 // using proxy, rewriting, virtual path, etc...
1475 $res = '';
1476 if ($type == 1) {
1477 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1478 }
1479 if ($type == 2) {
1480 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1481 }
1482 if ($type == 3) {
1483 $res = DOL_URL_ROOT.'/'.$path;
1484 }
1485
1486 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1487 if ($key == 'main') {
1488 if ($type == 3) {
1489 /*global $dolibarr_main_url_root;*/
1490
1491 // Define $urlwithroot
1492 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1493 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1494 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1495
1496 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1497 }
1498 continue;
1499 }
1500 $regs = array();
1501 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1502 if (!empty($regs[1])) {
1503 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1504 //if (file_exists($dirroot.'/'.$regs[1])) {
1505 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1506 if ($type == 1) {
1507 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1508 } elseif ($type == 2) {
1509 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1510 } elseif ($type == 3) {
1511 /*global $dolibarr_main_url_root;*/
1512
1513 // Define $urlwithroot
1514 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1515 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1516 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1517
1518 $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
1519 }
1520 break;
1521 }
1522 }
1523 }
1524 }
1525
1526 return $res;
1527}
1528
1539function dol_get_object_properties($obj, $properties = [])
1540{
1541 // Get real properties using get_object_vars() if $properties is empty
1542 if (empty($properties)) {
1543 return get_object_vars($obj);
1544 }
1545
1546 $existingProperties = [];
1547 $realProperties = get_object_vars($obj);
1548
1549 // Get the real or magic property values
1550 foreach ($properties as $property) {
1551 if (array_key_exists($property, $realProperties)) {
1552 // Real property, add the value
1553 $existingProperties[$property] = $obj->{$property};
1554 } elseif (property_exists($obj, $property)) {
1555 // Magic property
1556 $existingProperties[$property] = $obj->{$property};
1557 }
1558 }
1559
1560 return $existingProperties;
1561}
1562
1563
1579function dol_clone($object, $native = 2)
1580{
1581 if ($native == 0) {
1582 // deprecated method, use the method with native = 2 instead
1583 $tmpsavdb = null;
1584 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1585 $tmpsavdb = $object->db;
1586 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1587 }
1588
1589 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1590
1591 if (!empty($tmpsavdb)) {
1592 $object->db = $tmpsavdb;
1593 }
1594 } elseif ($native == 2) {
1595 // recommended method to have a full isolated cloned object
1596 $myclone = new stdClass();
1597 $tmparray = get_object_vars($object); // return only public properties
1598
1599 if (is_array($tmparray)) {
1600 foreach ($tmparray as $propertykey => $propertyval) {
1601 if (is_scalar($propertyval) || is_array($propertyval)) {
1602 $myclone->$propertykey = $propertyval;
1603 }
1604 }
1605 }
1606 } else {
1607 $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)
1608 }
1609
1610 return $myclone;
1611}
1612
1622function dol_size($size, $type = '')
1623{
1624 global $conf;
1625 if (empty($conf->dol_optimize_smallscreen)) {
1626 return $size;
1627 }
1628 if ($type == 'width' && $size > 250) {
1629 return 250;
1630 } else {
1631 return 10;
1632 }
1633}
1634
1635
1648function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0)
1649{
1650 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1651 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1652 // Char '/' and '\' are file delimiters.
1653 // 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
1654 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1655 if ($includequotes) {
1656 $filesystem_forbidden_chars[] = "'";
1657 }
1658 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1659 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1660 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1661 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1662 $tmp = str_replace('..', '', $tmp);
1663
1664 return $tmp;
1665}
1666
1667
1679function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1680{
1681 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1682 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1683 // 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
1684 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1685
1686 $tmp = $str;
1687 if ($unaccent) {
1688 $tmp = dol_string_unaccent($tmp);
1689 }
1690 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1691 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1692 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1693 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1694 $tmp = str_replace('..', '', $tmp);
1695 return $tmp;
1696}
1697
1705function dol_sanitizeUrl($stringtoclean, $type = 1)
1706{
1707 // 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)
1708 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1709 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1710 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1711 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1712
1713 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1714 if ($type == 1) {
1715 // removing : should disable links to external url like http:aaa)
1716 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1717 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1718 }
1719
1720 do {
1721 $oldstringtoclean = $stringtoclean;
1722 // removing '&colon' should disable links to external url like http:aaa)
1723 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1724 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1725 } while ($oldstringtoclean != $stringtoclean);
1726
1727 if ($type == 1) {
1728 // removing '//' should disable links to external url like //aaa or http//)
1729 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1730 }
1731
1732 return $stringtoclean;
1733}
1734
1741function dol_sanitizeEmail($stringtoclean)
1742{
1743 do {
1744 $oldstringtoclean = $stringtoclean;
1745 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1746 } while ($oldstringtoclean != $stringtoclean);
1747
1748 return $stringtoclean;
1749}
1750
1759function dol_sanitizeKeyCode($str)
1760{
1761 return preg_replace('/[^\w]+/', '', $str);
1762}
1763
1764
1773function dol_string_unaccent($str)
1774{
1775 if (is_null($str)) {
1776 return '';
1777 }
1778
1779 if (utf8_check($str)) {
1780 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1781 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1782 return $transliterator->transliterate($str);
1783 }
1784 // See http://www.utf8-chartable.de/
1785 $string = rawurlencode($str);
1786 $replacements = array(
1787 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1788 '%C3%87' => 'C',
1789 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1790 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1791 '%C3%91' => 'N',
1792 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1793 '%C5%A0' => 'S',
1794 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1795 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1796 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1797 '%C3%A7' => 'c',
1798 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1799 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1800 '%C3%B1' => 'n',
1801 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1802 '%C5%A1' => 's',
1803 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1804 '%C3%BD' => 'y', '%C3%BF' => 'y'
1805 );
1806 $string = strtr($string, $replacements);
1807 return rawurldecode($string);
1808 } else {
1809 // See http://www.ascii-code.com/
1810 $string = strtr(
1811 $str,
1812 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1813 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1814 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1815 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1816 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1817 \xF9\xFA\xFB\xFC\xFD\xFF",
1818 "AAAAAAC
1819 EEEEIIIIDN
1820 OOOOOUUUY
1821 aaaaaaceeee
1822 iiiidnooooo
1823 uuuuyy"
1824 );
1825 $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"));
1826 return $string;
1827 }
1828}
1829
1843function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1844{
1845 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1846 if (empty($keepspaces)) {
1847 $forbidden_chars_to_replace[] = " ";
1848 }
1849 $forbidden_chars_to_remove = array();
1850 //$forbidden_chars_to_remove=array("(",")");
1851
1852 if (is_array($badcharstoreplace)) {
1853 $forbidden_chars_to_replace = $badcharstoreplace;
1854 }
1855 if (is_array($badcharstoremove)) {
1856 $forbidden_chars_to_remove = $badcharstoremove;
1857 }
1858
1859 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1860 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1861}
1862
1863
1877function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1878{
1879 if ($removetabcrlf) {
1880 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1881 } else {
1882 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
1883 }
1884}
1885
1892function dolSlugify($stringtoslugify)
1893{
1894 $slug = dol_string_unaccent($stringtoslugify);
1895
1896 // Convert special characters to their ASCII equivalents
1897 if (function_exists('iconv')) {
1898 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
1899 }
1900
1901 // Convert to lowercase
1902 $slug = strtolower($slug);
1903
1904 // Replace non-alphanumeric characters with hyphens
1905 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
1906
1907 // Remove leading and trailing hyphens
1908 $slug = trim($slug, '-');
1909
1910 return $slug;
1911}
1912
1921function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1922{
1923 if (is_null($stringtoescape)) {
1924 return '';
1925 }
1926
1927 // escape quotes and backslashes, newlines, etc.
1928 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1929 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1930 if (empty($noescapebackslashn)) {
1931 $substitjs["\n"] = '\\n';
1932 $substitjs['\\'] = '\\\\';
1933 }
1934 if (empty($mode)) {
1935 $substitjs["'"] = "\\'";
1936 $substitjs['"'] = "\\'";
1937 } elseif ($mode == 1) {
1938 $substitjs["'"] = "\\'";
1939 } elseif ($mode == 2) {
1940 $substitjs['"'] = '\\"';
1941 } elseif ($mode == 3) {
1942 $substitjs["'"] = "\\'";
1943 $substitjs['"'] = "\\\"";
1944 }
1945 return strtr((string) $stringtoescape, $substitjs);
1946}
1947
1957function dol_escape_uri($stringtoescape)
1958{
1959 return rawurlencode($stringtoescape);
1960}
1961
1968function dol_escape_json($stringtoescape)
1969{
1970 return str_replace('"', '\"', $stringtoescape);
1971}
1972
1980function dol_escape_php($stringtoescape, $stringforquotes = 2)
1981{
1982 if (is_null($stringtoescape)) {
1983 return '';
1984 }
1985
1986 if ($stringforquotes == 2) {
1987 return str_replace('"', "'", $stringtoescape);
1988 } elseif ($stringforquotes == 1) {
1989 // We remove the \ char.
1990 // If we allow the \ char, we can have $stringtoescape =
1991 // abc\';phpcodedanger; so the escapement will become
1992 // abc\\';phpcodedanger; and injecting this into
1993 // $a='...' will give $ac='abc\\';phpcodedanger;
1994 $stringtoescape = str_replace('\\', '', $stringtoescape);
1995 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1996 }
1997
1998 return 'Bad parameter for stringforquotes in dol_escape_php';
1999}
2000
2007function dol_escape_all($stringtoescape)
2008{
2009 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2010}
2011
2018function dol_escape_xml($stringtoescape)
2019{
2020 return $stringtoescape;
2021}
2022
2031function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2032{
2033 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2034}
2035
2043function dolPrintText($s)
2044{
2045 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2046}
2047
2058function dolPrintHTML($s, $allowiframe = 0)
2059{
2060 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr((string) $s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2061}
2062
2073function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2074{
2075 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2076 if (!empty($allowothertags) && is_array($allowothertags)) {
2077 $allowedtags = array_merge($allowedtags, $allowothertags);
2078 }
2079 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2080 // The dol_escape_htmltag will escape html tags.
2081 if ($escapeonlyhtmltags) {
2082 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2083 } else {
2084 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2085 }
2086}
2087
2096function dolPrintHTMLForAttributeUrl($s)
2097{
2098 // The dol_htmlentitiesbr has been removed compared to dolPrintHTMLForAttribute because we know content is a HTML URL string (even if we have no way to detect it automatically)
2099 // The dol_escape_htmltag will escape html chars.
2100 $escapeonlyhtmltags = 1;
2101 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2102}
2103
2113function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2114{
2115 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2116}
2117
2124function dolPrintPassword($s)
2125{
2126 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
2127}
2128
2129
2146function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2147{
2148 if ($noescapetags == 'common') {
2149 $noescapetags = 'html,body,a,b,em,hr,i,u,ul,ol,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
2150 // Add also html5 tags
2151 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2152 }
2153 if ($cleanalsojavascript) {
2154 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2155 }
2156
2157 // escape quotes and backslashes, newlines, etc.
2158 if ($escapeonlyhtmltags) {
2159 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2160 } else {
2161 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2162 // Because content can be or not already HTML.
2163 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2164 // In a future, we should not need this
2165
2166 $tmp = (string) $stringtoescape;
2167
2168 // We protect the 6 special entities that we don't want to decode.
2169 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2170 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2171 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2172 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2173 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2174 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2175
2176 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2177
2178 // We restore the 6 special entities that we don't want to have been decoded by previous command
2179 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2180 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2181 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2182 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2183 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2184 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2185
2186 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2187 }
2188 if (!$keepb) {
2189 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2190 }
2191 if (!$keepn) {
2192 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2193 } elseif ($keepn == -1) {
2194 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2195 }
2196
2197 if ($escapeonlyhtmltags) {
2198 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2199 return $tmp;
2200 } else {
2201 // Now we protect all the tags we want to keep
2202 $tmparrayoftags = array();
2203 if ($noescapetags) {
2204 $tmparrayoftags = explode(',', $noescapetags);
2205 }
2206
2207 if (count($tmparrayoftags)) {
2208 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2209
2210 $reg = array();
2211 // Remove reserved keywords. They are forbidden in a source string
2212 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2213
2214 foreach ($tmparrayoftags as $tagtoreplace) {
2215 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2216 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2217 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2218 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2219
2220 // For case of tag with attributes
2221 do {
2222 $tmpold = $tmp;
2223
2224 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2225 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2226 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2227 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2228 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2229 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2230 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2231 }
2232
2233 $diff = strcmp($tmpold, $tmp);
2234 } while ($diff);
2235 }
2236
2237 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2238 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2239 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2240 }
2241
2242 // Warning: htmlentities encode HTML tags like <abc> & into &amp; and more (but not &lt; &gt; &quotes; &apos; &#39; &amp; that remains untouched).
2243 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2244
2245 //print $result;
2246
2247 if (count($tmparrayoftags)) {
2248 // Restore protected tags
2249 foreach ($tmparrayoftags as $tagtoreplace) {
2250 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2251 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2252 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2253 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2254 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2255 }
2256
2257 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2258
2259 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2260 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2261 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2262 }
2263
2264 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2265
2266 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2267
2268 return $result;
2269 }
2270}
2271
2279function dol_strtolower($string, $encoding = "UTF-8")
2280{
2281 if (function_exists('mb_strtolower')) {
2282 return mb_strtolower($string, $encoding);
2283 } else {
2284 return strtolower($string);
2285 }
2286}
2287
2296function dol_strtoupper($string, $encoding = "UTF-8")
2297{
2298 if (function_exists('mb_strtoupper')) {
2299 return mb_strtoupper($string, $encoding);
2300 } else {
2301 return strtoupper($string);
2302 }
2303}
2304
2313function dol_ucfirst($string, $encoding = "UTF-8")
2314{
2315 if (function_exists('mb_substr')) {
2316 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2317 } else {
2318 return ucfirst($string);
2319 }
2320}
2321
2330function dol_ucwords($string, $encoding = "UTF-8")
2331{
2332 if (function_exists('mb_convert_case')) {
2333 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2334 } else {
2335 return ucwords($string);
2336 }
2337}
2338
2339
2345function getCallerInfoString()
2346{
2347 $backtrace = debug_backtrace();
2348 $msg = "";
2349 if (count($backtrace) >= 1) {
2350 $pos = 1;
2351 if (count($backtrace) == 1) {
2352 $pos = 0;
2353 }
2354 $trace = $backtrace[$pos];
2355 if (isset($trace['file'], $trace['line'])) {
2356 $msg = " From {$trace['file']}:{$trace['line']}.";
2357 }
2358 }
2359 return $msg;
2360}
2361
2384function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2385{
2386 global $conf, $user, $debugbar;
2387
2388 // If syslog module enabled
2389 if (!isModEnabled('syslog')) {
2390 return;
2391 }
2392
2393 // Check if we are into execution of code of a website
2394 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2395 global $website, $websitekey;
2396 if (is_object($website) && !empty($website->ref)) {
2397 $suffixinfilename .= '_website_'.$website->ref;
2398 } elseif (!empty($websitekey)) {
2399 $suffixinfilename .= '_website_'.$websitekey;
2400 }
2401 }
2402
2403 // Check if we have a forced suffix
2404 if (defined('USESUFFIXINLOG')) {
2405 $suffixinfilename .= constant('USESUFFIXINLOG');
2406 }
2407
2408 if ($ident < 0) {
2409 foreach ($conf->loghandlers as $loghandlerinstance) {
2410 $loghandlerinstance->setIdent($ident);
2411 }
2412 }
2413
2414 if (!empty($message)) {
2415 // Test log level
2416 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2417 $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');
2418
2419 if (!array_key_exists($level, $logLevels)) {
2420 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2421 $level = $logLevels[LOG_ERR];
2422 }
2423 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2424 return;
2425 }
2426
2427 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2428 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2429 }
2430
2431 // If adding log inside HTML page is required
2432 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2433 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2434 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2435 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2436
2437 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2438 }
2439
2440 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2441 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2442 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2443 print "\n\n<!-- Log start\n";
2444 print dol_escape_htmltag($message)."\n";
2445 print "Log end -->\n";
2446 }
2447
2448 $data = array(
2449 'message' => $message,
2450 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2451 'level' => $level,
2452 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2453 'ip' => false,
2454 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2455 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2456 );
2457
2458 // For log, we want the reliable IP first.
2459 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2460 if (!empty($remoteip)) {
2461 $data['ip'] = $remoteip;
2462 // This is when server run behind a reverse proxy
2463 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2464 // $data['ip'] is last
2465 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2466 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2467 $data['ip'] = '';
2468 $foundremoteip = 0;
2469 $j = 0;
2470 foreach ($tmpips as $tmpip) {
2471 $tmpip = trim($tmpip);
2472 if (strtolower($tmpip) == strtolower($remoteip)) {
2473 $foundremoteip = 1;
2474 }
2475 if (empty($data['ip'])) {
2476 $data['ip'] = $tmpip;
2477 } else {
2478 $j++;
2479 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2480 }
2481 }
2482 if (!$foundremoteip) {
2483 $j++;
2484 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2485 }
2486 $data['ip'] .= (($j > 0) ? ']' : '');
2487 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) ) {
2488 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2489 $data['ip'] = '';
2490 $foundremoteip = 0;
2491 $j = 0;
2492 foreach ($tmpips as $tmpip) {
2493 $tmpip = trim($tmpip);
2494 if (strtolower($tmpip) == strtolower($remoteip)) {
2495 $foundremoteip = 1;
2496 }
2497 if (empty($data['ip'])) {
2498 $data['ip'] = $tmpip;
2499 } else {
2500 $j++;
2501 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2502 }
2503 }
2504 if (!$foundremoteip) {
2505 $j++;
2506 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2507 }
2508 $data['ip'] .= (($j > 0) ? ']' : '');
2509 }
2510 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2511 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2512 $data['ip'] = $_SERVER['SERVER_ADDR'];
2513 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2514 // 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).
2515 $data['ip'] = $_SERVER['COMPUTERNAME'];
2516 } else {
2517 $data['ip'] = '???';
2518 }
2519
2520 if (!empty($_SERVER['USERNAME'])) {
2521 // 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).
2522 $data['osuser'] = $_SERVER['USERNAME'];
2523 } elseif (!empty($_SERVER['LOGNAME'])) {
2524 // 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).
2525 $data['osuser'] = $_SERVER['LOGNAME'];
2526 }
2527
2528 // Loop on each log handler and send output
2529 foreach ($conf->loghandlers as $loghandlerinstance) {
2530 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2531 continue;
2532 }
2533 $loghandlerinstance->export($data, $suffixinfilename);
2534 }
2535 unset($data);
2536 }
2537
2538 if ($ident > 0) {
2539 foreach ($conf->loghandlers as $loghandlerinstance) {
2540 $loghandlerinstance->setIdent($ident);
2541 }
2542 }
2543}
2544
2556function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2557{
2558 global $langs, $db;
2559
2560 $form = new Form($db);
2561
2562 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2563 if (empty($templatenameforexport)) {
2564 $templatenameforexport = 'website_'.$website->ref;
2565 }
2566
2567 $out = '';
2568 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2569
2570 // for generate popup
2571 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2572 $out .= 'jQuery(document).ready(function () {';
2573 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2574 $out .= ' var dialogHtml = \'';
2575
2576 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2577 $dialogcontent .= ' <div style="margin-top: 20px;">';
2578 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2579 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2580 $dialogcontent .= ' </div>';
2581 $dialogcontent .= ' <br>';
2582 $dialogcontent .= ' <div style="margin-top: 20px;">';
2583 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2584 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2585 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2586 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2587 $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>';
2588 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2589 $dialogcontent .= ' </form>';
2590 $dialogcontent .= ' </div>';
2591 $dialogcontent .= ' </div>';
2592
2593 $out .= dol_escape_js($dialogcontent);
2594
2595 $out .= '\';';
2596
2597
2598 // Add the content of the dialog to the body of the page
2599 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2600 $out .= ' if ($dialog.length > 0) {
2601 $dialog.remove();
2602 }
2603 jQuery("body").append(dialogHtml);';
2604
2605 // Configuration of popup
2606 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2607 $out .= ' autoOpen: false,';
2608 $out .= ' modal: true,';
2609 $out .= ' height: 290,';
2610 $out .= ' width: "40%",';
2611 $out .= ' title: "' . dol_escape_js($label) . '",';
2612 $out .= ' });';
2613
2614 // Simulate a click on the original "submit" input to export the site.
2615 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2616 $out .= ' console.log("Clic on exportsite.");';
2617 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2618 $out .= ' console.log("element founded:", target.length > 0);';
2619 $out .= ' if (target.length > 0) { target.click(); }';
2620 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2621 $out .= ' });';
2622
2623 // open popup
2624 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2625 $out .= ' return false;';
2626 $out .= ' });';
2627 $out .= '});';
2628 $out .= '</script>';
2629
2630 return $out;
2631}
2632
2633
2650function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2651{
2652 global $conf;
2653
2654 if (strpos($url, '?') > 0) {
2655 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2656 } else {
2657 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2658 }
2659
2660 $out = '';
2661
2662 $backtopagejsfieldsid = '';
2663 $backtopagejsfieldslabel = '';
2664 if ($backtopagejsfields) {
2665 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2666 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2667 $backtopagejsfields = $name.":".$backtopagejsfields;
2668 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2669 } else {
2670 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2671 }
2672 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2673 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2674 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2675 }
2676
2677 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2678 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2679 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2680 if (empty($conf->use_javascript_ajax)) {
2681 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2682 } elseif ($jsonopen) {
2683 $out .= ' href="#" onclick="'.$jsonopen.'"';
2684 } else {
2685 $out .= ' href="#"';
2686 }
2687 $out .= '>'.$buttonstring.'</a>';
2688
2689 if (!empty($conf->use_javascript_ajax)) {
2690 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2691 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2692 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2693 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2694 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2695
2696 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2697 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2698 jQuery(document).ready(function () {
2699 jQuery(".button_'.$name.'").click(function () {
2700 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2701 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2702 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2703 $tmpdialog.dialog({
2704 autoOpen: false,
2705 modal: true,
2706 height: (window.innerHeight - 150),
2707 width: \'80%\',
2708 title: \''.dol_escape_js($label).'\',
2709 open: function (event, ui) {
2710 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2711 },
2712 close: function (event, ui) {
2713 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2714 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2715 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2716 if (returnedid != "" && returnedid != "div for returned id") {
2717 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2718 }
2719 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2720 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2721 }
2722 }
2723 });
2724
2725 $tmpdialog.dialog(\'open\');
2726 return false;
2727 });
2728 });
2729 </script>';
2730 }
2731 return $out;
2732}
2733
2750function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2751{
2752 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2753}
2754
2771function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2772{
2773 global $conf, $langs, $hookmanager;
2774
2775 // Show title
2776 $showtitle = 1;
2777 if (!empty($conf->dol_optimize_smallscreen)) {
2778 $showtitle = 0;
2779 }
2780
2781 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2782
2783 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2784 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2785 }
2786
2787 // Show right part
2788 if ($morehtmlright) {
2789 $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.
2790 }
2791
2792 // Show tabs
2793
2794 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2795 $maxkey = -1;
2796 if (is_array($links) && !empty($links)) {
2797 $keys = array_keys($links);
2798 if (count($keys)) {
2799 $maxkey = max($keys);
2800 }
2801 }
2802
2803 // Show tabs
2804 // if =0 we don't use the feature
2805 if (empty($limittoshow)) {
2806 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2807 }
2808 if (!empty($conf->dol_optimize_smallscreen)) {
2809 $limittoshow = 2;
2810 }
2811
2812 $displaytab = 0;
2813 $nbintab = 0;
2814 $popuptab = 0;
2815 $outmore = '';
2816 for ($i = 0; $i <= $maxkey; $i++) {
2817 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2818 // If active tab is already present
2819 if ($i >= $limittoshow) {
2820 $limittoshow--;
2821 }
2822 }
2823 }
2824
2825 for ($i = 0; $i <= $maxkey; $i++) {
2826 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2827 $isactive = true;
2828 } else {
2829 $isactive = false;
2830 }
2831
2832 if ($i < $limittoshow || $isactive) {
2833 // Output entry with a visible tab
2834 $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])).' -->';
2835
2836 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2837 if (!empty($links[$i][0])) {
2838 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2839 } else {
2840 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2841 }
2842 } elseif (!empty($links[$i][1])) {
2843 //print "x $i $active ".$links[$i][2]." z";
2844 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2845
2846 if (!empty($links[$i][0])) {
2847 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2848 $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).'">';
2849 }
2850
2851 if ($displaytab == 0 && $picto) {
2852 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2853 }
2854
2855 $out .= $links[$i][1];
2856 if (!empty($links[$i][0])) {
2857 $out .= '</a>'."\n";
2858 }
2859 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2860 $out .= '</div>';
2861 }
2862
2863 $out .= '</div>';
2864 } else {
2865 // Add entry into the combo popup with the other tabs
2866 if (!$popuptab) {
2867 $popuptab = 1;
2868 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2869 }
2870 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2871 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2872 if (!empty($links[$i][0])) {
2873 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2874 } else {
2875 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2876 }
2877 } elseif (!empty($links[$i][1])) {
2878 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2879 $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.
2880 $outmore .= '</a>'."\n";
2881 }
2882 $outmore .= '</div>';
2883
2884 $nbintab++;
2885 }
2886
2887 $displaytab = $i + 1;
2888 }
2889 if ($popuptab) {
2890 $outmore .= '</div>';
2891 }
2892
2893 if ($popuptab) { // If there is some tabs not shown
2894 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2895 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2896 $widthofpopup = 200;
2897
2898 $tabsname = $moretabssuffix;
2899 if (empty($tabsname)) {
2900 $tabsname = str_replace("@", "", $picto);
2901 }
2902 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2903 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2904 $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".
2905 }
2906 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2907 $out .= $outmore;
2908 $out .= '</div>';
2909 $out .= '<div></div>';
2910 $out .= "</div>\n";
2911
2912 $out .= '<script nonce="'.getNonce().'">';
2913 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2914 var x = this.offsetLeft, y = this.offsetTop;
2915 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2916 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2917 $('#moretabsList".$tabsname."').css('".$right."','8px');
2918 }
2919 $('#moretabsList".$tabsname."').css('".$left."','auto');
2920 });
2921 ";
2922 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2923 $out .= "</script>";
2924 }
2925
2926 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2927 $out .= "</div>\n";
2928 }
2929
2930 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
2931 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
2932 $out .= '">'."\n";
2933 }
2934 if (!empty($dragdropfile)) {
2935 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2936 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2937 }
2938 $parameters = array('tabname' => $active, 'out' => $out);
2939 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2940 if ($reshook > 0) {
2941 $out = $hookmanager->resPrint;
2942 }
2943
2944 return $out;
2945}
2946
2954function dol_fiche_end($notab = 0)
2955{
2956 print dol_get_fiche_end($notab);
2957}
2958
2965function dol_get_fiche_end($notab = 0)
2966{
2967 if (!$notab || $notab == -1) {
2968 return "\n</div>\n";
2969 } else {
2970 return '';
2971 }
2972}
2973
2993function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2994{
2995 global $conf, $form, $user, $langs, $hookmanager, $action;
2996
2997 $error = 0;
2998
2999 $maxvisiblephotos = 1;
3000 $showimage = 1;
3001 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3002 // @phan-suppress-next-line PhanUndeclaredMethod
3003 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3004 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3005 $showbarcode = 0;
3006 }
3007 $modulepart = 'unknown';
3008
3009 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3010 $modulepart = $object->element;
3011 } elseif ($object->element == 'member') {
3012 $modulepart = 'memberphoto';
3013 } elseif ($object->element == 'user') {
3014 $modulepart = 'userphoto';
3015 }
3016
3017 if (class_exists("Imagick")) {
3018 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3019 $modulepart = $object->element;
3020 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3021 $modulepart = 'ficheinter';
3022 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3023 $modulepart = 'contract';
3024 } elseif ($object->element == 'order_supplier') {
3025 $modulepart = 'supplier_order';
3026 } elseif ($object->element == 'invoice_supplier') {
3027 $modulepart = 'supplier_invoice';
3028 }
3029 }
3030
3031 if ($object->element == 'product') {
3033 '@phan-var-force Product $object';
3034 $width = 80;
3035 $cssclass = 'photowithmargin photoref';
3036 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3037 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3038 if ($conf->browser->layout == 'phone') {
3039 $maxvisiblephotos = 1;
3040 }
3041 if ($showimage) {
3042 $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>';
3043 } else {
3044 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3045 $nophoto = '';
3046 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3047 } else { // Show no photo link
3048 $nophoto = '/public/theme/common/nophoto.png';
3049 $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>';
3050 }
3051 }
3052 } elseif ($object->element == 'category') {
3054 '@phan-var-force Categorie $object';
3055 $width = 80;
3056 $cssclass = 'photowithmargin photoref';
3057 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3058 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3059 if ($conf->browser->layout == 'phone') {
3060 $maxvisiblephotos = 1;
3061 }
3062 if ($showimage) {
3063 $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>';
3064 } else {
3065 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3066 $nophoto = '';
3067 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3068 } else { // Show no photo link
3069 $nophoto = '/public/theme/common/nophoto.png';
3070 $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>';
3071 }
3072 }
3073 } elseif ($object->element == 'bom') {
3075 '@phan-var-force Bom $object';
3076 $width = 80;
3077 $cssclass = 'photowithmargin photoref';
3078 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3079 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3080 if ($conf->browser->layout == 'phone') {
3081 $maxvisiblephotos = 1;
3082 }
3083 if ($showimage) {
3084 $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>';
3085 } else {
3086 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3087 $nophoto = '';
3088 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3089 } else { // Show no photo link
3090 $nophoto = '/public/theme/common/nophoto.png';
3091 $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>';
3092 }
3093 }
3094 } elseif ($object->element == 'ticket') {
3095 $width = 80;
3096 $cssclass = 'photoref';
3098 '@phan-var-force Ticket $object';
3099 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
3100 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3101 if ($conf->browser->layout == 'phone') {
3102 $maxvisiblephotos = 1;
3103 }
3104
3105 if ($showimage) {
3106 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3107 if ($object->nbphoto > 0) {
3108 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
3109 } else {
3110 $showimage = 0;
3111 }
3112 }
3113 if (!$showimage) {
3114 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3115 $nophoto = '';
3116 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3117 } else { // Show no photo link
3118 $nophoto = img_picto('No photo', 'object_ticket');
3119 $morehtmlleft .= '<!-- No photo to show -->';
3120 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3121 $morehtmlleft .= $nophoto;
3122 $morehtmlleft .= '</div></div>';
3123 }
3124 }
3125 } else {
3126 if ($showimage) {
3127 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3128 $phototoshow = '';
3129 // Check if a preview file is available
3130 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3131 $objectref = dol_sanitizeFileName($object->ref);
3132 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3133 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3134 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3135 $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
3136 } else {
3137 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3138 }
3139 if (empty($subdir)) {
3140 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3141 }
3142
3143 $filepath = $dir_output.$subdir."/";
3144
3145 $filepdf = $filepath.$objectref.".pdf";
3146 $relativepath = $subdir.'/'.$objectref.'.pdf';
3147
3148 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3149 $fileimage = $filepdf.'_preview.png';
3150 $relativepathimage = $relativepath.'_preview.png';
3151
3152 $pdfexists = file_exists($filepdf);
3153
3154 // If PDF file exists
3155 if ($pdfexists) {
3156 // Conversion du PDF en image png si fichier png non existent
3157 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3158 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3159 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3160 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3161 if ($ret < 0) {
3162 $error++;
3163 }
3164 }
3165 }
3166 }
3167
3168 if ($pdfexists && !$error) {
3169 $heightforphotref = 80;
3170 if (!empty($conf->dol_optimize_smallscreen)) {
3171 $heightforphotref = 60;
3172 }
3173 // If the preview file is found
3174 if (file_exists($fileimage)) {
3175 $phototoshow = '<div class="photoref">';
3176 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3177 $phototoshow .= '</div>';
3178 }
3179 }
3180 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3181 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
3182 }
3183
3184 if ($phototoshow) {
3185 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3186 $morehtmlleft .= $phototoshow;
3187 $morehtmlleft .= '</div>';
3188 }
3189 }
3190
3191 if (empty($phototoshow)) { // Show No photo link (picto of object)
3192 if ($object->element == 'action') {
3193 $width = 80;
3194 $cssclass = 'photorefcenter';
3195 $nophoto = img_picto('No photo', 'title_agenda');
3196 } else {
3197 $width = 14;
3198 $cssclass = 'photorefcenter';
3199 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3200 $prefix = 'object_';
3201 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3202 $picto = 'project'; // instead of projectpub
3203 }
3204 if (strpos($picto, 'fontawesome_') !== false) {
3205 $prefix = '';
3206 }
3207 $nophoto = img_picto('No photo', $prefix.$picto);
3208 }
3209 $morehtmlleft .= '<!-- No photo to show -->';
3210 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3211 $morehtmlleft .= $nophoto;
3212 $morehtmlleft .= '</div></div>';
3213 }
3214 }
3215 }
3216
3217 // Show barcode
3218 if ($showbarcode) {
3219 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3220 }
3221
3222 if ($object->element == 'societe') {
3223 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3224 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3225 } else {
3226 $morehtmlstatus .= $object->getLibStatut(6);
3227 }
3228 } elseif ($object->element == 'product') {
3229 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3230 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3231 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3232 } else {
3233 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3234 }
3235 $morehtmlstatus .= ' &nbsp; ';
3236 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3237 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3238 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3239 } else {
3240 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3241 }
3242 } elseif (in_array($object->element, array('salary'))) {
3243 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3244 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3245 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3246 }
3247 $morehtmlstatus .= $tmptxt;
3248 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3249 $totalallpayments = $object->getSommePaiement(0);
3250 $totalallpayments += $object->getSumCreditNotesUsed(0);
3251 $totalallpayments += $object->getSumDepositsUsed(0);
3252 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3253 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3254 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3255 }
3256 $morehtmlstatus .= $tmptxt;
3257 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3258 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3259 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3260 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3261 }
3262 $morehtmlstatus .= $tmptxt;
3263 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3264 if ($object->statut == 0) {
3265 $morehtmlstatus .= $object->getLibStatut(5);
3266 } else {
3267 $morehtmlstatus .= $object->getLibStatut(4);
3268 }
3269 } elseif ($object->element == 'facturerec') {
3270 '@phan-var-force FactureRec $object';
3271 if ($object->frequency == 0) {
3272 $morehtmlstatus .= $object->getLibStatut(2);
3273 } else {
3274 $morehtmlstatus .= $object->getLibStatut(5);
3275 }
3276 } elseif ($object->element == 'project_task') {
3277 $object->fk_statut = 1;
3278 $object->status = 1;
3279 if ($object->progress > 0) {
3280 $object->fk_statut = 2;
3281 $object->status = 2;
3282 }
3283 if ($object->progress >= 100) {
3284 $object->fk_statut = 3;
3285 $object->status = 3;
3286 }
3287 $tmptxt = $object->getLibStatut(5);
3288 $morehtmlstatus .= $tmptxt; // No status on task
3289 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3290 $tmptxt = $object->getLibStatut(6);
3291 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3292 $tmptxt = $object->getLibStatut(5);
3293 }
3294 $morehtmlstatus .= $tmptxt;
3295 }
3296
3297 // Add if object was dispatched "into accountancy"
3298 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3299 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3300 if (method_exists($object, 'getVentilExportCompta')) {
3301 $accounted = $object->getVentilExportCompta();
3302 $langs->load("accountancy");
3303 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3304 }
3305 }
3306
3307 // Add alias for thirdparty
3308 if (!empty($object->name_alias)) {
3309 '@phan-var-force Societe $object';
3310 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3311 }
3312
3313 // Add label
3314 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3315 if (!empty($object->label)) {
3316 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3317 }
3318 }
3319 // Show address and email
3320 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3321 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3322 if ($moreaddress) {
3323 $morehtmlref .= '<div class="refidno refaddress">';
3324 $morehtmlref .= $moreaddress;
3325 $morehtmlref .= '</div>';
3326 }
3327 }
3328 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)) {
3329 $morehtmlref .= '<div style="clear: both;"></div>';
3330 $morehtmlref .= '<div class="refidno opacitymedium">';
3331 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3332 $morehtmlref .= '</div>';
3333 }
3334
3335 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3336 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3337 if ($reshook < 0) {
3338 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3339 } elseif (empty($reshook)) {
3340 $morehtmlref .= $hookmanager->resPrint;
3341 } elseif ($reshook > 0) {
3342 $morehtmlref = $hookmanager->resPrint;
3343 }
3344
3345 // $morehtml is the right part (link "Back to list")
3346 // $morehtmlleft is the picto or photo of banner
3347 // $morehtmlstatus is part under the status
3348 // $morehtmlright is part of htmlright
3349
3350 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3351 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3352 print '</div>';
3353 print '<div class="underrefbanner clearboth"></div>';
3354}
3355
3365function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3366{
3367 global $langs;
3368 $ret = '';
3369 if ($fieldrequired) {
3370 $ret .= '<span class="fieldrequired">';
3371 }
3372 $ret .= '<label for="'.$fieldkey.'">';
3373 $ret .= $langs->trans($langkey);
3374 $ret .= '</label>';
3375 if ($fieldrequired) {
3376 $ret .= '</span>';
3377 }
3378 return $ret;
3379}
3380
3394function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3395{
3396 global $langs, $hookmanager;
3397
3398 $ret = '';
3399 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3400
3401 // See format of addresses on https://en.wikipedia.org/wiki/Address
3402 // Address
3403 if (empty($mode)) {
3404 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3405 }
3406 // Zip/Town/State
3407 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3408 // US: title firstname name \n address lines \n town, state, zip \n country
3409 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3410 $ret .= (($ret && $town) ? $sep : '').$town;
3411
3412 if (!empty($object->state)) {
3413 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3414 }
3415 if (!empty($object->zip)) {
3416 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3417 }
3418 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3419 // UK: title firstname name \n address lines \n town state \n zip \n country
3420 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3421 $ret .= ($ret ? $sep : '').$town;
3422 if (!empty($object->state)) {
3423 $ret .= ($ret ? ", " : '').$object->state;
3424 }
3425 if (!empty($object->zip)) {
3426 $ret .= ($ret ? $sep : '').$object->zip;
3427 }
3428 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3429 // ES: title firstname name \n address lines \n zip town \n state \n country
3430 $ret .= ($ret ? $sep : '').$object->zip;
3431 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3432 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3433 if (!empty($object->state)) {
3434 $ret .= $sep.$object->state;
3435 }
3436 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3437 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3438 // See https://www.sljfaq.org/afaq/addresses.html
3439 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3440 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3441 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3442 // IT: title firstname name\n address lines \n zip town state_code \n country
3443 $ret .= ($ret ? $sep : '').$object->zip;
3444 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3445 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3446 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3447 } else {
3448 // Other: title firstname name \n address lines \n zip town[, state] \n country
3449 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3450 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3451 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3452 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3453 $ret .= ($ret ? ", " : '').$object->state;
3454 }
3455 }
3456
3457 if (!is_object($outputlangs)) {
3458 $outputlangs = $langs;
3459 }
3460 if ($withcountry) {
3461 $langs->load("dict");
3462 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3463 }
3464 if ($hookmanager) {
3465 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3466 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3467 if ($reshook > 0) {
3468 $ret = '';
3469 }
3470 $ret .= $hookmanager->resPrint;
3471 }
3472
3473 return $ret;
3474}
3475
3476
3477
3487function dol_strftime($fmt, $ts = false, $is_gmt = false)
3488{
3489 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3490 return dol_print_date($ts, $fmt, $is_gmt);
3491 } else {
3492 return 'Error date outside supported range';
3493 }
3494}
3495
3517function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3518{
3519 global $conf, $langs;
3520
3521 // If date undefined or "", we return ""
3522 if (dol_strlen($time) == 0) {
3523 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3524 }
3525
3526 if ($tzoutput === 'auto') {
3527 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3528 }
3529
3530 // Clean parameters
3531 $to_gmt = false;
3532 $offsettz = $offsetdst = 0;
3533 if ($tzoutput) {
3534 $to_gmt = true; // For backward compatibility
3535 if (is_string($tzoutput)) {
3536 if ($tzoutput == 'tzserver') {
3537 $to_gmt = false;
3538 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3539 // @phan-suppress-next-line PhanPluginRedundantAssignment
3540 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3541 // @phan-suppress-next-line PhanPluginRedundantAssignment
3542 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3543 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3544 $to_gmt = true;
3545 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3546
3547 if (class_exists('DateTimeZone')) {
3548 $user_date_tz = new DateTimeZone($offsettzstring);
3549 $user_dt = new DateTime();
3550 $user_dt->setTimezone($user_date_tz);
3551 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3552 $offsettz = $user_dt->getOffset(); // should include dst ?
3553 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3554 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3555 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3556 }
3557 }
3558 }
3559 }
3560 if (!is_object($outputlangs)) {
3561 $outputlangs = $langs;
3562 }
3563 if (!$format) {
3564 $format = 'daytextshort';
3565 }
3566
3567 // Do we have to reduce the length of date (year on 2 chars) to save space.
3568 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3569 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3570 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3571 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3572 if ($formatwithoutreduce != $format) {
3573 $format = $formatwithoutreduce;
3574 $reduceformat = 1;
3575 } // so format 'dayreduceformat' is processed like day
3576
3577 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3578 // TODO Add format daysmallyear and dayhoursmallyear
3579 if ($format == 'day') {
3580 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3581 } elseif ($format == 'hour') {
3582 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3583 } elseif ($format == 'hourduration') {
3584 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3585 } elseif ($format == 'daytext') {
3586 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3587 } elseif ($format == 'daytextshort') {
3588 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3589 } elseif ($format == 'dayhour') {
3590 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3591 } elseif ($format == 'dayhoursec') {
3592 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3593 } elseif ($format == 'dayhourtext') {
3594 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3595 } elseif ($format == 'dayhourtextshort') {
3596 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3597 } elseif ($format == 'dayhourlog') {
3598 // Format not sensitive to language
3599 $format = '%Y%m%d%H%M%S';
3600 } elseif ($format == 'dayhourlogsmall') {
3601 // Format not sensitive to language
3602 $format = '%y%m%d%H%M';
3603 } elseif ($format == 'dayhourldap') {
3604 $format = '%Y%m%d%H%M%SZ';
3605 } elseif ($format == 'dayhourxcard') {
3606 $format = '%Y%m%dT%H%M%SZ';
3607 } elseif ($format == 'dayxcard') {
3608 $format = '%Y%m%d';
3609 } elseif ($format == 'dayrfc') {
3610 $format = '%Y-%m-%d'; // DATE_RFC3339
3611 } elseif ($format == 'dayhourrfc') {
3612 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3613 } elseif ($format == 'standard') {
3614 $format = '%Y-%m-%d %H:%M:%S';
3615 }
3616
3617 if ($reduceformat) {
3618 $format = str_replace('%Y', '%y', $format);
3619 $format = str_replace('yyyy', 'yy', $format);
3620 }
3621
3622 // Clean format
3623 if (preg_match('/%b/i', $format)) { // There is some text to translate
3624 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3625 $format = str_replace('%b', '__b__', $format);
3626 $format = str_replace('%B', '__B__', $format);
3627 }
3628 if (preg_match('/%a/i', $format)) { // There is some text to translate
3629 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3630 $format = str_replace('%a', '__a__', $format);
3631 $format = str_replace('%A', '__A__', $format);
3632 }
3633
3634 // Analyze date
3635 $reg = array();
3636 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
3637 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"]));
3638 return '';
3639 } 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
3640 // This part of code should not be used anymore.
3641 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);
3642 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3643 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3644 $syear = (!empty($reg[1]) ? $reg[1] : '');
3645 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3646 $sday = (!empty($reg[3]) ? $reg[3] : '');
3647 $shour = (!empty($reg[4]) ? $reg[4] : '');
3648 $smin = (!empty($reg[5]) ? $reg[5] : '');
3649 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3650
3651 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3652
3653 if ($to_gmt) {
3654 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3655 } else {
3656 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3657 }
3658 $dtts = new DateTime();
3659 $dtts->setTimestamp($time);
3660 $dtts->setTimezone($tzo);
3661 $newformat = str_replace(
3662 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3663 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3664 $format
3665 );
3666 $ret = $dtts->format($newformat);
3667 $ret = str_replace(
3668 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3669 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3670 $ret
3671 );
3672 } else {
3673 // Date is a timestamps
3674 if ($time < 100000000000) { // Protection against bad date values
3675 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3676
3677 if ($to_gmt) {
3678 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3679 } else {
3680 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3681 }
3682 $dtts = new DateTime();
3683 $dtts->setTimestamp($timetouse);
3684 $dtts->setTimezone($tzo);
3685 $newformat = str_replace(
3686 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3687 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3688 $format
3689 );
3690 $ret = $dtts->format($newformat);
3691 $ret = str_replace(
3692 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3693 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3694 $ret
3695 );
3696 //var_dump($ret);exit;
3697 } else {
3698 $ret = 'Bad value '.$time.' for date';
3699 }
3700 }
3701
3702 if (preg_match('/__b__/i', $format)) {
3703 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3704
3705 if ($to_gmt) {
3706 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3707 } else {
3708 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3709 }
3710 $dtts = new DateTime();
3711 $dtts->setTimestamp($timetouse);
3712 $dtts->setTimezone($tzo);
3713 $month = (int) $dtts->format("m");
3714 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3715 if ($encodetooutput) {
3716 $monthtext = $outputlangs->transnoentities('Month'.$month);
3717 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3718 } else {
3719 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3720 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3721 }
3722 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3723 $ret = str_replace('__b__', $monthtextshort, $ret);
3724 $ret = str_replace('__B__', $monthtext, $ret);
3725 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3726 //return $ret;
3727 }
3728 if (preg_match('/__a__/i', $format)) {
3729 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3730 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3731
3732 if ($to_gmt) {
3733 $tzo = new DateTimeZone('UTC');
3734 } else {
3735 $tzo = new DateTimeZone(date_default_timezone_get());
3736 }
3737 $dtts = new DateTime();
3738 $dtts->setTimestamp($timetouse);
3739 $dtts->setTimezone($tzo);
3740 $w = $dtts->format("w");
3741 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3742
3743 $ret = str_replace('__A__', $dayweek, $ret);
3744 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3745 }
3746
3747 return $ret;
3748}
3749
3750
3771function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3772{
3773 if ($timestamp === '') {
3774 return array();
3775 }
3776
3777 $datetimeobj = new DateTime();
3778 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3779 if ($forcetimezone) {
3780 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3781 }
3782 $arrayinfo = array(
3783 'year' => ((int) date_format($datetimeobj, 'Y')),
3784 'mon' => ((int) date_format($datetimeobj, 'm')),
3785 'mday' => ((int) date_format($datetimeobj, 'd')),
3786 'wday' => ((int) date_format($datetimeobj, 'w')),
3787 'yday' => ((int) date_format($datetimeobj, 'z')),
3788 'hours' => ((int) date_format($datetimeobj, 'H')),
3789 'minutes' => ((int) date_format($datetimeobj, 'i')),
3790 'seconds' => ((int) date_format($datetimeobj, 's')),
3791 '0' => $timestamp
3792 );
3793
3794 return $arrayinfo;
3795}
3796
3818function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3819{
3820 global $conf;
3821 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3822
3823 if ($gm === 'auto') {
3824 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3825 }
3826 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3827
3828 // Clean parameters
3829 if ($hour == -1 || empty($hour)) {
3830 $hour = 0;
3831 }
3832 if ($minute == -1 || empty($minute)) {
3833 $minute = 0;
3834 }
3835 if ($second == -1 || empty($second)) {
3836 $second = 0;
3837 }
3838
3839 // Check parameters
3840 if ($check) {
3841 if (!$month || !$day) {
3842 return '';
3843 }
3844 if ($day > 31) {
3845 return '';
3846 }
3847 if ($month > 12) {
3848 return '';
3849 }
3850 if ($hour < 0 || $hour > 24) {
3851 return '';
3852 }
3853 if ($minute < 0 || $minute > 60) {
3854 return '';
3855 }
3856 if ($second < 0 || $second > 60) {
3857 return '';
3858 }
3859 }
3860
3861 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3862 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3863 $localtz = new DateTimeZone($default_timezone);
3864 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3865 // We use dol_tz_string first because it is more reliable.
3866 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3867 try {
3868 $localtz = new DateTimeZone($default_timezone);
3869 } catch (Exception $e) {
3870 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3871 $default_timezone = @date_default_timezone_get();
3872 }
3873 } elseif (strrpos($gm, "tz,") !== false) {
3874 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3875 try {
3876 $localtz = new DateTimeZone($timezone);
3877 } catch (Exception $e) {
3878 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3879 }
3880 }
3881
3882 if (empty($localtz)) {
3883 $localtz = new DateTimeZone('UTC');
3884 }
3885 //var_dump($localtz);
3886 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3887 $dt = new DateTime('now', $localtz);
3888 $dt->setDate((int) $year, (int) $month, (int) $day);
3889 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3890 $date = $dt->getTimestamp(); // should include daylight saving time
3891 //var_dump($date);
3892 return $date;
3893}
3894
3895
3906function dol_now($mode = 'auto')
3907{
3908 $ret = 0;
3909
3910 if ($mode === 'auto') {
3911 $mode = 'gmt';
3912 }
3913
3914 if ($mode == 'gmt') {
3915 $ret = time(); // Time for now at greenwich.
3916 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3917 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3918 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3919 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3920 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3921 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3922 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3923 // $ret=dol_now('gmt')+($tzsecond*3600);
3924 //}
3925 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3926 // Time for now with user timezone added
3927 //print 'time: '.time();
3928 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3929 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3930 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3931 }
3932
3933 return $ret;
3934}
3935
3936
3945function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3946{
3947 global $conf, $langs;
3948 $level = 1024;
3949
3950 if (!empty($conf->dol_optimize_smallscreen)) {
3951 $shortunit = 1;
3952 }
3953
3954 // Set value text
3955 if (empty($shortvalue) || $size < ($level * 10)) {
3956 $ret = $size;
3957 $textunitshort = $langs->trans("b");
3958 $textunitlong = $langs->trans("Bytes");
3959 } else {
3960 $ret = round($size / $level, 0);
3961 $textunitshort = $langs->trans("Kb");
3962 $textunitlong = $langs->trans("KiloBytes");
3963 }
3964 // Use long or short text unit
3965 if (empty($shortunit)) {
3966 $ret .= ' '.$textunitlong;
3967 } else {
3968 $ret .= ' '.$textunitshort;
3969 }
3970
3971 return $ret;
3972}
3973
3984function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3985{
3986 global $langs;
3987
3988 if (empty($url)) {
3989 return '';
3990 }
3991
3992 $linkstart = '<a href="';
3993 if (!preg_match('/^http/i', $url)) {
3994 $linkstart .= 'http://';
3995 }
3996 $linkstart .= $url;
3997 $linkstart .= '"';
3998 if ($target) {
3999 $linkstart .= ' target="'.$target.'"';
4000 }
4001 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
4002 $linkstart .= '>';
4003
4004 $link = '';
4005 if (!preg_match('/^http/i', $url)) {
4006 $link .= 'http://';
4007 }
4008 $link .= dol_trunc($url, $max);
4009
4010 $linkend = '</a>';
4011
4012 if ($morecss == 'float') { // deprecated
4013 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
4014 } else {
4015 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
4016 }
4017}
4018
4032function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
4033{
4034 global $user, $langs, $hookmanager;
4035
4036 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4037 //$showinvalid = 1; $email = 'rrrrr';
4038
4039 $newemail = dol_escape_htmltag($email);
4040
4041 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4042 $withpicto = 0;
4043 }
4044
4045 if (empty($email)) {
4046 return '&nbsp;';
4047 }
4048
4049 if ($addlink == 1) {
4050 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
4051 if (!preg_match('/^mailto:/i', $email)) {
4052 $newemail .= 'mailto:';
4053 }
4054 $newemail .= $email;
4055 $newemail .= '" target="_blank">';
4056
4057 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4058
4059 if ($max > 0) {
4060 $newemail .= dol_trunc($email, $max);
4061 } else {
4062 $newemail .= $email;
4063 }
4064 $newemail .= '</a>';
4065 if ($showinvalid && !isValidEmail($email)) {
4066 $langs->load("errors");
4067 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
4068 }
4069
4070 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4071 $type = 'AC_EMAIL';
4072 $linktoaddaction = '';
4073 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4074 $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>';
4075 }
4076 if ($linktoaddaction) {
4077 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
4078 }
4079 }
4080 } elseif ($addlink === 'thirdparty') {
4081 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
4082 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4083 if ($withpicto == 1) {
4084 $tmpnewemail .= $newemail;
4085 }
4086 $tmpnewemail .= '</a>';
4087
4088 $newemail = $tmpnewemail;
4089 } else {
4090 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4091
4092 if ($showinvalid && !isValidEmail($email)) {
4093 $langs->load("errors");
4094 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
4095 }
4096 }
4097
4098 //$rep = '<div class="nospan" style="margin-right: 10px">';
4099 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4100 //$rep .= '</div>';
4101 $rep = $newemail;
4102
4103 if ($hookmanager) {
4104 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4105
4106 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4107 if ($reshook > 0) {
4108 $rep = '';
4109 }
4110 $rep .= $hookmanager->resPrint;
4111 }
4112
4113 return $rep;
4114}
4115
4121function getArrayOfSocialNetworks()
4122{
4123 global $conf, $db;
4124
4125 $socialnetworks = array();
4126 // Enable caching of array
4127 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4128 $cachekey = 'socialnetworks_' . $conf->entity;
4129 $dataretrieved = dol_getcache($cachekey);
4130 if (!is_null($dataretrieved)) {
4131 $socialnetworks = $dataretrieved;
4132 } else {
4133 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4134 $sql .= " WHERE entity=".$conf->entity;
4135 $resql = $db->query($sql);
4136 if ($resql) {
4137 while ($obj = $db->fetch_object($resql)) {
4138 $socialnetworks[$obj->code] = array(
4139 'rowid' => $obj->rowid,
4140 'label' => $obj->label,
4141 'url' => $obj->url,
4142 'icon' => $obj->icon,
4143 'active' => $obj->active,
4144 );
4145 }
4146 }
4147 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4148 }
4149 return $socialnetworks;
4150}
4151
4162function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
4163{
4164 global $hookmanager, $langs, $user;
4165
4166 $htmllink = $value;
4167
4168 if (empty($value)) {
4169 return '&nbsp;';
4170 }
4171
4172 if (!empty($type)) {
4173 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4174 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4175 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
4176 if ($type == 'skype') {
4177 $htmllink .= dol_escape_htmltag($value);
4178 $htmllink .= '&nbsp; <a href="skype:';
4179 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4180 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
4181 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
4182 $htmllink .= '</a><a href="skype:';
4183 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4184 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
4185 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
4186 $htmllink .= '</a>';
4187 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4188 $addlink = 'AC_SKYPE';
4189 $link = '';
4190 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4191 $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>';
4192 }
4193 $htmllink .= ($link ? ' '.$link : '');
4194 }
4195 } else {
4196 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
4197 if (getDolGlobalString($networkconstname)) {
4198 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
4199 if (preg_match('/^https?:\/\//i', $link)) {
4200 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4201 } elseif ($link) {
4202 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4203 }
4204 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
4205 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4206 if ($tmpvirginurl) {
4207 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4208 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4209
4210 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4211 if ($tmpvirginurl3) {
4212 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4213 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4214 }
4215
4216 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4217 if ($tmpvirginurl2) {
4218 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4219 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4220 }
4221 }
4222 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4223 if (preg_match('/^https?:\/\//i', $link)) {
4224 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4225 } else {
4226 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4227 }
4228 } else {
4229 $htmllink .= dol_escape_htmltag($value);
4230 }
4231 }
4232 $htmllink .= '</div>';
4233 } else {
4234 $langs->load("errors");
4235 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4236 }
4237
4238 if ($hookmanager) {
4239 $parameters = array(
4240 'value' => $value,
4241 'cid' => $cid,
4242 'socid' => $socid,
4243 'type' => $type,
4244 'dictsocialnetworks' => $dictsocialnetworks,
4245 );
4246
4247 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4248 if ($reshook > 0) {
4249 $htmllink = '';
4250 }
4251 $htmllink .= $hookmanager->resPrint;
4252 }
4253
4254 return $htmllink;
4255}
4256
4266function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4267{
4268 global $mysoc;
4269
4270 if (empty($profID) || empty($profIDtype)) {
4271 return '';
4272 }
4273 if (empty($countrycode)) {
4274 $countrycode = $mysoc->country_code;
4275 }
4276 $newProfID = $profID;
4277 $id = substr($profIDtype, -1);
4278 $ret = '';
4279 if (strtoupper($countrycode) == 'FR') {
4280 // France
4281 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4282
4283 if ($id == 1 && dol_strlen($newProfID) == 9) {
4284 // SIREN (ex: 123 123 123)
4285 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4286 }
4287 if ($id == 2 && dol_strlen($newProfID) == 14) {
4288 // SIRET (ex: 123 123 123 12345)
4289 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4290 }
4291 if ($id == 3 && dol_strlen($newProfID) == 5) {
4292 // NAF/APE (ex: 69.20Z)
4293 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4294 }
4295 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4296 // TVA intracommunautaire (ex: FR12 123 123 123)
4297 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4298 }
4299 }
4300 if (!empty($addcpButton)) {
4301 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4302 } else {
4303 $ret = $newProfID;
4304 }
4305 return $ret;
4306}
4307
4323function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4324{
4325 global $conf, $user, $langs, $mysoc, $hookmanager;
4326
4327 // Clean phone parameter
4328 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4329 if (empty($phone)) {
4330 return '';
4331 }
4332 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4333 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4334 }
4335 if (empty($countrycode) && is_object($mysoc)) {
4336 $countrycode = $mysoc->country_code;
4337 }
4338
4339 // Short format for small screens
4340 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4341 $separ = '';
4342 }
4343
4344 $newphone = $phone;
4345 $newphonewa = $phone;
4346 if (strtoupper($countrycode) == "FR") {
4347 // France
4348 if (dol_strlen($phone) == 10) {
4349 $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);
4350 } elseif (dol_strlen($phone) == 7) {
4351 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4352 } elseif (dol_strlen($phone) == 9) {
4353 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4354 } elseif (dol_strlen($phone) == 11) {
4355 $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);
4356 } elseif (dol_strlen($phone) == 12) {
4357 $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);
4358 } elseif (dol_strlen($phone) == 13) {
4359 $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);
4360 }
4361 } elseif (strtoupper($countrycode) == "CA") {
4362 if (dol_strlen($phone) == 10) {
4363 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4364 }
4365 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4366 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4367 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4368 }
4369 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4370 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4371 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4372 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4373 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4374 }
4375 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4376 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4377 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4378 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4379 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4380 }
4381 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4382 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4383 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4384 }
4385 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4386 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4387 $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);
4388 }
4389 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4390 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4391 $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);
4392 }
4393 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4394 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4395 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4396 }
4397 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4398 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4399 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4400 }
4401 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4402 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4403 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4404 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4405 $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);
4406 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4407 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4408 }
4409 } elseif (strtoupper($countrycode) == "ML") {//Mali
4410 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4411 $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);
4412 }
4413 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4414 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4415 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4416 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4417 $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);
4418 }
4419 } elseif (strtoupper($countrycode) == "MU") {
4420 //Maurice
4421 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4422 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4423 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4424 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4425 }
4426 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4427 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4428 $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);
4429 }
4430 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4431 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4432 $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);
4433 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4434 $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);
4435 }
4436 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4437 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4438 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4439 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4440 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4441 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4442 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4443 }
4444 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4445 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4446 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4447 }
4448 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4449 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4450 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4451 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4452 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4453 }
4454 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4455 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4456 $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);
4457 }
4458 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4459 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4460 $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);
4461 }
4462 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4463 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4464 $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);
4465 }
4466 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4467 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4468 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4469 }
4470 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4471 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4472 $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);
4473 }
4474 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4475 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4476 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4477 }
4478 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4479 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4480 $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);
4481 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4482 $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);
4483 }
4484 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4485 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4486 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4487 }
4488 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4489 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4490 $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);
4491 }
4492 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4493 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4494 $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);
4495 }
4496 } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4497 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4498 $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);
4499 }
4500 } elseif (strtoupper($countrycode) == "IT") {//Italie
4501 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4502 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4503 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4504 $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);
4505 }
4506 } elseif (strtoupper($countrycode) == "AU") {
4507 //Australie
4508 if (dol_strlen($phone) == 12) {
4509 //ex: +61_A_BCDE_FGHI
4510 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4511 }
4512 } elseif (strtoupper($countrycode) == "LU") {
4513 // Luxembourg
4514 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4515 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4516 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4517 $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);
4518 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4519 $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);
4520 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4521 $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);
4522 }
4523 } elseif (strtoupper($countrycode) == "PE") {
4524 // Peru
4525 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4526 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4527 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4528 $newphonewa = '+51'.$newphone;
4529 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4530 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4531 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4532 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4533 $newphonewa = $newphone;
4534 $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);
4535 }
4536 } elseif (strtoupper($countrycode) == "IN") {//India
4537 if (dol_strlen($phone) == 13) {
4538 if ($withpicto == 'phone') {//ex: +91_AB_CDEF_GHIJ
4539 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 4).$separ.substr($newphone, 9, 4);
4540 } else {//ex: +91_ABCDE_FGHIJ
4541 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 5).$separ.substr($newphone, 8, 5);
4542 }
4543 }
4544 }
4545
4546 $newphoneastart = $newphoneaend = '';
4547 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4548 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
4549 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4550 $newphoneaend .= '</a>';
4551 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4552 if (empty($user->clicktodial_loaded)) {
4553 $user->fetch_clicktodial();
4554 }
4555
4556 // Define urlmask
4557 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4558 if (!empty($user->clicktodial_url)) {
4559 $urlmask = $user->clicktodial_url;
4560 }
4561
4562 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4563 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4564 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4565 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4566 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4567 // Those lines are for substitution
4568 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4569 '__PHONETO__' => urlencode($phone),
4570 '__LOGIN__' => $clicktodial_login,
4571 '__PASS__' => $clicktodial_password);
4572 $url = make_substitutions($url, $substitarray);
4573 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4574 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4575 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4576 $newphoneaend = '</a>';
4577 } else {
4578 // Old method
4579 $newphoneastart = '<a href="'.$url.'"';
4580 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4581 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4582 }
4583 $newphoneastart .= '>';
4584 $newphoneaend .= '</a>';
4585 }
4586 }
4587
4588 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4589 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4590 $type = 'AC_TEL';
4591 $addlinktoagenda = '';
4592 if ($addlink == 'AC_FAX') {
4593 $type = 'AC_FAX';
4594 }
4595 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4596 $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>';
4597 }
4598 if ($addlinktoagenda) {
4599 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4600 }
4601 }
4602 }
4603
4604 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4605 // Link to Whatsapp
4606 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4607 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4608 }
4609
4610 if (empty($titlealt)) {
4611 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4612 }
4613 $rep = '';
4614
4615 if ($hookmanager) {
4616 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4617 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4618 $rep .= $hookmanager->resPrint;
4619 }
4620 if (empty($reshook)) {
4621 $picto = '';
4622 if ($withpicto) {
4623 if ($withpicto == 'fax') {
4624 $picto = 'phoning_fax';
4625 } elseif ($withpicto == 'phone') {
4626 $picto = 'phoning';
4627 } elseif ($withpicto == 'mobile') {
4628 $picto = 'phoning_mobile';
4629 } else {
4630 $picto = '';
4631 }
4632 }
4633 if ($adddivfloat == 1) {
4634 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4635 } elseif (empty($adddivfloat)) {
4636 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4637 }
4638
4639 $rep .= $newphoneastart;
4640 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4641 if ($separ != 'hidenum') {
4642 $rep .= ($withpicto ? ' ' : '').$newphone;
4643 }
4644 $rep .= $newphoneaend;
4645
4646 if ($adddivfloat == 1) {
4647 $rep .= '</div>';
4648 } elseif (empty($adddivfloat)) {
4649 $rep .= '</span>';
4650 }
4651 }
4652
4653 return $rep;
4654}
4655
4663function dol_print_ip($ip, $mode = 0)
4664{
4665 global $langs;
4666
4667 $ret = '';
4668
4669 if (empty($mode)) {
4670 $ret .= $ip;
4671 }
4672
4673 if ($mode != 2) {
4674 $countrycode = dolGetCountryCodeFromIp($ip);
4675 if ($countrycode) { // If success, countrycode is us, fr, ...
4676 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4677 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4678 } else {
4679 $ret .= ' ('.$countrycode.')';
4680 }
4681 } else {
4682 // Nothing
4683 }
4684 }
4685
4686 return $ret;
4687}
4688
4701function getUserRemoteIP($trusted = 0)
4702{
4703 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
4704 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
4705 // Note that if apache module remoteip has been enabled, REMOTE_ADDR can contain the real client (the value from cloudFlare HTTP_CF_CONNECTING_IP for example)
4706 // if the proxy were added in the list of trusted proxy.
4707 return $ip;
4708 }
4709
4710 // Try to guess the real IP of client (but this may not be reliable)
4711 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4712 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
4713 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4714 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
4715 } else {
4716 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4717 }
4718 } else {
4719 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
4720 }
4721 } else {
4722 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
4723 }
4724 return $ip;
4725}
4726
4735function isHTTPS()
4736{
4737 $isSecure = false;
4738 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4739 $isSecure = true;
4740 } 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') {
4741 $isSecure = true;
4742 }
4743 return $isSecure;
4744}
4745
4752function dolGetCountryCodeFromIp($ip)
4753{
4754 global $conf;
4755
4756 $countrycode = '';
4757
4758 if (isModEnabled('geoipmaxmind')) {
4759 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4760 //$ip='24.24.24.24';
4761 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4762 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4763 $geoip = new DolGeoIP('country', $datafile);
4764 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4765 $countrycode = $geoip->getCountryCodeFromIP($ip);
4766 }
4767
4768 return $countrycode;
4769}
4770
4771
4778function dol_user_country()
4779{
4780 global $conf, $langs, $user;
4781
4782 //$ret=$user->xxx;
4783 $ret = '';
4784 if (isModEnabled('geoipmaxmind')) {
4785 $ip = getUserRemoteIP();
4786 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4787 //$ip='24.24.24.24';
4788 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4789 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4790 $geoip = new DolGeoIP('country', $datafile);
4791 $countrycode = $geoip->getCountryCodeFromIP($ip);
4792 $ret = $countrycode;
4793 }
4794 return $ret;
4795}
4796
4809function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4810{
4811 global $conf, $user, $langs, $hookmanager;
4812
4813 $out = '';
4814
4815 if ($address) {
4816 if ($hookmanager) {
4817 $parameters = array('element' => $element, 'id' => $id);
4818 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4819 $out .= $hookmanager->resPrint;
4820 }
4821 if (empty($reshook)) {
4822 if (empty($charfornl)) {
4823 $out .= nl2br($address);
4824 } else {
4825 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4826 }
4827
4828 // TODO Remove this block, we can add this using the hook now
4829 $showgmap = $showomap = 0;
4830 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4831 $showgmap = 1;
4832 }
4833 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4834 $showgmap = 1;
4835 }
4836 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4837 $showgmap = 1;
4838 }
4839 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4840 $showgmap = 1;
4841 }
4842 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4843 $showomap = 1;
4844 }
4845 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4846 $showomap = 1;
4847 }
4848 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4849 $showomap = 1;
4850 }
4851 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4852 $showomap = 1;
4853 }
4854 if ($showgmap) {
4855 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4856 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4857 }
4858 if ($showomap) {
4859 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4860 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4861 }
4862 }
4863 }
4864 if ($noprint) {
4865 return $out;
4866 } else {
4867 print $out;
4868 return null;
4869 }
4870}
4871
4872
4882function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4883{
4884 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4885 return true;
4886 }
4887 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4888 return true;
4889 }
4890 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4891 return true;
4892 }
4893
4894 return false;
4895}
4896
4906function isValidMXRecord($domain)
4907{
4908 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4909 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4910 return 0;
4911 }
4912 if (function_exists('getmxrr')) {
4913 $mxhosts = array();
4914 $weight = array();
4915 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4916 if (count($mxhosts) > 1) {
4917 return 1;
4918 }
4919 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4920 return 1;
4921 }
4922
4923 return 0;
4924 }
4925 }
4926
4927 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4928 return -1;
4929}
4930
4938function isValidPhone($phone)
4939{
4940 return true;
4941}
4942
4943
4953function dolGetFirstLetters($s, $nbofchar = 1)
4954{
4955 $ret = '';
4956 $tmparray = explode(' ', $s);
4957 foreach ($tmparray as $tmps) {
4958 $ret .= dol_substr($tmps, 0, $nbofchar);
4959 }
4960
4961 return $ret;
4962}
4963
4964
4972function dol_strlen($string, $stringencoding = 'UTF-8')
4973{
4974 if (is_null($string)) {
4975 return 0;
4976 }
4977
4978 if (function_exists('mb_strlen')) {
4979 return mb_strlen($string, $stringencoding);
4980 } else {
4981 return strlen($string);
4982 }
4983}
4984
4995function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4996{
4997 global $langs;
4998
4999 if (empty($stringencoding)) {
5000 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5001 }
5002
5003 $ret = '';
5004 if (empty($trunconbytes)) {
5005 if (function_exists('mb_substr')) {
5006 $ret = mb_substr($string, $start, $length, $stringencoding);
5007 } else {
5008 $ret = substr($string, $start, $length);
5009 }
5010 } else {
5011 if (function_exists('mb_strcut')) {
5012 $ret = mb_strcut($string, $start, $length, $stringencoding);
5013 } else {
5014 $ret = substr($string, $start, $length);
5015 }
5016 }
5017 return $ret;
5018}
5019
5020
5034function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5035{
5036 global $conf;
5037
5038 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5039 return $string;
5040 }
5041
5042 if (empty($stringencoding)) {
5043 $stringencoding = 'UTF-8';
5044 }
5045 // reduce for small screen
5046 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5047 $size = round($size / 3);
5048 }
5049
5050 // We go always here
5051 if ($trunc == 'right') {
5052 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5053 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5054 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5055 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
5056 } else {
5057 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5058 return $string;
5059 }
5060 } elseif ($trunc == 'middle') {
5061 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5062 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5063 $size1 = (int) round($size / 2);
5064 $size2 = (int) round($size / 2);
5065 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5066 } else {
5067 return $string;
5068 }
5069 } elseif ($trunc == 'left') {
5070 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5071 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5072 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5073 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5074 } else {
5075 return $string;
5076 }
5077 } elseif ($trunc == 'wrap') {
5078 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5079 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5080 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5081 } else {
5082 return $string;
5083 }
5084 } else {
5085 return 'BadParam3CallingDolTrunc';
5086 }
5087}
5088
5096function getPictoForType($key, $morecss = '')
5097{
5098 // Set array with type -> picto
5099 $type2picto = array(
5100 'varchar' => 'font',
5101 'text' => 'font',
5102 'html' => 'code',
5103 'int' => 'sort-numeric-down',
5104 'double' => 'sort-numeric-down',
5105 'price' => 'currency',
5106 'pricecy' => 'multicurrency',
5107 'password' => 'key',
5108 'boolean' => 'check-square',
5109 'date' => 'calendar',
5110 'datetime' => 'calendar',
5111 'duration' => 'hourglass',
5112 'phone' => 'phone',
5113 'mail' => 'email',
5114 'url' => 'url',
5115 'ip' => 'country',
5116 'select' => 'list',
5117 'sellist' => 'list',
5118 'stars' => 'fontawesome_star_fas',
5119 'radio' => 'check-circle',
5120 'checkbox' => 'list',
5121 'chkbxlst' => 'list',
5122 'link' => 'link',
5123 'icon' => "question",
5124 'point' => "country",
5125 'multipts' => 'country',
5126 'linestrg' => "country",
5127 'polygon' => "country",
5128 'separate' => 'minus'
5129 );
5130
5131 if (!empty($type2picto[$key])) {
5132 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5133 }
5134
5135 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5136}
5137
5138
5161function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5162{
5163 global $conf;
5164
5165 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5166 $url = DOL_URL_ROOT;
5167 $theme = isset($conf->theme) ? $conf->theme : null;
5168 $path = 'theme/'.$theme;
5169 if (empty($picto)) {
5170 $picto = 'generic';
5171 }
5172
5173 // Define fullpathpicto to use into src
5174 if ($pictoisfullpath) {
5175 // Clean parameters
5176 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5177 $picto .= '.png';
5178 }
5179 $fullpathpicto = $picto;
5180 $reg = array();
5181 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5182 $morecss .= ($morecss ? ' ' : '').$reg[1];
5183 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5184 }
5185 } else {
5186 // $picto can not be null since replaced with 'generic' in that case
5187 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5188 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5189 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5190 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5191
5192 // Fix some values of $pictowithouttext
5193 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5194 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5195 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5196 }
5197
5198 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5199 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5200 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5201 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5202
5203 // Compatibility with old fontawesome versions
5204 if ($pictowithouttext == 'file-o') {
5205 $pictowithouttext = 'file';
5206 }
5207
5208 $pictowithouttextarray = explode('_', $pictowithouttext);
5209 $marginleftonlyshort = 0;
5210
5211 if (!empty($pictowithouttextarray[1])) {
5212 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5213 $fakey = 'fa-'.$pictowithouttextarray[0];
5214 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5215 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5216 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5217 } else {
5218 $fakey = 'fa-'.$pictowithouttext;
5219 $faprefix = 'fas';
5220 $facolor = '';
5221 $fasize = '';
5222 }
5223
5224 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5225 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5226 $morestyle = '';
5227 $reg = array();
5228 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5229 $morecss .= ($morecss ? ' ' : '').$reg[1];
5230 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5231 }
5232 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5233 $morestyle = $reg[1];
5234 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5235 }
5236 $moreatt = trim($moreatt);
5237
5238 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5239 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5240 $enabledisablehtml .= '</span>';
5241
5242 return $enabledisablehtml;
5243 }
5244
5245 if (empty($srconly) && in_array($pictowithouttext, array(
5246 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5247 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5248 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5249 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5250 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5251 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5252 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
5253 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5254 'commercial', 'companies',
5255 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5256 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5257 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5258 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5259 'hands-helping', 'help', 'holiday',
5260 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5261 'key', 'knowledgemanagement',
5262 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5263 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5264 'off', 'on', 'order',
5265 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5266 'stock', 'resize', 'service', 'stats',
5267 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_grey', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
5268 'discord', 'facebook', 'flickr', 'instagram','linkedin', 'github', 'google', 'jabber', 'meetup', 'microsoft', 'skype', 'slack', 'twitter', 'pinterest', 'reddit', 'snapchat', 'tumblr', 'youtube', 'viadeo', 'google-plus-g', 'whatsapp',
5269 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5270 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5271 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5272 'technic', 'ticket',
5273 'error', 'warning',
5274 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
5275 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5276 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5277 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5278 'conferenceorbooth', 'eventorganization',
5279 'stamp', 'signature',
5280 'webportal'
5281 ))) {
5282 $fakey = $pictowithouttext;
5283 $facolor = '';
5284 $fasize = '';
5285 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5286 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'))) {
5287 $fa = 'far';
5288 }
5289 if (in_array($pictowithouttext, array('black-tie', 'discord', 'facebook', 'flickr', 'github', 'google', 'google-plus-g', 'instagram', 'linkedin', 'meetup', 'microsoft', 'pinterest', 'skype', 'slack', 'twitter', 'reddit', 'snapchat', 'stripe', 'stripe-s', 'tumblr', 'viadeo', 'whatsapp', 'youtube'))) {
5290 $fa = 'fab';
5291 }
5292
5293 $arrayconvpictotofa = array(
5294 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5295 'asset' => 'money-check-alt', 'autofill' => 'fill',
5296 'back' => 'arrow-left', 'bank_account' => 'university',
5297 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5298 'bookcal' => 'calendar-check',
5299 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5300 'bom' => 'shapes',
5301 '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',
5302 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5303 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5304 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5305 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5306 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5307 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5308 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5309 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5310 'generic' => 'file', 'holiday' => 'umbrella-beach',
5311 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5312 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5313 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5314 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5315 'sign-out' => 'sign-out-alt',
5316 'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_grey' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
5317 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5318 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5319 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5320 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5321 'other' => 'square',
5322 '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',
5323 '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',
5324 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5325 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5326 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5327 'service' => 'concierge-bell',
5328 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5329 'status' => 'stop-circle',
5330 'stripe' => 'stripe-s', 'supplier' => 'building',
5331 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5332 'title_agenda' => 'calendar-alt',
5333 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5334 'jabber' => 'comment-o',
5335 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5336 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5337 'webportal' => 'door-open'
5338 );
5339 if ($conf->currency == 'EUR') {
5340 $arrayconvpictotofa['currency'] = 'euro-sign';
5341 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5342 } else {
5343 $arrayconvpictotofa['currency'] = 'dollar-sign';
5344 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5345 }
5346 if ($pictowithouttext == 'off') {
5347 $fakey = 'fa-square';
5348 $fasize = '1.3em';
5349 } elseif ($pictowithouttext == 'on') {
5350 $fakey = 'fa-check-square';
5351 $fasize = '1.3em';
5352 } elseif ($pictowithouttext == 'listlight') {
5353 $fakey = 'fa-download';
5354 $marginleftonlyshort = 1;
5355 } elseif ($pictowithouttext == 'printer') {
5356 $fakey = 'fa-print';
5357 $fasize = '1.2em';
5358 } elseif ($pictowithouttext == 'note') {
5359 $fakey = 'fa-sticky-note';
5360 $marginleftonlyshort = 1;
5361 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5362 $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');
5363 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5364 if (preg_match('/selected/', $pictowithouttext)) {
5365 $facolor = '#888';
5366 }
5367 $marginleftonlyshort = 1;
5368 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5369 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5370 } else {
5371 $fakey = 'fa-'.$pictowithouttext;
5372 }
5373
5374 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5375 $morecss .= ' em092';
5376 }
5377 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5378 $morecss .= ' em088';
5379 }
5380 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5381 $morecss .= ' em080';
5382 }
5383
5384 // Define $marginleftonlyshort
5385 $arrayconvpictotomarginleftonly = array(
5386 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5387 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5388 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5389 );
5390 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5391 $marginleftonlyshort = 0;
5392 }
5393
5394 // Add CSS
5395 $arrayconvpictotomorcess = array(
5396 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5397 'bank_account' => 'infobox-bank_account',
5398 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5399 'bookcal' => 'infobox-action',
5400 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5401 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5402 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5403 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5404 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5405 'incoterm' => 'infobox-supplier_proposal',
5406 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5407 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5408 'order' => 'infobox-commande',
5409 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5410 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5411 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5412 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5413 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5414 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5415 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5416 'resource' => 'infobox-action',
5417 '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',
5418 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5419 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5420 'vat' => 'infobox-bank_account',
5421 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5422 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5423 );
5424 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5425 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5426 }
5427
5428 // Define $color
5429 $arrayconvpictotocolor = array(
5430 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5431 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5432 'dynamicprice' => '#a69944',
5433 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5434 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5435 'lock' => '#ddd', 'lot' => '#a69944',
5436 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5437 'other' => '#ddd', 'world' => '#986c6a',
5438 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5439 //'shipment'=>'#a69944',
5440 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5441 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5442 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5443 'website' => '#304', 'workstation' => '#a69944'
5444 );
5445 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5446 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5447 }
5448
5449 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5450 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5451 $morestyle = '';
5452 $reg = array();
5453 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5454 $morecss .= ($morecss ? ' ' : '').$reg[1];
5455 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5456 }
5457 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5458 $morestyle = $reg[1];
5459 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5460 }
5461 $moreatt = trim($moreatt);
5462
5463 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5464 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5465 $enabledisablehtml .= '</span>';
5466
5467 return $enabledisablehtml;
5468 }
5469
5470 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5471 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5472 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5473 $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
5474 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5475 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5476 }
5477
5478 // If we ask an image into $url/$mymodule/img (instead of default path)
5479 $regs = array();
5480 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5481 $picto = $regs[1];
5482 $path = $regs[2]; // $path is $mymodule
5483 }
5484
5485 // Clean parameters
5486 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5487 $picto .= '.png';
5488 }
5489 // If alt path are defined, define url where img file is, according to physical path
5490 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5491 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5492 if ($type == 'main') {
5493 continue;
5494 }
5495 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5496 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5497 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5498 break;
5499 }
5500 }
5501
5502 // $url is '' or '/custom', $path is current theme or
5503 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5504 }
5505
5506 if ($srconly) {
5507 return $fullpathpicto;
5508 }
5509
5510 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5511 return '<img src="'.$fullpathpicto.'"'.($notitle ? '' : ' alt="'.dolPrintHTMLForAttribute($alt, 0, $allowothertags).'"').(($notitle || empty($titlealt)) ? '' : ' title="'.dolPrintHTMLForAttribute($titlealt, 0, $allowothertags).'"').($moreatt ? ' '.$moreatt.($morecss ? ' class="'.$morecss.'"' : '') : ' class="inline-block'.($morecss ? ' '.$morecss : '').'"').'>'; // Alt is used for accessibility, title for popup
5512}
5513
5528function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
5529{
5530 if (strpos($picto, '^') === 0) {
5531 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5532 } else {
5533 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5534 }
5535}
5536
5548function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5549{
5550 global $conf;
5551
5552 if (is_numeric($picto)) {
5553 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5554 //$picto = $leveltopicto[$picto];
5555 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5556 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5557 $picto .= '.png';
5558 }
5559
5560 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5561
5562 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5563}
5564
5576function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5577{
5578 global $conf;
5579
5580 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5581 $picto .= '.png';
5582 }
5583
5584 if ($pictoisfullpath) {
5585 $path = $picto;
5586 } else {
5587 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5588
5589 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5590 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5591
5592 if (file_exists($themepath)) {
5593 $path = $themepath;
5594 }
5595 }
5596 }
5597
5598 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5599}
5600
5614function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5615{
5616 global $langs;
5617
5618 if (empty($titlealt) || $titlealt == 'default') {
5619 if ($numaction == '-1' || $numaction == 'ST_NO') {
5620 $numaction = -1;
5621 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5622 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5623 $numaction = 0;
5624 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5625 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5626 $numaction = 1;
5627 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5628 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5629 $numaction = 2;
5630 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5631 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5632 $numaction = 3;
5633 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5634 } else {
5635 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5636 $numaction = 0;
5637 }
5638 }
5639 if (!is_numeric($numaction)) {
5640 $numaction = 0;
5641 }
5642
5643 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5644}
5645
5653function img_pdf($titlealt = 'default', $size = 3)
5654{
5655 global $langs;
5656
5657 if ($titlealt == 'default') {
5658 $titlealt = $langs->trans('Show');
5659 }
5660
5661 return img_picto($titlealt, 'pdf'.$size.'.png');
5662}
5663
5671function img_edit_add($titlealt = 'default', $other = '')
5672{
5673 global $langs;
5674
5675 if ($titlealt == 'default') {
5676 $titlealt = $langs->trans('Add');
5677 }
5678
5679 return img_picto($titlealt, 'edit_add.png', $other);
5680}
5688function img_edit_remove($titlealt = 'default', $other = '')
5689{
5690 global $langs;
5691
5692 if ($titlealt == 'default') {
5693 $titlealt = $langs->trans('Remove');
5694 }
5695
5696 return img_picto($titlealt, 'edit_remove.png', $other);
5697}
5698
5707function img_edit($titlealt = 'default', $float = 0, $other = '')
5708{
5709 global $langs;
5710
5711 if ($titlealt == 'default') {
5712 $titlealt = $langs->trans('Modify');
5713 }
5714
5715 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5716}
5717
5726function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5727{
5728 global $langs;
5729
5730 if ($titlealt == 'default') {
5731 $titlealt = $langs->trans('View');
5732 }
5733
5734 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5735
5736 return img_picto($titlealt, 'eye', $moreatt);
5737}
5738
5747function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5748{
5749 global $langs;
5750
5751 if ($titlealt == 'default') {
5752 $titlealt = $langs->trans('Delete');
5753 }
5754
5755 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5756}
5757
5765function img_printer($titlealt = "default", $other = '')
5766{
5767 global $langs;
5768 if ($titlealt == "default") {
5769 $titlealt = $langs->trans("Print");
5770 }
5771 return img_picto($titlealt, 'printer.png', $other);
5772}
5773
5781function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5782{
5783 global $langs;
5784
5785 if ($titlealt == 'default') {
5786 $titlealt = $langs->trans('Split');
5787 }
5788
5789 return img_picto($titlealt, 'split.png', $other);
5790}
5791
5799function img_help($usehelpcursor = 1, $usealttitle = 1)
5800{
5801 global $langs;
5802
5803 if ($usealttitle) {
5804 if (is_string($usealttitle)) {
5805 $usealttitle = dol_escape_htmltag($usealttitle);
5806 } else {
5807 $usealttitle = $langs->trans('Info');
5808 }
5809 }
5810
5811 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5812}
5813
5820function img_info($titlealt = 'default')
5821{
5822 global $langs;
5823
5824 if ($titlealt == 'default') {
5825 $titlealt = $langs->trans('Informations');
5826 }
5827
5828 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5829}
5830
5839function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5840{
5841 global $langs;
5842
5843 if ($titlealt == 'default') {
5844 $titlealt = $langs->trans('Warning');
5845 }
5846
5847 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5848 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5849}
5850
5857function img_error($titlealt = 'default')
5858{
5859 global $langs;
5860
5861 if ($titlealt == 'default') {
5862 $titlealt = $langs->trans('Error');
5863 }
5864
5865 return img_picto($titlealt, 'error.png');
5866}
5867
5875function img_next($titlealt = 'default', $moreatt = '')
5876{
5877 global $langs;
5878
5879 if ($titlealt == 'default') {
5880 $titlealt = $langs->trans('Next');
5881 }
5882
5883 //return img_picto($titlealt, 'next.png', $moreatt);
5884 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5885}
5886
5894function img_previous($titlealt = 'default', $moreatt = '')
5895{
5896 global $langs;
5897
5898 if ($titlealt == 'default') {
5899 $titlealt = $langs->trans('Previous');
5900 }
5901
5902 //return img_picto($titlealt, 'previous.png', $moreatt);
5903 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5904}
5905
5914function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5915{
5916 global $langs;
5917
5918 if ($titlealt == 'default') {
5919 $titlealt = $langs->trans('Down');
5920 }
5921
5922 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5923}
5924
5933function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5934{
5935 global $langs;
5936
5937 if ($titlealt == 'default') {
5938 $titlealt = $langs->trans('Up');
5939 }
5940
5941 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5942}
5943
5952function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5953{
5954 global $langs;
5955
5956 if ($titlealt == 'default') {
5957 $titlealt = $langs->trans('Left');
5958 }
5959
5960 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5961}
5962
5971function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5972{
5973 global $langs;
5974
5975 if ($titlealt == 'default') {
5976 $titlealt = $langs->trans('Right');
5977 }
5978
5979 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5980}
5981
5989function img_allow($allow, $titlealt = 'default')
5990{
5991 global $langs;
5992
5993 if ($titlealt == 'default') {
5994 $titlealt = $langs->trans('Active');
5995 }
5996
5997 if ($allow == 1) {
5998 return img_picto($titlealt, 'tick.png');
5999 }
6000
6001 return '-';
6002}
6003
6011function img_credit_card($brand, $morecss = null)
6012{
6013 if (is_null($morecss)) {
6014 $morecss = 'fa-2x';
6015 }
6016
6017 if ($brand == 'visa' || $brand == 'Visa') {
6018 $brand = 'cc-visa';
6019 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6020 $brand = 'cc-mastercard';
6021 } elseif ($brand == 'amex' || $brand == 'American Express') {
6022 $brand = 'cc-amex';
6023 } elseif ($brand == 'discover' || $brand == 'Discover') {
6024 $brand = 'cc-discover';
6025 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6026 $brand = 'cc-jcb';
6027 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6028 $brand = 'cc-diners-club';
6029 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6030 $brand = 'credit-card';
6031 }
6032
6033 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
6034}
6035
6044function img_mime($file, $titlealt = '', $morecss = '')
6045{
6046 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6047
6048 $mimetype = dol_mimetype($file, '', 1);
6049 //$mimeimg = dol_mimetype($file, '', 2);
6050 $mimefa = dol_mimetype($file, '', 4);
6051
6052 if (empty($titlealt)) {
6053 $titlealt = 'Mime type: '.$mimetype;
6054 }
6055
6056 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6057 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
6058}
6059
6060
6068function img_search($titlealt = 'default', $other = '')
6069{
6070 global $langs;
6071
6072 if ($titlealt == 'default') {
6073 $titlealt = $langs->trans('Search');
6074 }
6075
6076 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
6077
6078 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
6079 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6080
6081 return $input;
6082}
6083
6091function img_searchclear($titlealt = 'default', $other = '')
6092{
6093 global $langs;
6094
6095 if ($titlealt == 'default') {
6096 $titlealt = $langs->trans('Search');
6097 }
6098
6099 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6100
6101 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
6102 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6103
6104 return $input;
6105}
6106
6119function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6120{
6121 global $conf, $langs;
6122
6123 if ($infoonimgalt) {
6124 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
6125 } else {
6126 if (empty($conf->use_javascript_ajax)) {
6127 $textfordropdown = '';
6128 }
6129
6130 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
6131 $fa = 'info-circle';
6132 if ($picto == 'warning') {
6133 $fa = 'exclamation-triangle';
6134 }
6135 $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> ';
6136 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6137 $result .= ($nodiv ? '' : '</div>');
6138
6139 if ($textfordropdown) {
6140 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6141 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6142 jQuery(document).ready(function() {
6143 jQuery(".'.$class.'text").click(function() {
6144 console.log("toggle text");
6145 jQuery(".'.$class.'").toggle();
6146 });
6147 });
6148 </script>';
6149
6150 $result = $tmpresult.$result;
6151 }
6152 }
6153
6154 return $result;
6155}
6156
6157
6169function dol_print_error($db = null, $error = '', $errors = null)
6170{
6171 global $conf, $langs, $user, $argv;
6172 global $dolibarr_main_prod;
6173
6174 $out = '';
6175 $syslog = '';
6176
6177 // If error occurs before the $lang object was loaded
6178 if (!$langs) {
6179 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6180 $langs = new Translate('', $conf);
6181 $langs->load("main");
6182 }
6183
6184 // Load translation files required by the error messages
6185 $langs->loadLangs(array('main', 'errors'));
6186
6187 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6188 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6189 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6190 $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";
6191 }
6192 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6193
6194 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6195 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6196 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6197 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6198 }
6199 if ($user instanceof User) {
6200 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6201 }
6202 if (function_exists("phpversion")) {
6203 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6204 }
6205 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6206 if (function_exists("php_uname")) {
6207 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6208 }
6209 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6210 $out .= "<br>\n";
6211 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
6212 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
6213 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
6214 $out .= "<br>\n";
6215 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
6216 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
6217 } else { // Mode CLI
6218 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6219 $syslog .= "pid=".dol_getmypid();
6220 }
6221
6222 if (!empty($conf->modules)) {
6223 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6224 }
6225
6226 if (is_object($db)) {
6227 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6228 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6229 $lastqueryerror = $db->lastqueryerror();
6230 if (!utf8_check($lastqueryerror)) {
6231 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6232 }
6233 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6234 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6235 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6236 $out .= "<br>\n";
6237 } else { // Mode CLI
6238 // No dol_escape_htmltag for output, we are in CLI mode
6239 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6240 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6241 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6242 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6243 }
6244 $syslog .= ", sql=".$db->lastquery();
6245 $syslog .= ", db_error=".$db->lasterror();
6246 }
6247
6248 if ($error || $errors) {
6249 // Merge all into $errors array
6250 if (is_array($error) && is_array($errors)) {
6251 $errors = array_merge($error, $errors);
6252 } elseif (is_array($error)) { // deprecated, use second parameters
6253 $errors = $error;
6254 } elseif (is_array($errors) && !empty($error)) {
6255 $errors = array_merge(array($error), $errors);
6256 } elseif (!empty($error)) {
6257 $errors = array_merge(array($error), array($errors));
6258 }
6259
6260 $langs->load("errors");
6261
6262 foreach ($errors as $msg) {
6263 if (empty($msg)) {
6264 continue;
6265 }
6266 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6267 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6268 } else { // Mode CLI
6269 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6270 }
6271 $syslog .= ", msg=".$msg;
6272 }
6273 }
6274 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6275 xdebug_print_function_stack();
6276 $out .= '<b>XDebug information:</b>'."<br>\n";
6277 $out .= 'File: '.xdebug_call_file()."<br>\n";
6278 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6279 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6280 $out .= "<br>\n";
6281 }
6282
6283 // Return a http header with error code if possible
6284 if (!headers_sent()) {
6285 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6286 top_httphead();
6287 }
6288 //http_response_code(500); // If we use 500, message is not output with some command line tools
6289 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
6290 }
6291
6292 if (empty($dolibarr_main_prod)) {
6293 print $out;
6294 } else {
6295 if (empty($langs->defaultlang)) {
6296 $langs->setDefaultLang();
6297 }
6298 $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.
6299 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6300 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";
6301 print $langs->trans("DolibarrHasDetectedError").'. ';
6302 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6303 if (!defined("MAIN_CORE_ERROR")) {
6304 define("MAIN_CORE_ERROR", 1);
6305 }
6306 }
6307
6308 dol_syslog("Error ".$syslog, LOG_ERR);
6309}
6310
6321function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6322{
6323 global $langs;
6324
6325 if (empty($email)) {
6326 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6327 }
6328
6329 $langs->load("errors");
6330 $now = dol_now();
6331
6332 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6333 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6334 if ($errormessage) {
6335 print '<br><br>'.$errormessage;
6336 }
6337 if (is_array($errormessages) && count($errormessages)) {
6338 foreach ($errormessages as $mesgtoshow) {
6339 print '<br><br>'.$mesgtoshow;
6340 }
6341 }
6342 print '</div></div>';
6343}
6344
6361function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6362{
6363 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6364}
6365
6384function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6385{
6386 global $langs, $form;
6387 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6388
6389 if ($moreattrib == 'class="right"') {
6390 $prefix .= 'right '; // For backward compatibility
6391 }
6392
6393 $sortorder = strtoupper($sortorder);
6394 $out = '';
6395 $sortimg = '';
6396
6397 $tag = 'th';
6398 if ($thead == 2) {
6399 $tag = 'div';
6400 }
6401
6402 $tmpsortfield = explode(',', $sortfield);
6403 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6404 $tmpfield = explode(',', $field);
6405 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6406
6407 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6408 $prefix = 'wrapcolumntitle '.$prefix;
6409 }
6410
6411 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6412 // If field is used as sort criteria we use a specific css class liste_titre_sel
6413 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6414 $liste_titre = 'liste_titre';
6415 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6416 $liste_titre = 'liste_titre_sel';
6417 }
6418
6419 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6420 //$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)).'"' : '');
6421 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6422 $tagstart .= '>';
6423
6424 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6425 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6426 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6427 $options = preg_replace('/&+/i', '&', $options);
6428 if (!preg_match('/^&/', $options)) {
6429 $options = '&'.$options;
6430 }
6431
6432 $sortordertouseinlink = '';
6433 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6434 if (preg_match('/^DESC/i', $sortorder)) {
6435 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6436 } else { // We reverse the var $sortordertouseinlink
6437 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6438 }
6439 } else { // We are on field that is the first current sorting criteria
6440 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6441 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6442 } else {
6443 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6444 }
6445 }
6446 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6447 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6448 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6449 $out .= '>';
6450 }
6451 if ($tooltip) {
6452 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6453 if (preg_match('/:\w+$/', $tooltip)) {
6454 $tmptooltip = explode(':', $tooltip);
6455 } else {
6456 $tmptooltip = array($tooltip);
6457 }
6458 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6459 } else {
6460 $out .= $langs->trans($name);
6461 }
6462
6463 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6464 $out .= '</a>';
6465 }
6466
6467 if (empty($thead) && $field) { // If this is a sort field
6468 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6469 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6470 $options = preg_replace('/&+/i', '&', $options);
6471 if (!preg_match('/^&/', $options)) {
6472 $options = '&'.$options;
6473 }
6474
6475 if (!$sortorder || ($field1 != $sortfield1)) {
6476 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6477 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6478 } else {
6479 if (preg_match('/^DESC/', $sortorder)) {
6480 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6481 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6482 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6483 }
6484 if (preg_match('/^ASC/', $sortorder)) {
6485 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6486 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6487 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6488 }
6489 }
6490 }
6491
6492 $tagend = '</'.$tag.'>';
6493
6494 $out = $tagstart.$sortimg.$out.$tagend;
6495
6496 return $out;
6497}
6498
6507function print_titre($title)
6508{
6509 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6510
6511 print '<div class="titre">'.$title.'</div>';
6512}
6513
6525function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6526{
6527 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6528}
6529
6543function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6544{
6545 $return = '';
6546
6547 if ($picto == 'setup') {
6548 $picto = 'generic';
6549 }
6550
6551 $return .= "\n";
6552 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6553 $return .= '<tr class="toptitle">';
6554 if ($picto) {
6555 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6556 }
6557 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6558 $return .= '<div class="titre inline-block">';
6559 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6560 $return .= '</div>';
6561 $return .= '</td>';
6562 if (dol_strlen($morehtmlcenter)) {
6563 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6564 }
6565 if (dol_strlen($morehtmlright)) {
6566 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6567 }
6568 $return .= '</tr></table>'."\n";
6569
6570 return $return;
6571}
6572
6596function print_barre_liste($title, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $selectlimitsuffix = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
6597{
6598 global $conf, $langs;
6599
6600 $savlimit = $limit;
6601 $savtotalnboflines = $totalnboflines;
6602 if (is_numeric($totalnboflines)) {
6603 $totalnboflines = abs($totalnboflines);
6604 }
6605
6606 // Detect if there is a subtitle
6607 $subtitle = '';
6608 $tmparray = preg_split('/<br>/i', $title, 2);
6609 if (!empty($tmparray[1])) {
6610 $title = $tmparray[0];
6611 $subtitle = $tmparray[1];
6612 }
6613
6614 $page = (int) $page;
6615
6616 if ($picto == 'setup') {
6617 $picto = 'title_setup.png';
6618 }
6619 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6620 $picto = 'title.gif';
6621 }
6622 if ($limit < 0) {
6623 $limit = $conf->liste_limit;
6624 }
6625
6626 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6627 $nextpage = 1;
6628 } else {
6629 $nextpage = 0;
6630 }
6631 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6632
6633 print "\n";
6634 print "<!-- Begin print_barre_liste -->\n";
6635 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6636 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6637
6638 // Left
6639
6640 if ($picto && $title) {
6641 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6642 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6643 print '</td>';
6644 }
6645
6646 print '<td class="nobordernopadding valignmiddle col-title">';
6647 print '<div class="titre inline-block">';
6648 print '<span class="inline-block valignmiddle print-barre-liste">'.$title.'</span>'; // $title may contains HTML like a combo list from page consumption.php, so we do not use dolPrintLabel here()
6649 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6650 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6651 }
6652 print '</div>';
6653 if (!empty($subtitle)) {
6654 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6655 }
6656 print '</td>';
6657
6658 // Center
6659 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6660 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6661 }
6662
6663 // Right
6664 print '<td class="nobordernopadding valignmiddle right col-right">';
6665 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6666 if ($sortfield) {
6667 $options .= "&sortfield=".urlencode($sortfield);
6668 }
6669 if ($sortorder) {
6670 $options .= "&sortorder=".urlencode($sortorder);
6671 }
6672 // Show navigation bar
6673 $pagelist = '';
6674 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6675 if ($totalnboflines) { // If we know total nb of lines
6676 // Define nb of extra page links before and after selected page + ... + first or last
6677 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6678
6679 if ($limit > 0) {
6680 $nbpages = ceil($totalnboflines / $limit);
6681 } else {
6682 $nbpages = 1;
6683 }
6684 $cpt = ($page - $maxnbofpage);
6685 if ($cpt < 0) {
6686 $cpt = 0;
6687 }
6688
6689 if ($cpt >= 1) {
6690 if (empty($pagenavastextinput)) {
6691 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6692 if ($cpt > 2) {
6693 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6694 } elseif ($cpt == 2) {
6695 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6696 }
6697 }
6698 }
6699
6700 do {
6701 if ($pagenavastextinput) {
6702 if ($cpt == $page) {
6703 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6704 $pagelist .= '/';
6705 }
6706 } else {
6707 if ($cpt == $page) {
6708 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6709 } else {
6710 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6711 }
6712 }
6713 $cpt++;
6714 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6715
6716 if (empty($pagenavastextinput)) {
6717 if ($cpt < $nbpages) {
6718 if ($cpt < $nbpages - 2) {
6719 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6720 } elseif ($cpt == $nbpages - 2) {
6721 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6722 }
6723 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6724 }
6725 } else {
6726 //var_dump($page.' '.$cpt.' '.$nbpages);
6727 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6728 }
6729 } else {
6730 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6731 }
6732 }
6733
6734 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6735 print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $selectlimitsuffix, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
6736 }
6737
6738 // js to autoselect page field on focus
6739 if ($pagenavastextinput) {
6740 print ajax_autoselect('.pageplusone');
6741 }
6742
6743 print '</td>';
6744 print '</tr>';
6745
6746 print '</table>'."\n";
6747
6748 // Center
6749 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6750 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6751 }
6752
6753 print "<!-- End title -->\n\n";
6754}
6755
6772function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6773{
6774 global $conf, $langs;
6775
6776 print '<div class="pagination"><ul>';
6777 if ($beforearrows) {
6778 print '<li class="paginationbeforearrows">';
6779 print $beforearrows;
6780 print '</li>';
6781 }
6782
6783 if (empty($hidenavigation)) {
6784 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6785 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6786 $pagesizechoices .= ',5000:5000';
6787 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6788 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6789 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6790 //$pagesizechoices .= ',2:2';
6791 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6792 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6793 }
6794
6795 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6796 print '<li class="pagination">';
6797 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.'">';
6798 print '<datalist id="limitlist">';
6799 } else {
6800 print '<li class="paginationcombolimit valignmiddle">';
6801 print '<select id="limit'.(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix).'" name="limit" class="flat selectlimit nopadding maxwidth75 center'.(is_numeric($selectlimitsuffix) ? '' : ' '.$selectlimitsuffix).'" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6802 }
6803 $tmpchoice = explode(',', $pagesizechoices);
6804 $tmpkey = $limit.':'.$limit;
6805 if (!in_array($tmpkey, $tmpchoice)) {
6806 $tmpchoice[$tmpkey] = $tmpkey;
6807 }
6808 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6809 if (!in_array($tmpkey, $tmpchoice)) {
6810 $tmpchoice[$tmpkey] = $tmpkey;
6811 }
6812 asort($tmpchoice, SORT_NUMERIC);
6813 foreach ($tmpchoice as $val) {
6814 $selected = '';
6815 $tmp = explode(':', $val);
6816 $key = $tmp[0];
6817 $val = $tmp[1];
6818 if ($key != '' && $val != '') {
6819 if ((int) $key == (int) $limit) {
6820 $selected = ' selected="selected"';
6821 }
6822 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6823 }
6824 }
6825 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6826 print '</datalist>';
6827 } else {
6828 print '</select>';
6829 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6830 //print ajax_combobox("limit");
6831 }
6832
6833 if ($conf->use_javascript_ajax) {
6834 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6835 <script>
6836 jQuery(document).ready(function () {
6837 jQuery(".selectlimit").change(function() {
6838 console.log("We change limit so we submit the form");
6839 $(this).parents(\'form:first\').submit();
6840 });
6841 });
6842 </script>
6843 ';
6844 }
6845 print '</li>';
6846 }
6847 if ($page > 0) {
6848 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>';
6849 }
6850 if ($betweenarrows) {
6851 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6852 print $betweenarrows;
6853 print '<!--</div>-->';
6854 }
6855 if ($nextpage > 0) {
6856 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>';
6857 }
6858 if ($afterarrows) {
6859 print '<li class="paginationafterarrows">';
6860 print $afterarrows;
6861 print '</li>';
6862 }
6863 }
6864 print '</ul></div>'."\n";
6865}
6866
6867
6879function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6880{
6881 $morelabel = '';
6882
6883 if (preg_match('/%/', $rate)) {
6884 $rate = str_replace('%', '', $rate);
6885 $addpercent = true;
6886 }
6887 $reg = array();
6888 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6889 $morelabel = ' ('.$reg[1].')';
6890 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6891 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6892 }
6893 if (preg_match('/\*/', $rate)) {
6894 $rate = str_replace('*', '', $rate);
6895 $info_bits |= 1;
6896 }
6897
6898 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6899 if (!preg_match('/\//', $rate)) {
6900 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6901 } else {
6902 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6903 $ret = $rate.($addpercent ? '%' : '');
6904 }
6905 if (($info_bits & 1) && $usestarfornpr >= 0) {
6906 $ret .= ' *';
6907 }
6908 $ret .= $morelabel;
6909 return $ret;
6910}
6911
6912
6928function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6929{
6930 global $langs, $conf;
6931
6932 // Clean parameters
6933 if (empty($amount)) {
6934 $amount = 0; // To have a numeric value if amount not defined or = ''
6935 }
6936 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6937 if ($rounding == -1) {
6938 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6939 }
6940 $nbdecimal = $rounding;
6941
6942 if ($outlangs === 'none') {
6943 // Use international separators
6944 $dec = '.';
6945 $thousand = '';
6946 } else {
6947 // Output separators by default (french)
6948 $dec = ',';
6949 $thousand = ' ';
6950
6951 // If $outlangs not forced, we use use language
6952 if (!($outlangs instanceof Translate)) {
6953 $outlangs = $langs;
6954 }
6955
6956 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6957 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6958 }
6959 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6960 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6961 }
6962 if ($thousand == 'None') {
6963 $thousand = '';
6964 } elseif ($thousand == 'Space') {
6965 $thousand = ' ';
6966 }
6967 }
6968 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6969
6970 //print "amount=".$amount."-";
6971 $amount = str_replace(',', '.', $amount); // should be useless
6972 //print $amount."-";
6973 $data = explode('.', $amount);
6974 $decpart = isset($data[1]) ? $data[1] : '';
6975 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6976 //print "decpart=".$decpart."<br>";
6977 $end = '';
6978
6979 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6980 if (dol_strlen($decpart) > $nbdecimal) {
6981 $nbdecimal = dol_strlen($decpart);
6982 }
6983
6984 // If nbdecimal is higher than max to show
6985 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6986 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6987 $nbdecimal = $nbdecimalmaxshown;
6988 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6989 // If output is truncated, we show ...
6990 $end = '...';
6991 }
6992 }
6993
6994 // If force rounding
6995 if ((string) $forcerounding != '-1') {
6996 if ($forcerounding === 'MU') {
6997 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
6998 } elseif ($forcerounding === 'MT') {
6999 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7000 } elseif ($forcerounding >= 0) {
7001 $nbdecimal = $forcerounding;
7002 }
7003 }
7004
7005 // Format number
7006 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7007 if ($form) {
7008 $output = preg_replace('/\s/', '&nbsp;', $output);
7009 $output = preg_replace('/\'/', '&#039;', $output);
7010 }
7011 // Add symbol of currency if requested
7012 $cursymbolbefore = $cursymbolafter = '';
7013 if ($currency_code && is_object($outlangs)) {
7014 if ($currency_code == 'auto') {
7015 $currency_code = $conf->currency;
7016 }
7017
7018 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7019 $listoflanguagesbefore = array('nl_NL');
7020 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7021 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7022 } else {
7023 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7024 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
7025 }
7026 }
7027 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
7028
7029 return $output;
7030}
7031
7056function price2num($amount, $rounding = '', $option = 0)
7057{
7058 global $langs, $conf;
7059
7060 // Clean parameters
7061 if (is_null($amount)) {
7062 $amount = '';
7063 }
7064
7065 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7066 // Numbers must be '1234.56'
7067 // Decimal delimiter for PHP and database SQL requests must be '.'
7068 $dec = ',';
7069 $thousand = ' ';
7070 if (is_null($langs)) { // $langs is not defined, we use english values.
7071 $dec = '.';
7072 $thousand = ',';
7073 } else {
7074 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7075 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7076 }
7077 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7078 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7079 }
7080 }
7081 if ($thousand == 'None') {
7082 $thousand = '';
7083 } elseif ($thousand == 'Space') {
7084 $thousand = ' ';
7085 }
7086 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7087
7088 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7089 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7090 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7091 if (!is_numeric($amount)) {
7092 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7093 }
7094
7095 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
7096 $amount = str_replace($thousand, '', $amount);
7097 }
7098
7099 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7100 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7101 // So if number was already a good number, it is converted into local Dolibarr setup.
7102 if (is_numeric($amount)) {
7103 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7104 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7105 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7106 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7107 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7108 }
7109 //print "QQ".$amount."<br>\n";
7110
7111 // Now make replace (the main goal of function)
7112 if ($thousand != ',' && $thousand != '.') {
7113 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7114 }
7115
7116 $amount = str_replace(' ', '', $amount); // To avoid spaces
7117 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7118 $amount = str_replace($dec, '.', $amount);
7119
7120 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7121 }
7122 //print ' XX'.$amount.' '.$rounding;
7123
7124 // Now, $amount is a real PHP float number. We make a rounding if required.
7125 if ($rounding) {
7126 $nbofdectoround = '';
7127 if ($rounding == 'MU') {
7128 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
7129 } elseif ($rounding == 'MT') {
7130 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
7131 } elseif ($rounding == 'MS') {
7132 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
7133 } elseif ($rounding == 'CU') {
7134 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
7135 } elseif ($rounding == 'CT') {
7136 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
7137 } elseif (is_numeric($rounding)) {
7138 $nbofdectoround = (int) $rounding;
7139 }
7140
7141 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7142 if (dol_strlen($nbofdectoround)) {
7143 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7144 } else {
7145 return 'ErrorBadParameterProvidedToFunction';
7146 }
7147 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7148
7149 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7150 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7151 if (is_numeric($amount)) {
7152 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7153 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7154 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7155 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7156 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7157 }
7158 //print "TT".$amount.'<br>';
7159
7160 // Always make replace because each math function (like round) replace
7161 // with local values and we want a number that has a SQL string format x.y
7162 if ($thousand != ',' && $thousand != '.') {
7163 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7164 }
7165
7166 $amount = str_replace(' ', '', $amount); // To avoid spaces
7167 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7168 $amount = str_replace($dec, '.', $amount);
7169
7170 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7171 }
7172
7173 return $amount;
7174}
7175
7188function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7189{
7190 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7191
7192 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7193 $dimension *= 1000000;
7194 $unit -= 6;
7195 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7196 $dimension *= 1000;
7197 $unit -= 3;
7198 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7199 $dimension /= 1000000;
7200 $unit += 6;
7201 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7202 $dimension /= 1000;
7203 $unit += 3;
7204 }
7205 // Special case when we want output unit into pound or ounce
7206 /* TODO
7207 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7208 {
7209 $dimension = // convert dimension from standard unit into ounce or pound
7210 $unit = $forceunitoutput;
7211 }
7212 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7213 {
7214 $dimension = // convert dimension from standard unit into ounce or pound
7215 $unit = $forceunitoutput;
7216 }*/
7217
7218 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7219 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7220 $ret .= ' '.measuringUnitString(0, $type, (string) $unit, $use_short_label, $outputlangs);
7221
7222 return $ret;
7223}
7224
7225
7238function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7239{
7240 global $db, $conf, $mysoc;
7241
7242 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7243 $thirdparty_seller = $mysoc;
7244 }
7245
7246 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);
7247
7248 $vatratecleaned = $vatrate;
7249 $reg = array();
7250 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7251 $vatratecleaned = trim($reg[1]);
7252 $vatratecode = $reg[2];
7253 }
7254
7255 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7256 {
7257 return 0;
7258 }*/
7259
7260 // Some test to guess with no need to make database access
7261 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7262 if ($local == 1) {
7263 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7264 return 0;
7265 }
7266 if ($thirdparty_seller->id == $mysoc->id) {
7267 if (!$thirdparty_buyer->localtax1_assuj) {
7268 return 0;
7269 }
7270 } else {
7271 if (!$thirdparty_seller->localtax1_assuj) {
7272 return 0;
7273 }
7274 }
7275 }
7276
7277 if ($local == 2) {
7278 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7279 if (!$mysoc->localtax2_assuj) {
7280 return 0; // If main vat is 0, IRPF may be different than 0.
7281 }
7282 if ($thirdparty_seller->id == $mysoc->id) {
7283 if (!$thirdparty_buyer->localtax2_assuj) {
7284 return 0;
7285 }
7286 } else {
7287 if (!$thirdparty_seller->localtax2_assuj) {
7288 return 0;
7289 }
7290 }
7291 }
7292 } else {
7293 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7294 return 0;
7295 }
7296 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7297 return 0;
7298 }
7299 }
7300
7301 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7302 if (in_array($mysoc->country_code, array('ES'))) {
7303 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7304 }
7305
7306 // Search local taxes
7307 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7308 if ($local == 1) {
7309 if ($thirdparty_seller != $mysoc) {
7310 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7311 return $thirdparty_seller->localtax1_value;
7312 }
7313 } else { // i am the seller
7314 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7315 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7316 }
7317 }
7318 }
7319 if ($local == 2) {
7320 if ($thirdparty_seller != $mysoc) {
7321 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7322 // TODO We should also return value defined on thirdparty only if defined
7323 return $thirdparty_seller->localtax2_value;
7324 }
7325 } else { // i am the seller
7326 if (in_array($mysoc->country_code, array('ES'))) {
7327 return $thirdparty_buyer->localtax2_value;
7328 } else {
7329 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7330 }
7331 }
7332 }
7333 }
7334
7335 // By default, search value of local tax on line of common tax
7336 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7337 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7338 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7339 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7340 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7341 if (!empty($vatratecode)) {
7342 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7343 } else {
7344 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7345 }
7346
7347 $resql = $db->query($sql);
7348
7349 if ($resql) {
7350 $obj = $db->fetch_object($resql);
7351 if ($obj) {
7352 if ($local == 1) {
7353 return $obj->localtax1;
7354 } elseif ($local == 2) {
7355 return $obj->localtax2;
7356 }
7357 }
7358 }
7359
7360 return 0;
7361}
7362
7363
7372function isOnlyOneLocalTax($local)
7373{
7374 $tax = get_localtax_by_third($local);
7375
7376 $valors = explode(":", $tax);
7377
7378 if (count($valors) > 1) {
7379 return false;
7380 } else {
7381 return true;
7382 }
7383}
7384
7391function get_localtax_by_third($local)
7392{
7393 global $db, $mysoc;
7394
7395 $sql = " SELECT t.localtax".$local." as localtax";
7396 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7397 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7398 $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";
7399 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7400 $sql .= " AND t.localtax".$local."_type <> '0'";
7401 $sql .= " ORDER BY t.rowid DESC";
7402
7403 $resql = $db->query($sql);
7404 if ($resql) {
7405 $obj = $db->fetch_object($resql);
7406 if ($obj) {
7407 return $obj->localtax;
7408 } else {
7409 return '0';
7410 }
7411 }
7412
7413 return 'Error';
7414}
7415
7416
7428function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7429{
7430 global $db;
7431
7432 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7433
7434 // Search local taxes
7435 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7436 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7437 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7438 if ($firstparamisid) {
7439 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7440 } else {
7441 $vatratecleaned = $vatrate;
7442 $vatratecode = '';
7443 $reg = array();
7444 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7445 $vatratecleaned = $reg[1];
7446 $vatratecode = $reg[2];
7447 }
7448
7449 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7450 /*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 ??
7451 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7452 $sql .= " WHERE t.fk_pays = c.rowid";
7453 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7454 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7455 } else {
7456 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7457 }
7458 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7459 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7460 if ($vatratecode) {
7461 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7462 }
7463 }
7464
7465 $resql = $db->query($sql);
7466 if ($resql) {
7467 $obj = $db->fetch_object($resql);
7468 if ($obj) {
7469 return array(
7470 'rowid' => $obj->rowid,
7471 'code' => $obj->code,
7472 'rate' => $obj->rate,
7473 'localtax1' => $obj->localtax1,
7474 'localtax1_type' => $obj->localtax1_type,
7475 'localtax2' => $obj->localtax2,
7476 'localtax2_type' => $obj->localtax2_type,
7477 'npr' => $obj->npr,
7478 'accountancy_code_sell' => $obj->accountancy_code_sell,
7479 'accountancy_code_buy' => $obj->accountancy_code_buy
7480 );
7481 } else {
7482 return array();
7483 }
7484 } else {
7485 dol_print_error($db);
7486 }
7487
7488 return array();
7489}
7490
7507function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7508{
7509 global $db, $mysoc;
7510
7511 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7512
7513 // Search local taxes
7514 $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";
7515 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7516 if ($firstparamisid) {
7517 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7518 } else {
7519 $vatratecleaned = $vatrate;
7520 $vatratecode = '';
7521 $reg = array();
7522 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7523 $vatratecleaned = $reg[1];
7524 $vatratecode = $reg[2];
7525 }
7526
7527 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7528 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7529 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7530 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7531 } else {
7532 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7533 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7534 }
7535 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7536 if ($vatratecode) {
7537 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7538 }
7539 }
7540
7541 $resql = $db->query($sql);
7542 if ($resql) {
7543 $obj = $db->fetch_object($resql);
7544
7545 if ($obj) {
7546 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7547
7548 if ($local == 1) {
7549 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7550 } elseif ($local == 2) {
7551 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7552 } else {
7553 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);
7554 }
7555 }
7556 }
7557
7558 return array();
7559}
7560
7571function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7572{
7573 global $db, $mysoc;
7574
7575 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7576
7577 $ret = 0;
7578 $found = 0;
7579
7580 if ($idprod > 0) {
7581 // Load product
7582 $product = new Product($db);
7583 $product->fetch($idprod);
7584
7585 if (($mysoc->country_code == $thirdpartytouse->country_code)
7586 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7587 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7588 ) {
7589 // If country of thirdparty to consider is ours
7590 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7591 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7592 if ($result > 0) {
7593 $ret = $product->vatrate_supplier;
7594 if ($product->default_vat_code_supplier) {
7595 $ret .= ' ('.$product->default_vat_code_supplier.')';
7596 }
7597 $found = 1;
7598 }
7599 }
7600 if (!$found) {
7601 $ret = $product->tva_tx; // Default sales vat of product
7602 if ($product->default_vat_code) {
7603 $ret .= ' ('.$product->default_vat_code.')';
7604 }
7605 $found = 1;
7606 }
7607 } else {
7608 // TODO Read default product vat according to product and an other countrycode.
7609 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7610 }
7611 }
7612
7613 if (!$found) {
7614 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7615 // 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).
7616 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7617 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7618 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7619 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7620 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7621 $sql .= $db->plimit(1);
7622
7623 $resql = $db->query($sql);
7624 if ($resql) {
7625 $obj = $db->fetch_object($resql);
7626 if ($obj) {
7627 $ret = $obj->vat_rate;
7628 if ($obj->default_vat_code) {
7629 $ret .= ' ('.$obj->default_vat_code.')';
7630 }
7631 }
7632 $db->free($resql);
7633 } else {
7634 dol_print_error($db);
7635 }
7636 } else {
7637 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7638 // '1.23'
7639 // or '1.23 (CODE)'
7640 $defaulttx = '';
7641 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7642 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7643 }
7644 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7645 $defaultcode = $reg[1];
7646 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7647 }*/
7648
7649 $ret = $defaulttx;
7650 }
7651 }
7652
7653 dol_syslog("get_product_vat_for_country: ret=".$ret);
7654 return $ret;
7655}
7656
7666function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7667{
7668 global $db, $mysoc;
7669
7670 if (!class_exists('Product')) {
7671 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7672 }
7673
7674 $ret = 0;
7675 $found = 0;
7676
7677 if ($idprod > 0) {
7678 // Load product
7679 $product = new Product($db);
7680 $result = $product->fetch($idprod);
7681
7682 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7683 /* Not defined yet, so we don't use this
7684 if ($local==1) $ret=$product->localtax1_tx;
7685 elseif ($local==2) $ret=$product->localtax2_tx;
7686 $found=1;
7687 */
7688 } else {
7689 // TODO Read default product vat according to product and another countrycode.
7690 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7691 }
7692 }
7693
7694 if (!$found) {
7695 // If vat of product for the country not found or not defined, we return higher vat of country.
7696 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7697 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7698 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7699 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7700 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7701 $sql .= $db->plimit(1);
7702
7703 $resql = $db->query($sql);
7704 if ($resql) {
7705 $obj = $db->fetch_object($resql);
7706 if ($obj) {
7707 if ($local == 1) {
7708 $ret = $obj->localtax1;
7709 } elseif ($local == 2) {
7710 $ret = $obj->localtax2;
7711 }
7712 }
7713 } else {
7714 dol_print_error($db);
7715 }
7716 }
7717
7718 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7719 return $ret;
7720}
7721
7739function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7740{
7741 global $conf, $db;
7742
7743 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7744
7745 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7746 $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;
7747
7748 $seller_country_code = $thirdparty_seller->country_code;
7749 $seller_in_cee = isInEEC($thirdparty_seller);
7750
7751 $buyer_country_code = $thirdparty_buyer->country_code;
7752 $buyer_in_cee = isInEEC($thirdparty_buyer);
7753
7754 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('SERVICE_ARE_ECOMMERCE_200238EC') ? $conf->global->SERVICE_ARE_ECOMMERCE_200238EC : ''));
7755
7756 // 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)
7757 // we use the buyer VAT.
7758 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7759 if ($seller_in_cee && $buyer_in_cee) {
7760 $isacompany = $thirdparty_buyer->isACompany();
7761 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7762 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7763 if (!isValidVATID($thirdparty_buyer)) {
7764 $isacompany = 0;
7765 }
7766 }
7767
7768 if (!$isacompany) {
7769 //print 'VATRULE 0';
7770 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7771 }
7772 }
7773 }
7774
7775 // If seller does not use VAT, default VAT is 0. End of rule.
7776 if (!$seller_use_vat) {
7777 //print 'VATRULE 1';
7778 return 0;
7779 }
7780
7781 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7782 if (!empty($thirdparty_buyer->state_id)) {
7783 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7784 $sql .= " FROM ".$db->prefix()."c_tva as t";
7785 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7786 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7787 $sql .= " AND t.active > 0";
7788 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7789 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7790
7791 $res = $db->query($sql);
7792 if ($res) {
7793 if ($db->num_rows($res)) {
7794 $obj = $db->fetch_object($res);
7795 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7796 }
7797 $db->free($res);
7798 }
7799 }
7800
7801 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7802 if (($seller_country_code == $buyer_country_code)
7803 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7804 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7805 ) { // Warning ->country_code not always defined
7806 //print 'VATRULE 3';
7807 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7808
7809 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7810 // Special case for india.
7811 //print 'VATRULE 3b';
7812 $reg = array();
7813 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7814 // we must revert the C+S into I
7815 $tmpvat = str_replace("C+S", "I", $tmpvat);
7816 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7817 // we must revert the I into C+S
7818 $tmpvat = str_replace("I", "C+S", $tmpvat);
7819 }
7820 }
7821
7822 return $tmpvat;
7823 }
7824
7825 // 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.
7826 // 'VATRULE 4' - Not supported
7827
7828 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7829 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7830 if (($seller_in_cee && $buyer_in_cee)) {
7831 $isacompany = $thirdparty_buyer->isACompany();
7832 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7833 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7834 if (!isValidVATID($thirdparty_buyer)) {
7835 $isacompany = 0;
7836 }
7837 }
7838
7839 if (!$isacompany) {
7840 //print 'VATRULE 5';
7841 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7842 } else {
7843 //print 'VATRULE 6';
7844 return 0;
7845 }
7846 }
7847
7848 // 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
7849 // I don't see any use case that need this rule.
7850 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7851 $isacompany = $thirdparty_buyer->isACompany();
7852 if (!$isacompany) {
7853 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7854 //print 'VATRULE extra';
7855 }
7856 }
7857
7858 // Otherwise the VAT proposed by default=0. End of rule.
7859 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7860 //print 'VATRULE 7';
7861 return 0;
7862}
7863
7864
7875function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7876{
7877 global $db;
7878
7879 if ($idprodfournprice > 0) {
7880 if (!class_exists('ProductFournisseur')) {
7881 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7882 }
7883 $prodprice = new ProductFournisseur($db);
7884 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7885 return $prodprice->fourn_tva_npr;
7886 } elseif ($idprod > 0) {
7887 if (!class_exists('Product')) {
7888 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7889 }
7890 $prod = new Product($db);
7891 $prod->fetch($idprod);
7892 return $prod->tva_npr;
7893 }
7894
7895 return 0;
7896}
7897
7911function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7912{
7913 global $mysoc;
7914
7915 if (!is_object($thirdparty_seller)) {
7916 return -1;
7917 }
7918 if (!is_object($thirdparty_buyer)) {
7919 return -1;
7920 }
7921
7922 if ($local == 1) { // Localtax 1
7923 if ($mysoc->country_code == 'ES') {
7924 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7925 return 0;
7926 }
7927 } else {
7928 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7929 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7930 return 0;
7931 }
7932 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7933 return 0;
7934 }
7935 }
7936 } elseif ($local == 2) { //I Localtax 2
7937 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7938 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7939 return 0;
7940 }
7941 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7942 return 0;
7943 }
7944 }
7945
7946 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7947 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7948 }
7949
7950 return 0;
7951}
7952
7961function yn($yesno, $format = 1, $color = 0)
7962{
7963 global $langs;
7964
7965 $result = 'unknown';
7966 $classname = '';
7967 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7968 $result = $langs->trans('yes');
7969 if ($format == 1 || $format == 3) {
7970 $result = $langs->trans("Yes");
7971 }
7972 if ($format == 2) {
7973 $result = '<input type="checkbox" value="1" checked disabled>';
7974 }
7975 if ($format == 3) {
7976 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7977 }
7978 if ($format == 4 || !is_numeric($format)) {
7979 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
7980 }
7981
7982 $classname = 'ok';
7983 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7984 $result = $langs->trans("no");
7985 if ($format == 1 || $format == 3) {
7986 $result = $langs->trans("No");
7987 }
7988 if ($format == 2) {
7989 $result = '<input type="checkbox" value="0" disabled>';
7990 }
7991 if ($format == 3) {
7992 $result = '<input type="checkbox" value="0" disabled> '.$result;
7993 }
7994 if ($format == 4 || !is_numeric($format)) {
7995 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
7996 }
7997
7998 if ($color == 2) {
7999 $classname = 'ok';
8000 } else {
8001 $classname = 'error';
8002 }
8003 }
8004 if ($color) {
8005 return '<span class="'.$classname.'">'.$result.'</span>';
8006 }
8007 return $result;
8008}
8009
8028function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8029{
8030 if (empty($modulepart) && is_object($object)) {
8031 if (!empty($object->module)) {
8032 $modulepart = $object->module;
8033 } elseif (!empty($object->element)) {
8034 $modulepart = $object->element;
8035 }
8036 }
8037
8038 $path = '';
8039
8040 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8041 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8042 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8043 $arrayforoldpath['product'] = 2;
8044 }
8045
8046 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8047 $level = $arrayforoldpath[$modulepart];
8048 }
8049 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8050 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8051 if (empty($num) && is_object($object)) {
8052 $num = $object->id;
8053 }
8054 if (empty($alpha)) {
8055 $num = preg_replace('/([^0-9])/i', '', $num);
8056 } else {
8057 $num = preg_replace('/^.*\-/i', '', $num);
8058 }
8059 $num = substr("000".$num, -$level);
8060 if ($level == 1) {
8061 $path = substr($num, 0, 1);
8062 }
8063 if ($level == 2) {
8064 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
8065 }
8066 if ($level == 3) {
8067 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
8068 }
8069 } else {
8070 // We will enhance here a common way of forging path for document storage.
8071 // In a future, we may distribute directories on several levels depending on setup and object.
8072 // Here, $object->id, $object->ref and $modulepart are required.
8073 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
8074 // Special case for thirdparty, where the ref is a company name that is not unique so path on disk is using the ID instead of the ref
8075 $path = dol_sanitizeFileName((string) $object->id);
8076 } else {
8077 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8078 }
8079 }
8080
8081 if (empty($withoutslash) && !empty($path)) {
8082 $path .= '/';
8083 }
8084
8085 return $path;
8086}
8087
8096function dol_mkdir($dir, $dataroot = '', $newmask = '')
8097{
8098 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
8099
8100 $dir = dol_sanitizePathName($dir, '_', 0);
8101
8102 $dir_osencoded = dol_osencode($dir);
8103 if (@is_dir($dir_osencoded)) {
8104 return 0;
8105 }
8106
8107 $nberr = 0;
8108 $nbcreated = 0;
8109
8110 $ccdir = '';
8111 if (!empty($dataroot)) {
8112 // Remove data root from loop
8113 $dir = str_replace($dataroot.'/', '', $dir);
8114 $ccdir = $dataroot.'/';
8115 }
8116
8117 $cdir = explode("/", $dir);
8118 $num = count($cdir);
8119 for ($i = 0; $i < $num; $i++) {
8120 if ($i > 0) {
8121 $ccdir .= '/'.$cdir[$i];
8122 } else {
8123 $ccdir .= $cdir[$i];
8124 }
8125 $regs = array();
8126 if (preg_match("/^.:$/", $ccdir, $regs)) {
8127 continue; // If the Windows path is incomplete, continue with next directory
8128 }
8129
8130 // Attention, is_dir() can fail event if the directory exists
8131 // (i.e. according the open_basedir configuration)
8132 if ($ccdir) {
8133 $ccdir_osencoded = dol_osencode($ccdir);
8134 if (!@is_dir($ccdir_osencoded)) {
8135 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8136
8137 umask(0);
8138 $dirmaskdec = octdec((string) $newmask);
8139 if (empty($newmask)) {
8140 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8141 }
8142 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8143 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8144 // If the is_dir has returned a false information, we arrive here
8145 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' (no permission to write into parent or directory already exists).", LOG_WARNING);
8146 $nberr++;
8147 } else {
8148 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8149 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8150 $nbcreated++;
8151 }
8152 } else {
8153 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8154 }
8155 }
8156 }
8157 return ($nberr ? -$nberr : $nbcreated);
8158}
8159
8160
8168function dolChmod($filepath, $newmask = '')
8169{
8170 global $conf;
8171
8172 if (!empty($newmask)) {
8173 @chmod($filepath, octdec($newmask));
8174 } elseif (getDolGlobalString('MAIN_UMASK')) {
8175 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
8176 }
8177}
8178
8179
8185function picto_required()
8186{
8187 return '<span class="fieldrequired">*</span>';
8188}
8189
8190
8207function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8208{
8209 if (is_null($stringtoclean)) {
8210 return '';
8211 }
8212
8213 if ($removelinefeed == 2) {
8214 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8215 }
8216 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8217
8218 // 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)
8219 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8220
8221 $temp = str_replace('< ', '__ltspace__', $temp);
8222 $temp = str_replace('<:', '__lttwopoints__', $temp);
8223
8224 if ($strip_tags) {
8225 $temp = strip_tags($temp);
8226 } else {
8227 // 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).
8228 $pattern = "/<[^<>]+>/";
8229 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8230 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8231 // pass 2 - $temp after pass 2: 0000-021
8232 $tempbis = $temp;
8233 do {
8234 $temp = $tempbis;
8235 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8236 $tempbis = preg_replace($pattern, '', $tempbis);
8237 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8238 } while ($tempbis != $temp);
8239
8240 $temp = $tempbis;
8241
8242 // 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).
8243 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8244 }
8245
8246 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8247
8248 // Remove also carriage returns
8249 if ($removelinefeed == 1) {
8250 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8251 }
8252
8253 // And double spaces
8254 if ($removedoublespaces) {
8255 while (strpos($temp, " ") !== false) {
8256 $temp = str_replace(" ", " ", $temp);
8257 }
8258 }
8259
8260 $temp = str_replace('__ltspace__', '< ', $temp);
8261 $temp = str_replace('__lttwopoints__', '<:', $temp);
8262
8263 return trim($temp);
8264}
8265
8284function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8285{
8286 if (empty($allowed_tags)) {
8287 $allowed_tags = array(
8288 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8289 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8290 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8291 );
8292 }
8293 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8294 if ($allowiframe) {
8295 if (!in_array('iframe', $allowed_tags)) {
8296 $allowed_tags[] = "iframe";
8297 }
8298 }
8299 if ($allowlink) {
8300 if (!in_array('link', $allowed_tags)) {
8301 $allowed_tags[] = "link";
8302 }
8303 }
8304 if ($allowscript) {
8305 if (!in_array('script', $allowed_tags)) {
8306 $allowed_tags[] = "script";
8307 }
8308 }
8309 if ($allowstyle) {
8310 if (!in_array('style', $allowed_tags)) {
8311 $allowed_tags[] = "style";
8312 }
8313 }
8314
8315 $allowed_tags_string = implode("><", $allowed_tags);
8316 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8317
8318 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8319
8320 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8321
8322 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8323 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8324
8325 if ($allowphp) {
8326 $allowed_tags[] = "commentphp";
8327 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8328 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8329 }
8330
8331 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8332 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8333
8334 // Remove all HTML tags
8335 $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
8336
8337 if ($cleanalsosomestyles) { // Clean for remaining html tags
8338 $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
8339 }
8340 if ($removeclassattribute) { // Clean for remaining html tags
8341 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8342 }
8343
8344 // Remove 'javascript:' that we should not find into a text
8345 // 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)).
8346 if ($cleanalsojavascript) {
8347 $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);
8348 }
8349
8350 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8351
8352 if ($allowphp) {
8353 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8354 }
8355
8356 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8357
8358
8359 return $temp;
8360}
8361
8362
8375function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8376{
8377 if (is_null($allowed_attributes)) {
8378 $allowed_attributes = array(
8379 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8380 // HTML5
8381 "header", "footer", "nav", "section", "menu", "menuitem"
8382 );
8383 }
8384 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8385 if (!in_array("content", $allowed_attributes)) {
8386 $allowed_attributes[] = "content";
8387 }
8388 if (!in_array("http-equiv", $allowed_attributes)) {
8389 $allowed_attributes[] = "http-equiv";
8390 }
8391
8392 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8393 $stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8394
8395 // Warning: loadHTML does not support HTML5 on old libxml versions.
8396 $dom = new DOMDocument('', 'UTF-8');
8397 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8398 $savwarning = error_reporting();
8399 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8400 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8401 error_reporting($savwarning);
8402
8403 if ($dom instanceof DOMDocument) {
8404 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8405 $el = $els->item($i);
8406 if (!$el instanceof DOMElement) {
8407 continue;
8408 }
8409 $attrs = $el->attributes;
8410 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8411 //var_dump($attrs->item($ii));
8412 if (!empty($attrs->item($ii)->name)) {
8413 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8414 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8415 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8416 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8417 // If attribute is 'style'
8418 $valuetoclean = $attrs->item($ii)->value;
8419
8420 if (isset($valuetoclean)) {
8421 do {
8422 $oldvaluetoclean = $valuetoclean;
8423 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8424 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8425 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8426 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8427 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8428 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8429 }
8430
8431 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8432 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8433 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8434 } while ($oldvaluetoclean != $valuetoclean);
8435 }
8436
8437 $attrs->item($ii)->value = $valuetoclean;
8438 }
8439 }
8440 }
8441 }
8442 }
8443
8444 $dom->encoding = 'UTF-8';
8445
8446 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8447 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8448
8449 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8450 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8451 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8452
8453 return trim($return);
8454 } else {
8455 return $stringtoclean;
8456 }
8457}
8458
8470function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8471{
8472 $temp = $stringtoclean;
8473 foreach ($disallowed_tags as $tagtoremove) {
8474 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8475 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8476 }
8477
8478 if ($cleanalsosomestyles) {
8479 $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
8480 }
8481
8482 return $temp;
8483}
8484
8485
8495function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8496{
8497 if ($nboflines == 1) {
8498 if (dol_textishtml($text)) {
8499 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8500 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8501 } else {
8502 if (isset($text)) {
8503 $firstline = preg_replace('/[\n\r].*/', '', $text);
8504 } else {
8505 $firstline = '';
8506 }
8507 }
8508 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8509 } else {
8510 $ishtml = 0;
8511 if (dol_textishtml($text)) {
8512 $text = preg_replace('/\n/', '', $text);
8513 $ishtml = 1;
8514 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8515 } else {
8516 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8517 }
8518
8519 $text = strtr($text, $repTable);
8520 if ($charset == 'UTF-8') {
8521 $pattern = '/(<br[^>]*>)/Uu';
8522 } else {
8523 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8524 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8525 }
8526 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8527
8528 $firstline = '';
8529 $i = 0;
8530 $countline = 0;
8531 $lastaddediscontent = 1;
8532 while ($countline < $nboflines && isset($a[$i])) {
8533 if (preg_match('/<br[^>]*>/', $a[$i])) {
8534 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8535 $firstline .= ($ishtml ? "<br>\n" : "\n");
8536 // Is it a br for a new line of after a printed line ?
8537 if (!$lastaddediscontent) {
8538 $countline++;
8539 }
8540 $lastaddediscontent = 0;
8541 }
8542 } else {
8543 $firstline .= $a[$i];
8544 $lastaddediscontent = 1;
8545 $countline++;
8546 }
8547 $i++;
8548 }
8549
8550 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8551 //unset($a);
8552 $ret = $firstline.($adddots ? '...' : '');
8553 //exit;
8554 return $ret;
8555 }
8556}
8557
8558
8570function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8571{
8572 if (is_null($stringtoencode)) {
8573 return '';
8574 }
8575
8576 if (!$nl2brmode) {
8577 return nl2br($stringtoencode, $forxml);
8578 } else {
8579 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8580 return $ret;
8581 }
8582}
8583
8593function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8594{
8595 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8596 // TODO using sandbox on inline html content is not possible yet with current browsers
8597 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8598 //$s .= $stringtoencode;
8599 //$s .= '</body></html></iframe>';
8600 return $stringtoencode;
8601 } else {
8602 $out = $stringtoencode;
8603
8604 // First clean HTML content
8605 do {
8606 $oldstringtoclean = $out;
8607
8608 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8609 try {
8610 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8611 if (LIBXML_VERSION < 20900) {
8612 // Avoid load of external entities (security problem).
8613 // Required only if LIBXML_VERSION < 20900
8614 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8615 libxml_disable_entity_loader(true);
8616 }
8617
8618 $dom = new DOMDocument();
8619 // Add a trick to solve pb with text without parent tag
8620 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8621 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8622
8623 if (dol_textishtml($out)) {
8624 $out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.$out.'</div></body></html>';
8625 } else {
8626 $out = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body><div class="tricktoremove">'.dol_nl2br($out).'</div></body></html>';
8627 }
8628
8629 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8630
8631 $dom->encoding = 'UTF-8';
8632
8633 $out = trim($dom->saveHTML());
8634
8635 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8636 $out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8637 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8638 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8639 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8640 // $out = preg_replace('/<\/div>$/', '', $out);
8641 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8642 } catch (Exception $e) {
8643 // If error, invalid HTML string with no way to clean it
8644 //print $e->getMessage();
8645 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8646 }
8647 }
8648
8649 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript'))) {
8650 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8651 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8652 try {
8653 // Try cleaning using tidy
8654 if (extension_loaded('tidy') && class_exists("tidy")) {
8655 //print "aaa".$out."\n";
8656
8657 // See options at https://tidy.sourceforge.net/docs/quickref.html
8658 $config = array(
8659 'clean' => false,
8660 // Best will be to set 'quote-marks' to false to not replace " that are used for real text content (not a string symbol for html attribute) into &quot;
8661 'quote-marks' => false,
8662 'doctype' => 'strict',
8663 'show-body-only' => true,
8664 "indent-attributes" => false,
8665 "vertical-space" => false,
8666 //'ident' => false, // Not always supported
8667 "wrap" => 0
8668 // HTML5 tags
8669 //'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',
8670 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8671 //'new-empty-tags' => 'command embed keygen source track wbr',
8672 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8673 );
8674
8675 // Tidy
8676 $tidy = new tidy();
8677 $out = $tidy->repairString($out, $config, 'utf8');
8678
8679 //print "xxx".$out;exit;
8680 }
8681 } catch (Exception $e) {
8682 // If error, invalid HTML string with no way to clean it
8683 //print $e->getMessage();
8684 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8685 }
8686 }
8687
8688 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8689 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8690
8691 // Clean some html entities that are useless so text is cleaner
8692 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8693
8694 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8695 // encoded using text entities) so we can then exclude all numeric entities.
8696 $out = preg_replace('/&#39;/i', '&apos;', $out);
8697
8698 // 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).
8699 // 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
8700 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8701 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
8702 $out = preg_replace_callback(
8703 '/&#(x?[0-9][0-9a-f]+;?)/i',
8708 static function ($m) {
8709 return realCharForNumericEntities($m);
8710 },
8711 $out
8712 );
8713 }
8714
8715 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8716 $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'.
8717
8718 // Keep only some html tags and remove also some 'javascript:' strings
8719 if ($check == 'restricthtmlallowlinkscript') {
8720 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8721 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8722 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8723 } elseif ($check == 'restricthtmlallowiframe') {
8724 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
8725 } else {
8726 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8727 }
8728
8729 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8730 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8732 }
8733
8734 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8735 $out = preg_replace('/&apos;/i', "&#39;", $out);
8736
8737 // Now remove js
8738 // List of dom events is on https://www.w3schools.com/jsref/dom_obj_event.asp and https://developer.mozilla.org/en-US/docs/Web/Events
8739 $out = preg_replace('/on(mouse|drag|key|load|touch|pointer|select|transition)[a-z]*\s*=/i', '', $out); // onmousexxx can be set on img or any html tag like <img title='...' onmouseover=alert(1)>
8740 $out = preg_replace('/on(abort|after|animation|auxclick|before|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', '', $out);
8741 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8742 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8743 $out = preg_replace('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', '', $out);
8744 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8745 // More not into the previous list
8746 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8747 } while ($oldstringtoclean != $out);
8748
8749 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8750 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8751 // 'url(' to avoid inline style like background: url(http...
8752 // '<link' to avoid <link href="http...">
8753 $reg = array();
8754 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8755 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8756 $nblinks = count($reg[0]);
8757 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8758 $out = 'ErrorTooManyLinksIntoHTMLString';
8759 }
8760
8761 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8762 if ($nblinks > 0) {
8763 $out = 'ErrorHTMLLinksNotAllowed';
8764 }
8765 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8766 $nblinks = 0;
8767 // Loop on each url in src= and url(
8768 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8769
8770 $matches = array();
8771 if (preg_match_all($pattern, $out, $matches)) {
8772 // URLs are into $matches[1]
8773 $urls = $matches[1];
8774
8775 // Affiche les URLs
8776 foreach ($urls as $url) {
8777 $nblinks++;
8778 echo "Found url = ".$url . "\n";
8779 }
8780 if ($nblinks > 0) {
8781 $out = 'ErrorHTMLExternalLinksNotAllowed';
8782 }
8783 }
8784 }
8785
8786 return $out;
8787 }
8788}
8789
8810function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8811{
8812 if (is_null($stringtoencode)) {
8813 return '';
8814 }
8815
8816 $newstring = $stringtoencode;
8817 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8818 $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.
8819 if ($removelasteolbr) {
8820 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8821 }
8822 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8823 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
8824 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8825 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
8826 } else {
8827 if ($removelasteolbr) {
8828 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8829 }
8830 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8831 }
8832 // Other substitutions that htmlentities does not do
8833 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8834 return $newstring;
8835}
8836
8844function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8845{
8846 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8847 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8848 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8849 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8850 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8851 return $ret;
8852}
8853
8860function dol_htmlcleanlastbr($stringtodecode)
8861{
8862 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8863 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8864 return $ret;
8865}
8866
8876function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8877{
8878 $newstring = $a;
8879 if ($keepsomeentities) {
8880 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8881 }
8882 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8883 if ($keepsomeentities) {
8884 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8885 }
8886 return $newstring;
8887}
8888
8900function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8901{
8902 return htmlentities($string, $flags, $encoding, $double_encode);
8903}
8904
8916function dol_string_is_good_iso($s, $clean = 0)
8917{
8918 $len = dol_strlen($s);
8919 $out = '';
8920 $ok = 1;
8921 for ($scursor = 0; $scursor < $len; $scursor++) {
8922 $ordchar = ord($s[$scursor]);
8923 //print $scursor.'-'.$ordchar.'<br>';
8924 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8925 $ok = 0;
8926 break;
8927 } elseif ($ordchar > 126 && $ordchar < 160) {
8928 $ok = 0;
8929 break;
8930 } elseif ($clean) {
8931 $out .= $s[$scursor];
8932 }
8933 }
8934 if ($clean) {
8935 return $out;
8936 }
8937 return $ok;
8938}
8939
8948function dol_nboflines($s, $maxchar = 0)
8949{
8950 if ($s == '') {
8951 return 0;
8952 }
8953 $arraystring = explode("\n", $s);
8954 $nb = count($arraystring);
8955
8956 return $nb;
8957}
8958
8959
8969function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8970{
8971 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8972 if (dol_textishtml($text)) {
8973 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8974 }
8975
8976 $text = strtr($text, $repTable);
8977 if ($charset == 'UTF-8') {
8978 $pattern = '/(<br[^>]*>)/Uu';
8979 } else {
8980 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8981 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8982 }
8983 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8984
8985 $nblines = (int) floor((count($a) + 1) / 2);
8986 // count possible auto line breaks
8987 if ($maxlinesize) {
8988 foreach ($a as $line) {
8989 if (dol_strlen($line) > $maxlinesize) {
8990 //$line_dec = html_entity_decode(strip_tags($line));
8991 $line_dec = html_entity_decode($line);
8992 if (dol_strlen($line_dec) > $maxlinesize) {
8993 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8994 $nblines += substr_count($line_dec, '\n');
8995 }
8996 }
8997 }
8998 }
8999
9000 unset($a);
9001 return $nblines;
9002}
9003
9012function dol_textishtml($msg, $option = 0)
9013{
9014 if (is_null($msg)) {
9015 return false;
9016 }
9017
9018 if ($option == 1) {
9019 if (preg_match('/<(html|link|script)/i', $msg)) {
9020 return true;
9021 } elseif (preg_match('/<body/i', $msg)) {
9022 return true;
9023 } elseif (preg_match('/<\/textarea/i', $msg)) {
9024 return true;
9025 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9026 return true;
9027 } elseif (preg_match('/<br/i', $msg)) {
9028 return true;
9029 }
9030 return false;
9031 } else {
9032 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9033 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9034 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9035 return true;
9036 } elseif (preg_match('/<\/textarea/i', $msg)) {
9037 return true;
9038 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9039 return true;
9040 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9041 return true;
9042 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9043 return true;
9044 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9045 return true;
9046 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9047 return true; // must accept <img src="http://example.com/aaa.png" />
9048 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9049 return true; // must accept <a href="http://example.com/aaa.png" />
9050 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9051 return true;
9052 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9053 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9054 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9055 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9056 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9057 }
9058
9059 return false;
9060 }
9061}
9062
9077function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9078{
9079 if (!empty($invert)) {
9080 $tmp = $text1;
9081 $text1 = $text2;
9082 $text2 = $tmp;
9083 }
9084
9085 $ret = '';
9086 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
9087 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
9088 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
9089 return $ret;
9090}
9091
9092
9093
9107function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
9108{
9109 global $db, $conf, $mysoc, $user, $extrafields;
9110
9111 $substitutionarray = array();
9112
9113 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
9114 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
9115 // this will include signature content first and then replace var found into content of signature
9116 //var_dump($onlykey);
9117 $emailsendersignature = $user->signature; // By default, we use the signature of current user. We must complete substitution with signature in c_email_senderprofile of array after calling getCommonSubstitutionArray()
9118 $usersignature = $user->signature;
9119 $substitutionarray = array_merge($substitutionarray, array(
9120 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
9121 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
9122 ));
9123
9124 if (is_object($user) && ($user instanceof User)) {
9125 $substitutionarray = array_merge($substitutionarray, array(
9126 '__USER_ID__' => (string) $user->id,
9127 '__USER_LOGIN__' => (string) $user->login,
9128 '__USER_EMAIL__' => (string) $user->email,
9129 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
9130 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
9131 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
9132 '__USER_FAX__' => (string) $user->office_fax,
9133 '__USER_LASTNAME__' => (string) $user->lastname,
9134 '__USER_FIRSTNAME__' => (string) $user->firstname,
9135 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
9136 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
9137 '__USER_JOB__' => (string) $user->job,
9138 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
9139 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
9140 ));
9141 }
9142 }
9143 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
9144 $substitutionarray = array_merge($substitutionarray, array(
9145 '__MYCOMPANY_NAME__' => $mysoc->name,
9146 '__MYCOMPANY_EMAIL__' => $mysoc->email,
9147 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
9148 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
9149 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
9150 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
9151 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
9152 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
9153 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
9154 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
9155 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
9156 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
9157 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
9158 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
9159 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
9160 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
9161 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
9162 '__MYCOMPANY_ZIP__' => $mysoc->zip,
9163 '__MYCOMPANY_TOWN__' => $mysoc->town,
9164 '__MYCOMPANY_STATE__' => $mysoc->state,
9165 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9166 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9167 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9168 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9169 ));
9170 }
9171
9172 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9173 if ($onlykey) {
9174 $substitutionarray['__ID__'] = '__ID__';
9175 $substitutionarray['__REF__'] = '__REF__';
9176 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9177 $substitutionarray['__LABEL__'] = '__LABEL__';
9178 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9179 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9180 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9181 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9182 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9183
9184 if (isModEnabled("societe")) { // Most objects are concerned
9185 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9186 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9187 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9188 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9189 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9190 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9191 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9192 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9193 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9194 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9195 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9196 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9197 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9198 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9199 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9200 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9201 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9202 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9203 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9204 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9205 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9206 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9207 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9208 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9209 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9210 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9211 }
9212 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9213 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9214 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9215 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9216 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9217 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9218 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9219 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9220 }
9221 // add substitution variables for ticket
9222 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9223 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9224 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9225 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9226 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9227 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9228 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9229 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9230 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9231 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9232 }
9233
9234 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9235 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9236 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9237 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9238 }
9239 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9240 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9241 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9242 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9243 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9244 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9245 }
9246 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9247 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9248 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9249 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9250 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9251 }
9252 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9253 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9254 }
9255 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9256 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9257 }
9258 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9259 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9260 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9261 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9262 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9263 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9264 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9265
9266 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9267 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9268 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9269 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9270 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9271
9272 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9273 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9274 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9275 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9276 }
9277 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9278 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9279 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9280 }
9281 } else {
9282 '@phan-var-force Adherent|Delivery $object';
9283 $substitutionarray['__ID__'] = $object->id;
9284 $substitutionarray['__REF__'] = $object->ref;
9285 $substitutionarray['__NEWREF__'] = $object->newref;
9286 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9287 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9288 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9289 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9290 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9291
9292 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9293 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9294 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9295
9296 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
9297 $date_delivery = null;
9298 if (property_exists($object, 'date_delivery')) {
9299 $date_delivery = $object->date_delivery;
9300 } elseif (property_exists($object, 'delivery_date')) {
9301 $date_delivery = $object->delivery_date;
9302 }
9303 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9304 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
9305 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
9306 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
9307 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
9308 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
9309 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
9310 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
9311 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
9312
9313 // For backward compatibility (deprecated)
9314 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9315 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9316
9317 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9318 $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 : '')) : '');
9319 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9320
9321 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9322 '@phan-var-force Adherent $object';
9323 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9324
9325 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9326 if (method_exists($object, 'getCivilityLabel')) {
9327 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9328 }
9329 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9330 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9331 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9332 if (method_exists($object, 'getFullName')) {
9333 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9334 }
9335 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9336 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9337 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9338 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9339 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9340 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9341 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9342 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9343 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9344 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9345 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9346 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9347 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9348 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9349 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9350 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9351
9352 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9353 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9354 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9355 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9356 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9357 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9358 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9359 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9360 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9361 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9362 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9363 }
9364
9365 if (is_object($object) && $object->element == 'societe') {
9366 '@phan-var-force Societe $object';
9367 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
9368 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
9369 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
9370 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
9371 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
9372 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
9373 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
9374 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
9375 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
9376 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
9377 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
9378 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
9379 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object) ? $object->state : '');
9380 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
9381 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
9382 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
9383 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
9384 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
9385 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
9386 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
9387 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
9388 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
9389 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
9390 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
9391 } elseif (is_object($object->thirdparty)) {
9392 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9393 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9394 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9395 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9396 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9397 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9398 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9399 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9400 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9401 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9402 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9403 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9404 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object->thirdparty) ? $object->thirdparty->state : '');
9405 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9406 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9407 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9408 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9409 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9410 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9411 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9412 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9413 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9414 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9415 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9416 }
9417
9418 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9419 '@phan-var-force RecruitmentCandidature $object';
9420 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9421 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9422 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9423 }
9424 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9425 '@phan-var-force ConferenceOrBoothAttendee $object';
9426 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9427 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9428 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9429 }
9430
9431 if (is_object($object) && $object->element == 'project') {
9432 '@phan-var-force Project $object';
9433 $substitutionarray['__PROJECT_ID__'] = $object->id;
9434 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9435 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9436 } elseif (is_object($object)) {
9437 $project = null;
9438 if (!empty($object->project)) {
9439 $project = $object->project;
9440 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9441 $project = $object->projet;
9442 }
9443 if (!is_null($project) && is_object($project)) {
9444 $substitutionarray['__PROJECT_ID__'] = $project->id;
9445 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9446 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9447 } else {
9448 // can substitute variables for project : uses lazy load in "make_substitutions" method
9449 $project_id = 0;
9450 if (!empty($object->fk_project) && $object->fk_project > 0) {
9451 $project_id = $object->fk_project;
9452 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9453 $project_id = $object->fk_project;
9454 }
9455 if ($project_id > 0) {
9456 // path:class:method:id
9457 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9458 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9459 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9460 }
9461 }
9462 }
9463
9464 if (is_object($object) && $object->element == 'facture') {
9465 '@phan-var-force Facture $object';
9466 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9467 }
9468 if (is_object($object) && $object->element == 'shipping') {
9469 '@phan-var-force Expedition $object';
9470 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9471 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9472 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9473 }
9474 if (is_object($object) && $object->element == 'reception') {
9475 '@phan-var-force Reception $object';
9476 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9477 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9478 }
9479
9480 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9481 '@phan-var-force Contrat $object';
9482 $dateplannedstart = '';
9483 $datenextexpiration = '';
9484 foreach ($object->lines as $line) {
9485 if ($line->date_start > $dateplannedstart) {
9486 $dateplannedstart = $line->date_start;
9487 }
9488 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9489 $datenextexpiration = $line->date_end;
9490 }
9491 }
9492 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9493 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9494 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9495
9496 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9497 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9498 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9499 }
9500 // add substitution variables for ticket
9501 if (is_object($object) && $object->element == 'ticket') {
9502 '@phan-var-force Ticket $object';
9503 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9504 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9505 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9506 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9507 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9508 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9509 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9510 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9511 $userstat = new User($db);
9512 if ($object->fk_user_assign > 0) {
9513 $userstat->fetch($object->fk_user_assign);
9514 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9515 }
9516
9517 if ($object->fk_user_create > 0) {
9518 $userstat->fetch($object->fk_user_create);
9519 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9520 }
9521 }
9522
9523 // Create dynamic tags for __EXTRAFIELD_FIELD__
9524 if ($object->table_element && $object->id > 0) {
9525 if (!is_object($extrafields)) {
9526 $extrafields = new ExtraFields($db);
9527 }
9528 $extrafields->fetch_name_optionals_label($object->table_element, true);
9529
9530 if ($object->fetch_optionals() > 0) {
9531 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9532 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9533 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9534 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9535 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9536 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9537 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9538 $datetime = $object->array_options['options_'.$key];
9539 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9540 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9541 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9542 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9543 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9544 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9545 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9546 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9547 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9548 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9549 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9550 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9551 }
9552 }
9553 }
9554 }
9555 }
9556
9557 // Complete substitution array with the url to make online payment
9558 if (empty($substitutionarray['__REF__'])) {
9559 $paymenturl = '';
9560 } else {
9561 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9562 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9563 $outputlangs->loadLangs(array('paypal', 'other'));
9564
9565 $amounttouse = 0;
9566 $typeforonlinepayment = 'free';
9567 if (is_object($object) && $object->element == 'commande') {
9568 $typeforonlinepayment = 'order';
9569 }
9570 if (is_object($object) && $object->element == 'facture') {
9571 $typeforonlinepayment = 'invoice';
9572 }
9573 if (is_object($object) && $object->element == 'member') {
9574 $typeforonlinepayment = 'member';
9575 if (!empty($object->last_subscription_amount)) {
9576 $amounttouse = $object->last_subscription_amount;
9577 }
9578 }
9579 if (is_object($object) && $object->element == 'contrat') {
9580 $typeforonlinepayment = 'contract';
9581 }
9582 if (is_object($object) && $object->element == 'fichinter') {
9583 $typeforonlinepayment = 'ficheinter';
9584 }
9585
9586 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9587 $paymenturl = $url;
9588 }
9589
9590 if ($object->id > 0) {
9591 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9592 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9593
9594 // Show structured communication
9595 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9596 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9597 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9598 }
9599
9600 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9601 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9602 } else {
9603 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9604 }
9605 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9606 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9607 } else {
9608 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9609 }
9610 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9611 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9612 } else {
9613 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9614 }
9615 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9616 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9617 } else {
9618 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9619 }
9620 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9621 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9622 } else {
9623 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9624 }
9625 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9626 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9627 } else {
9628 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9629 }
9630
9631 if (is_object($object) && $object->element == 'propal') {
9632 '@phan-var-force Propal $object';
9633 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9634 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9635 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9636 }
9637 if (is_object($object) && $object->element == 'commande') {
9638 '@phan-var-force Commande $object';
9639 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9640 }
9641 if (is_object($object) && $object->element == 'facture') {
9642 '@phan-var-force Facture $object';
9643 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9644 }
9645 if (is_object($object) && $object->element == 'contrat') {
9646 '@phan-var-force Contrat $object';
9647 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9648 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9649 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9650 }
9651 if (is_object($object) && $object->element == 'fichinter') {
9652 '@phan-var-force Fichinter $object';
9653 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9654 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9655 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9656 }
9657 if (is_object($object) && $object->element == 'supplier_proposal') {
9658 '@phan-var-force SupplierProposal $object';
9659 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9660 }
9661 if (is_object($object) && $object->element == 'invoice_supplier') {
9662 '@phan-var-force FactureFournisseur $object';
9663 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9664 }
9665 if (is_object($object) && $object->element == 'shipping') {
9666 '@phan-var-force Expedition $object';
9667 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9668 }
9669 }
9670
9671 if (is_object($object) && $object->element == 'action') {
9672 '@phan-var-force ActionComm $object';
9673 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9674 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9675 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9676 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9677 }
9678 }
9679 }
9680 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9681 '@phan-var-force Facture|FactureRec $object';
9682 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9683
9684 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9685 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9686 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9687 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9688
9689 $already_payed_all = 0;
9690 if (is_object($object) && ($object instanceof Facture)) {
9691 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9692 }
9693
9694 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9695 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9696 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9697
9698 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9699 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9700 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9701
9702 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9703
9704 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9705 $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)) : '';
9706 $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)) : '';
9707
9708 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9709 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9710 }
9711 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9712 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9713 }
9714
9715 // Amount keys formatted in a currency
9716 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9717 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9718 $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) : '';
9719 $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)) : '';
9720 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9721 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9722 }
9723 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9724 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9725 }
9726 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9727 if ($onlykey != 2) {
9728 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9729 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9730 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9731 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9732 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9733 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9734 }
9735 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9736 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9737 }
9738 }
9739
9740 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9741 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9742 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9743 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9744 // TODO Add other keys for foreign multicurrency
9745
9746 // For backward compatibility
9747 if ($onlykey != 2) {
9748 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9749 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9750 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9751 }
9752 }
9753
9754
9755 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9756 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9757
9758 $now = dol_now();
9759
9760 $tmp = dol_getdate($now, true);
9761 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9762 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9763 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9764 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9765
9766 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9767
9768 $substitutionarray = array_merge($substitutionarray, array(
9769 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9770 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9771 '__DAY__' => (string) $tmp['mday'],
9772 '__DAY_TEXT__' => $daytext, // Monday
9773 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9774 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9775 '__MONTH__' => (string) $tmp['mon'],
9776 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9777 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9778 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9779 '__YEAR__' => (string) $tmp['year'],
9780 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9781 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9782 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9783 '__NEXT_DAY__' => (string) $tmp4['day'],
9784 '__NEXT_MONTH__' => (string) $tmp5['month'],
9785 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9786 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9787 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9788 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9789 ));
9790 }
9791
9792 if (isModEnabled('multicompany')) {
9793 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9794 }
9795 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9796 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9797 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9798 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9799 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9800 }
9801
9802 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9803
9804 return $substitutionarray;
9805}
9806
9823function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9824{
9825 global $conf, $db, $langs;
9826
9827 if (!is_array($substitutionarray)) {
9828 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9829 }
9830
9831 if (empty($outputlangs)) {
9832 $outputlangs = $langs;
9833 }
9834
9835 // Is initial text HTML or simple text ?
9836 $msgishtml = 0;
9837 if (dol_textishtml($text, 1)) {
9838 $msgishtml = 1;
9839 }
9840
9841 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9842 if (is_object($outputlangs)) {
9843 $reg = array();
9844 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9845 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9846 $tmp = explode('|', $reg[1]);
9847 if (!empty($tmp[1])) {
9848 $outputlangs->load($tmp[1]);
9849 }
9850
9851 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9852
9853 if (empty($converttextinhtmlifnecessary)) {
9854 // convert $newval into HTML is necessary
9855 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9856 } else {
9857 if (! $msgishtml) {
9858 $valueishtml = dol_textishtml($value, 1);
9859 //var_dump("valueishtml=".$valueishtml);
9860
9861 if ($valueishtml) {
9862 $text = dol_htmlentitiesbr($text);
9863 $msgishtml = 1;
9864 }
9865 } else {
9866 $value = dol_nl2br("$value");
9867 }
9868
9869 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9870 }
9871 }
9872 }
9873
9874 // Make substitution for constant keys.
9875 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9876 $reg = array();
9877 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9878 $keyfound = $reg[1];
9879 if (isASecretKey($keyfound)) {
9880 $value = '*****forbidden*****';
9881 } else {
9882 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9883 }
9884
9885 if (empty($converttextinhtmlifnecessary)) {
9886 // convert $newval into HTML is necessary
9887 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9888 } else {
9889 if (! $msgishtml) {
9890 $valueishtml = dol_textishtml($value, 1);
9891
9892 if ($valueishtml) {
9893 $text = dol_htmlentitiesbr($text);
9894 $msgishtml = 1;
9895 }
9896 } else {
9897 $value = dol_nl2br("$value");
9898 }
9899
9900 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9901 }
9902 }
9903
9904 // Make substitution for array $substitutionarray
9905 foreach ($substitutionarray as $key => $value) {
9906 if (!isset($value)) {
9907 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9908 }
9909
9910 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9911 $value = ''; // Protection
9912 }
9913
9914 if (empty($converttextinhtmlifnecessary)) {
9915 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9916 } else {
9917 if (! $msgishtml) {
9918 $valueishtml = dol_textishtml($value, 1);
9919
9920 if ($valueishtml) {
9921 $text = dol_htmlentitiesbr($text);
9922 $msgishtml = 1;
9923 }
9924 } else {
9925 $value = dol_nl2br("$value");
9926 }
9927 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9928 }
9929 }
9930
9931 // TODO Implement the lazyload substitution
9932 /*
9933 add a loop to scan $substitutionarray:
9934 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.
9935 If no, we don't need to make replacement, so we do nothing.
9936 If yes, we can make the substitution:
9937
9938 include_once $path;
9939 $tmpobj = new $class($db);
9940 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9941 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9942 */
9943 $memory_object_list = array();
9944 foreach ($substitutionarray as $key => $value) {
9945 $lazy_load_arr = array();
9946 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9947 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9948 $key_to_substitute = $lazy_load_arr[1];
9949 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9950 $param_arr = explode(':', $value);
9951 // path:class:method:id
9952 if (count($param_arr) == 4) {
9953 $path = $param_arr[0];
9954 $class = $param_arr[1];
9955 $method = $param_arr[2];
9956 $id = (int) $param_arr[3];
9957
9958 // load class file and init object list in memory
9959 if (!isset($memory_object_list[$class])) {
9960 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9961 require_once DOL_DOCUMENT_ROOT . $path;
9962 if (class_exists($class)) {
9963 $memory_object_list[$class] = array(
9964 'list' => array(),
9965 );
9966 }
9967 }
9968 }
9969
9970 // fetch object and set substitution
9971 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9972 if (method_exists($class, $method)) {
9973 if (!isset($memory_object_list[$class]['list'][$id])) {
9974 $tmpobj = new $class($db);
9975 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9976 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9977 $memory_object_list[$class]['list'][$id] = $tmpobj;
9978 } else {
9979 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9980 $tmpobj = $memory_object_list[$class]['list'][$id];
9981 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9982 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9983 }
9984
9985 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9986 }
9987 }
9988 }
9989 }
9990 }
9991 }
9992 }
9993
9994 return $text;
9995}
9996
10009function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
10010{
10011 global $conf, $user;
10012
10013 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
10014
10015 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
10016
10017 // Check if there is external substitution to do, requested by plugins
10018 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
10019
10020 foreach ($dirsubstitutions as $reldir) {
10021 $dir = dol_buildpath($reldir, 0);
10022
10023 // Check if directory exists
10024 if (!dol_is_dir($dir)) {
10025 continue;
10026 }
10027
10028 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
10029 foreach ($substitfiles as $substitfile) {
10030 $reg = array();
10031 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
10032 $module = $reg[1];
10033
10034 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
10035 // Include the user's functions file
10036 require_once $dir.$substitfile['name'];
10037 // Call the user's function, and only if it is defined
10038 $function_name = $module."_".$callfunc;
10039 if (function_exists($function_name)) {
10040 $function_name($substitutionarray, $outputlangs, $object, $parameters);
10041 }
10042 }
10043 }
10044 }
10045 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
10046 // to list all tags in odt template
10047 $tags = '';
10048 foreach ($substitutionarray as $key => $value) {
10049 $tags .= '{'.$key.'} => '.$value."\n";
10050 }
10051 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
10052 }
10053}
10054
10064function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
10065{
10066 print get_date_range($date_start, $date_end, $format, $outputlangs);
10067}
10068
10079function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
10080{
10081 global $langs;
10082
10083 $out = '';
10084
10085 if (!is_object($outputlangs)) {
10086 $outputlangs = $langs;
10087 }
10088
10089 if ($date_start && $date_end) {
10090 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10091 }
10092 if ($date_start && !$date_end) {
10093 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10094 }
10095 if (!$date_start && $date_end) {
10096 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10097 }
10098
10099 return $out;
10100}
10101
10110function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
10111{
10112 global $conf;
10113
10114 $ret = '';
10115 // If order not defined, we use the setup
10116 if ($nameorder < 0) {
10117 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
10118 }
10119 if ($nameorder == 1) {
10120 $ret .= $firstname;
10121 if ($firstname && $lastname) {
10122 $ret .= ' ';
10123 }
10124 $ret .= $lastname;
10125 } elseif ($nameorder == 2 || $nameorder == 3) {
10126 $ret .= $firstname;
10127 if (empty($ret) && $nameorder == 3) {
10128 $ret .= $lastname;
10129 }
10130 } else { // 0, 4 or 5
10131 $ret .= $lastname;
10132 if (empty($ret) && $nameorder == 5) {
10133 $ret .= $firstname;
10134 }
10135 if ($nameorder == 0) {
10136 if ($firstname && $lastname) {
10137 $ret .= ' ';
10138 }
10139 $ret .= $firstname;
10140 }
10141 }
10142 return $ret;
10143}
10144
10145
10158function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
10159{
10160 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
10161 if (!is_array($mesgs)) {
10162 $mesgs = trim((string) $mesgs);
10163 // If mesgs is a not an empty string
10164 if ($mesgs) {
10165 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
10166 return;
10167 }
10168 if ($attop) {
10169 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10170 } else {
10171 $_SESSION['dol_events'][$style][] = $mesgs;
10172 }
10173 }
10174 } else {
10175 // If mesgs is an array
10176 foreach ($mesgs as $mesg) {
10177 $mesg = trim((string) $mesg);
10178 if ($mesg) {
10179 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10180 return;
10181 }
10182 if ($attop) {
10183 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10184 } else {
10185 $_SESSION['dol_events'][$style][] = $mesg;
10186 }
10187 }
10188 }
10189 }
10190}
10191
10205function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10206{
10207 if (empty($mesg) && empty($mesgs)) {
10208 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10209 } else {
10210 if ($messagekey) {
10211 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10212 // TODO
10213 $mesg .= '';
10214 }
10215 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10216 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10217 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10218 }
10219 if (empty($mesgs)) {
10220 setEventMessage($mesg, $style, $noduplicate, $attop);
10221 } else {
10222 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10223 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10224 }
10225 setEventMessage($mesgs, $style, $noduplicate, $attop);
10226 }
10227 }
10228 }
10229}
10230
10240function dol_htmloutput_events($disabledoutputofmessages = 0)
10241{
10242 // Show mesgs
10243 if (isset($_SESSION['dol_events']['mesgs'])) {
10244 if (empty($disabledoutputofmessages)) {
10245 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10246 }
10247 unset($_SESSION['dol_events']['mesgs']);
10248 }
10249 // Show errors
10250 if (isset($_SESSION['dol_events']['errors'])) {
10251 if (empty($disabledoutputofmessages)) {
10252 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10253 }
10254 unset($_SESSION['dol_events']['errors']);
10255 }
10256
10257 // Show warnings
10258 if (isset($_SESSION['dol_events']['warnings'])) {
10259 if (empty($disabledoutputofmessages)) {
10260 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10261 }
10262 unset($_SESSION['dol_events']['warnings']);
10263 }
10264}
10265
10280function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10281{
10282 global $conf, $langs;
10283
10284 $ret = 0;
10285 $return = '';
10286 $out = '';
10287 $divstart = $divend = '';
10288
10289 // If inline message with no format, we add it.
10290 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10291 $divstart = '<div class="'.$style.' clearboth">';
10292 $divend = '</div>';
10293 }
10294
10295 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10296 $langs->load("errors");
10297 $out .= $divstart;
10298 if (is_array($mesgarray) && count($mesgarray)) {
10299 foreach ($mesgarray as $message) {
10300 $ret++;
10301 $out .= $langs->trans($message);
10302 if ($ret < count($mesgarray)) {
10303 $out .= "<br>\n";
10304 }
10305 }
10306 }
10307 if ($mesgstring) {
10308 $ret++;
10309 $out .= $langs->trans($mesgstring);
10310 }
10311 $out .= $divend;
10312 }
10313
10314 if ($out) {
10315 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10316 $return = '<script nonce="'.getNonce().'">
10317 $(document).ready(function() {
10318 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
10319 if (block) {
10320 $.dolEventValid("","'.dol_escape_js($out).'");
10321 } else {
10322 /* jnotify(message, preset of message type, keepmessage) */
10323 $.jnotify("'.dol_escape_js($out).'",
10324 "'.($style == "ok" ? 3000 : $style).'",
10325 '.($style == "ok" ? "false" : "true").',
10326 { remove: function (){} } );
10327 }
10328 });
10329 </script>';
10330 } else {
10331 $return = $out;
10332 }
10333 }
10334
10335 return $return;
10336}
10337
10349function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10350{
10351 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10352}
10353
10367function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10368{
10369 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10370 return;
10371 }
10372
10373 $iserror = 0;
10374 $iswarning = 0;
10375 if (is_array($mesgarray)) {
10376 foreach ($mesgarray as $val) {
10377 if ($val && preg_match('/class="error"/i', $val)) {
10378 $iserror++;
10379 break;
10380 }
10381 if ($val && preg_match('/class="warning"/i', $val)) {
10382 $iswarning++;
10383 break;
10384 }
10385 }
10386 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10387 $iserror++;
10388 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10389 $iswarning++;
10390 }
10391 if ($style == 'error') {
10392 $iserror++;
10393 }
10394 if ($style == 'warning') {
10395 $iswarning++;
10396 }
10397
10398 if ($iserror || $iswarning) {
10399 // Remove div from texts
10400 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10401 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10402 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10403 // Remove div from texts array
10404 if (is_array($mesgarray)) {
10405 $newmesgarray = array();
10406 foreach ($mesgarray as $val) {
10407 if (is_string($val)) {
10408 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10409 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10410 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10411 $newmesgarray[] = $tmpmesgstring;
10412 } else {
10413 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10414 }
10415 }
10416 $mesgarray = $newmesgarray;
10417 }
10418 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10419 } else {
10420 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10421 }
10422}
10423
10435function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10436{
10437 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10438}
10439
10460function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10461{
10462 // Clean parameters
10463 $order = strtolower($order);
10464
10465 if (is_array($array)) {
10466 $sizearray = count($array);
10467 if ($sizearray > 0) {
10468 $temp = array();
10469 foreach (array_keys($array) as $key) {
10470 if (is_object($array[$key])) {
10471 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10472 } else {
10473 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10474 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10475 }
10476 if ($natsort == -1) {
10477 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10478 }
10479 }
10480
10481 if (empty($natsort) || $natsort == -1) {
10482 if ($order == 'asc') {
10483 asort($temp);
10484 } else {
10485 arsort($temp);
10486 }
10487 } else {
10488 if ($case_sensitive) {
10489 natsort($temp);
10490 } else {
10491 natcasesort($temp); // natecasesort is not sensible to case
10492 }
10493 if ($order != 'asc') {
10494 $temp = array_reverse($temp, true);
10495 }
10496 }
10497
10498 $sorted = array();
10499
10500 foreach (array_keys($temp) as $key) {
10501 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10502 }
10503
10504 return $sorted;
10505 }
10506 }
10507 return $array;
10508}
10509
10510
10518function utf8_check($str)
10519{
10520 $str = (string) $str; // Sometimes string is an int.
10521
10522 // We must use here a binary strlen function (so not dol_strlen)
10523 $strLength = strlen($str);
10524 for ($i = 0; $i < $strLength; $i++) {
10525 if (ord($str[$i]) < 0x80) {
10526 continue; // 0bbbbbbb
10527 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10528 $n = 1; // 110bbbbb
10529 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10530 $n = 2; // 1110bbbb
10531 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10532 $n = 3; // 11110bbb
10533 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10534 $n = 4; // 111110bb
10535 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10536 $n = 5; // 1111110b
10537 } else {
10538 return false; // Does not match any model
10539 }
10540 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10541 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10542 return false;
10543 }
10544 }
10545 }
10546 return true;
10547}
10548
10556function utf8_valid($str)
10557{
10558 /* 2 other methods to test if string is utf8
10559 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10560 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10561 */
10562 return preg_match('//u', $str) ? true : false;
10563}
10564
10565
10572function ascii_check($str)
10573{
10574 if (function_exists('mb_check_encoding')) {
10575 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10576 if (!mb_check_encoding($str, 'ASCII')) {
10577 return false;
10578 }
10579 } else {
10580 if (preg_match('/[^\x00-\x7f]/', $str)) {
10581 return false; // Contains a byte > 7f
10582 }
10583 }
10584
10585 return true;
10586}
10587
10588
10596function dol_osencode($str)
10597{
10598 $tmp = ini_get("unicode.filesystem_encoding");
10599 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10600 $tmp = 'iso-8859-1'; // By default for windows
10601 }
10602 if (empty($tmp)) {
10603 $tmp = 'utf-8'; // By default for other
10604 }
10605 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10606 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10607 }
10608
10609 if ($tmp == 'iso-8859-1') {
10610 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10611 }
10612 return $str;
10613}
10614
10615
10631function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10632{
10633 global $conf;
10634
10635 // If key empty
10636 if ($key == '') {
10637 return 0;
10638 }
10639
10640 // Check in cache
10641 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10642 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10643 }
10644
10645 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10646
10647 $sql = "SELECT ".$fieldid." as valuetoget";
10648 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10649 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10650 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10651 } else {
10652 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10653 }
10654 if (!empty($entityfilter)) {
10655 $sql .= " AND entity IN (".getEntity($tablename).")";
10656 }
10657 if ($filters) {
10658 $sql .= $filters;
10659 }
10660
10661 $resql = $db->query($sql);
10662 if ($resql) {
10663 $obj = $db->fetch_object($resql);
10664 $valuetoget = '';
10665 if ($obj) {
10666 $valuetoget = $obj->valuetoget;
10667 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10668 } else {
10669 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10670 }
10671 $db->free($resql);
10672
10673 return $valuetoget;
10674 } else {
10675 return -1;
10676 }
10677}
10678
10688function isStringVarMatching($var, $regextext, $matchrule = 1)
10689{
10690 if ($matchrule == 1) {
10691 if ($var == 'mainmenu') {
10692 global $mainmenu;
10693 return (preg_match('/^'.$regextext.'/', $mainmenu));
10694 } elseif ($var == 'leftmenu') {
10695 global $leftmenu;
10696 return (preg_match('/^'.$regextext.'/', $leftmenu));
10697 } else {
10698 return 'This variable is not accessible with dol_eval';
10699 }
10700 } else {
10701 return 'This value for matchrule is not implemented';
10702 }
10703}
10704
10705
10715function verifCond($strToEvaluate, $onlysimplestring = '1')
10716{
10717 //print $strToEvaluate."<br>\n";
10718 $rights = true;
10719 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10720 //var_dump($strToEvaluate);
10721 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10722 $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
10723 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10724 //var_dump($rights);
10725 }
10726 return $rights;
10727}
10728
10743function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10744{
10745 // Only this global variables can be read by eval function and returned to caller
10746 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10747 global $db, $langs, $user, $website, $websitepage;
10748 global $action, $mainmenu, $leftmenu;
10749 global $mysoc;
10750 global $objectoffield; // To allow the use of $objectoffield in computed fields
10751
10752 // Old variables used
10753 global $object;
10754 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10755
10756 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10757 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10758 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10759 }
10760
10761 try {
10762 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10763 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10764 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10765 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10766 // We must accept with 2: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) <= 99) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found"
10767
10768 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
10769 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10770 if ($onlysimplestring == '2') {
10771 $specialcharsallowed .= '<[]';
10772 }
10773 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10774 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10775 }
10776 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10777 if ($returnvalue) {
10778 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
10779 } else {
10780 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
10781 return '';
10782 }
10783 }
10784
10785 // Check if there is a < or <= without spaces before/after
10786 if (preg_match('/<=?[^\s]/', $s)) {
10787 if ($returnvalue) {
10788 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
10789 } else {
10790 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
10791 return '';
10792 }
10793 }
10794
10795 // Check if there is dynamic call (first we use black list patterns)
10796 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10797 if ($returnvalue) {
10798 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a call using of "$abc(" or "$abc (" instead of using the direct name of the function): '.$s;
10799 } else {
10800 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a call using of "$abc(" or "$abc (" instead of using the direct name of the function): '.$s, LOG_WARNING);
10801 return '';
10802 }
10803 }
10804
10805 // Now we check if we try dynamic call
10806 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
10807 $savescheck = '';
10808 $scheck = $s;
10809 while ($scheck && $savescheck != $scheck) {
10810 $savescheck = $scheck;
10811 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10812 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
10813 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
10814 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '&& (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
10815 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '|| (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
10816 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10817 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10818 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
10819 $scheck = preg_replace('/\s!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
10820 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10821 }
10822 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10823
10824 // Now test if it remains 1 one parenthesis.
10825 if (strpos($scheck, '(') !== false) {
10826 if ($returnvalue) {
10827 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10828 } else {
10829 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s, LOG_WARNING);
10830 return '';
10831 }
10832 }
10833
10834 // TODO
10835 // We can exclude $ char that are not:
10836 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10837 }
10838 if (is_array($s) || $s === 'Array') {
10839 if ($returnvalue) {
10840 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10841 } else {
10842 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10843 return '';
10844 }
10845 }
10846
10847 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
10848 if ($returnvalue) {
10849 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
10850 } else {
10851 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
10852 return '';
10853 }
10854 }
10855
10856 if (strpos($s, '`') !== false) {
10857 if ($returnvalue) {
10858 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10859 } else {
10860 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10861 return '';
10862 }
10863 }
10864
10865 // Disallow also concat
10866 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
10867 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10868 if ($returnvalue) {
10869 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10870 } else {
10871 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10872 return '';
10873 }
10874 }
10875 }
10876
10877 // We block use of php exec or php file functions
10878 $forbiddenphpstrings = array('$$', '$_', '}[');
10879 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10880
10881 // We list all forbidden function as keywords we don't want to see (we don't mind it if is "kewyord(" or just "keyword", we don't want "keyword" at all)
10882 // We must exclude all functions that allow to execute another function. This includes all function that has a parameter with type "callable" to avoid things
10883 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
10884 $forbiddenphpfunctions = array();
10885 // @phpcs:ignore
10886 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
10887
10888 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10889 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10890 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
10891
10892 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("array_all", "array_any", "array_diff_ukey", "array_filter", "array_find", "array_find_key", "array_map", "array_reduce", "array_intersect_uassoc", "array_intersect_ukey", "array_walk", "array_walk_recursive"));
10893 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
10894 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("set_error_handler", "set_exception_handler", "libxml_set_external_entity_loader", "register_shutdown_function", "register_tick_function", "unregister_tick_function"));
10895 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
10896 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
10897
10898 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
10899
10900 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10901 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10902 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10903 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace", "mb_ereg_replace_callback")); // function with eval capabilities
10904 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
10905 $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
10906 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10907 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
10908
10909 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10910
10911 $forbiddenphpregex = 'global\s*\$';
10912 $forbiddenphpregex .= '|';
10913 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
10914
10915 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10916
10917 do {
10918 $oldstringtoclean = $s;
10919 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10920 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10921 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10922 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10923 } while ($oldstringtoclean != $s);
10924
10925
10926 if (strpos($s, '__forbiddenstring__') !== false) {
10927 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10928 if ($returnvalue) {
10929 return 'Bad string syntax to evaluate: '.$s;
10930 } else {
10931 dol_syslog('Bad string syntax to evaluate: '.$s);
10932 return '';
10933 }
10934 }
10935
10936 //print $s."<br>\n";
10937 if ($returnvalue) {
10938 ob_start(); // An evaluation has no reason to output data
10939 $isObBufferActive = true;
10940 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
10941 $tmpo = ob_get_clean();
10942 $isObBufferActive = false;
10943 if ($tmpo) {
10944 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
10945 }
10946 return $tmps;
10947 } else {
10948 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10949 if ($hideerrors) {
10950 @eval($s);
10951 } else {
10952 eval($s);
10953 }
10954 return '';
10955 }
10956 } catch (Error $e) {
10957 if ($isObBufferActive) {
10958 // Clean up buffer which was left behind due to exception.
10959 $tmpo = ob_get_clean();
10960 $isObBufferActive = false;
10961 }
10962 $error = 'dol_eval try/catch error : ';
10963 $error .= $e->getMessage();
10964 dol_syslog($error, LOG_WARNING);
10965 if ($returnvalue) {
10966 return 'Exception during evaluation: '.$s;
10967 } else {
10968 return '';
10969 }
10970 }
10971}
10972
10980function dol_validElement($element)
10981{
10982 return (trim($element) != '');
10983}
10984
10993function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10994{
10995 if (empty($codelang)) {
10996 return '';
10997 }
10998
10999 if ($codelang == 'auto') {
11000 return '<span class="fa fa-language"></span>';
11001 }
11002
11003 $langtocountryflag = array(
11004 'ar_AR' => '',
11005 'ca_ES' => 'catalonia',
11006 'da_DA' => 'dk',
11007 'fr_CA' => 'mq',
11008 'sv_SV' => 'se',
11009 'sw_SW' => 'unknown',
11010 'AQ' => 'unknown',
11011 'CW' => 'unknown',
11012 'IM' => 'unknown',
11013 'JE' => 'unknown',
11014 'MF' => 'unknown',
11015 'BL' => 'unknown',
11016 'SX' => 'unknown'
11017 );
11018
11019 if (isset($langtocountryflag[$codelang])) {
11020 $flagImage = $langtocountryflag[$codelang];
11021 } else {
11022 $tmparray = explode('_', $codelang);
11023 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
11024 }
11025
11026 $morecss = '';
11027 $reg = array();
11028 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
11029 $morecss = $reg[1];
11030 $moreatt = "";
11031 }
11032
11033 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
11034 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
11035}
11036
11044function getLanguageCodeFromCountryCode($countrycode)
11045{
11046 global $mysoc;
11047
11048 if (empty($countrycode)) {
11049 return null;
11050 }
11051
11052 if (strtoupper($countrycode) == 'MQ') {
11053 return 'fr_CA';
11054 }
11055 if (strtoupper($countrycode) == 'SE') {
11056 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
11057 }
11058 if (strtoupper($countrycode) == 'CH') {
11059 if ($mysoc->country_code == 'FR') {
11060 return 'fr_CH';
11061 }
11062 if ($mysoc->country_code == 'DE') {
11063 return 'de_CH';
11064 }
11065 if ($mysoc->country_code == 'IT') {
11066 return 'it_CH';
11067 }
11068 }
11069
11070 // Locale list taken from:
11071 // http://stackoverflow.com/questions/3191664/
11072 // list-of-all-locales-and-their-short-codes
11073 $locales = array(
11074 'af-ZA',
11075 'am-ET',
11076 'ar-AE',
11077 'ar-BH',
11078 'ar-DZ',
11079 'ar-EG',
11080 'ar-IQ',
11081 'ar-JO',
11082 'ar-KW',
11083 'ar-LB',
11084 'ar-LY',
11085 'ar-MA',
11086 'ar-OM',
11087 'ar-QA',
11088 'ar-SA',
11089 'ar-SY',
11090 'ar-TN',
11091 'ar-YE',
11092 //'as-IN', // Moved after en-IN
11093 'ba-RU',
11094 'be-BY',
11095 'bg-BG',
11096 'bn-BD',
11097 //'bn-IN', // Moved after en-IN
11098 'bo-CN',
11099 'br-FR',
11100 'ca-ES',
11101 'co-FR',
11102 'cs-CZ',
11103 'cy-GB',
11104 'da-DK',
11105 'de-AT',
11106 'de-CH',
11107 'de-DE',
11108 'de-LI',
11109 'de-LU',
11110 'dv-MV',
11111 'el-GR',
11112 'en-AU',
11113 'en-BZ',
11114 'en-CA',
11115 'en-GB',
11116 'en-IE',
11117 'en-IN',
11118 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
11119 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
11120 'en-JM',
11121 'en-MY',
11122 'en-NZ',
11123 'en-PH',
11124 'en-SG',
11125 'en-TT',
11126 'en-US',
11127 'en-ZA',
11128 'en-ZW',
11129 'es-AR',
11130 'es-BO',
11131 'es-CL',
11132 'es-CO',
11133 'es-CR',
11134 'es-DO',
11135 'es-EC',
11136 'es-ES',
11137 'es-GT',
11138 'es-HN',
11139 'es-MX',
11140 'es-NI',
11141 'es-PA',
11142 'es-PE',
11143 'es-PR',
11144 'es-PY',
11145 'es-SV',
11146 'es-US',
11147 'es-UY',
11148 'es-VE',
11149 'et-EE',
11150 'eu-ES',
11151 'fa-IR',
11152 'fi-FI',
11153 'fo-FO',
11154 'fr-BE',
11155 'fr-CA',
11156 'fr-CH',
11157 'fr-FR',
11158 'fr-LU',
11159 'fr-MC',
11160 'fy-NL',
11161 'ga-IE',
11162 'gd-GB',
11163 'gl-ES',
11164 'gu-IN',
11165 'he-IL',
11166 'hi-IN',
11167 'hr-BA',
11168 'hr-HR',
11169 'hu-HU',
11170 'hy-AM',
11171 'id-ID',
11172 'ig-NG',
11173 'ii-CN',
11174 'is-IS',
11175 'it-CH',
11176 'it-IT',
11177 'ja-JP',
11178 'ka-GE',
11179 'kk-KZ',
11180 'kl-GL',
11181 'km-KH',
11182 'kn-IN',
11183 'ko-KR',
11184 'ky-KG',
11185 'lb-LU',
11186 'lo-LA',
11187 'lt-LT',
11188 'lv-LV',
11189 'mi-NZ',
11190 'mk-MK',
11191 'ml-IN',
11192 'mn-MN',
11193 'mr-IN',
11194 'ms-BN',
11195 'ms-MY',
11196 'mt-MT',
11197 'nb-NO',
11198 'ne-NP',
11199 'nl-BE',
11200 'nl-NL',
11201 'nn-NO',
11202 'oc-FR',
11203 'or-IN',
11204 'pa-IN',
11205 'pl-PL',
11206 'ps-AF',
11207 'pt-BR',
11208 'pt-PT',
11209 'rm-CH',
11210 'ro-MD',
11211 'ro-RO',
11212 'ru-RU',
11213 'rw-RW',
11214 'sa-IN',
11215 'se-FI',
11216 'se-NO',
11217 'se-SE',
11218 'si-LK',
11219 'sk-SK',
11220 'sl-SI',
11221 'sq-AL',
11222 'sv-FI',
11223 'sv-SE',
11224 'sw-KE',
11225 'ta-IN',
11226 'te-IN',
11227 'th-TH',
11228 'tk-TM',
11229 'tn-ZA',
11230 'tr-TR',
11231 'tt-RU',
11232 'ug-CN',
11233 'uk-UA',
11234 'ur-PK',
11235 'vi-VN',
11236 'wo-SN',
11237 'xh-ZA',
11238 'yo-NG',
11239 'zh-CN',
11240 'zh-HK',
11241 'zh-MO',
11242 'zh-SG',
11243 'zh-TW',
11244 'zu-ZA',
11245 );
11246
11247 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11248 if (in_array($buildprimarykeytotest, $locales)) {
11249 return strtolower($countrycode).'_'.strtoupper($countrycode);
11250 }
11251
11252 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11253 foreach ($locales as $locale) {
11254 $locale_language = locale_get_primary_language($locale);
11255 $locale_region = locale_get_region($locale);
11256 if (strtoupper($countrycode) == $locale_region) {
11257 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11258 return strtolower($locale_language).'_'.strtoupper($locale_region);
11259 }
11260 }
11261 } else {
11262 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11263 }
11264
11265 return null;
11266}
11267
11298function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11299{
11300 global $hookmanager, $db;
11301
11302 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11303 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11304 $values = explode(':', $value);
11305
11306 $reg = array();
11307 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11308 $newtab = array();
11309 $postab = $h;
11310 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11311 $str = $values[1];
11312 $posstart = strpos($str, '(');
11313 if ($posstart > 0) {
11314 $posend = strpos($str, ')');
11315 if ($posstart > 0) {
11316 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11317 if (is_numeric($res1)) {
11318 $postab = (int) $res1;
11319 $values[1] = '+' . substr($str, $posend + 1);
11320 }
11321 }
11322 }
11323 if (count($values) == 6) {
11324 // new declaration with permissions:
11325 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11326 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11327 if ($values[0] != $type) {
11328 continue;
11329 }
11330
11331 if (verifCond($values[4], '2')) {
11332 if ($values[3]) {
11333 if ($filterorigmodule) { // If a filter of module origin has been requested
11334 if (strpos($values[3], '@')) { // This is an external module
11335 if ($filterorigmodule != 'external') {
11336 continue;
11337 }
11338 } else { // This looks a core module
11339 if ($filterorigmodule != 'core') {
11340 continue;
11341 }
11342 }
11343 }
11344 $langs->load($values[3]);
11345 }
11346 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11347 // If label is "SUBSTITUION_..."
11348 $substitutionarray = array();
11349 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11350 $label = make_substitutions($reg[1], $substitutionarray);
11351 } else {
11352 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11353 $labeltemp = explode(',', $values[2]);
11354 $label = $langs->trans($labeltemp[0]);
11355
11356 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11357 dol_include_once($labeltemp[2]);
11358 $classtoload = $labeltemp[1];
11359 if (class_exists($classtoload)) {
11360 $obj = new $classtoload($db);
11361 $function = $labeltemp[3];
11362 if ($obj && $function && method_exists($obj, $function)) {
11363 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11364 $nbrec = $obj->$function($object->id, $obj);
11365 if (!empty($nbrec)) {
11366 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11367 }
11368 }
11369 }
11370 }
11371 }
11372
11373 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11374 $newtab[1] = $label;
11375 $newtab[2] = str_replace('+', '', $values[1]);
11376 $h++;
11377 } else {
11378 continue;
11379 }
11380 } elseif (count($values) == 5) { // case deprecated
11381 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11382
11383 if ($values[0] != $type) {
11384 continue;
11385 }
11386 if ($values[3]) {
11387 if ($filterorigmodule) { // If a filter of module origin has been requested
11388 if (strpos($values[3], '@')) { // This is an external module
11389 if ($filterorigmodule != 'external') {
11390 continue;
11391 }
11392 } else { // This looks a core module
11393 if ($filterorigmodule != 'core') {
11394 continue;
11395 }
11396 }
11397 }
11398 $langs->load($values[3]);
11399 }
11400 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11401 $substitutionarray = array();
11402 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11403 $label = make_substitutions($reg[1], $substitutionarray);
11404 } else {
11405 $label = $langs->trans($values[2]);
11406 }
11407
11408 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11409 $newtab[1] = $label;
11410 $newtab[2] = str_replace('+', '', $values[1]);
11411 $h++;
11412 }
11413 // set tab at its position
11414 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11415 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11416 if ($values[0] != $type) {
11417 continue;
11418 }
11419 $tabname = str_replace('-', '', $values[1]);
11420 foreach ($head as $key => $val) {
11421 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11422 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11423 if ($head[$key][2] == $tabname && $condition) {
11424 unset($head[$key]);
11425 break;
11426 }
11427 }
11428 }
11429 }
11430 }
11431
11432 // No need to make a return $head. Var is modified as a reference
11433 if (!empty($hookmanager)) {
11434 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11435 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11436 if ($reshook > 0) { // Hook ask to replace completely the array
11437 $head = $hookmanager->resArray;
11438 } else { // Hook
11439 $head = array_merge($head, $hookmanager->resArray);
11440 }
11441 $h = count($head);
11442 }
11443}
11444
11456function printCommonFooter($zone = 'private')
11457{
11458 global $conf, $hookmanager, $user, $langs;
11459 global $debugbar;
11460 global $action;
11461 global $micro_start_time;
11462
11463 if ($zone == 'private') {
11464 print "\n".'<!-- Common footer for private page -->'."\n";
11465 } else {
11466 print "\n".'<!-- Common footer for public page -->'."\n";
11467 }
11468
11469 // A div to store page_y POST parameter so we can read it using javascript
11470 print "\n<!-- A div to store page_y POST parameter -->\n";
11471 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11472
11473 $parameters = array();
11474 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11475 if (empty($reshook)) {
11476 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11477 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11478 }
11479
11480 print "\n";
11481 if (!empty($conf->use_javascript_ajax)) {
11482 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11483 print '<script>'."\n";
11484 print 'jQuery(document).ready(function() {'."\n";
11485
11486 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11487 print "\n";
11488 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11489 print 'jQuery("li.menuhider").click(function(event) {';
11490 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11491 print ' console.log("We click on .menuhider");'."\n";
11492 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11493 print '});'."\n";
11494 }
11495
11496 // Management of focus and mandatory for fields
11497 if ($action == 'create' || $action == 'add' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))) || ((empty($action) || $action == 'addline') && (preg_match('/card\.php/', $_SERVER["PHP_SELF"])))) {
11498 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11499 $relativepathstring = $_SERVER["PHP_SELF"];
11500 // Clean $relativepathstring
11501 if (constant('DOL_URL_ROOT')) {
11502 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11503 }
11504 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11505 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11506 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11507
11508 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11509 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11510 $qualified = 0;
11511 if ($defkey != '_noquery_') {
11512 $tmpqueryarraytohave = explode('&', $defkey);
11513 $foundintru = 0;
11514 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11515 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11516 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11517 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11518 $foundintru = 1;
11519 }
11520 }
11521 if (!$foundintru) {
11522 $qualified = 1;
11523 }
11524 //var_dump($defkey.'-'.$qualified);
11525 } else {
11526 $qualified = 1;
11527 }
11528
11529 if ($qualified) {
11530 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11531 foreach ($defval as $paramkey => $paramval) {
11532 // Set focus on field
11533 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11534 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11535 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11536 }
11537 }
11538 }
11539 }
11540 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11541 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11542 $qualified = 0;
11543 if ($defkey != '_noquery_') {
11544 $tmpqueryarraytohave = explode('&', $defkey);
11545 $foundintru = 0;
11546 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11547 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11548 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11549 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11550 $foundintru = 1;
11551 }
11552 }
11553 if (!$foundintru) {
11554 $qualified = 1;
11555 }
11556 //var_dump($defkey.'-'.$qualified);
11557 } else {
11558 $qualified = 1;
11559 }
11560
11561 if ($qualified) {
11562 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11563
11564 foreach ($defval as $paramkey => $paramval) {
11565 // Solution 1: Add handler on submit to check if mandatory fields are empty
11566 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11567 print "form.on('submit', function(event) {
11568 var submitter = $(this).find(':submit:focus').get(0);
11569 if (submitter) {
11570 var buttonName = $(submitter).attr('name');
11571 if (buttonName == 'cancel') {
11572 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11573 return true;
11574 }
11575 }
11576
11577 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11578
11579 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11580 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11581
11582 if (tmptypefield == 'textarea') {
11583 // We must instead check the content of ckeditor
11584 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11585 if (tmpeditor) {
11586 tmpvalue = tmpeditor.getData();
11587 console.log('For textarea tmpvalue is '+tmpvalue);
11588 }
11589 }
11590
11591 let tmpvalueisempty = false;
11592 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
11593 tmpvalueisempty = true;
11594 }
11595 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
11596 tmpvalueisempty = true;
11597 }
11598 if (tmpvalueisempty && (buttonName == 'save')) {
11599 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11600 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11601 event.stopPropagation(); // Stop other handlers.
11602 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11603 return false;
11604 }
11605 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11606 return true;
11607 });
11608 \n";
11609
11610 // Solution 2: Add property 'required' on input
11611 // so browser will check value and try to focus on it when submitting the form.
11612 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11613 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11614 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11615 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11616 //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";
11617 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11618 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11619 // Add 'field required' class on closest td for all input elements : input, textarea and select
11620 //print '}, 500);'; // 500 milliseconds delay
11621
11622 // Now set the class "fieldrequired"
11623 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11624 }
11625
11626
11627 // If we submit using the cancel button, we remove the required attributes
11628 print 'jQuery("input[name=\'cancel\']").click(function() {
11629 console.log("We click on cancel button so removed all required attribute");
11630 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11631 });'."\n";
11632 }
11633 }
11634 }
11635 }
11636
11637 print '});'."\n";
11638
11639 // End of tuning
11640 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11641 print "\n";
11642 print "/* JS CODE TO ENABLE to add memory info */\n";
11643 print 'window.console && console.log("';
11644 if (getDolGlobalString('MEMCACHED_SERVER')) {
11645 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11646 }
11647 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11648 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11649 $micro_end_time = microtime(true);
11650 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11651 }
11652
11653 if (function_exists("memory_get_usage")) {
11654 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11655 }
11656 if (function_exists("memory_get_peak_usage")) {
11657 print ' - Real mem peak: '.memory_get_peak_usage(true);
11658 }
11659 if (function_exists("zend_loader_file_encoded")) {
11660 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11661 }
11662 print '");'."\n";
11663 }
11664
11665 print "\n".'</script>'."\n";
11666
11667 // Google Analytics
11668 // TODO Add a hook here
11669 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11670 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11671 foreach ($tmptagarray as $tmptag) {
11672 print "\n";
11673 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11674 print '
11675 <!-- Global site tag (gtag.js) - Google Analytics -->
11676 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11677 <script>
11678 window.dataLayer = window.dataLayer || [];
11679 function gtag(){dataLayer.push(arguments);}
11680 gtag(\'js\', new Date());
11681
11682 gtag(\'config\', \''.trim($tmptag).'\');
11683 </script>';
11684 print "\n";
11685 }
11686 }
11687 }
11688
11689 // Add Xdebug coverage of code
11690 if (defined('XDEBUGCOVERAGE')) {
11691 print_r(xdebug_get_code_coverage());
11692 }
11693
11694 // Add DebugBar data
11695 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11696 if (isset($debugbar['time'])) {
11697 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11698 $debugbar['time']->stopMeasure('pageaftermaster');
11699 }
11700 print '<!-- Output debugbar data -->'."\n";
11701 $renderer = $debugbar->getJavascriptRenderer();
11702 print $renderer->render();
11703 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11704 print "\n";
11705 print "<!-- Start of log output\n";
11706 //print '<div class="hidden">'."\n";
11707 foreach ($conf->logbuffer as $logline) {
11708 print $logline."<br>\n";
11709 }
11710 //print '</div>'."\n";
11711 print "End of log output -->\n";
11712 }
11713 }
11714}
11715
11725function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11726{
11727 if (is_null($string)) {
11728 return array();
11729 }
11730
11731 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11732 // This is a regex string
11733 $newdelimiter = $delimiter;
11734 } else {
11735 // This is a simple string
11736 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11737 $newdelimiter = preg_quote($delimiter, '/');
11738 }
11739
11740 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11741 $ka = array();
11742 foreach ($a as $s) { // each part
11743 if ($s) {
11744 if ($pos = strpos($s, $kv)) { // key/value delimiter
11745 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11746 } else { // key delimiter not found
11747 $ka[] = trim($s);
11748 }
11749 }
11750 }
11751 return $ka;
11752 }
11753
11754 return array();
11755}
11756
11757
11764function dol_set_focus($selector)
11765{
11766 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11767 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11768}
11769
11770
11778function dol_getmypid()
11779{
11780 if (!function_exists('getmypid')) {
11781 return mt_rand(99900000, 99965535);
11782 } else {
11783 return getmypid(); // May be a number on 64 bits (depending on OS)
11784 }
11785}
11786
11787
11809function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11810{
11811 global $db, $langs;
11812
11813 $value = trim($value);
11814
11815 if ($mode == 0) {
11816 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11817 }
11818 if ($mode == 1) {
11819 $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
11820 }
11821
11822 $value = preg_replace('/\s*\|\s*/', '|', $value);
11823
11824 // Split criteria on ' '.
11825 // For mode 3, the split is done later on the , only and not on the ' '.
11826 if ($mode != -3 && $mode != 3) {
11827 $crits = explode(' ', $value);
11828 } else {
11829 $crits = array($value);
11830 }
11831
11832 $res = '';
11833 if (!is_array($fields)) {
11834 $fields = array($fields);
11835 }
11836
11837 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
11838 foreach ($crits as $crit) { // Loop on each AND criteria
11839 $crit = trim($crit);
11840 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11841 $newres = '';
11842 foreach ($fields as $field) {
11843 if ($mode == 1) {
11844 $tmpcrits = explode('|', $crit);
11845 $i3 = 0; // count the nb of valid criteria added for this current field
11846 foreach ($tmpcrits as $tmpcrit) {
11847 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11848 continue;
11849 }
11850 $tmpcrit = trim($tmpcrit);
11851
11852 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11853
11854 $operator = '=';
11855 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11856
11857 $reg = array();
11858 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11859 if (!empty($reg[1])) {
11860 $operator = $reg[1];
11861 }
11862 if ($newcrit != '') {
11863 $numnewcrit = price2num($newcrit);
11864 if (is_numeric($numnewcrit)) {
11865 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11866 } else {
11867 $newres .= '1 = 2'; // force false, we received a corrupted data
11868 }
11869 $i3++; // a criteria was added to string
11870 }
11871 }
11872 $i2++; // a criteria for 1 more field was added to string
11873 } elseif ($mode == 2 || $mode == -2) {
11874 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11875 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11876 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11877 if ($mode == -2) {
11878 $newres .= ' OR '.$field.' IS NULL';
11879 }
11880 $i2++; // a criteria for 1 more field was added to string
11881 } elseif ($mode == 3 || $mode == -3) {
11882 $tmparray = explode(',', $crit);
11883 if (count($tmparray)) {
11884 $listofcodes = '';
11885 foreach ($tmparray as $val) {
11886 $val = trim($val);
11887 if ($val) {
11888 $listofcodes .= ($listofcodes ? ',' : '');
11889 $listofcodes .= "'".$db->escape($val)."'";
11890 }
11891 }
11892 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
11893 $i2++; // a criteria for 1 more field was added to string
11894 }
11895 if ($mode == -3) {
11896 $newres .= ' OR '.$field.' IS NULL';
11897 }
11898 } elseif ($mode == 4) {
11899 $tmparray = explode(',', $crit);
11900 if (count($tmparray)) {
11901 $listofcodes = '';
11902 foreach ($tmparray as $val) {
11903 $val = trim($val);
11904 if ($val) {
11905 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11906 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11907 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11908 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11909 $newres .= ')';
11910 $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)
11911 }
11912 }
11913 }
11914 } else { // $mode=0
11915 $tmpcrits = explode('|', $crit);
11916 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11917 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11918 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11919 continue;
11920 }
11921 $tmpcrit = trim($tmpcrit);
11922
11923 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11924 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11925 } else {
11926 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11927 }
11928
11929 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11930 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11931 } else {
11932 $tmpcrit2 = $tmpcrit;
11933 $tmpbefore = '%';
11934 $tmpafter = '%';
11935 $tmps = '';
11936
11937 if (preg_match('/^!/', $tmpcrit)) {
11938 $tmps .= $field." NOT LIKE "; // ! as exclude character
11939 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11940 } else {
11941 $tmps .= $field." LIKE ";
11942 }
11943 $tmps .= "'";
11944
11945 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11946 $tmpbefore = '';
11947 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11948 }
11949 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11950 $tmpafter = '';
11951 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11952 }
11953
11954 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11955 $tmps = "(".$tmps;
11956 }
11957 $newres .= $tmps;
11958 $newres .= $tmpbefore;
11959 $newres .= $db->escape($tmpcrit2);
11960 $newres .= $tmpafter;
11961 $newres .= "'";
11962 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11963 $newres .= " OR ".$field." IS NULL)";
11964 }
11965 }
11966
11967 $i3++;
11968 }
11969
11970 $i2++; // a criteria for 1 more field was added to string
11971 }
11972 }
11973
11974 if ($newres) {
11975 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11976 }
11977 $i1++;
11978 }
11979 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11980
11981 return $res;
11982}
11983
11990function showDirectDownloadLink($object)
11991{
11992 global $langs;
11993
11994 $out = '';
11995 $url = $object->getLastMainDocLink($object->element);
11996
11997 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
11998 if ($url) {
11999 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
12000 $out .= ajax_autoselect("directdownloadlink", '');
12001 } else {
12002 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
12003 }
12004
12005 return $out;
12006}
12007
12016function getImageFileNameForSize($file, $extName, $extImgTarget = '')
12017{
12018 $dirName = dirname($file);
12019 if ($dirName == '.') {
12020 $dirName = '';
12021 }
12022
12023 if (!in_array($extName, array('', '_small', '_mini'))) {
12024 return 'Bad parameter extName';
12025 }
12026
12027 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
12028 $fileName = basename($fileName);
12029
12030 if (empty($extImgTarget)) {
12031 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
12032 }
12033 if (empty($extImgTarget)) {
12034 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
12035 }
12036 if (empty($extImgTarget)) {
12037 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
12038 }
12039 if (empty($extImgTarget)) {
12040 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
12041 }
12042 if (empty($extImgTarget)) {
12043 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
12044 }
12045 if (empty($extImgTarget)) {
12046 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
12047 }
12048
12049 if (!$extImgTarget) {
12050 return $file;
12051 }
12052
12053 $subdir = '';
12054 if ($extName) {
12055 $subdir = 'thumbs/';
12056 }
12057
12058 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
12059}
12060
12061
12071function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
12072{
12073 global $conf, $langs;
12074
12075 if (empty($conf->use_javascript_ajax)) {
12076 return '';
12077 }
12078
12079 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
12080
12081 if ($alldata == 1) {
12082 if ($isAllowedForPreview) {
12083 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));
12084 } else {
12085 return array();
12086 }
12087 }
12088
12089 // old behavior, return a string
12090 if ($isAllowedForPreview) {
12091 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
12092 $title = $langs->transnoentities("Preview");
12093 //$title = '%27-alert(document.domain)-%27'; // An example of js injection into a corrupted title string, that should be blocked by the dol_escape_uri().
12094 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
12095
12096 // We need to do a dol_escape_uri() on the full string after the javascript: because such parts are the URI and when we click on such links, a RFC3986 decode is done,
12097 // by the browser, converting the %27 (like when having param file=abc%27def), or when having a corrupted title), into a ', BEFORE interpreting the content that can be a js code.
12098 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
12099 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
12100 } else {
12101 return '';
12102 }
12103}
12104
12111function getLabelSpecialCode($idcode)
12112{
12113 global $langs;
12114
12115 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
12116 if ($idcode > 10) {
12117 return 'Module ID '.$idcode;
12118 }
12119 if (!empty($arrayspecialines[$idcode])) {
12120 return $langs->trans($arrayspecialines[$idcode]);
12121 }
12122 return '';
12123}
12124
12133function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
12134{
12135 global $langs;
12136 $out = '<script nonce="'.getNonce().'">
12137 jQuery(document).ready(function () {
12138 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
12139 });
12140 </script>';
12141 if ($addlink) {
12142 if ($textonlink === 'image') {
12143 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
12144 } else {
12145 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
12146 }
12147 }
12148 return $out;
12149}
12150
12158function dolIsAllowedForPreview($file)
12159{
12160 // Check .noexe extension in filename
12161 if (preg_match('/\.noexe$/i', $file)) {
12162 return 0;
12163 }
12164
12165 // Check mime types
12166 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
12167 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
12168 $mime_preview[] = 'svg+xml';
12169 }
12170 //$mime_preview[]='vnd.oasis.opendocument.presentation';
12171 //$mime_preview[]='archive';
12172 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
12173 if ($num_mime !== false) {
12174 return 1;
12175 }
12176
12177 // By default, not allowed for preview
12178 return 0;
12179}
12180
12181
12191function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
12192{
12193 $mime = $default;
12194 $imgmime = 'other.png';
12195 $famime = 'file-o';
12196 $srclang = '';
12197
12198 $tmpfile = preg_replace('/\.noexe$/', '', $file);
12199
12200 // Plain text files
12201 if (preg_match('/\.txt$/i', $tmpfile)) {
12202 $mime = 'text/plain';
12203 $imgmime = 'text.png';
12204 $famime = 'file-alt';
12205 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12206 $mime = 'text/richtext';
12207 $imgmime = 'text.png';
12208 $famime = 'file-alt';
12209 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12210 $mime = 'text/csv';
12211 $imgmime = 'text.png';
12212 $famime = 'file-csv';
12213 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12214 $mime = 'text/tab-separated-values';
12215 $imgmime = 'text.png';
12216 $famime = 'file-alt';
12217 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12218 $mime = 'text/plain';
12219 $imgmime = 'text.png';
12220 $famime = 'file-alt';
12221 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12222 $mime = 'text/plain';
12223 $imgmime = 'text.png';
12224 $srclang = 'ini';
12225 $famime = 'file-alt';
12226 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12227 $mime = 'text/plain';
12228 $imgmime = 'text.png';
12229 $srclang = 'md';
12230 $famime = 'file-alt';
12231 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12232 $mime = 'text/css';
12233 $imgmime = 'css.png';
12234 $srclang = 'css';
12235 $famime = 'file-alt';
12236 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12237 $mime = 'text/plain';
12238 $imgmime = 'text.png';
12239 $srclang = 'lang';
12240 $famime = 'file-alt';
12241 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12242 $mime = 'text/plain';
12243 $imgmime = 'text.png';
12244 $famime = 'file-alt';
12245 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12246 $mime = 'text/html';
12247 $imgmime = 'html.png';
12248 $srclang = 'html';
12249 $famime = 'file-alt';
12250 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12251 $mime = 'text/xml';
12252 $imgmime = 'other.png';
12253 $srclang = 'xml';
12254 $famime = 'file-alt';
12255 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12256 $mime = 'text/xml';
12257 $imgmime = 'other.png';
12258 $srclang = 'xaml';
12259 $famime = 'file-alt';
12260 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12261 $mime = 'text/plain';
12262 $imgmime = 'text.png';
12263 $srclang = 'bas';
12264 $famime = 'file-code';
12265 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12266 $mime = 'text/plain';
12267 $imgmime = 'text.png';
12268 $srclang = 'c';
12269 $famime = 'file-code';
12270 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12271 $mime = 'text/plain';
12272 $imgmime = 'text.png';
12273 $srclang = 'cpp';
12274 $famime = 'file-code';
12275 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12276 $mime = 'text/plain';
12277 $imgmime = 'text.png';
12278 $srclang = 'cs';
12279 $famime = 'file-code';
12280 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12281 $mime = 'text/plain';
12282 $imgmime = 'text.png';
12283 $srclang = 'h';
12284 $famime = 'file-code';
12285 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12286 $mime = 'text/plain';
12287 $imgmime = 'text.png';
12288 $srclang = 'java';
12289 $famime = 'file-code';
12290 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12291 $mime = 'text/plain';
12292 $imgmime = 'php.png';
12293 $srclang = 'php';
12294 $famime = 'file-code';
12295 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12296 $mime = 'text/plain';
12297 $imgmime = 'php.png';
12298 $srclang = 'php';
12299 $famime = 'file-code';
12300 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12301 $mime = 'text/plain';
12302 $imgmime = 'pl.png';
12303 $srclang = 'perl';
12304 $famime = 'file-code';
12305 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12306 $mime = 'text/plain';
12307 $imgmime = 'text.png';
12308 $srclang = 'sql';
12309 $famime = 'file-code';
12310 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12311 $mime = 'text/x-javascript';
12312 $imgmime = 'jscript.png';
12313 $srclang = 'js';
12314 $famime = 'file-code';
12315 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12316 $mime = 'application/vnd.oasis.opendocument.presentation';
12317 $imgmime = 'ooffice.png';
12318 $famime = 'file-powerpoint';
12319 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12320 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12321 $imgmime = 'ooffice.png';
12322 $famime = 'file-excel';
12323 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12324 $mime = 'application/vnd.oasis.opendocument.text';
12325 $imgmime = 'ooffice.png';
12326 $famime = 'file-word';
12327 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12328 $mime = 'application/msaccess';
12329 $imgmime = 'mdb.png';
12330 $famime = 'file';
12331 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12332 $mime = 'application/msword';
12333 $imgmime = 'doc.png';
12334 $famime = 'file-word';
12335 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12336 $mime = 'application/msword';
12337 $imgmime = 'doc.png';
12338 $famime = 'file-word';
12339 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12340 $mime = 'application/vnd.ms-excel';
12341 $imgmime = 'xls.png';
12342 $famime = 'file-excel';
12343 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12344 $mime = 'application/vnd.ms-excel';
12345 $imgmime = 'xls.png';
12346 $famime = 'file-excel';
12347 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12348 $mime = 'application/vnd.ms-excel';
12349 $imgmime = 'xls.png';
12350 $famime = 'file-excel';
12351 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12352 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12353 $imgmime = 'xls.png';
12354 $famime = 'file-excel';
12355 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12356 $mime = 'application/vnd.ms-powerpoint';
12357 $imgmime = 'ppt.png';
12358 $famime = 'file-powerpoint';
12359 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12360 $mime = 'application/x-mspowerpoint';
12361 $imgmime = 'ppt.png';
12362 $famime = 'file-powerpoint';
12363 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12364 $mime = 'application/pdf';
12365 $imgmime = 'pdf.png';
12366 $famime = 'file-pdf';
12367 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12368 $mime = 'text/x-bat';
12369 $imgmime = 'script.png';
12370 $srclang = 'dos';
12371 $famime = 'file-code';
12372 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12373 $mime = 'text/x-sh';
12374 $imgmime = 'script.png';
12375 $srclang = 'bash';
12376 $famime = 'file-code';
12377 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12378 $mime = 'text/x-ksh';
12379 $imgmime = 'script.png';
12380 $srclang = 'bash';
12381 $famime = 'file-code';
12382 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12383 $mime = 'text/x-bash';
12384 $imgmime = 'script.png';
12385 $srclang = 'bash';
12386 $famime = 'file-code';
12387 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12388 $mime = 'image/x-icon';
12389 $imgmime = 'image.png';
12390 $famime = 'file-image';
12391 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12392 $mime = 'image/jpeg';
12393 $imgmime = 'image.png';
12394 $famime = 'file-image';
12395 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12396 $mime = 'image/png';
12397 $imgmime = 'image.png';
12398 $famime = 'file-image';
12399 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12400 $mime = 'image/gif';
12401 $imgmime = 'image.png';
12402 $famime = 'file-image';
12403 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12404 $mime = 'image/bmp';
12405 $imgmime = 'image.png';
12406 $famime = 'file-image';
12407 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12408 $mime = 'image/tiff';
12409 $imgmime = 'image.png';
12410 $famime = 'file-image';
12411 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12412 $mime = 'image/svg+xml';
12413 $imgmime = 'image.png';
12414 $famime = 'file-image';
12415 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12416 $mime = 'image/webp';
12417 $imgmime = 'image.png';
12418 $famime = 'file-image';
12419 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12420 $mime = 'text/calendar';
12421 $imgmime = 'other.png';
12422 $famime = 'file-alt';
12423 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12424 $mime = 'text/calendar';
12425 $imgmime = 'other.png';
12426 $famime = 'file-alt';
12427 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12428 $mime = 'application/x-bittorrent';
12429 $imgmime = 'other.png';
12430 $famime = 'file-o';
12431 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12432 $mime = 'audio';
12433 $imgmime = 'audio.png';
12434 $famime = 'file-audio';
12435 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12436 $mime = 'video/mp4';
12437 $imgmime = 'video.png';
12438 $famime = 'file-video';
12439 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12440 $mime = 'video/ogg';
12441 $imgmime = 'video.png';
12442 $famime = 'file-video';
12443 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12444 $mime = 'video/webm';
12445 $imgmime = 'video.png';
12446 $famime = 'file-video';
12447 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12448 $mime = 'video/x-msvideo';
12449 $imgmime = 'video.png';
12450 $famime = 'file-video';
12451 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12452 $mime = 'video/divx';
12453 $imgmime = 'video.png';
12454 $famime = 'file-video';
12455 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12456 $mime = 'video/xvid';
12457 $imgmime = 'video.png';
12458 $famime = 'file-video';
12459 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12460 $mime = 'video';
12461 $imgmime = 'video.png';
12462 $famime = 'file-video';
12463 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12464 // application/xxx where zzz is zip, ...
12465 $mime = 'archive';
12466 $imgmime = 'archive.png';
12467 $famime = 'file-archive';
12468 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12469 $mime = 'application/octet-stream';
12470 $imgmime = 'other.png';
12471 $famime = 'file-o';
12472 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12473 $mime = 'library';
12474 $imgmime = 'library.png';
12475 $famime = 'file-o';
12476 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12477 $mime = 'error';
12478 $imgmime = 'error.png';
12479 $famime = 'file-alt';
12480 }
12481
12482 if ($famime == 'file-o') {
12483 // file-o seems to not work in fontawesome 5
12484 $famime = 'file';
12485 }
12486
12487 // Return mimetype string
12488 switch ((int) $mode) {
12489 case 1:
12490 $tmp = explode('/', $mime);
12491 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12492 case 2:
12493 return $imgmime;
12494 case 3:
12495 return $srclang;
12496 case 4:
12497 return $famime;
12498 }
12499 return $mime;
12500}
12501
12513function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12514{
12515 global $conf, $db;
12516
12517 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12518
12519 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12520
12521 if (is_null($dictvalues)) {
12522 $dictvalues = array();
12523
12524 $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
12525 if ($checkentity) {
12526 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12527 }
12528
12529 $resql = $db->query($sql);
12530 if ($resql) {
12531 while ($obj = $db->fetch_object($resql)) {
12532 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12533 }
12534 } else {
12535 dol_print_error($db);
12536 }
12537
12538 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12539 }
12540
12541 if (!empty($dictvalues[$id])) {
12542 // Found
12543 $tmp = $dictvalues[$id];
12544 return (property_exists($tmp, $field) ? $tmp->$field : '');
12545 } else {
12546 // Not found
12547 return '';
12548 }
12549}
12550
12557function colorIsLight($stringcolor)
12558{
12559 $stringcolor = str_replace('#', '', $stringcolor);
12560 $res = -1;
12561 if (!empty($stringcolor)) {
12562 $res = 0;
12563 $tmp = explode(',', $stringcolor);
12564 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12565 $r = $tmp[0];
12566 $g = $tmp[1];
12567 $b = $tmp[2];
12568 } else {
12569 $hexr = $stringcolor[0].$stringcolor[1];
12570 $hexg = $stringcolor[2].$stringcolor[3];
12571 $hexb = $stringcolor[4].$stringcolor[5];
12572 $r = hexdec($hexr);
12573 $g = hexdec($hexg);
12574 $b = hexdec($hexb);
12575 }
12576 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12577 if ($bright > 0.6) {
12578 $res = 1;
12579 }
12580 }
12581 return $res;
12582}
12583
12592function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12593{
12594 global $conf;
12595
12596 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12597 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12598 if (empty($menuentry['enabled'])) {
12599 return 0; // Entry disabled by condition
12600 }
12601 if ($type_user && $menuentry['module']) {
12602 $tmploops = explode('|', $menuentry['module']);
12603 $found = 0;
12604 foreach ($tmploops as $tmploop) {
12605 if (in_array($tmploop, $listofmodulesforexternal)) {
12606 $found++;
12607 break;
12608 }
12609 }
12610 if (!$found) {
12611 return 0; // Entry is for menus all excluded to external users
12612 }
12613 }
12614 if (!$menuentry['perms'] && $type_user) {
12615 return 0; // No permissions and user is external
12616 }
12617 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12618 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12619 }
12620 if (!$menuentry['perms']) {
12621 return 2; // No permissions and user is external
12622 }
12623 return 1;
12624}
12625
12633function roundUpToNextMultiple($n, $x = 5)
12634{
12635 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12636 return (int) $result;
12637}
12638
12650function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12651{
12652 $csstouse = 'badge';
12653 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12654 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12655 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12656
12657 $attr = array(
12658 'class' => $csstouse
12659 );
12660
12661 if (empty($html)) {
12662 $html = $label;
12663 }
12664
12665 if (!empty($url)) {
12666 $attr['href'] = $url;
12667 }
12668
12669 if ($mode === 'dot') {
12670 $attr['class'] .= ' classfortooltip';
12671 $attr['title'] = $html;
12672 $attr['aria-label'] = $label;
12673 $html = '';
12674 }
12675
12676 // Override attr
12677 if (!empty($params['attr']) && is_array($params['attr'])) {
12678 foreach ($params['attr'] as $key => $value) {
12679 if ($key == 'class') {
12680 $attr['class'] .= ' '.$value;
12681 } elseif ($key == 'classOverride') {
12682 $attr['class'] = $value;
12683 } else {
12684 $attr[$key] = $value;
12685 }
12686 }
12687 }
12688
12689 // TODO: add hook
12690
12691 // escape all attribute
12692 $attr = array_map('dolPrintHTMLForAttribute', $attr);
12693
12694 $TCompiledAttr = array();
12695 foreach ($attr as $key => $value) {
12696 $TCompiledAttr[] = $key.'="'.$value.'"';
12697 }
12698
12699 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12700
12701 $tag = !empty($url) ? 'a' : 'span';
12702
12703 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12704}
12705
12706
12719function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12720{
12721 global $conf;
12722
12723 $return = '';
12724 $dolGetBadgeParams = array();
12725
12726 if (!empty($params['badgeParams'])) {
12727 $dolGetBadgeParams = $params['badgeParams'];
12728 }
12729
12730 // TODO : add a hook
12731 if ($displayMode == 0) {
12732 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12733 } elseif ($displayMode == 1) {
12734 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12735 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12736 // Use status with images (for backward compatibility)
12737 $return = '';
12738 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12739 $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>' : '');
12740
12741 // For small screen, we always use the short label instead of long label.
12742 if (!empty($conf->dol_optimize_smallscreen)) {
12743 if ($displayMode == 0) {
12744 $displayMode = 1;
12745 } elseif ($displayMode == 4) {
12746 $displayMode = 2;
12747 } elseif ($displayMode == 6) {
12748 $displayMode = 5;
12749 }
12750 }
12751
12752 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12753 $statusImg = array(
12754 'status0' => 'statut0',
12755 'status1' => 'statut1',
12756 'status2' => 'statut2',
12757 'status3' => 'statut3',
12758 'status4' => 'statut4',
12759 'status5' => 'statut5',
12760 'status6' => 'statut6',
12761 'status7' => 'statut7',
12762 'status8' => 'statut8',
12763 'status9' => 'statut9'
12764 );
12765
12766 if (!empty($statusImg[$statusType])) {
12767 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12768 } else {
12769 $htmlImg = img_picto($statusLabel, $statusType);
12770 }
12771
12772 if ($displayMode === 2) {
12773 $return = $htmlImg.' '.$htmlLabelShort;
12774 } elseif ($displayMode === 3) {
12775 $return = $htmlImg;
12776 } elseif ($displayMode === 4) {
12777 $return = $htmlImg.' '.$htmlLabel;
12778 } elseif ($displayMode === 5) {
12779 $return = $htmlLabelShort.' '.$htmlImg;
12780 } else { // $displayMode >= 6
12781 $return = $htmlLabel.' '.$htmlImg;
12782 }
12783 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12784 // Use new badge
12785 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12786
12787 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12788 if (empty($dolGetBadgeParams['attr']['title'])) {
12789 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12790 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12791 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12792 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
12793 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12794 }
12795
12796 if ($displayMode == 3) {
12797 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12798 } elseif ($displayMode === 5) {
12799 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12800 } else {
12801 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12802 }
12803 }
12804
12805 return $return;
12806}
12807
12808
12847function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12848{
12849 global $hookmanager, $action, $object, $langs;
12850
12851 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12852 if (is_array($url)) {
12853 // Loop on $url array to remove entries of disabled modules
12854 foreach ($url as $key => $subbutton) {
12855 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12856 unset($url[$key]);
12857 }
12858 }
12859
12860 $out = '';
12861
12862 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
12863 foreach ($url as $button) {
12864 if (!empty($button['lang'])) {
12865 $langs->load($button['lang']);
12866 }
12867 $label = $langs->trans($button['label']);
12868 $text = $button['text'] ?? '';
12869 $actionType = $button['actionType'] ?? '';
12870 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12871 $id = $button['id'] ?? '';
12872 $userRight = $button['perm'] ?? 1;
12873 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
12874
12875 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12876 }
12877 return $out;
12878 }
12879
12880 if (count($url) > 1) {
12881 $out .= '<div class="dropdown inline-block dropdown-holder">';
12882 $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>';
12883 $out .= '<div class="dropdown-content">';
12884 foreach ($url as $subbutton) {
12885 if (!empty($subbutton['lang'])) {
12886 $langs->load($subbutton['lang']);
12887 }
12888
12889 if (!empty($subbutton['urlraw'])) {
12890 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12891 } else {
12892 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12893 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12894 }
12895
12896 $subbuttonparam = array();
12897 if (!empty($subbutton['attr'])) {
12898 $subbuttonparam['attr'] = $subbutton['attr'];
12899 }
12900 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
12901
12902 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12903 }
12904 $out .= "</div>";
12905 $out .= "</div>";
12906 } else {
12907 foreach ($url as $subbutton) { // Should loop on 1 record only
12908 if (!empty($subbutton['lang'])) {
12909 $langs->load($subbutton['lang']);
12910 }
12911
12912 if (!empty($subbutton['urlraw'])) {
12913 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12914 } else {
12915 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12916 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12917 }
12918
12919 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12920 }
12921 }
12922
12923 return $out;
12924 }
12925
12926 // Here, $url is a simple link
12927
12928 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12929 $class = "dropdown-item";
12930 } else {
12931 $class = 'butAction';
12932 if ($actionType == 'danger' || $actionType == 'delete') {
12933 $class = 'butActionDelete';
12934 if (!empty($url) && strpos($url, 'token=') === false) {
12935 $url .= '&token='.newToken();
12936 }
12937 }
12938 }
12939 $attr = array(
12940 'class' => $class,
12941 'href' => empty($url) ? '' : $url,
12942 'title' => $label
12943 );
12944
12945 if (empty($text)) {
12946 $text = $label;
12947 $attr['title'] = ''; // if html not set, leave label on title is redundant
12948 } else {
12949 $attr['title'] = $label;
12950 $attr['aria-label'] = $label;
12951 }
12952
12953 if (empty($userRight) || $userRight < 0) {
12954 $attr['class'] = 'butActionRefused';
12955 $attr['href'] = '';
12956 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12957 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '').(empty($userRight) ? $langs->trans('NotEnoughPermissions') : '');
12958 }
12959
12960 if (!empty($id)) {
12961 $attr['id'] = $id;
12962 }
12963
12964 // Override attr
12965 if (!empty($params['attr']) && is_array($params['attr'])) {
12966 foreach ($params['attr'] as $key => $value) {
12967 if ($key == 'class') {
12968 $attr['class'] .= ' '.$value;
12969 } elseif ($key == 'classOverride') {
12970 $attr['class'] = $value;
12971 } else {
12972 $attr[$key] = $value;
12973 }
12974 }
12975 }
12976
12977 // automatic add tooltip when title is detected
12978 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12979 $attr['class'] .= ' classfortooltip';
12980 }
12981
12982 // Js Confirm button
12983 if ($userRight && !empty($params['confirm'])) {
12984 if (!is_array($params['confirm'])) {
12985 $params['confirm'] = array();
12986 }
12987
12988 if (empty($params['confirm']['url'])) {
12989 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12990 }
12991
12992 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12993 $attr['data-confirm-url'] = $params['confirm']['url'];
12994 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12995 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
12996 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
12997 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
12998 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
12999 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
13000
13001 $attr['class'] .= ' butActionConfirm';
13002 }
13003
13004 if (isset($attr['href']) && empty($attr['href'])) {
13005 unset($attr['href']);
13006 }
13007
13008 $TCompiledAttr = array();
13009 foreach ($attr as $key => $value) {
13010 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
13011 // Not recommended
13012 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13013 } elseif ($key == 'href') {
13014 $value = dolPrintHTMLForAttributeUrl($value);
13015 } else {
13016 $value = dolPrintHTMLForAttribute($value);
13017 }
13018
13019 $TCompiledAttr[] = $key.'="'.$value.'"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13020 }
13021
13022 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
13023
13024 $tag = !empty($attr['href']) ? 'a' : 'span';
13025
13026
13027 $parameters = array(
13028 'TCompiledAttr' => $TCompiledAttr, // array
13029 'compiledAttributes' => $compiledAttributes, // string
13030 'attr' => $attr,
13031 'tag' => $tag,
13032 'label' => $label,
13033 'html' => $text,
13034 'actionType' => $actionType,
13035 'url' => $url,
13036 'id' => $id,
13037 'userRight' => $userRight,
13038 'params' => $params
13039 );
13040
13041 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
13042 if ($reshook < 0) {
13043 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
13044 }
13045
13046 if (empty($reshook)) {
13047 if (dol_textishtml($text)) { // If content already HTML encoded
13048 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
13049 } else {
13050 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
13051 }
13052 } else {
13053 return $hookmanager->resPrint;
13054 }
13055}
13056
13057
13066function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
13067{
13068 if (empty($url)) {
13069 return '';
13070 }
13071
13072 $parsedUrl = parse_url($url);
13073 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
13074 return $url;
13075 }
13076
13077 if (!empty($parsedUrl['query'])) {
13078 // Use parse_str() function to parse the string passed via URL
13079 parse_str($parsedUrl['query'], $urlQuery);
13080 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
13081 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
13082 }
13083 }
13084
13085 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
13086 $url = DOL_URL_ROOT.$url;
13087 }
13088
13089 return $url;
13090}
13091
13092
13099function dolGetButtonTitleSeparator($moreClass = "")
13100{
13101 return '<span class="button-title-separator '.$moreClass.'" ></span>';
13102}
13103
13110function getFieldErrorIcon($fieldValidationErrorMsg)
13111{
13112 $out = '';
13113 if (!empty($fieldValidationErrorMsg)) {
13114 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
13115 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
13116 $out .= '</span>';
13117 }
13118
13119 return $out;
13120}
13121
13134function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
13135{
13136 global $langs, $user;
13137
13138 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
13139 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
13140 return '';
13141 }
13142
13143 $class = 'btnTitle';
13144 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
13145 $class .= ' btnTitlePlus';
13146 }
13147 $useclassfortooltip = 1;
13148
13149 if (!empty($params['morecss'])) {
13150 $class .= ' '.$params['morecss'];
13151 }
13152
13153 $attr = array(
13154 'class' => $class,
13155 'href' => empty($url) ? '' : $url
13156 );
13157
13158 if (!empty($helpText)) {
13159 $attr['title'] = $helpText;
13160 } elseif ($label) { // empty($attr['title']) &&
13161 $attr['title'] = $label;
13162 $useclassfortooltip = 0;
13163 }
13164
13165 if ($status == 2) {
13166 $attr['class'] .= ' btnTitleSelected';
13167 } elseif ($status <= 0) {
13168 $attr['class'] .= ' refused';
13169
13170 $attr['href'] = '';
13171
13172 if ($status == -1) { // disable
13173 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
13174 } elseif ($status == 0) { // Not enough permissions
13175 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
13176 }
13177 }
13178
13179 if (!empty($attr['title']) && $useclassfortooltip) {
13180 $attr['class'] .= ' classfortooltip';
13181 }
13182
13183 if (!empty($id)) {
13184 $attr['id'] = $id;
13185 }
13186
13187 // Override attr
13188 if (!empty($params['attr']) && is_array($params['attr'])) {
13189 foreach ($params['attr'] as $key => $value) {
13190 if ($key == 'class') {
13191 $attr['class'] .= ' '.$value;
13192 } elseif ($key == 'classOverride') {
13193 $attr['class'] = $value;
13194 } else {
13195 $attr[$key] = $value;
13196 }
13197 }
13198 }
13199
13200 if (isset($attr['href']) && empty($attr['href'])) {
13201 unset($attr['href']);
13202 }
13203
13204 // TODO : add a hook
13205
13206 // Generate attributes with escapement
13207 $TCompiledAttr = array();
13208 foreach ($attr as $key => $value) {
13209 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
13210 }
13211
13212 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
13213
13214 $tag = (empty($attr['href']) ? 'span' : 'a');
13215
13216 $button = '<'.$tag.' '.$compiledAttributes.'>';
13217 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13218 if (!empty($params['forcenohideoftext'])) {
13219 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13220 }
13221 $button .= '</'.$tag.'>';
13222
13223 return $button;
13224}
13225
13235function getElementProperties($elementType)
13236{
13237 global $conf, $db, $hookmanager;
13238
13239 $regs = array();
13240
13241 //$element_type='facture';
13242
13243 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13244
13245 // Parse element/subelement
13246 $module = $elementType;
13247 $element = $elementType;
13248 $subelement = $elementType;
13249 $table_element = $elementType;
13250
13251 // If we ask a resource form external module (instead of default path)
13252 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13253 $element = $subelement = $regs[1];
13254 $module = $regs[2];
13255 }
13256
13257 // If we ask a resource for a string with an element and a subelement
13258 // Example 'project_task'
13259 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13260 $module = $element = $regs[1];
13261 $subelement = $regs[2];
13262 }
13263
13264 // Object lines will use parent classpath and module ref
13265 if (substr($elementType, -3) == 'det') {
13266 $module = preg_replace('/det$/', '', $element);
13267 $subelement = preg_replace('/det$/', '', $subelement);
13268 $classpath = $module.'/class';
13269 $classfile = $module;
13270 $classname = preg_replace('/det$/', 'Line', $element);
13271 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13272 $classname = preg_replace('/det$/', 'Ligne', $element);
13273 }
13274 }
13275 // For compatibility and to work with non standard path
13276 if ($elementType == "action" || $elementType == "actioncomm") {
13277 $classpath = 'comm/action/class';
13278 $subelement = 'Actioncomm';
13279 $module = 'agenda';
13280 $table_element = 'actioncomm';
13281 } elseif ($elementType == 'cronjob') {
13282 $classpath = 'cron/class';
13283 $module = 'cron';
13284 $table_element = 'cron';
13285 } elseif ($elementType == 'adherent_type') {
13286 $classpath = 'adherents/class';
13287 $classfile = 'adherent_type';
13288 $module = 'adherent';
13289 $subelement = 'adherent_type';
13290 $classname = 'AdherentType';
13291 $table_element = 'adherent_type';
13292 } elseif ($elementType == 'bank_account') {
13293 $classpath = 'compta/bank/class';
13294 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13295 $classfile = 'account';
13296 $classname = 'Account';
13297 } elseif ($elementType == 'category') {
13298 $classpath = 'categories/class';
13299 $module = 'categorie';
13300 $subelement = 'categorie';
13301 $table_element = 'categorie';
13302 } elseif ($elementType == 'contact') {
13303 $classpath = 'contact/class';
13304 $classfile = 'contact';
13305 $module = 'societe';
13306 $subelement = 'contact';
13307 $table_element = 'socpeople';
13308 } elseif ($elementType == 'inventory') {
13309 $module = 'product';
13310 $classpath = 'product/inventory/class';
13311 } elseif ($elementType == 'inventoryline') {
13312 $module = 'product';
13313 $classpath = 'product/inventory/class';
13314 $table_element = 'inventorydet';
13315 $parent_element = 'inventory';
13316 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
13317 $module = 'stock';
13318 $classpath = 'product/stock/class';
13319 $classfile = 'entrepot';
13320 $classname = 'Entrepot';
13321 $table_element = 'entrepot';
13322 } elseif ($elementType == 'project') {
13323 $classpath = 'projet/class';
13324 $module = 'projet';
13325 $table_element = 'projet';
13326 } elseif ($elementType == 'project_task') {
13327 $classpath = 'projet/class';
13328 $module = 'projet';
13329 $subelement = 'task';
13330 $table_element = 'projet_task';
13331 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13332 $classpath = 'compta/facture/class';
13333 $module = 'facture';
13334 $subelement = 'facture';
13335 $table_element = 'facture';
13336 } elseif ($elementType == 'facturedet') {
13337 $classpath = 'compta/facture/class';
13338 $classfile = 'facture';
13339 $classname = 'FactureLigne';
13340 $module = 'facture';
13341 $table_element = 'facturedet';
13342 $parent_element = 'facture';
13343 } elseif ($elementType == 'facturerec') {
13344 $classpath = 'compta/facture/class';
13345 $classfile = 'facture-rec';
13346 $module = 'facture';
13347 $classname = 'FactureRec';
13348 } elseif ($elementType == 'commande' || $elementType == 'order') {
13349 $classpath = 'commande/class';
13350 $module = 'commande';
13351 $subelement = 'commande';
13352 $table_element = 'commande';
13353 } elseif ($elementType == 'commandedet') {
13354 $classpath = 'commande/class';
13355 $classfile = 'commande';
13356 $classname = 'OrderLine';
13357 $module = 'commande';
13358 $table_element = 'commandedet';
13359 $parent_element = 'commande';
13360 } elseif ($elementType == 'propal') {
13361 $classpath = 'comm/propal/class';
13362 $table_element = 'propal';
13363 } elseif ($elementType == 'propaldet') {
13364 $classpath = 'comm/propal/class';
13365 $classfile = 'propal';
13366 $subelement = 'propaleligne';
13367 $module = 'propal';
13368 $table_element = 'propaldet';
13369 $parent_element = 'propal';
13370 } elseif ($elementType == 'shipping') {
13371 $classpath = 'expedition/class';
13372 $classfile = 'expedition';
13373 $classname = 'Expedition';
13374 $module = 'expedition';
13375 $table_element = 'expedition';
13376 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13377 $classpath = 'expedition/class';
13378 $classfile = 'expedition';
13379 $classname = 'ExpeditionLigne';
13380 $module = 'expedition';
13381 $table_element = 'expeditiondet';
13382 $parent_element = 'expedition';
13383 } elseif ($elementType == 'delivery_note') {
13384 $classpath = 'delivery/class';
13385 $subelement = 'delivery';
13386 $module = 'expedition';
13387 } elseif ($elementType == 'delivery') {
13388 $classpath = 'delivery/class';
13389 $subelement = 'delivery';
13390 $module = 'expedition';
13391 } elseif ($elementType == 'deliverydet') {
13392 // @todo
13393 } elseif ($elementType == 'supplier_proposal') {
13394 $classpath = 'supplier_proposal/class';
13395 $module = 'supplier_proposal';
13396 $element = 'supplierproposal';
13397 $classfile = 'supplier_proposal';
13398 $subelement = 'supplierproposal';
13399 } elseif ($elementType == 'supplier_proposaldet') {
13400 $classpath = 'supplier_proposal/class';
13401 $module = 'supplier_proposal';
13402 $classfile = 'supplier_proposal';
13403 $table_element = 'supplier_proposaldet';
13404 $parent_element = 'supplier_proposal';
13405 } elseif ($elementType == 'contract') {
13406 $classpath = 'contrat/class';
13407 $module = 'contrat';
13408 $subelement = 'contrat';
13409 $table_element = 'contract';
13410 } elseif ($elementType == 'contratdet') {
13411 $classpath = 'contrat/class';
13412 $module = 'contrat';
13413 $table_element = 'contratdet';
13414 $parent_element = 'contrat';
13415 } elseif ($elementType == 'mailing') {
13416 $classpath = 'comm/mailing/class';
13417 $module = 'mailing';
13418 $classfile = 'mailing';
13419 $classname = 'Mailing';
13420 $subelement = '';
13421 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13422 $classpath = 'adherents/class';
13423 $module = 'adherent';
13424 $subelement = 'adherent';
13425 $table_element = 'adherent';
13426 } elseif ($elementType == 'usergroup') {
13427 $classpath = 'user/class';
13428 $module = 'user';
13429 } elseif ($elementType == 'mo') {
13430 $classpath = 'mrp/class';
13431 $classfile = 'mo';
13432 $classname = 'Mo';
13433 $module = 'mrp';
13434 $subelement = '';
13435 $table_element = 'mrp_mo';
13436 } elseif ($elementType == 'mrp_production') {
13437 $classpath = 'mrp/class';
13438 $classfile = 'mo';
13439 $classname = 'MoLine';
13440 $module = 'mrp';
13441 $subelement = '';
13442 $table_element = 'mrp_production';
13443 $parent_element = 'mo';
13444 } elseif ($elementType == 'cabinetmed_cons') {
13445 $classpath = 'cabinetmed/class';
13446 $module = 'cabinetmed';
13447 $subelement = 'cabinetmedcons';
13448 $table_element = 'cabinetmedcons';
13449 } elseif ($elementType == 'fichinter') {
13450 $classpath = 'fichinter/class';
13451 $module = 'ficheinter';
13452 $subelement = 'fichinter';
13453 $table_element = 'fichinter';
13454 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13455 $classpath = 'resource/class';
13456 $module = 'resource';
13457 $subelement = 'dolresource';
13458 $table_element = 'resource';
13459 } elseif ($elementType == 'opensurvey_sondage') {
13460 $classpath = 'opensurvey/class';
13461 $module = 'opensurvey';
13462 $subelement = 'opensurveysondage';
13463 } elseif ($elementType == 'order_supplier' || $elementType == 'commande_fournisseur') {
13464 $classpath = 'fourn/class';
13465 $module = 'fournisseur';
13466 $classfile = 'fournisseur.commande';
13467 $element = 'order_supplier';
13468 $subelement = '';
13469 $classname = 'CommandeFournisseur';
13470 $table_element = 'commande_fournisseur';
13471 } elseif ($elementType == 'commande_fournisseurdet') {
13472 $classpath = 'fourn/class';
13473 $module = 'fournisseur';
13474 $classfile = 'fournisseur.commande';
13475 $element = 'commande_fournisseurdet';
13476 $subelement = '';
13477 $classname = 'CommandeFournisseurLigne';
13478 $table_element = 'commande_fournisseurdet';
13479 $parent_element = 'commande_fournisseur';
13480 } elseif ($elementType == 'invoice_supplier' || $elementType == 'facture_fourn') {
13481 $classpath = 'fourn/class';
13482 $module = 'fournisseur';
13483 $classfile = 'fournisseur.facture';
13484 $element = 'invoice_supplier';
13485 $subelement = '';
13486 $classname = 'FactureFournisseur';
13487 $table_element = 'facture_fourn';
13488 } elseif ($elementType == 'facture_fourn_det') {
13489 $classpath = 'fourn/class';
13490 $module = 'fournisseur';
13491 $classfile = 'fournisseur.facture';
13492 $element = 'facture_fourn_det';
13493 $subelement = '';
13494 $classname = 'SupplierInvoiceLine';
13495 $table_element = 'facture_fourn_det';
13496 $parent_element = 'invoice_supplier';
13497 } elseif ($elementType == "service") {
13498 $classpath = 'product/class';
13499 $subelement = 'product';
13500 $table_element = 'product';
13501 } elseif ($elementType == 'salary') {
13502 $classpath = 'salaries/class';
13503 $module = 'salaries';
13504 } elseif ($elementType == 'payment_salary') {
13505 $classpath = 'salaries/class';
13506 $classfile = 'paymentsalary';
13507 $classname = 'PaymentSalary';
13508 $module = 'salaries';
13509 } elseif ($elementType == 'productlot') {
13510 $module = 'productbatch';
13511 $classpath = 'product/stock/class';
13512 $classfile = 'productlot';
13513 $classname = 'Productlot';
13514 $element = 'productlot';
13515 $subelement = '';
13516 $table_element = 'product_lot';
13517 } elseif ($elementType == 'societeaccount') {
13518 $classpath = 'societe/class';
13519 $classfile = 'societeaccount';
13520 $classname = 'SocieteAccount';
13521 $module = 'societe';
13522 } elseif ($elementType == 'websitepage') {
13523 $classpath = 'website/class';
13524 $classfile = 'websitepage';
13525 $classname = 'Websitepage';
13526 $module = 'website';
13527 $subelement = 'websitepage';
13528 $table_element = 'website_page';
13529 } elseif ($elementType == 'fiscalyear') {
13530 $classpath = 'core/class';
13531 $module = 'accounting';
13532 $subelement = 'fiscalyear';
13533 } elseif ($elementType == 'chargesociales') {
13534 $classpath = 'compta/sociales/class';
13535 $module = 'tax';
13536 $table_element = 'chargesociales';
13537 } elseif ($elementType == 'tva') {
13538 $classpath = 'compta/tva/class';
13539 $module = 'tax';
13540 $subdir = '/vat';
13541 $table_element = 'tva';
13542 } elseif ($elementType == 'emailsenderprofile') {
13543 $module = '';
13544 $classpath = 'core/class';
13545 $classfile = 'emailsenderprofile';
13546 $classname = 'EmailSenderProfile';
13547 $table_element = 'c_email_senderprofile';
13548 $subelement = '';
13549 } elseif ($elementType == 'conferenceorboothattendee') {
13550 $classpath = 'eventorganization/class';
13551 $classfile = 'conferenceorboothattendee';
13552 $classname = 'ConferenceOrBoothAttendee';
13553 $module = 'eventorganization';
13554 } elseif ($elementType == 'conferenceorbooth') {
13555 $classpath = 'eventorganization/class';
13556 $classfile = 'conferenceorbooth';
13557 $classname = 'ConferenceOrBooth';
13558 $module = 'eventorganization';
13559 } elseif ($elementType == 'ccountry') {
13560 $module = '';
13561 $classpath = 'core/class';
13562 $classfile = 'ccountry';
13563 $classname = 'Ccountry';
13564 $table_element = 'c_country';
13565 $subelement = '';
13566 } elseif ($elementType == 'ecmfiles') {
13567 $module = 'ecm';
13568 $classpath = 'ecm/class';
13569 $classfile = 'ecmfiles';
13570 $classname = 'Ecmfiles';
13571 $table_element = 'ecmfiles';
13572 $subelement = '';
13573 } elseif ($elementType == 'knowledgerecord') {
13574 $module = '';
13575 $classpath = 'knowledgemanagement/class';
13576 $classfile = 'knowledgerecord';
13577 $classname = 'KnowledgeRecord';
13578 $table_element = 'knowledgemanagement_knowledgerecord';
13579 $subelement = '';
13580 }
13581
13582 if (empty($classfile)) {
13583 $classfile = strtolower($subelement);
13584 }
13585 if (empty($classname)) {
13586 $classname = ucfirst($subelement);
13587 }
13588 if (empty($classpath)) {
13589 $classpath = $module.'/class';
13590 }
13591
13592 //print 'getElementProperties subdir='.$subdir;
13593
13594 // Set dir_output
13595 if ($module && isset($conf->$module)) { // The generic case
13596 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13597 $dir_output = $conf->$module->multidir_output[$conf->entity];
13598 } elseif (!empty($conf->$module->output[$conf->entity])) {
13599 $dir_output = $conf->$module->output[$conf->entity];
13600 } elseif (!empty($conf->$module->dir_output)) {
13601 $dir_output = $conf->$module->dir_output;
13602 }
13603 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
13604 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
13605 } elseif (!empty($conf->$module->temp[$conf->entity])) {
13606 $dir_temp = $conf->$module->temp[$conf->entity];
13607 } elseif (!empty($conf->$module->dir_temp)) {
13608 $dir_temp = $conf->$module->dir_temp;
13609 }
13610 }
13611
13612 // Overwrite value for special cases
13613 if ($element == 'order_supplier') {
13614 $dir_output = $conf->fournisseur->commande->dir_output;
13615 $dir_temp = $conf->fournisseur->commande->dir_temp;
13616 } elseif ($element == 'invoice_supplier') {
13617 $dir_output = $conf->fournisseur->facture->dir_output;
13618 $dir_temp = $conf->fournisseur->facture->dir_temp;
13619 }
13620 $dir_output .= $subdir;
13621 $dir_temp .= $subdir;
13622
13623 $elementProperties = array(
13624 'module' => $module,
13625 'element' => $element,
13626 'table_element' => $table_element,
13627 'subelement' => $subelement,
13628 'classpath' => $classpath,
13629 'classfile' => $classfile,
13630 'classname' => $classname,
13631 'dir_output' => $dir_output,
13632 'dir_temp' => $dir_temp,
13633 'parent_element' => $parent_element,
13634 );
13635
13636
13637 // Add hook
13638 if (!is_object($hookmanager)) {
13639 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13640 $hookmanager = new HookManager($db);
13641 }
13642 $hookmanager->initHooks(array('elementproperties'));
13643
13644
13645 // Hook params
13646 $parameters = array(
13647 'elementType' => $elementType,
13648 'elementProperties' => $elementProperties
13649 );
13650
13651 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13652
13653 if ($reshook) {
13654 $elementProperties = $hookmanager->resArray;
13655 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13656 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13657 }
13658
13659 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13660 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13661 unset($hookmanager->contextarray[$key]);
13662 }
13663
13664 return $elementProperties;
13665}
13666
13679function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13680{
13681 global $db, $conf;
13682
13683 $ret = 0;
13684
13685 $element_prop = getElementProperties($element_type);
13686
13687 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13688 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13689 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13690 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13691 // of service and we will return properties of a product.
13692 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13693 } elseif ($element_prop['module'] == 'societeaccount') {
13694 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13695 } else {
13696 $ismodenabled = isModEnabled($element_prop['module']);
13697 }
13698 //var_dump('element_type='.$element_type);
13699 //var_dump($element_prop);
13700 //var_dump($element_prop['module'].' '.$ismodenabled);
13701 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13702 if ($useCache === 1
13703 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13704 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13705 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13706 ) {
13707 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13708 }
13709
13710 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13711
13712 if (class_exists($element_prop['classname'])) {
13713 $className = $element_prop['classname'];
13714 $objecttmp = new $className($db);
13715 '@phan-var-force CommonObject $objecttmp';
13716
13717 if ($element_id > 0 || !empty($element_ref)) {
13718 $ret = $objecttmp->fetch($element_id, $element_ref);
13719 if ($ret >= 0) {
13720 if (empty($objecttmp->module)) {
13721 $objecttmp->module = $element_prop['module'];
13722 }
13723
13724 if ($useCache > 0) {
13725 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13726 $conf->cache['fetchObjectByElement'][$element_type] = [];
13727 }
13728
13729 // Manage cache limit
13730 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13731 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13732 }
13733
13734 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13735 }
13736
13737 return $objecttmp;
13738 }
13739 } else {
13740 return $objecttmp; // returned an object without fetch
13741 }
13742 } else {
13743 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13744 return -1;
13745 }
13746 }
13747
13748 return $ret;
13749}
13750
13757function isAFileWithExecutableContent($filename)
13758{
13759 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)) {
13760 return true;
13761 }
13762
13763 return false;
13764}
13765
13773function newToken()
13774{
13775 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13776}
13777
13785function currentToken()
13786{
13787 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13788}
13789
13795function getNonce()
13796{
13797 global $conf;
13798
13799 if (empty($conf->cache['nonce'])) {
13800 $conf->cache['nonce'] = dolGetRandomBytes(8);
13801 }
13802
13803 return $conf->cache['nonce'];
13804}
13805
13806
13820function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13821{
13822 global $langs;
13823
13824 print '<div class="div-table-responsive-no-min">';
13825 print '<table class="noborder centpercent">';
13826 print '<tr class="liste_titre">';
13827
13828 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13829
13830 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13831
13832 if (!empty($link)) {
13833 if (!empty($arguments)) {
13834 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13835 } else {
13836 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13837 }
13838 }
13839
13840 if ($number > -1) {
13841 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13842 } elseif (!empty($link)) {
13843 print '<span class="badge marginleftonlyshort">...</span>';
13844 }
13845
13846 if (!empty($link)) {
13847 print '</a>';
13848 }
13849
13850 print '</th>';
13851
13852 if ($number < 0 && !empty($link)) {
13853 print '<th class="right">';
13854 print '</th>';
13855 }
13856
13857 print '</tr>';
13858}
13859
13868function finishSimpleTable($addLineBreak = false)
13869{
13870 print '</table>';
13871 print '</div>';
13872
13873 if ($addLineBreak) {
13874 print '<br>';
13875 }
13876}
13877
13889function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13890{
13891 global $langs;
13892
13893 if ($num === 0) {
13894 print '<tr class="oddeven">';
13895 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13896 print '</tr>';
13897 return;
13898 }
13899
13900 if ($nbofloop === 0) {
13901 // don't show a summary line
13902 return;
13903 }
13904
13905 /* Case already handled above, commented to satisfy phpstan.
13906 if ($num === 0) {
13907 $colspan = $tableColumnCount;
13908 } else
13909 */
13910 if ($num > $nbofloop) {
13911 $colspan = $tableColumnCount;
13912 } else {
13913 $colspan = $tableColumnCount - 1;
13914 }
13915
13916 if ($extraRightColumn) {
13917 $colspan--;
13918 }
13919
13920 print '<tr class="liste_total">';
13921
13922 if ($nbofloop > 0 && $num > $nbofloop) {
13923 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13924 } else {
13925 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13926 print '<td class="right centpercent">'.price($total).'</td>';
13927 }
13928
13929 if ($extraRightColumn) {
13930 print '<td></td>';
13931 }
13932
13933 print '</tr>';
13934}
13935
13944function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13945{
13946 if ($method == -1) {
13947 $method = 0;
13948 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13949 $method = 1;
13950 }
13951 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13952 $method = 2;
13953 }
13954 }
13955
13956 // Be sure we don't have output buffering enabled to have readfile working correctly
13957 while (ob_get_level()) {
13958 ob_end_flush();
13959 }
13960
13961 // Solution 0
13962 if ($method == 0) {
13963 readfile($fullpath_original_file_osencoded);
13964 } elseif ($method == 1) {
13965 // Solution 1
13966 $handle = fopen($fullpath_original_file_osencoded, "rb");
13967 while (!feof($handle)) {
13968 print fread($handle, 8192);
13969 }
13970 fclose($handle);
13971 } elseif ($method == 2) {
13972 // Solution 2
13973 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13974 $handle2 = fopen("php://output", "wb");
13975 stream_copy_to_stream($handle1, $handle2);
13976 fclose($handle1);
13977 fclose($handle2);
13978 }
13979}
13980
13990function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13991{
13992 global $langs;
13993
13994 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13995
13996 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
13997 if ($texttoshow === 'none') {
13998 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
13999 $result .= '<span class="clipboardCPValueToPrint"></span>';
14000 } elseif ($texttoshow) {
14001 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14002 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
14003 } else {
14004 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14005 }
14006 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
14007 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
14008 $result .= '<span class="clipboardCPText"></span>';
14009 $result .= '</span>';
14010
14011 return $result;
14012}
14013
14014
14021function jsonOrUnserialize($stringtodecode)
14022{
14023 $result = json_decode($stringtodecode);
14024 if ($result === null) {
14025 $result = unserialize($stringtodecode);
14026 }
14027
14028 return $result;
14029}
14030
14031
14048function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
14049{
14050 global $db, $user;
14051
14052 if (is_null($filter) || !is_string($filter) || $filter === '') {
14053 return '';
14054 }
14055 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
14056 $filter = '(' . $filter . ')';
14057 }
14058
14059 $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'
14060 $firstandlastparenthesis = 0;
14061
14062 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
14063 if ($noerror) {
14064 return '1 = 2';
14065 } else {
14066 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
14067 }
14068 }
14069
14070 // Test the filter syntax
14071 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
14072 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
14073 // If the string result contains something else than '()', the syntax was wrong
14074
14075 if (preg_match('/[^\‍(\‍)]/', $t)) {
14076 $tmperrorstr = 'Bad syntax of the search string';
14077 $errorstr = 'Bad syntax of the search string: '.$filter;
14078 if ($noerror) {
14079 return '1 = 2';
14080 } else {
14081 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
14082 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
14083 }
14084 }
14085
14086 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
14087
14088 if (is_object($db)) {
14089 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
14090 }
14091 if (is_object($user)) {
14092 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
14093 }
14094
14095 return $ret;
14096}
14097
14105function dolForgeExplodeAnd($sqlfilters)
14106{
14107 $arrayofandtags = array();
14108 $nbofchars = dol_strlen($sqlfilters);
14109
14110 $error = '';
14111 $parenthesislevel = 0;
14112 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
14113 if (!$result) {
14114 return array();
14115 }
14116 if ($parenthesislevel >= 1) {
14117 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
14118 }
14119
14120 $i = 0;
14121 $s = '';
14122 $countparenthesis = 0;
14123 while ($i < $nbofchars) {
14124 $char = dol_substr($sqlfilters, $i, 1);
14125
14126 if ($char == '(') {
14127 $countparenthesis++;
14128 } elseif ($char == ')') {
14129 $countparenthesis--;
14130 }
14131
14132 if ($countparenthesis == 0) {
14133 $char2 = dol_substr($sqlfilters, $i + 1, 1);
14134 $char3 = dol_substr($sqlfilters, $i + 2, 1);
14135 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
14136 // We found a AND
14137 $s = trim($s);
14138 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14139 $s = '('.$s.')';
14140 }
14141 $arrayofandtags[] = $s;
14142 $s = '';
14143 $i += 2;
14144 } else {
14145 $s .= $char;
14146 }
14147 } else {
14148 $s .= $char;
14149 }
14150 $i++;
14151 }
14152 if ($s) {
14153 $s = trim($s);
14154 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14155 $s = '('.$s.')';
14156 }
14157 $arrayofandtags[] = $s;
14158 }
14159
14160 return $arrayofandtags;
14161}
14162
14172function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
14173{
14174 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
14175 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
14176 $tmp = $sqlfilters;
14177
14178 $nb = dol_strlen($tmp);
14179 $counter = 0;
14180 $parenthesislevel = 0;
14181
14182 $error = '';
14183
14184 $i = 0;
14185 while ($i < $nb) {
14186 $char = dol_substr($tmp, $i, 1);
14187
14188 if ($char == '(') {
14189 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
14190 // We open a parenthesis and it is the first char
14191 $parenthesislevel++;
14192 }
14193 $counter++;
14194 } elseif ($char == ')') {
14195 $nbcharremaining = ($nb - $i - 1);
14196 if ($nbcharremaining >= $counter) {
14197 $parenthesislevel = min($parenthesislevel, $counter - 1);
14198 }
14199 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
14200 $parenthesislevel = $counter;
14201 }
14202 $counter--;
14203 }
14204
14205 if ($counter < 0) {
14206 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14207 $parenthesislevel = 0;
14208 dol_syslog($error, LOG_WARNING);
14209 return false;
14210 }
14211
14212 $i++;
14213 }
14214
14215 if ($counter > 0) {
14216 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14217 $parenthesislevel = 0;
14218 dol_syslog($error, LOG_WARNING);
14219 return false;
14220 }
14221
14222 return true;
14223}
14224
14232function dolForgeDummyCriteriaCallback($matches)
14233{
14234 //dol_syslog("Convert matches ".$matches[1]);
14235 if (empty($matches[1])) {
14236 return '';
14237 }
14238 $tmp = explode(':', $matches[1]);
14239 if (count($tmp) < 3) {
14240 return '';
14241 }
14242
14243 return '()'; // An empty criteria
14244}
14245
14254function dolForgeSQLCriteriaCallback($matches)
14255{
14256 global $db;
14257
14258 //dol_syslog("Convert matches ".$matches[1]);
14259 if (empty($matches[1])) {
14260 return '';
14261 }
14262 $tmp = explode(':', $matches[1], 3);
14263 if (count($tmp) < 3) {
14264 return '';
14265 }
14266
14267 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14268
14269 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14270
14271 $realOperator = [
14272 'NOTLIKE' => 'NOT LIKE',
14273 'ISNOT' => 'IS NOT',
14274 'NOTIN' => 'NOT IN',
14275 '!=' => '<>',
14276 ];
14277
14278 if (array_key_exists($operator, $realOperator)) {
14279 $operator = $realOperator[$operator];
14280 }
14281
14282 $tmpescaped = $tmp[2];
14283
14284 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14285
14286 $regbis = array();
14287
14288 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
14289 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14290 $tmpescaped2 = '(';
14291 // Explode and sanitize each element in list
14292 $tmpelemarray = explode(',', $tmpescaped);
14293 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14294 $reg = array();
14295 $tmpelem = trim($tmpelem);
14296 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14297 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
14298 } else {
14299 // TODO Replace with $tmpelemarray[$tmpkey] = (float) $tmpelem;
14300 // or allow subrequests.
14301 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1, 1));
14302 }
14303 }
14304 $tmpescaped2 .= implode(',', $tmpelemarray);
14305 $tmpescaped2 .= ')';
14306
14307 $tmpescaped = $tmpescaped2;
14308 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14309 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14310 $tmpescaped = $regbis[1];
14311 }
14312 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14313 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14314 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14315 // TODO Retrieve type of field for $operand field name.
14316 // So we can complete format. For example we could complete a year with month and day.
14317 $tmpescaped = "'".$db->escape($regbis[1])."'";
14318 } else {
14319 if (strtoupper($tmpescaped) == 'NULL') {
14320 $tmpescaped = 'NULL';
14321 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
14322 $tmpescaped = (int) $tmpescaped;
14323 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
14324 $tmpescaped = (float) $tmpescaped;
14325 } else {
14326 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
14327 }
14328 }
14329
14330 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14331}
14332
14333
14343function getTimelineIcon($actionstatic, &$histo, $key)
14344{
14345 global $langs;
14346
14347 $out = '<!-- timeline icon -->'."\n";
14348 $iconClass = 'fa fa-comments';
14349 $img_picto = '';
14350 $colorClass = '';
14351 $pictoTitle = '';
14352
14353 if ($histo[$key]['percent'] == -1) {
14354 $colorClass = 'timeline-icon-not-applicble';
14355 $pictoTitle = $langs->trans('StatusNotApplicable');
14356 } elseif ($histo[$key]['percent'] == 0) {
14357 $colorClass = 'timeline-icon-todo';
14358 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14359 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14360 $colorClass = 'timeline-icon-in-progress';
14361 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14362 } elseif ($histo[$key]['percent'] >= 100) {
14363 $colorClass = 'timeline-icon-done';
14364 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14365 }
14366
14367 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14368 $iconClass = 'fa fa-ticket';
14369 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14370 $iconClass = 'fa fa-pencilxxx';
14371 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14372 $iconClass = 'fa fa-comments';
14373 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14374 $iconClass = 'fa fa-mask';
14375 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14376 if ($actionstatic->type_picto) {
14377 $img_picto = img_picto('', $actionstatic->type_picto);
14378 } else {
14379 if ($actionstatic->type_code == 'AC_RDV') {
14380 $iconClass = 'fa fa-handshake';
14381 } elseif ($actionstatic->type_code == 'AC_TEL') {
14382 $iconClass = 'fa fa-phone';
14383 } elseif ($actionstatic->type_code == 'AC_FAX') {
14384 $iconClass = 'fa fa-fax';
14385 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14386 $iconClass = 'fa fa-envelope';
14387 } elseif ($actionstatic->type_code == 'AC_INT') {
14388 $iconClass = 'fa fa-shipping-fast';
14389 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14390 $iconClass = 'fa fa-robot';
14391 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14392 $iconClass = 'fa fa-robot';
14393 }
14394 }
14395 }
14396
14397 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14398 return $out;
14399}
14400
14408{
14409 global $conf, $db;
14410
14411 $documents = array();
14412
14413 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
14414 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14415 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14416 //$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
14417 $sql .= ' ORDER BY ecm.position ASC';
14418
14419 $resql = $db->query($sql);
14420 if ($resql) {
14421 if ($db->num_rows($resql)) {
14422 while ($obj = $db->fetch_object($resql)) {
14423 $documents[$obj->id] = $obj;
14424 }
14425 }
14426 }
14427
14428 return $documents;
14429}
14430
14431
14449function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
14450{
14451 global $user, $conf;
14452 global $form;
14453
14454 global $param, $massactionbutton;
14455
14456 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
14457
14458 // Check parameters
14459 if (!is_object($filterobj) && !is_object($objcon)) {
14460 dol_print_error(null, 'BadParameter');
14461 }
14462
14463 $histo = array();
14464 '@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';
14465
14466 $numaction = 0;
14467 $now = dol_now();
14468
14469 $sortfield_list = explode(',', $sortfield);
14470 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
14471 $sortfield_new_list = array();
14472 foreach ($sortfield_list as $sortfield_value) {
14473 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
14474 }
14475 $sortfield_new = implode(',', $sortfield_new_list);
14476
14477 $sql = null;
14478 $sql2 = null;
14479
14480 if (isModEnabled('agenda')) {
14481 // Search histo on actioncomm
14482 if (is_object($objcon) && $objcon->id > 0) {
14483 $sql = "SELECT DISTINCT a.id, a.label as label,";
14484 } else {
14485 $sql = "SELECT a.id, a.label as label,";
14486 }
14487 $sql .= " a.datep as dp,";
14488 $sql .= " a.note as message,";
14489 $sql .= " a.datep2 as dp2,";
14490 $sql .= " a.percent as percent, 'action' as type,";
14491 $sql .= " a.fk_element, a.elementtype,";
14492 $sql .= " a.fk_contact,";
14493 $sql .= " a.email_from as msg_from,";
14494 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
14495 $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";
14496 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14497 $sql .= ", sp.lastname, sp.firstname";
14498 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14499 $sql .= ", m.lastname, m.firstname";
14500 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
14501 $sql .= ", o.ref";
14502 }
14503 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
14504 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
14505 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
14506
14507 $force_filter_contact = $filterobj instanceof User;
14508
14509 if (is_object($objcon) && $objcon->id > 0) {
14510 $force_filter_contact = true;
14511 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
14512 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
14513 }
14514
14515 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
14516 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
14517 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
14518 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
14519 $sql .= " ON er.resource_type = 'dolresource'";
14520 $sql .= " AND er.element_id = a.id";
14521 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
14522 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14523 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
14524 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14525 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
14526 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14527 $sql .= ", ".MAIN_DB_PREFIX."product as o";
14528 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14529 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
14530 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14531 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
14532 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14533 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
14534 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14535 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
14536 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14537 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
14538 }
14539
14540 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
14541 if (!$force_filter_contact) {
14542 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14543 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14544 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14545 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14546 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14547 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14548 if ($filterobj->id) {
14549 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14550 }
14551 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
14552 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
14553 if ($filterobj->id) {
14554 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14555 }
14556 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14557 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14558 if ($filterobj->id) {
14559 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14560 }
14561 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14562 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14563 if ($filterobj->id) {
14564 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14565 }
14566 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14567 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14568 if ($filterobj->id) {
14569 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14570 }
14571 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14572 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14573 if ($filterobj->id) {
14574 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14575 }
14576 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14577 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14578 if ($filterobj->id) {
14579 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14580 }
14581 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
14582 $sql .= " AND a.fk_contact = sp.rowid";
14583 if ($filterobj->id) {
14584 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
14585 }
14586 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14587 $sql .= " AND a.fk_element = o.rowid";
14588 if ($filterobj->id) {
14589 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
14590 }
14591 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14592 $sql .= " AND a.fk_element = o.rowid";
14593 if ($filterobj->id) {
14594 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
14595 }
14596 }
14597 } else {
14598 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14599 }
14600
14601 // Condition on actioncode
14602 if (!empty($actioncode) && $actioncode != '-1') {
14603 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14604 if ($actioncode == 'AC_NON_AUTO') {
14605 $sql .= " AND c.type != 'systemauto'";
14606 } elseif ($actioncode == 'AC_ALL_AUTO') {
14607 $sql .= " AND c.type = 'systemauto'";
14608 } else {
14609 if ($actioncode == 'AC_OTH') {
14610 $sql .= " AND c.type != 'systemauto'";
14611 } elseif ($actioncode == 'AC_OTH_AUTO') {
14612 $sql .= " AND c.type = 'systemauto'";
14613 }
14614 }
14615 } else {
14616 if ($actioncode == 'AC_NON_AUTO') {
14617 $sql .= " AND c.type != 'systemauto'";
14618 } elseif ($actioncode == 'AC_ALL_AUTO') {
14619 $sql .= " AND c.type = 'systemauto'";
14620 } else {
14621 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14622 }
14623 }
14624 }
14625 if ($donetodo == 'todo') {
14626 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14627 } elseif ($donetodo == 'done') {
14628 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14629 }
14630 if (is_array($filters) && $filters['search_agenda_label']) {
14631 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14632 }
14633 }
14634
14635 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14636 if (isModEnabled('mailing') && !empty($objcon->email)
14637 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14638 $langs->load("mails");
14639
14640 $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";
14641 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14642 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14643 $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
14644 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14645 $sql2 .= ", '' as lastname, '' as firstname";
14646 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14647 $sql2 .= ", '' as lastname, '' as firstname";
14648 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14649 $sql2 .= ", '' as ref";
14650 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14651 $sql2 .= ", '' as ref";
14652 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14653 $sql2 .= ", '' as ref";
14654 }
14655 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14656 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14657 $sql2 .= " AND mc.statut = 1";
14658 $sql2 .= " AND u.rowid = m.fk_user_valid";
14659 $sql2 .= " AND mc.fk_mailing=m.rowid";
14660 }
14661
14662 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14663 if (!empty($sql) && !empty($sql2)) {
14664 $sql = $sql." UNION ".$sql2;
14665 } elseif (empty($sql) && !empty($sql2)) {
14666 $sql = $sql2;
14667 }
14668
14669 //TODO Add navigation with this limits...
14670 $offset = 0;
14671 $limit = 1000;
14672
14673 // Complete request and execute it with limit
14674 $sql .= $db->order($sortfield_new, $sortorder);
14675 if ($limit) {
14676 $sql .= $db->plimit($limit + 1, $offset);
14677 }
14678
14679 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14680 $resql = $db->query($sql);
14681 if ($resql) {
14682 $i = 0;
14683 $num = $db->num_rows($resql);
14684
14685 $imaxinloop = ($limit ? min($num, $limit) : $num);
14686 while ($i < $imaxinloop) {
14687 $obj = $db->fetch_object($resql);
14688
14689 if ($obj->type == 'action') {
14690 $contactaction = new ActionComm($db);
14691 $contactaction->id = $obj->id;
14692 $result = $contactaction->fetchResources();
14693 if ($result < 0) {
14694 dol_print_error($db);
14695 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14696 }
14697
14698 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14699 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14700 $tododone = '';
14701 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14702 $tododone = 'todo';
14703 }
14704
14705 $histo[$numaction] = array(
14706 'type' => $obj->type,
14707 'tododone' => $tododone,
14708 'id' => $obj->id,
14709 'datestart' => $db->jdate($obj->dp),
14710 'dateend' => $db->jdate($obj->dp2),
14711 'note' => $obj->label,
14712 'message' => dol_htmlentitiesbr($obj->message),
14713 'percent' => $obj->percent,
14714
14715 'userid' => $obj->user_id,
14716 'login' => $obj->user_login,
14717 'userfirstname' => $obj->user_firstname,
14718 'userlastname' => $obj->user_lastname,
14719 'userphoto' => $obj->user_photo,
14720 'msg_from' => $obj->msg_from,
14721
14722 'contact_id' => $obj->fk_contact,
14723 'socpeopleassigned' => $contactaction->socpeopleassigned,
14724 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14725 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14726 'fk_element' => $obj->fk_element,
14727 'elementtype' => $obj->elementtype,
14728 // Type of event
14729 'acode' => $obj->acode,
14730 'alabel' => $obj->alabel,
14731 'libelle' => $obj->alabel, // deprecated
14732 'apicto' => $obj->apicto
14733 );
14734 } else {
14735 $histo[$numaction] = array(
14736 'type' => $obj->type,
14737 'tododone' => 'done',
14738 'id' => $obj->id,
14739 'datestart' => $db->jdate($obj->dp),
14740 'dateend' => $db->jdate($obj->dp2),
14741 'note' => $obj->label,
14742 'message' => dol_htmlentitiesbr($obj->message),
14743 'percent' => $obj->percent,
14744 'acode' => $obj->acode,
14745
14746 'userid' => $obj->user_id,
14747 'login' => $obj->user_login,
14748 'userfirstname' => $obj->user_firstname,
14749 'userlastname' => $obj->user_lastname,
14750 'userphoto' => $obj->user_photo
14751 );
14752 }
14753
14754 $numaction++;
14755 $i++;
14756 }
14757 } else {
14758 dol_print_error($db);
14759 }
14760 }
14761
14762 // Set $out to show events
14763 $out = '';
14764
14765 if (!isModEnabled('agenda')) {
14766 $langs->loadLangs(array("admin", "errors"));
14767 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14768 }
14769
14770 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14771 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
14772
14773 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14774 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14775 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14776 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14777
14778 $formactions = new FormActions($db);
14779
14780 $actionstatic = new ActionComm($db);
14781 $userstatic = new User($db);
14782 $contactstatic = new Contact($db);
14783 $userGetNomUrlCache = array();
14784 $contactGetNomUrlCache = array();
14785
14786 $out .= '<div class="filters-container" >';
14787 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14788 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14789
14790 if ($objcon && get_class($objcon) == 'Contact' &&
14791 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14792 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14793 } else {
14794 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14795 }
14796 if (($filterobj && get_class($filterobj) == 'Societe')) {
14797 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14798 } else {
14799 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14800 }
14801
14802 $out .= "\n";
14803
14804 $out .= '<div class="div-table-responsive-no-min">';
14805 $out .= '<table class="noborder borderbottom centpercent">';
14806
14807 $out .= '<tr class="liste_titre">';
14808
14809 // Action column
14810 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14811 $out .= '<th class="liste_titre width50 middle">';
14812 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14813 $out .= $searchpicto;
14814 $out .= '</th>';
14815 }
14816
14817 // Date
14818 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
14819
14820 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14821 if ($donetodo) {
14822 $out .= '<th class="liste_titre"></th>';
14823 }
14824 // Type of event
14825 $out .= '<th class="liste_titre">';
14826 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14827 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
14828 $out .= '</th>';
14829 // Label
14830 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14831 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14832 $out .= '</th>';
14833
14834 // Action column
14835 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14836 $out .= '<th class="liste_titre width50 middle">';
14837 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14838 $out .= $searchpicto;
14839 $out .= '</th>';
14840 }
14841
14842 $out .= '</tr>';
14843
14844 $out .= '</table>';
14845
14846 $out .= '</form>';
14847 $out .= '</div>';
14848
14849 $out .= "\n";
14850
14851 $out .= '<ul class="timeline">';
14852
14853 if ($donetodo) {
14854 $tmp = '';
14855 if ($filterobj instanceof Societe) {
14856 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14857 }
14858 if ($filterobj instanceof User) {
14859 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14860 }
14861 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14862 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14863 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14864 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14865 if ($filterobj instanceof Societe) {
14866 $tmp .= '</a>';
14867 }
14868 if ($filterobj instanceof User) {
14869 $tmp .= '</a>';
14870 }
14871 $out .= getTitleFieldOfList($tmp);
14872 }
14873
14874 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14875 $caction = new CActionComm($db);
14876 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14877
14878 $actualCycleDate = false;
14879
14880 // Loop on each event to show it
14881 foreach ($histo as $key => $value) {
14882 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14883
14884 $actionstatic->type_picto = $histo[$key]['apicto'];
14885 $actionstatic->type_code = $histo[$key]['acode'];
14886
14887 $labeltype = $actionstatic->type_code;
14888 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14889 $labeltype = 'AC_OTH';
14890 }
14891 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14892 $labeltype = $langs->trans("Message");
14893 } else {
14894 if (!empty($arraylist[$labeltype])) {
14895 $labeltype = $arraylist[$labeltype];
14896 }
14897 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14898 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14899 }
14900 }
14901
14902 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14903
14904 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14905
14906 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14907 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14908 $out .= '<!-- timeline time label -->';
14909 $out .= '<li class="time-label">';
14910 $out .= '<span class="timeline-badge-date">';
14911 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14912 $out .= '</span>';
14913 $out .= '</li>';
14914 $out .= '<!-- /.timeline-label -->';
14915 }
14916
14917
14918 $out .= '<!-- timeline item -->'."\n";
14919 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
14920
14921 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14922 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14923 //$out .= $timelineicon;
14924 //var_dump($timelineicon);
14925 $out .= $typeicon;
14926
14927 $out .= '<div class="timeline-item">'."\n";
14928
14929 $out .= '<span class="time timeline-header-action2">';
14930
14931 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14932 $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").' ';
14933 $out .= $histo[$key]['id'];
14934 $out .= '</a> ';
14935 } else {
14936 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14937 }
14938
14939 if ($user->hasRight('agenda', 'allactions', 'create') ||
14940 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14941 $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).'">';
14942 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14943 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14944 $out .= '</a>';
14945 }
14946
14947 $out .= '</span>';
14948
14949 // Date
14950 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14951 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14952 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14953 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14954 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14955 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14956 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14957 } else {
14958 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14959 }
14960 }
14961 $late = 0;
14962 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14963 $late = 1;
14964 }
14965 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14966 $late = 1;
14967 }
14968 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14969 $late = 1;
14970 }
14971 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14972 $late = 1;
14973 }
14974 if ($late) {
14975 $out .= img_warning($langs->trans("Late")).' ';
14976 }
14977 $out .= "</span></span>\n";
14978
14979 // Ref
14980 $out .= '<h3 class="timeline-header">';
14981
14982 // Author of event
14983 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14984 if ($histo[$key]['userid'] > 0) {
14985 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14986 $userstatic->fetch($histo[$key]['userid']);
14987 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14988 }
14989 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14990 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14991 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14992 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14993 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14994 } else {
14995 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
14996 }
14997 }
14998 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
14999 }
15000 $out .= '</div>';
15001
15002 // Title
15003 $out .= ' <div class="messaging-title inline-block">';
15004 //$out .= $actionstatic->getTypePicto();
15005 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
15006 $out .= $labeltype.' - ';
15007 }
15008
15009 $libelle = '';
15010 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15011 $out .= $langs->trans('TicketNewMessage');
15012 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
15013 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
15014 } elseif (isset($histo[$key]['type'])) {
15015 if ($histo[$key]['type'] == 'action') {
15016 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15017 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
15018 $libelle = $histo[$key]['note'];
15019 $actionstatic->id = $histo[$key]['id'];
15020 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15021 } elseif ($histo[$key]['type'] == 'mailing') {
15022 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
15023 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15024 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
15025 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15026 } else {
15027 $libelle .= $histo[$key]['note'];
15028 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15029 }
15030 }
15031
15032 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
15033 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
15034 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
15035 } else {
15036 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
15037 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
15038 }
15039 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
15040 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
15041 }
15042 if ($link) {
15043 $out .= ' - '.$link;
15044 }
15045 }
15046
15047 $out .= '</div>';
15048
15049 $out .= '</h3>';
15050
15051 // Message
15052 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
15053 && $actionstatic->code != 'AC_TICKET_CREATE'
15054 && $actionstatic->code != 'AC_TICKET_MODIFY'
15055 ) {
15056 $out .= '<div class="timeline-body wordbreak small">';
15057 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
15058 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
15059 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
15060 $out .= '<div class="readmore-block --closed" >';
15061 $out .= ' <div class="readmore-block__excerpt">';
15062 $out .= dolPrintHTML($truncatedText);
15063 $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>';
15064 $out .= ' </div>';
15065 $out .= ' <div class="readmore-block__full-text" >';
15066 $out .= dolPrintHTML($histo[$key]['message']);
15067 $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>';
15068 $out .= ' </div>';
15069 $out .= '</div>';
15070 } else {
15071 $out .= dolPrintHTML($histo[$key]['message']);
15072 }
15073
15074 $out .= '</div>';
15075 }
15076
15077 // Timeline footer
15078 $footer = '';
15079
15080 // Contact for this action
15081 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
15082 $contactList = '';
15083 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
15084 if (empty($conf->cache['contact'][$cid])) {
15085 $contact = new Contact($db);
15086 $contact->fetch($cid);
15087 $conf->cache['contact'][$cid] = $contact;
15088 } else {
15089 $contact = $conf->cache['contact'][$cid];
15090 }
15091
15092 if ($contact) {
15093 $contactList .= !empty($contactList) ? ', ' : '';
15094 $contactList .= $contact->getNomUrl(1);
15095 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15096 if (!empty($contact->phone_pro)) {
15097 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
15098 }
15099 }
15100 }
15101 }
15102
15103 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
15104 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
15105 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
15106 $contact = new Contact($db);
15107 $result = $contact->fetch($histo[$key]['contact_id']);
15108 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
15109 } else {
15110 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
15111 $result = ($contact instanceof Contact) ? $contact->id : 0;
15112 }
15113
15114 if ($result > 0) {
15115 $footer .= $contact->getNomUrl(1);
15116 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15117 if (!empty($contact->phone_pro)) {
15118 $footer .= '('.dol_print_phone($contact->phone_pro).')';
15119 }
15120 }
15121 }
15122 }
15123
15124 $documents = getActionCommEcmList($actionstatic);
15125 if (!empty($documents)) {
15126 $footer .= '<div class="timeline-documents-container">';
15127 foreach ($documents as $doc) {
15128 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
15129 $footer .= ' data-id="'.$doc->id.'" ';
15130 $footer .= ' data-path="'.$doc->filepath.'"';
15131 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
15132 $footer .= '>';
15133
15134 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
15135 $mime = dol_mimetype($filePath);
15136 $file = $actionstatic->id.'/'.$doc->filename;
15137 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
15138 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
15139 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
15140
15141 $mimeAttr = ' mime="'.$mime.'" ';
15142 $class = '';
15143 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
15144 $class .= ' documentpreview';
15145 }
15146
15147 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
15148 $footer .= img_mime($filePath).' '.$doc->filename;
15149 $footer .= '</a>';
15150
15151 $footer .= '</span>';
15152 }
15153 $footer .= '</div>';
15154 }
15155
15156 if (!empty($footer)) {
15157 $out .= '<div class="timeline-footer">'.$footer.'</div>';
15158 }
15159
15160 $out .= '</div>'."\n"; // end timeline-item
15161
15162 $out .= '</li>';
15163 $out .= '<!-- END timeline item -->';
15164 }
15165
15166 $out .= "</ul>\n";
15167
15168 $out .= '<script>
15169 jQuery(document).ready(function () {
15170 $(document).on("click", "[data-read-more-action]", function(e){
15171 let readMoreBloc = $(this).closest(".readmore-block");
15172 if(readMoreBloc.length > 0){
15173 e.preventDefault();
15174 if($(this).attr("data-read-more-action") == "close"){
15175 readMoreBloc.addClass("--closed").removeClass("--open");
15176 $("html, body").animate({
15177 scrollTop: readMoreBloc.offset().top - 200
15178 }, 100);
15179 }else{
15180 readMoreBloc.addClass("--open").removeClass("--closed");
15181 }
15182 }
15183 });
15184 });
15185 </script>';
15186
15187
15188 if (empty($histo)) {
15189 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
15190 }
15191 }
15192
15193 if ($noprint) {
15194 return $out;
15195 } else {
15196 print $out;
15197 return null;
15198 }
15199}
15200
15212function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
15213{
15214 if ($timestamp === null) {
15215 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
15216 }
15217 $TParam = array(
15218 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
15219 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
15220 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
15221 );
15222 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15223 $TParam = array_merge($TParam, array(
15224 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15225 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
15226 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
15227 ));
15228 }
15229
15230 return '&' . http_build_query($TParam);
15231}
15232
15251function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15252{
15253 global $conf, $db, $langs, $hookmanager;
15254 global $action, $object;
15255
15256 if (!is_object($langs)) {
15257 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15258 $langs = new Translate('', $conf);
15259 $langs->setDefaultLang();
15260 }
15261
15262 $langs->load("errors");
15263
15264 if ($printheader) {
15265 if (function_exists("llxHeader")) {
15266 llxHeader('');
15267 } elseif (function_exists("llxHeaderVierge")) {
15268 llxHeaderVierge('');
15269 }
15270 }
15271
15272 print '<div class="error">';
15273 if (empty($message)) {
15274 print $langs->trans("ErrorRecordNotFound");
15275 } else {
15276 print $langs->trans($message);
15277 }
15278 print '</div>';
15279 print '<br>';
15280
15281 if (empty($showonlymessage)) {
15282 if (empty($hookmanager)) {
15283 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15284 $hookmanager = new HookManager($db);
15285 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15286 $hookmanager->initHooks(array('main'));
15287 }
15288
15289 $parameters = array('message' => $message, 'params' => $params);
15290 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15291 print $hookmanager->resPrint;
15292 }
15293
15294 if ($printfooter && function_exists("llxFooter")) {
15295 llxFooter();
15296 }
15297 exit(0);
15298}
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
if(!defined( 'NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined( 'NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) if(!defined( 'NOLOGIN')) if(!defined('NOCSRFCHECK')) if(!defined( 'NOIPCHECK')) llxHeaderVierge($title, $head="", $disablejs=0, $disablehead=0, $arrayofjs=[], $arrayofcss=[])
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:463
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='', $htmlname='', $forcenojs=0, $moreparam='')
On/off button to change a property status of an object This uses the ajax service objectonoff....
Definition ajax.lib.php:754
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:87
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:71
$c
Definition line.php:327
$object ref
Definition info.php:89
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:519
dol_get_next_day($day, $month, $year)
Return next day.
Definition date.lib.php:504
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:86
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition date.lib.php:488
dol_get_next_month($month, $year)
Return next month.
Definition date.lib.php:538
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.
getDolGlobalFloat($key, $default=0)
Return a Dolibarr global constant float value.
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formatted size.
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) If ...
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
finishSimpleTable($addLineBreak=false)
Add the correct HTML close tags for "startSimpleTable(...)" (use after the last table line)
show_actions_messaging($conf, $langs, $db, $filterobj, $objcon=null, $noprint=0, $actioncode='', $donetodo='done', $filters=array(), $sortfield='a.datep, a.id', $sortorder='DESC')
Show html area with actions in messaging format.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
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_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.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
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_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
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.
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.
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_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
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.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
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.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
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.
dolGetBadge($label, $html='', $type='primary', $mode='', $url='', $params=array())
Function dolGetBadge.
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
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.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot=true)
An function to complete dropdown url in dolGetButtonAction.
dol_get_object_properties($obj, $properties=[])
Get properties for an object - including magic properties when requested.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_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.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
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_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
dolPrintLabel($s, $escapeonlyhtmltags=0)
Return a string label (so on 1 line only and that should not contains any HTML) ready to be output on...
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)
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
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...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolPrintPassword($s)
Return a string ready to be output on an HTML attribute (alt, title, ...)
colorIsLight($stringcolor)
Return true if the color is light.
dol_escape_all($stringtoescape)
Returns text escaped for all protocols (so only alpha chars and numbers)
GETPOSTFLOAT($paramname, $rounding='')
Return the value of a $_GET or $_POST supervariable, converted into float.
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
dolForgeSQLCriteriaCallback($matches)
Function to forge a SQL criteria from a USF (Universal Filter Syntax) string.
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.
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
dol_escape_uri($stringtoescape)
Returns text escaped by RFC 3986 for inclusion into a clicable link.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
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).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
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_clone($object, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
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.
dolSlugify($stringtoslugify)
Returns text slugified (no special char, separator is "-".
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.
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $selectlimitsuffix='', $beforearrows='', $hidenavigation=0)
Function to show navigation arrows into lists.
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.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
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_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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.
dol_sanitizeKeyCode($str)
Clean a string to use it as a key or code.
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.
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.
dolPrintHTMLForAttributeUrl($s)
Return a string ready to be output on a href attribute (this one need a special because we need conte...
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 ...
dolBECalculateStructuredCommunication($invoice_number, $invoice_type)
Calculate Structured Communication / BE Bank payment reference number.
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
div refaddress div address
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition inc.php:424
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
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
dol_setcache($memoryid, $data, $expire=0, $filecache=0)
Save data into a memory area shared by all users, all sessions on server.
dol_getcache($memoryid, $filecache=0)
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:150
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:153
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.