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 if ($paramname == 'hashp' && $out == 'shared') {
1128 $out = ''; // We refuse to have hashp=shared as a parameter
1129 }
1130
1131 return $out;
1132}
1133
1143function GETPOSTINT($paramname, $method = 0)
1144{
1145 return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1146}
1147
1156function GETPOSTFLOAT($paramname, $rounding = '')
1157{
1158 // 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.)
1159 return (float) price2num(GETPOST($paramname), $rounding, 2);
1160}
1161
1175function GETPOSTDATE($prefix, $hourTime = '', $gm = 'auto')
1176{
1177 $m = array();
1178 if ($hourTime === 'getpost') {
1179 $hour = GETPOSTINT($prefix . 'hour');
1180 $minute = GETPOSTINT($prefix . 'min');
1181 $second = GETPOSTINT($prefix . 'sec');
1182 } elseif (preg_match('/^(\d\d):(\d\d):(\d\d)$/', $hourTime, $m)) {
1183 $hour = intval($m[1]);
1184 $minute = intval($m[2]);
1185 $second = intval($m[3]);
1186 } else {
1187 $hour = $minute = $second = 0;
1188 }
1189 // normalize out of range values
1190 $hour = (int) min($hour, 23);
1191 $minute = (int) min($minute, 59);
1192 $second = (int) min($second, 59);
1193
1194 return dol_mktime($hour, $minute, $second, GETPOSTINT($prefix . 'month'), GETPOSTINT($prefix . 'day'), GETPOSTINT($prefix . 'year'), $gm);
1195}
1196
1197
1208function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1209{
1210 return sanitizeVal($out, $check, $filter, $options);
1211}
1212
1222function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1223{
1224 // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1225 // Check is done after replacement
1226 switch ($check) {
1227 case 'none':
1228 case 'password':
1229 break;
1230 case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1231 if (!is_numeric($out)) {
1232 $out = '';
1233 }
1234 break;
1235 case 'intcomma':
1236 if (is_array($out)) {
1237 $out = implode(',', $out);
1238 }
1239 if (preg_match('/[^0-9,-]+/i', $out)) {
1240 $out = '';
1241 }
1242 break;
1243 case 'san_alpha':
1244 $out = filter_var($out, FILTER_SANITIZE_STRING);
1245 break;
1246 case 'email':
1247 $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1248 break;
1249 case 'aZ':
1250 if (!is_array($out)) {
1251 $out = trim($out);
1252 if (preg_match('/[^a-z]+/i', $out)) {
1253 $out = '';
1254 }
1255 }
1256 break;
1257 case 'aZ09':
1258 if (!is_array($out)) {
1259 $out = trim($out);
1260 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1261 $out = '';
1262 }
1263 }
1264 break;
1265 case 'aZ09arobase': // great to sanitize $objecttype parameter
1266 if (!is_array($out)) {
1267 $out = trim($out);
1268 if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1269 $out = '';
1270 }
1271 }
1272 break;
1273 case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1274 if (!is_array($out)) {
1275 $out = trim($out);
1276 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1277 $out = '';
1278 }
1279 }
1280 break;
1281 case 'alpha': // No html and no ../ and "
1282 case 'alphanohtml': // Recommended for most scalar parameters and search parameters. Not valid for json string.
1283 if (!is_array($out)) {
1284 $out = trim($out);
1285 do {
1286 $oldstringtoclean = $out;
1287 // Remove html tags
1288 $out = dol_string_nohtmltag($out, 0);
1289 // 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).
1290 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1291 // Remove also other dangerous string sequences
1292 // '../' or '..\' is dangerous because it allows dir transversals
1293 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1294 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1295 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1296 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1297 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1298 } while ($oldstringtoclean != $out);
1299 // keep lines feed
1300 }
1301 break;
1302 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'
1303 if (!is_array($out)) {
1304 $out = trim($out);
1305 do {
1306 $oldstringtoclean = $out;
1307 // Decode html entities
1308 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1309 // 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).
1310 $out = preg_replace('/\\\‍([0-9xu])/', '/\1', $out);
1311 // Remove also other dangerous string sequences
1312 // '../' or '..\' is dangerous because it allows dir transversals
1313 // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1314 // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1315 // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1316 // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1317 $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1318 } while ($oldstringtoclean != $out);
1319 }
1320 break;
1321 case 'nohtml': // No html. Valid for JSON strings.
1322 $out = dol_string_nohtmltag($out, 0);
1323 break;
1324 case 'restricthtmlnolink':
1325 case 'restricthtml': // Recommended for most html textarea
1326 case 'restricthtmlallowclass':
1327 case 'restricthtmlallowiframe':
1328 case 'restricthtmlallowlinkscript': // Allow link and script tag for head section.
1329 case 'restricthtmlallowunvalid':
1330 $out = dol_htmlwithnojs($out, 1, $check);
1331 break;
1332 case 'custom':
1333 if (!empty($out)) {
1334 if (empty($filter)) {
1335 return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1336 }
1337 if (is_null($options)) {
1338 $options = 0;
1339 }
1340 $out = filter_var($out, $filter, $options);
1341 }
1342 break;
1343 default:
1344 dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1345 $out = GETPOST($out, 'alphanohtml');
1346 break;
1347 }
1348
1349 return $out;
1350}
1351
1352
1353if (!function_exists('dol_getprefix')) {
1364 function dol_getprefix($mode = '')
1365 {
1366 // If prefix is for email (we need to have $conf already loaded for this case)
1367 if ($mode == 'email') {
1368 global $conf;
1369
1370 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1371 if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1372 return getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID');
1373 } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1374 return $_SERVER["SERVER_NAME"];
1375 }
1376 }
1377
1378 // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1379 if (!empty($conf->file->instance_unique_id)) {
1380 return sha1('dolibarr'.$conf->file->instance_unique_id);
1381 }
1382
1383 // For backward compatibility when instance_unique_id is not set
1384 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1385 }
1386
1387 // If prefix is for session (no need to have $conf loaded)
1388 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1389 $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
1390
1391 // The recommended value (may be not defined for old versions)
1392 if (!empty($tmp_instance_unique_id)) {
1393 return sha1('dolibarr'.$tmp_instance_unique_id);
1394 }
1395
1396 // For backward compatibility when instance_unique_id is not set
1397 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1398 return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1399 } else {
1400 return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1401 }
1402 }
1403}
1404
1415function dol_include_once($relpath, $classname = '')
1416{
1417 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']
1418
1419 $fullpath = dol_buildpath($relpath);
1420
1421 if (!file_exists($fullpath)) {
1422 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1423 return false;
1424 }
1425
1426 if (!empty($classname) && !class_exists($classname)) {
1427 return include $fullpath;
1428 } else {
1429 return include_once $fullpath;
1430 }
1431}
1432
1433
1447function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1448{
1449 global $conf;
1450
1451 $path = preg_replace('/^\//', '', $path);
1452
1453 if (empty($type)) { // For a filesystem path
1454 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1455 if (is_array($conf->file->dol_document_root)) {
1456 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1457 if ($key == 'main') {
1458 continue;
1459 }
1460 // if (@file_exists($dirroot.'/'.$path)) {
1461 if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1462 $res = $dirroot.'/'.$path;
1463 return $res;
1464 }
1465 }
1466 }
1467 if ($returnemptyifnotfound) {
1468 // Not found into alternate dir
1469 if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1470 return '';
1471 }
1472 }
1473 } else {
1474 // For an url path
1475 // We try to get local path of file on filesystem from url
1476 // Note that trying to know if a file on disk exist by forging path on disk from url
1477 // works only for some web server and some setup. This is bugged when
1478 // using proxy, rewriting, virtual path, etc...
1479 $res = '';
1480 if ($type == 1) {
1481 $res = DOL_URL_ROOT.'/'.$path; // Standard value
1482 }
1483 if ($type == 2) {
1484 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1485 }
1486 if ($type == 3) {
1487 $res = DOL_URL_ROOT.'/'.$path;
1488 }
1489
1490 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1491 if ($key == 'main') {
1492 if ($type == 3) {
1493 /*global $dolibarr_main_url_root;*/
1494
1495 // Define $urlwithroot
1496 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1497 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1498 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1499
1500 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1501 }
1502 continue;
1503 }
1504 $regs = array();
1505 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1506 if (!empty($regs[1])) {
1507 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1508 //if (file_exists($dirroot.'/'.$regs[1])) {
1509 if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1510 if ($type == 1) {
1511 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1512 } elseif ($type == 2) {
1513 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1514 } elseif ($type == 3) {
1515 /*global $dolibarr_main_url_root;*/
1516
1517 // Define $urlwithroot
1518 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1519 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1520 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1521
1522 $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
1523 }
1524 break;
1525 }
1526 }
1527 }
1528 }
1529
1530 return $res;
1531}
1532
1543function dol_get_object_properties($obj, $properties = [])
1544{
1545 // Get real properties using get_object_vars() if $properties is empty
1546 if (empty($properties)) {
1547 return get_object_vars($obj);
1548 }
1549
1550 $existingProperties = [];
1551 $realProperties = get_object_vars($obj);
1552
1553 // Get the real or magic property values
1554 foreach ($properties as $property) {
1555 if (array_key_exists($property, $realProperties)) {
1556 // Real property, add the value
1557 $existingProperties[$property] = $obj->{$property};
1558 } elseif (property_exists($obj, $property)) {
1559 // Magic property
1560 $existingProperties[$property] = $obj->{$property};
1561 }
1562 }
1563
1564 return $existingProperties;
1565}
1566
1567
1583function dol_clone($object, $native = 2)
1584{
1585 if ($native == 0) {
1586 // deprecated method, use the method with native = 2 instead
1587 $tmpsavdb = null;
1588 if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1589 $tmpsavdb = $object->db;
1590 unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1591 }
1592
1593 $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1594
1595 if (!empty($tmpsavdb)) {
1596 $object->db = $tmpsavdb;
1597 }
1598 } elseif ($native == 2) {
1599 // recommended method to have a full isolated cloned object
1600 $myclone = new stdClass();
1601 $tmparray = get_object_vars($object); // return only public properties
1602
1603 if (is_array($tmparray)) {
1604 foreach ($tmparray as $propertykey => $propertyval) {
1605 if (is_scalar($propertyval) || is_array($propertyval)) {
1606 $myclone->$propertykey = $propertyval;
1607 }
1608 }
1609 }
1610 } else {
1611 $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)
1612 }
1613
1614 return $myclone;
1615}
1616
1626function dol_size($size, $type = '')
1627{
1628 global $conf;
1629 if (empty($conf->dol_optimize_smallscreen)) {
1630 return $size;
1631 }
1632 if ($type == 'width' && $size > 250) {
1633 return 250;
1634 } else {
1635 return 10;
1636 }
1637}
1638
1639
1652function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes = 0)
1653{
1654 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1655 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1656 // Char '/' and '\' are file delimiters.
1657 // 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
1658 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1659 if ($includequotes) {
1660 $filesystem_forbidden_chars[] = "'";
1661 }
1662 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1663 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1664 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1665 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1666 $tmp = str_replace('..', '', $tmp);
1667
1668 return $tmp;
1669}
1670
1671
1683function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1684{
1685 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1686 // Char '>' '<' '|' '$' and ';' are special chars for shells.
1687 // 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
1688 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1689
1690 $tmp = $str;
1691 if ($unaccent) {
1692 $tmp = dol_string_unaccent($tmp);
1693 }
1694 $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars);
1695 $tmp = preg_replace('/\-\-+/', '_', $tmp);
1696 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1697 $tmp = preg_replace('/\s+\-$/', '', $tmp);
1698 $tmp = str_replace('..', '', $tmp);
1699 return $tmp;
1700}
1701
1709function dol_sanitizeUrl($stringtoclean, $type = 1)
1710{
1711 // 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)
1712 // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1713 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1714 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1715 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1716
1717 $stringtoclean = str_replace('\\', '/', $stringtoclean);
1718 if ($type == 1) {
1719 // removing : should disable links to external url like http:aaa)
1720 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1721 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1722 }
1723
1724 do {
1725 $oldstringtoclean = $stringtoclean;
1726 // removing '&colon' should disable links to external url like http:aaa)
1727 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1728 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1729 } while ($oldstringtoclean != $stringtoclean);
1730
1731 if ($type == 1) {
1732 // removing '//' should disable links to external url like //aaa or http//)
1733 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1734 }
1735
1736 return $stringtoclean;
1737}
1738
1745function dol_sanitizeEmail($stringtoclean)
1746{
1747 do {
1748 $oldstringtoclean = $stringtoclean;
1749 $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1750 } while ($oldstringtoclean != $stringtoclean);
1751
1752 return $stringtoclean;
1753}
1754
1763function dol_sanitizeKeyCode($str)
1764{
1765 return preg_replace('/[^\w]+/', '', $str);
1766}
1767
1768
1777function dol_string_unaccent($str)
1778{
1779 if (is_null($str)) {
1780 return '';
1781 }
1782
1783 if (utf8_check($str)) {
1784 if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1785 $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1786 return $transliterator->transliterate($str);
1787 }
1788 // See http://www.utf8-chartable.de/
1789 $string = rawurlencode($str);
1790 $replacements = array(
1791 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1792 '%C3%87' => 'C',
1793 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1794 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1795 '%C3%91' => 'N',
1796 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%90' => 'O',
1797 '%C5%A0' => 'S',
1798 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C5%B0' => 'U',
1799 '%C3%9D' => 'Y', '%C5%B8' => 'y',
1800 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1801 '%C3%A7' => 'c',
1802 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1803 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1804 '%C3%B1' => 'n',
1805 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%91' => 'o',
1806 '%C5%A1' => 's',
1807 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C5%B1' => 'u',
1808 '%C3%BD' => 'y', '%C3%BF' => 'y'
1809 );
1810 $string = strtr($string, $replacements);
1811 return rawurldecode($string);
1812 } else {
1813 // See http://www.ascii-code.com/
1814 $string = strtr(
1815 $str,
1816 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1817 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1818 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1819 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1820 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1821 \xF9\xFA\xFB\xFC\xFD\xFF",
1822 "AAAAAAC
1823 EEEEIIIIDN
1824 OOOOOUUUY
1825 aaaaaaceeee
1826 iiiidnooooo
1827 uuuuyy"
1828 );
1829 $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"));
1830 return $string;
1831 }
1832}
1833
1847function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1848{
1849 $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1850 if (empty($keepspaces)) {
1851 $forbidden_chars_to_replace[] = " ";
1852 }
1853 $forbidden_chars_to_remove = array();
1854 //$forbidden_chars_to_remove=array("(",")");
1855
1856 if (is_array($badcharstoreplace)) {
1857 $forbidden_chars_to_replace = $badcharstoreplace;
1858 }
1859 if (is_array($badcharstoremove)) {
1860 $forbidden_chars_to_remove = $badcharstoremove;
1861 }
1862
1863 // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1864 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1865}
1866
1867
1881function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1882{
1883 if ($removetabcrlf) {
1884 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1885 } else {
1886 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
1887 }
1888}
1889
1896function dolSlugify($stringtoslugify)
1897{
1898 $slug = dol_string_unaccent($stringtoslugify);
1899
1900 // Convert special characters to their ASCII equivalents
1901 if (function_exists('iconv')) {
1902 $slug = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $slug);
1903 }
1904
1905 // Convert to lowercase
1906 $slug = strtolower($slug);
1907
1908 // Replace non-alphanumeric characters with hyphens
1909 $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
1910
1911 // Remove leading and trailing hyphens
1912 $slug = trim($slug, '-');
1913
1914 return $slug;
1915}
1916
1925function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1926{
1927 if (is_null($stringtoescape)) {
1928 return '';
1929 }
1930
1931 // escape quotes and backslashes, newlines, etc.
1932 $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1933 //$substitjs['</']='<\/'; // We removed this. Should be useless.
1934 if (empty($noescapebackslashn)) {
1935 $substitjs["\n"] = '\\n';
1936 $substitjs['\\'] = '\\\\';
1937 }
1938 if (empty($mode)) {
1939 $substitjs["'"] = "\\'";
1940 $substitjs['"'] = "\\'";
1941 } elseif ($mode == 1) {
1942 $substitjs["'"] = "\\'";
1943 } elseif ($mode == 2) {
1944 $substitjs['"'] = '\\"';
1945 } elseif ($mode == 3) {
1946 $substitjs["'"] = "\\'";
1947 $substitjs['"'] = "\\\"";
1948 }
1949 return strtr((string) $stringtoescape, $substitjs);
1950}
1951
1961function dol_escape_uri($stringtoescape)
1962{
1963 return rawurlencode($stringtoescape);
1964}
1965
1972function dol_escape_json($stringtoescape)
1973{
1974 return str_replace('"', '\"', $stringtoescape);
1975}
1976
1984function dol_escape_php($stringtoescape, $stringforquotes = 2)
1985{
1986 if (is_null($stringtoescape)) {
1987 return '';
1988 }
1989
1990 if ($stringforquotes == 2) {
1991 return str_replace('"', "'", $stringtoescape);
1992 } elseif ($stringforquotes == 1) {
1993 // We remove the \ char.
1994 // If we allow the \ char, we can have $stringtoescape =
1995 // abc\';phpcodedanger; so the escapement will become
1996 // abc\\';phpcodedanger; and injecting this into
1997 // $a='...' will give $ac='abc\\';phpcodedanger;
1998 $stringtoescape = str_replace('\\', '', $stringtoescape);
1999 return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
2000 }
2001
2002 return 'Bad parameter for stringforquotes in dol_escape_php';
2003}
2004
2011function dol_escape_all($stringtoescape)
2012{
2013 return preg_replace('/[^a-z0-9_]/i', '', $stringtoescape);
2014}
2015
2022function dol_escape_xml($stringtoescape)
2023{
2024 return $stringtoescape;
2025}
2026
2035function dolPrintLabel($s, $escapeonlyhtmltags = 0)
2036{
2037 return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', $escapeonlyhtmltags, 1);
2038}
2039
2047function dolPrintText($s)
2048{
2049 return dol_escape_htmltag(dol_string_nohtmltag($s, 2, 'UTF-8', 0, 0), 0, 1, '', 0, 1);
2050}
2051
2062function dolPrintHTML($s, $allowiframe = 0)
2063{
2064 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr((string) $s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
2065}
2066
2077function dolPrintHTMLForAttribute($s, $escapeonlyhtmltags = 0, $allowothertags = array())
2078{
2079 $allowedtags = array('br', 'b', 'font', 'hr', 'span');
2080 if (!empty($allowothertags) && is_array($allowothertags)) {
2081 $allowedtags = array_merge($allowedtags, $allowothertags);
2082 }
2083 // The dol_htmlentitiesbr will convert simple text into html, including switching accent into HTML entities
2084 // The dol_escape_htmltag will escape html tags.
2085 if ($escapeonlyhtmltags) {
2086 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 0, 0, 0, $allowedtags), 1, -1, '', 1, 1);
2087 } else {
2088 return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, $allowedtags), 1, -1, '', 0, 1);
2089 }
2090}
2091
2100function dolPrintHTMLForAttributeUrl($s)
2101{
2102 // 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)
2103 // The dol_escape_htmltag will escape html chars.
2104 $escapeonlyhtmltags = 1;
2105 return dol_escape_htmltag(dol_string_onlythesehtmltags($s, 1, 1, 1, 0, array()), 0, 0, '', $escapeonlyhtmltags, 1);
2106}
2107
2117function dolPrintHTMLForTextArea($s, $allowiframe = 0)
2118{
2119 return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
2120}
2121
2128function dolPrintPassword($s)
2129{
2130 return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
2131}
2132
2133
2150function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
2151{
2152 if ($noescapetags == 'common') {
2153 $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';
2154 // Add also html5 tags
2155 $noescapetags .= ',header,footer,nav,section,menu,menuitem';
2156 }
2157 if ($cleanalsojavascript) {
2158 $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
2159 }
2160
2161 // escape quotes and backslashes, newlines, etc.
2162 if ($escapeonlyhtmltags) {
2163 $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
2164 } else {
2165 // We make a manipulation by calling the html_entity_decode() to convert content into NON HTML UTF8 string.
2166 // Because content can be or not already HTML.
2167 // For example, this decode &egrave; into è so string is UTF8 (but numbers entities like &#39; is not decoded).
2168 // In a future, we should not need this
2169
2170 $tmp = (string) $stringtoescape;
2171
2172 // We protect the 6 special entities that we don't want to decode.
2173 $tmp = str_ireplace('&lt', '__DONOTDECODELT', $tmp);
2174 $tmp = str_ireplace('&gt', '__DONOTDECODEGT', $tmp);
2175 $tmp = str_ireplace('&amp', '__DONOTDECODEAMP', $tmp);
2176 $tmp = str_ireplace('&quot', '__DONOTDECODEQUOT', $tmp);
2177 $tmp = str_ireplace('&apos', '__DONOTDECODEAPOS', $tmp);
2178 $tmp = str_ireplace('&#39', '__DONOTDECODE39', $tmp);
2179
2180 $tmp = html_entity_decode((string) $tmp, ENT_COMPAT, 'UTF-8'); // Convert entities into UTF8
2181
2182 // We restore the 6 special entities that we don't want to have been decoded by previous command
2183 $tmp = str_ireplace('__DONOTDECODELT', '&lt', $tmp);
2184 $tmp = str_ireplace('__DONOTDECODEGT', '&gt', $tmp);
2185 $tmp = str_ireplace('__DONOTDECODEAMP', '&amp', $tmp);
2186 $tmp = str_ireplace('__DONOTDECODEQUOT', '&quot', $tmp);
2187 $tmp = str_ireplace('__DONOTDECODEAPOS', '&apos', $tmp);
2188 $tmp = str_ireplace('__DONOTDECODE39', '&#39', $tmp);
2189
2190 $tmp = str_ireplace('&#39;', '__SIMPLEQUOTE__', $tmp); // HTML 4
2191 }
2192 if (!$keepb) {
2193 $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
2194 }
2195 if (!$keepn) {
2196 $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
2197 } elseif ($keepn == -1) {
2198 $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
2199 }
2200
2201 if ($escapeonlyhtmltags) {
2202 $tmp = htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
2203 return $tmp;
2204 } else {
2205 // Now we protect all the tags we want to keep
2206 $tmparrayoftags = array();
2207 if ($noescapetags) {
2208 $tmparrayoftags = explode(',', $noescapetags);
2209 }
2210
2211 if (count($tmparrayoftags)) {
2212 // Now we will protect tags (defined into $tmparrayoftags) that we want to keep untouched
2213
2214 $reg = array();
2215 // Remove reserved keywords. They are forbidden in a source string
2216 $tmp = str_ireplace(array('__DOUBLEQUOTE', '__BEGINTAGTOREPLACE', '__ENDTAGTOREPLACE', '__BEGINENDTAGTOREPLACE'), '', $tmp);
2217
2218 foreach ($tmparrayoftags as $tagtoreplace) {
2219 // For case of tag without attributes '<abc>', '</abc>', '<abc />', we protect them to avoid transformation by htmlentities() later
2220 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2221 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2222 $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
2223
2224 // For case of tag with attributes
2225 do {
2226 $tmpold = $tmp;
2227
2228 if (preg_match('/<'.preg_quote($tagtoreplace, '/').'(\s+)([^>]+)>/', $tmp, $reg)) {
2229 // We want to protect the attribute part ... in '<xxx ...>' to avoid transformation by htmlentities() later
2230 $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[2]); // We must never have [ ] inside the attribute string
2231 $tmpattributes = str_ireplace('"', '__DOUBLEQUOTE__', $tmpattributes);
2232 $tmpattributes = preg_replace('/[^a-z0-9_%,\/\?\;\s=&\.\-@:\.#\+]/i', '', $tmpattributes);
2233 //$tmpattributes = preg_replace("/float:\s*(left|right)/", "", $tmpattributes); // Disabled: we must not remove content
2234 $tmp = str_replace('<'.$tagtoreplace.$reg[1].$reg[2].'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
2235 }
2236
2237 $diff = strcmp($tmpold, $tmp);
2238 } while ($diff);
2239 }
2240
2241 $tmp = str_ireplace('&quot', '__DOUBLEQUOTENOSEMICOLON__', $tmp);
2242 $tmp = str_ireplace('&lt', '__LESSTHAN__', $tmp);
2243 $tmp = str_ireplace('&gt', '__GREATERTHAN__', $tmp);
2244 }
2245
2246 // Warning: htmlentities encode HTML tags like <abc> & into &amp; and more (but not &lt; &gt; &quotes; &apos; &#39; &amp; that remains untouched).
2247 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
2248
2249 //print $result;
2250
2251 if (count($tmparrayoftags)) {
2252 // Restore protected tags
2253 foreach ($tmparrayoftags as $tagtoreplace) {
2254 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
2255 $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1>', $result);
2256 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
2257 $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
2258 $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[([^\]]*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
2259 }
2260
2261 $result = str_ireplace('__DOUBLEQUOTE__', '"', $result);
2262
2263 $result = str_ireplace('__DOUBLEQUOTENOSEMICOLON__', '&quot', $result);
2264 $result = str_ireplace('__LESSTHAN__', '&lt', $result);
2265 $result = str_ireplace('__GREATERTHAN__', '&gt', $result);
2266 }
2267
2268 $result = str_ireplace('__SIMPLEQUOTE__', '&#39;', $result);
2269
2270 //$result="\n\n\n".var_export($tmp, true)."\n\n\n".var_export($result, true);
2271
2272 return $result;
2273 }
2274}
2275
2283function dol_strtolower($string, $encoding = "UTF-8")
2284{
2285 if (function_exists('mb_strtolower')) {
2286 return mb_strtolower($string, $encoding);
2287 } else {
2288 return strtolower($string);
2289 }
2290}
2291
2300function dol_strtoupper($string, $encoding = "UTF-8")
2301{
2302 if (function_exists('mb_strtoupper')) {
2303 return mb_strtoupper($string, $encoding);
2304 } else {
2305 return strtoupper($string);
2306 }
2307}
2308
2317function dol_ucfirst($string, $encoding = "UTF-8")
2318{
2319 if (function_exists('mb_substr')) {
2320 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2321 } else {
2322 return ucfirst($string);
2323 }
2324}
2325
2334function dol_ucwords($string, $encoding = "UTF-8")
2335{
2336 if (function_exists('mb_convert_case')) {
2337 return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2338 } else {
2339 return ucwords($string);
2340 }
2341}
2342
2343
2349function getCallerInfoString()
2350{
2351 $backtrace = debug_backtrace();
2352 $msg = "";
2353 if (count($backtrace) >= 1) {
2354 $pos = 1;
2355 if (count($backtrace) == 1) {
2356 $pos = 0;
2357 }
2358 $trace = $backtrace[$pos];
2359 if (isset($trace['file'], $trace['line'])) {
2360 $msg = " From {$trace['file']}:{$trace['line']}.";
2361 }
2362 }
2363 return $msg;
2364}
2365
2388function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2389{
2390 global $conf, $user, $debugbar;
2391
2392 // If syslog module enabled
2393 if (!isModEnabled('syslog')) {
2394 return;
2395 }
2396
2397 // Check if we are into execution of code of a website
2398 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2399 global $website, $websitekey;
2400 if (is_object($website) && !empty($website->ref)) {
2401 $suffixinfilename .= '_website_'.$website->ref;
2402 } elseif (!empty($websitekey)) {
2403 $suffixinfilename .= '_website_'.$websitekey;
2404 }
2405 }
2406
2407 // Check if we have a forced suffix
2408 if (defined('USESUFFIXINLOG')) {
2409 $suffixinfilename .= constant('USESUFFIXINLOG');
2410 }
2411
2412 if ($ident < 0) {
2413 foreach ($conf->loghandlers as $loghandlerinstance) {
2414 $loghandlerinstance->setIdent($ident);
2415 }
2416 }
2417
2418 if (!empty($message)) {
2419 // Test log level
2420 // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2421 $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');
2422
2423 if (!array_key_exists($level, $logLevels)) {
2424 dol_syslog('Error Bad Log Level '.$level, LOG_ERR);
2425 $level = $logLevels[LOG_ERR];
2426 }
2427 if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2428 return;
2429 }
2430
2431 if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2432 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2433 }
2434
2435 // If adding log inside HTML page is required
2436 if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2437 || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2438 $ospid = sprintf("%7s", dol_trunc((string) getmypid(), 7, 'right', 'UTF-8', 1));
2439 $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2440
2441 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2442 }
2443
2444 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2445 // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2446 if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2447 print "\n\n<!-- Log start\n";
2448 print dol_escape_htmltag($message)."\n";
2449 print "Log end -->\n";
2450 }
2451
2452 $data = array(
2453 'message' => $message,
2454 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2455 'level' => $level,
2456 'user' => ((is_object($user) && $user->id) ? $user->login : false),
2457 'ip' => false,
2458 'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2459 'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2460 );
2461
2462 // For log, we want the reliable IP first.
2463 $remoteip = getUserRemoteIP(1); // Get ip when page run on a web server
2464 if (!empty($remoteip)) {
2465 $data['ip'] = $remoteip;
2466 // This is when server run behind a reverse proxy
2467 // A HTTP_X_FORWARDED_FOR as format "ip real of user, ip of proxy1, ip of proxy2, ..."
2468 // $data['ip'] is last
2469 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2470 $tmpips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
2471 $data['ip'] = '';
2472 $foundremoteip = 0;
2473 $j = 0;
2474 foreach ($tmpips as $tmpip) {
2475 $tmpip = trim($tmpip);
2476 if (strtolower($tmpip) == strtolower($remoteip)) {
2477 $foundremoteip = 1;
2478 }
2479 if (empty($data['ip'])) {
2480 $data['ip'] = $tmpip;
2481 } else {
2482 $j++;
2483 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2484 }
2485 }
2486 if (!$foundremoteip) {
2487 $j++;
2488 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2489 }
2490 $data['ip'] .= (($j > 0) ? ']' : '');
2491 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) ) {
2492 $tmpips = explode(',', $_SERVER['HTTP_CLIENT_IP']);
2493 $data['ip'] = '';
2494 $foundremoteip = 0;
2495 $j = 0;
2496 foreach ($tmpips as $tmpip) {
2497 $tmpip = trim($tmpip);
2498 if (strtolower($tmpip) == strtolower($remoteip)) {
2499 $foundremoteip = 1;
2500 }
2501 if (empty($data['ip'])) {
2502 $data['ip'] = $tmpip;
2503 } else {
2504 $j++;
2505 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$tmpip;
2506 }
2507 }
2508 if (!$foundremoteip) {
2509 $j++;
2510 $data['ip'] .= (($j == 1) ? ' [via ' : ',').$remoteip;
2511 }
2512 $data['ip'] .= (($j > 0) ? ']' : '');
2513 }
2514 } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2515 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2516 $data['ip'] = $_SERVER['SERVER_ADDR'];
2517 } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2518 // 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).
2519 $data['ip'] = $_SERVER['COMPUTERNAME'];
2520 } else {
2521 $data['ip'] = '???';
2522 }
2523
2524 if (!empty($_SERVER['USERNAME'])) {
2525 // 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).
2526 $data['osuser'] = $_SERVER['USERNAME'];
2527 } elseif (!empty($_SERVER['LOGNAME'])) {
2528 // 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).
2529 $data['osuser'] = $_SERVER['LOGNAME'];
2530 }
2531
2532 // Loop on each log handler and send output
2533 foreach ($conf->loghandlers as $loghandlerinstance) {
2534 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2535 continue;
2536 }
2537 $loghandlerinstance->export($data, $suffixinfilename);
2538 }
2539 unset($data);
2540 }
2541
2542 if ($ident > 0) {
2543 foreach ($conf->loghandlers as $loghandlerinstance) {
2544 $loghandlerinstance->setIdent($ident);
2545 }
2546 }
2547}
2548
2560function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2561{
2562 global $langs, $db;
2563
2564 $form = new Form($db);
2565
2566 $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2567 if (empty($templatenameforexport)) {
2568 $templatenameforexport = 'website_'.$website->ref;
2569 }
2570
2571 $out = '';
2572 $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2573
2574 // for generate popup
2575 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2576 $out .= 'jQuery(document).ready(function () {';
2577 $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2578 $out .= ' var dialogHtml = \'';
2579
2580 $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2581 $dialogcontent .= ' <div style="margin-top: 20px;">';
2582 $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2583 $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2584 $dialogcontent .= ' </div>';
2585 $dialogcontent .= ' <br>';
2586 $dialogcontent .= ' <div style="margin-top: 20px;">';
2587 $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2588 $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2589 $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2590 $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2591 $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>';
2592 $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2593 $dialogcontent .= ' </form>';
2594 $dialogcontent .= ' </div>';
2595 $dialogcontent .= ' </div>';
2596
2597 $out .= dol_escape_js($dialogcontent);
2598
2599 $out .= '\';';
2600
2601
2602 // Add the content of the dialog to the body of the page
2603 $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2604 $out .= ' if ($dialog.length > 0) {
2605 $dialog.remove();
2606 }
2607 jQuery("body").append(dialogHtml);';
2608
2609 // Configuration of popup
2610 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2611 $out .= ' autoOpen: false,';
2612 $out .= ' modal: true,';
2613 $out .= ' height: 290,';
2614 $out .= ' width: "40%",';
2615 $out .= ' title: "' . dol_escape_js($label) . '",';
2616 $out .= ' });';
2617
2618 // Simulate a click on the original "submit" input to export the site.
2619 $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2620 $out .= ' console.log("Clic on exportsite.");';
2621 $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2622 $out .= ' console.log("element founded:", target.length > 0);';
2623 $out .= ' if (target.length > 0) { target.click(); }';
2624 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2625 $out .= ' });';
2626
2627 // open popup
2628 $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2629 $out .= ' return false;';
2630 $out .= ' });';
2631 $out .= '});';
2632 $out .= '</script>';
2633
2634 return $out;
2635}
2636
2637
2654function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2655{
2656 global $conf;
2657
2658 if (strpos($url, '?') > 0) {
2659 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2660 } else {
2661 $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2662 }
2663
2664 $out = '';
2665
2666 $backtopagejsfieldsid = '';
2667 $backtopagejsfieldslabel = '';
2668 if ($backtopagejsfields) {
2669 $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2670 if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2671 $backtopagejsfields = $name.":".$backtopagejsfields;
2672 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2673 } else {
2674 $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2675 }
2676 $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2677 $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2678 $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2679 }
2680
2681 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2682 $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2683 $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2684 if (empty($conf->use_javascript_ajax)) {
2685 $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2686 } elseif ($jsonopen) {
2687 $out .= ' href="#" onclick="'.$jsonopen.'"';
2688 } else {
2689 $out .= ' href="#"';
2690 }
2691 $out .= '>'.$buttonstring.'</a>';
2692
2693 if (!empty($conf->use_javascript_ajax)) {
2694 // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2695 $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2696 $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2697 $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2698 $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2699
2700 $out .= '<!-- Add js code to open dialog popup on dialog -->';
2701 $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2702 jQuery(document).ready(function () {
2703 jQuery(".button_'.$name.'").click(function () {
2704 console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2705 var $tmpdialog = $(\'#idfordialog'.$name.'\');
2706 $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2707 $tmpdialog.dialog({
2708 autoOpen: false,
2709 modal: true,
2710 height: (window.innerHeight - 150),
2711 width: \'80%\',
2712 title: \''.dol_escape_js($label).'\',
2713 open: function (event, ui) {
2714 console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2715 },
2716 close: function (event, ui) {
2717 var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2718 var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2719 console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2720 if (returnedid != "" && returnedid != "div for returned id") {
2721 jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2722 }
2723 if (returnedlabel != "" && returnedlabel != "div for returned label") {
2724 jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2725 }
2726 }
2727 });
2728
2729 $tmpdialog.dialog(\'open\');
2730 return false;
2731 });
2732 });
2733 </script>';
2734 }
2735 return $out;
2736}
2737
2754function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2755{
2756 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2757}
2758
2775function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2776{
2777 global $conf, $langs, $hookmanager;
2778
2779 // Show title
2780 $showtitle = 1;
2781 if (!empty($conf->dol_optimize_smallscreen)) {
2782 $showtitle = 0;
2783 }
2784
2785 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2786
2787 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2788 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2789 }
2790
2791 // Show right part
2792 if ($morehtmlright) {
2793 $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.
2794 }
2795
2796 // Show tabs
2797
2798 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2799 $maxkey = -1;
2800 if (is_array($links) && !empty($links)) {
2801 $keys = array_keys($links);
2802 if (count($keys)) {
2803 $maxkey = max($keys);
2804 }
2805 }
2806
2807 // Show tabs
2808 // if =0 we don't use the feature
2809 if (empty($limittoshow)) {
2810 $limittoshow = getDolGlobalInt('MAIN_MAXTABS_IN_CARD', 99);
2811 }
2812 if (!empty($conf->dol_optimize_smallscreen)) {
2813 $limittoshow = 2;
2814 }
2815
2816 $displaytab = 0;
2817 $nbintab = 0;
2818 $popuptab = 0;
2819 $outmore = '';
2820 for ($i = 0; $i <= $maxkey; $i++) {
2821 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2822 // If active tab is already present
2823 if ($i >= $limittoshow) {
2824 $limittoshow--;
2825 }
2826 }
2827 }
2828
2829 for ($i = 0; $i <= $maxkey; $i++) {
2830 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2831 $isactive = true;
2832 } else {
2833 $isactive = false;
2834 }
2835
2836 if ($i < $limittoshow || $isactive) {
2837 // Output entry with a visible tab
2838 $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])).' -->';
2839
2840 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2841 if (!empty($links[$i][0])) {
2842 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2843 } else {
2844 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2845 }
2846 } elseif (!empty($links[$i][1])) {
2847 //print "x $i $active ".$links[$i][2]." z";
2848 $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2849
2850 if (!empty($links[$i][0])) {
2851 $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2852 $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).'">';
2853 }
2854
2855 if ($displaytab == 0 && $picto) {
2856 $out .= img_picto($title, $picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle paddingright marginrightonlyshort');
2857 }
2858
2859 $out .= $links[$i][1];
2860 if (!empty($links[$i][0])) {
2861 $out .= '</a>'."\n";
2862 }
2863 $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2864 $out .= '</div>';
2865 }
2866
2867 $out .= '</div>';
2868 } else {
2869 // Add entry into the combo popup with the other tabs
2870 if (!$popuptab) {
2871 $popuptab = 1;
2872 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2873 }
2874 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2875 if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2876 if (!empty($links[$i][0])) {
2877 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2878 } else {
2879 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2880 }
2881 } elseif (!empty($links[$i][1])) {
2882 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2883 $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.
2884 $outmore .= '</a>'."\n";
2885 }
2886 $outmore .= '</div>';
2887
2888 $nbintab++;
2889 }
2890
2891 $displaytab = $i + 1;
2892 }
2893 if ($popuptab) {
2894 $outmore .= '</div>';
2895 }
2896
2897 if ($popuptab) { // If there is some tabs not shown
2898 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2899 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2900 $widthofpopup = 200;
2901
2902 $tabsname = $moretabssuffix;
2903 if (empty($tabsname)) {
2904 $tabsname = str_replace("@", "", $picto);
2905 }
2906 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2907 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2908 $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".
2909 }
2910 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2911 $out .= $outmore;
2912 $out .= '</div>';
2913 $out .= '<div></div>';
2914 $out .= "</div>\n";
2915
2916 $out .= '<script nonce="'.getNonce().'">';
2917 $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2918 var x = this.offsetLeft, y = this.offsetTop;
2919 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2920 if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2921 $('#moretabsList".$tabsname."').css('".$right."','8px');
2922 }
2923 $('#moretabsList".$tabsname."').css('".$left."','auto');
2924 });
2925 ";
2926 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2927 $out .= "</script>";
2928 }
2929
2930 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2931 $out .= "</div>\n";
2932 }
2933
2934 if (!$notab || $notab == -1 || $notab == -2 || $notab == -3 || $notab == -4) {
2935 $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ((($notab == -3 || $notab == -4) ? ' noborderbottom' : '').($notab == -4 ? '' : ' tabBarWithBottom'))));
2936 $out .= '">'."\n";
2937 }
2938 if (!empty($dragdropfile)) {
2939 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2940 $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2941 }
2942 $parameters = array('tabname' => $active, 'out' => $out);
2943 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2944 if ($reshook > 0) {
2945 $out = $hookmanager->resPrint;
2946 }
2947
2948 return $out;
2949}
2950
2958function dol_fiche_end($notab = 0)
2959{
2960 print dol_get_fiche_end($notab);
2961}
2962
2969function dol_get_fiche_end($notab = 0)
2970{
2971 if (!$notab || $notab == -1) {
2972 return "\n</div>\n";
2973 } else {
2974 return '';
2975 }
2976}
2977
2997function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2998{
2999 global $conf, $form, $user, $langs, $hookmanager, $action;
3000
3001 $error = 0;
3002
3003 $maxvisiblephotos = 1;
3004 $showimage = 1;
3005 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
3006 // @phan-suppress-next-line PhanUndeclaredMethod
3007 $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
3008 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
3009 $showbarcode = 0;
3010 }
3011 $modulepart = 'unknown';
3012
3013 if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
3014 $modulepart = $object->element;
3015 } elseif ($object->element == 'member') {
3016 $modulepart = 'memberphoto';
3017 } elseif ($object->element == 'user') {
3018 $modulepart = 'userphoto';
3019 }
3020
3021 if (class_exists("Imagick")) {
3022 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
3023 $modulepart = $object->element;
3024 } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
3025 $modulepart = 'ficheinter';
3026 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3027 $modulepart = 'contract';
3028 } elseif ($object->element == 'order_supplier') {
3029 $modulepart = 'supplier_order';
3030 } elseif ($object->element == 'invoice_supplier') {
3031 $modulepart = 'supplier_invoice';
3032 }
3033 }
3034
3035 if ($object->element == 'product') {
3037 '@phan-var-force Product $object';
3038 $width = 80;
3039 $cssclass = 'photowithmargin photoref';
3040 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
3041 $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
3042 if ($conf->browser->layout == 'phone') {
3043 $maxvisiblephotos = 1;
3044 }
3045 if ($showimage) {
3046 $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>';
3047 } else {
3048 if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
3049 $nophoto = '';
3050 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3051 } else { // Show no photo link
3052 $nophoto = '/public/theme/common/nophoto.png';
3053 $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>';
3054 }
3055 }
3056 } elseif ($object->element == 'category') {
3058 '@phan-var-force Categorie $object';
3059 $width = 80;
3060 $cssclass = 'photowithmargin photoref';
3061 $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
3062 $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
3063 if ($conf->browser->layout == 'phone') {
3064 $maxvisiblephotos = 1;
3065 }
3066 if ($showimage) {
3067 $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>';
3068 } else {
3069 if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
3070 $nophoto = '';
3071 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3072 } else { // Show no photo link
3073 $nophoto = '/public/theme/common/nophoto.png';
3074 $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>';
3075 }
3076 }
3077 } elseif ($object->element == 'bom') {
3079 '@phan-var-force Bom $object';
3080 $width = 80;
3081 $cssclass = 'photowithmargin photoref';
3082 $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
3083 $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
3084 if ($conf->browser->layout == 'phone') {
3085 $maxvisiblephotos = 1;
3086 }
3087 if ($showimage) {
3088 $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>';
3089 } else {
3090 if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
3091 $nophoto = '';
3092 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3093 } else { // Show no photo link
3094 $nophoto = '/public/theme/common/nophoto.png';
3095 $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>';
3096 }
3097 }
3098 } elseif ($object->element == 'ticket') {
3099 $width = 80;
3100 $cssclass = 'photoref';
3102 '@phan-var-force Ticket $object';
3103 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
3104 $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
3105 if ($conf->browser->layout == 'phone') {
3106 $maxvisiblephotos = 1;
3107 }
3108
3109 if ($showimage) {
3110 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
3111 if ($object->nbphoto > 0) {
3112 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
3113 } else {
3114 $showimage = 0;
3115 }
3116 }
3117 if (!$showimage) {
3118 if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
3119 $nophoto = '';
3120 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
3121 } else { // Show no photo link
3122 $nophoto = img_picto('No photo', 'object_ticket');
3123 $morehtmlleft .= '<!-- No photo to show -->';
3124 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3125 $morehtmlleft .= $nophoto;
3126 $morehtmlleft .= '</div></div>';
3127 }
3128 }
3129 } else {
3130 if ($showimage) {
3131 if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
3132 $phototoshow = '';
3133 // Check if a preview file is available
3134 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
3135 $objectref = dol_sanitizeFileName($object->ref);
3136 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
3137 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
3138 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
3139 $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
3140 } else {
3141 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
3142 }
3143 if (empty($subdir)) {
3144 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
3145 }
3146
3147 $filepath = $dir_output.$subdir."/";
3148
3149 $filepdf = $filepath.$objectref.".pdf";
3150 $relativepath = $subdir.'/'.$objectref.'.pdf';
3151
3152 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
3153 $fileimage = $filepdf.'_preview.png';
3154 $relativepathimage = $relativepath.'_preview.png';
3155
3156 $pdfexists = file_exists($filepdf);
3157
3158 // If PDF file exists
3159 if ($pdfexists) {
3160 // Conversion du PDF en image png si fichier png non existent
3161 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
3162 if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
3163 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3164 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
3165 if ($ret < 0) {
3166 $error++;
3167 }
3168 }
3169 }
3170 }
3171
3172 if ($pdfexists && !$error) {
3173 $heightforphotref = 80;
3174 if (!empty($conf->dol_optimize_smallscreen)) {
3175 $heightforphotref = 60;
3176 }
3177 // If the preview file is found
3178 if (file_exists($fileimage)) {
3179 $phototoshow = '<div class="photoref">';
3180 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
3181 $phototoshow .= '</div>';
3182 }
3183 }
3184 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
3185 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
3186 }
3187
3188 if ($phototoshow) {
3189 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
3190 $morehtmlleft .= $phototoshow;
3191 $morehtmlleft .= '</div>';
3192 }
3193 }
3194
3195 if (empty($phototoshow)) { // Show No photo link (picto of object)
3196 if ($object->element == 'action') {
3197 $width = 80;
3198 $cssclass = 'photorefcenter';
3199 $nophoto = img_picto('No photo', 'title_agenda');
3200 } else {
3201 $width = 14;
3202 $cssclass = 'photorefcenter';
3203 $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
3204 $prefix = 'object_';
3205 if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
3206 $picto = 'project'; // instead of projectpub
3207 }
3208 if (strpos($picto, 'fontawesome_') !== false) {
3209 $prefix = '';
3210 }
3211 $nophoto = img_picto('No photo', $prefix.$picto);
3212 }
3213 $morehtmlleft .= '<!-- No photo to show -->';
3214 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
3215 $morehtmlleft .= $nophoto;
3216 $morehtmlleft .= '</div></div>';
3217 }
3218 }
3219 }
3220
3221 // Show barcode
3222 if ($showbarcode) {
3223 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
3224 }
3225
3226 if ($object->element == 'societe') {
3227 if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3228 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
3229 } else {
3230 $morehtmlstatus .= $object->getLibStatut(6);
3231 }
3232 } elseif ($object->element == 'product') {
3233 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
3234 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3235 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
3236 } else {
3237 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
3238 }
3239 $morehtmlstatus .= ' &nbsp; ';
3240 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
3241 if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
3242 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
3243 } else {
3244 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
3245 }
3246 } elseif (in_array($object->element, array('salary'))) {
3247 $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
3248 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3249 $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
3250 }
3251 $morehtmlstatus .= $tmptxt;
3252 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier'))) { // TODO Move this to use ->alreadypaid
3253 $totalallpayments = $object->getSommePaiement(0);
3254 $totalallpayments += $object->getSumCreditNotesUsed(0);
3255 $totalallpayments += $object->getSumDepositsUsed(0);
3256 $tmptxt = $object->getLibStatut(6, $totalallpayments);
3257 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3258 $tmptxt = $object->getLibStatut(5, $totalallpayments);
3259 }
3260 $morehtmlstatus .= $tmptxt;
3261 } elseif (in_array($object->element, array('chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
3262 $tmptxt = $object->getLibStatut(6, $object->totalpaid);
3263 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3264 $tmptxt = $object->getLibStatut(5, $object->totalpaid);
3265 }
3266 $morehtmlstatus .= $tmptxt;
3267 } elseif ($object->element == 'contrat' || $object->element == 'contract') {
3268 if ($object->statut == 0) {
3269 $morehtmlstatus .= $object->getLibStatut(5);
3270 } else {
3271 $morehtmlstatus .= $object->getLibStatut(4);
3272 }
3273 } elseif ($object->element == 'facturerec') {
3274 '@phan-var-force FactureRec $object';
3275 if ($object->frequency == 0) {
3276 $morehtmlstatus .= $object->getLibStatut(2);
3277 } else {
3278 $morehtmlstatus .= $object->getLibStatut(5);
3279 }
3280 } elseif ($object->element == 'project_task') {
3281 $object->fk_statut = 1;
3282 $object->status = 1;
3283 if ($object->progress > 0) {
3284 $object->fk_statut = 2;
3285 $object->status = 2;
3286 }
3287 if ($object->progress >= 100) {
3288 $object->fk_statut = 3;
3289 $object->status = 3;
3290 }
3291 $tmptxt = $object->getLibStatut(5);
3292 $morehtmlstatus .= $tmptxt; // No status on task
3293 } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
3294 $tmptxt = $object->getLibStatut(6);
3295 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
3296 $tmptxt = $object->getLibStatut(5);
3297 }
3298 $morehtmlstatus .= $tmptxt;
3299 }
3300
3301 // Add if object was dispatched "into accountancy"
3302 if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
3303 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
3304 if (method_exists($object, 'getVentilExportCompta')) {
3305 $accounted = $object->getVentilExportCompta();
3306 $langs->load("accountancy");
3307 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
3308 }
3309 }
3310
3311 // Add alias for thirdparty
3312 if (!empty($object->name_alias)) {
3313 '@phan-var-force Societe $object';
3314 $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
3315 }
3316
3317 // Add label
3318 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
3319 if (!empty($object->label)) {
3320 $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
3321 }
3322 }
3323 // Show address and email
3324 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
3325 $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
3326 if ($moreaddress) {
3327 $morehtmlref .= '<div class="refidno refaddress">';
3328 $morehtmlref .= $moreaddress;
3329 $morehtmlref .= '</div>';
3330 }
3331 }
3332 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)) {
3333 $morehtmlref .= '<div style="clear: both;"></div>';
3334 $morehtmlref .= '<div class="refidno opacitymedium">';
3335 $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
3336 $morehtmlref .= '</div>';
3337 }
3338
3339 $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
3340 $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
3341 if ($reshook < 0) {
3342 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3343 } elseif (empty($reshook)) {
3344 $morehtmlref .= $hookmanager->resPrint;
3345 } elseif ($reshook > 0) {
3346 $morehtmlref = $hookmanager->resPrint;
3347 }
3348
3349 // $morehtml is the right part (link "Back to list")
3350 // $morehtmlleft is the picto or photo of banner
3351 // $morehtmlstatus is part under the status
3352 // $morehtmlright is part of htmlright
3353
3354 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3355 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3356 print '</div>';
3357 print '<div class="underrefbanner clearboth"></div>';
3358}
3359
3369function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3370{
3371 global $langs;
3372 $ret = '';
3373 if ($fieldrequired) {
3374 $ret .= '<span class="fieldrequired">';
3375 }
3376 $ret .= '<label for="'.$fieldkey.'">';
3377 $ret .= $langs->trans($langkey);
3378 $ret .= '</label>';
3379 if ($fieldrequired) {
3380 $ret .= '</span>';
3381 }
3382 return $ret;
3383}
3384
3398function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3399{
3400 global $langs, $hookmanager;
3401
3402 $ret = '';
3403 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3404
3405 // See format of addresses on https://en.wikipedia.org/wiki/Address
3406 // Address
3407 if (empty($mode)) {
3408 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3409 }
3410 // Zip/Town/State
3411 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3412 // US: title firstname name \n address lines \n town, state, zip \n country
3413 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3414 $ret .= (($ret && $town) ? $sep : '').$town;
3415
3416 if (!empty($object->state)) {
3417 $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3418 }
3419 if (!empty($object->zip)) {
3420 $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3421 }
3422 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3423 // UK: title firstname name \n address lines \n town state \n zip \n country
3424 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3425 $ret .= ($ret ? $sep : '').$town;
3426 if (!empty($object->state)) {
3427 $ret .= ($ret ? ", " : '').$object->state;
3428 }
3429 if (!empty($object->zip)) {
3430 $ret .= ($ret ? $sep : '').$object->zip;
3431 }
3432 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3433 // ES: title firstname name \n address lines \n zip town \n state \n country
3434 $ret .= ($ret ? $sep : '').$object->zip;
3435 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3436 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3437 if (!empty($object->state)) {
3438 $ret .= $sep.$object->state;
3439 }
3440 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3441 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3442 // See https://www.sljfaq.org/afaq/addresses.html
3443 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3444 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3445 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3446 // IT: title firstname name\n address lines \n zip town state_code \n country
3447 $ret .= ($ret ? $sep : '').$object->zip;
3448 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3449 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3450 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3451 } else {
3452 // Other: title firstname name \n address lines \n zip town[, state] \n country
3453 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3454 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3455 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3456 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3457 $ret .= ($ret ? ", " : '').$object->state;
3458 }
3459 }
3460
3461 if (!is_object($outputlangs)) {
3462 $outputlangs = $langs;
3463 }
3464 if ($withcountry) {
3465 $langs->load("dict");
3466 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3467 }
3468 if ($hookmanager) {
3469 $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3470 $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3471 if ($reshook > 0) {
3472 $ret = '';
3473 }
3474 $ret .= $hookmanager->resPrint;
3475 }
3476
3477 return $ret;
3478}
3479
3480
3481
3491function dol_strftime($fmt, $ts = false, $is_gmt = false)
3492{
3493 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3494 return dol_print_date($ts, $fmt, $is_gmt);
3495 } else {
3496 return 'Error date outside supported range';
3497 }
3498}
3499
3521function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3522{
3523 global $conf, $langs;
3524
3525 // If date undefined or "", we return ""
3526 if (dol_strlen($time) == 0) {
3527 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3528 }
3529
3530 if ($tzoutput === 'auto') {
3531 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3532 }
3533
3534 // Clean parameters
3535 $to_gmt = false;
3536 $offsettz = $offsetdst = 0;
3537 if ($tzoutput) {
3538 $to_gmt = true; // For backward compatibility
3539 if (is_string($tzoutput)) {
3540 if ($tzoutput == 'tzserver') {
3541 $to_gmt = false;
3542 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3543 // @phan-suppress-next-line PhanPluginRedundantAssignment
3544 $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3545 // @phan-suppress-next-line PhanPluginRedundantAssignment
3546 $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3547 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3548 $to_gmt = true;
3549 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3550
3551 if (class_exists('DateTimeZone')) {
3552 $user_date_tz = new DateTimeZone($offsettzstring);
3553 $user_dt = new DateTime();
3554 $user_dt->setTimezone($user_date_tz);
3555 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3556 $offsettz = $user_dt->getOffset(); // should include dst ?
3557 } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3558 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3559 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3560 }
3561 }
3562 }
3563 }
3564 if (!is_object($outputlangs)) {
3565 $outputlangs = $langs;
3566 }
3567 if (!$format) {
3568 $format = 'daytextshort';
3569 }
3570
3571 // Do we have to reduce the length of date (year on 2 chars) to save space.
3572 // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3573 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3574 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3575 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3576 if ($formatwithoutreduce != $format) {
3577 $format = $formatwithoutreduce;
3578 $reduceformat = 1;
3579 } // so format 'dayreduceformat' is processed like day
3580
3581 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3582 // TODO Add format daysmallyear and dayhoursmallyear
3583 if ($format == 'day') {
3584 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3585 } elseif ($format == 'hour') {
3586 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3587 } elseif ($format == 'hourduration') {
3588 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3589 } elseif ($format == 'daytext') {
3590 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3591 } elseif ($format == 'daytextshort') {
3592 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3593 } elseif ($format == 'dayhour') {
3594 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3595 } elseif ($format == 'dayhoursec') {
3596 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3597 } elseif ($format == 'dayhourtext') {
3598 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3599 } elseif ($format == 'dayhourtextshort') {
3600 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3601 } elseif ($format == 'dayhourlog') {
3602 // Format not sensitive to language
3603 $format = '%Y%m%d%H%M%S';
3604 } elseif ($format == 'dayhourlogsmall') {
3605 // Format not sensitive to language
3606 $format = '%y%m%d%H%M';
3607 } elseif ($format == 'dayhourldap') {
3608 $format = '%Y%m%d%H%M%SZ';
3609 } elseif ($format == 'dayhourxcard') {
3610 $format = '%Y%m%dT%H%M%SZ';
3611 } elseif ($format == 'dayxcard') {
3612 $format = '%Y%m%d';
3613 } elseif ($format == 'dayrfc') {
3614 $format = '%Y-%m-%d'; // DATE_RFC3339
3615 } elseif ($format == 'dayhourrfc') {
3616 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3617 } elseif ($format == 'standard') {
3618 $format = '%Y-%m-%d %H:%M:%S';
3619 }
3620
3621 if ($reduceformat) {
3622 $format = str_replace('%Y', '%y', $format);
3623 $format = str_replace('yyyy', 'yy', $format);
3624 }
3625
3626 // Clean format
3627 if (preg_match('/%b/i', $format)) { // There is some text to translate
3628 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3629 $format = str_replace('%b', '__b__', $format);
3630 $format = str_replace('%B', '__B__', $format);
3631 }
3632 if (preg_match('/%a/i', $format)) { // There is some text to translate
3633 // We inhibit translation to text made by strftime functions. We will use trans instead later.
3634 $format = str_replace('%a', '__a__', $format);
3635 $format = str_replace('%A', '__A__', $format);
3636 }
3637
3638 // Analyze date
3639 $reg = array();
3640 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
3641 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"]));
3642 return '';
3643 } 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
3644 // This part of code should not be used anymore.
3645 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);
3646 //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3647 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3648 $syear = (!empty($reg[1]) ? $reg[1] : '');
3649 $smonth = (!empty($reg[2]) ? $reg[2] : '');
3650 $sday = (!empty($reg[3]) ? $reg[3] : '');
3651 $shour = (!empty($reg[4]) ? $reg[4] : '');
3652 $smin = (!empty($reg[5]) ? $reg[5] : '');
3653 $ssec = (!empty($reg[6]) ? $reg[6] : '');
3654
3655 $time = dol_mktime((int) $shour, (int) $smin, (int) $ssec, (int) $smonth, (int) $sday, (int) $syear, true);
3656
3657 if ($to_gmt) {
3658 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3659 } else {
3660 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3661 }
3662 $dtts = new DateTime();
3663 $dtts->setTimestamp($time);
3664 $dtts->setTimezone($tzo);
3665 $newformat = str_replace(
3666 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3667 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3668 $format
3669 );
3670 $ret = $dtts->format($newformat);
3671 $ret = str_replace(
3672 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3673 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3674 $ret
3675 );
3676 } else {
3677 // Date is a timestamps
3678 if ($time < 100000000000) { // Protection against bad date values
3679 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3680
3681 if ($to_gmt) {
3682 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3683 } else {
3684 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3685 }
3686 $dtts = new DateTime();
3687 $dtts->setTimestamp($timetouse);
3688 $dtts->setTimezone($tzo);
3689 $newformat = str_replace(
3690 array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3691 array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3692 $format
3693 );
3694 $ret = $dtts->format($newformat);
3695 $ret = str_replace(
3696 array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3697 array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3698 $ret
3699 );
3700 //var_dump($ret);exit;
3701 } else {
3702 $ret = 'Bad value '.$time.' for date';
3703 }
3704 }
3705
3706 if (preg_match('/__b__/i', $format)) {
3707 $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3708
3709 if ($to_gmt) {
3710 $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3711 } else {
3712 $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3713 }
3714 $dtts = new DateTime();
3715 $dtts->setTimestamp($timetouse);
3716 $dtts->setTimezone($tzo);
3717 $month = (int) $dtts->format("m");
3718 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3719 if ($encodetooutput) {
3720 $monthtext = $outputlangs->transnoentities('Month'.$month);
3721 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3722 } else {
3723 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3724 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3725 }
3726 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3727 $ret = str_replace('__b__', $monthtextshort, $ret);
3728 $ret = str_replace('__B__', $monthtext, $ret);
3729 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3730 //return $ret;
3731 }
3732 if (preg_match('/__a__/i', $format)) {
3733 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3734 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3735
3736 if ($to_gmt) {
3737 $tzo = new DateTimeZone('UTC');
3738 } else {
3739 $tzo = new DateTimeZone(date_default_timezone_get());
3740 }
3741 $dtts = new DateTime();
3742 $dtts->setTimestamp($timetouse);
3743 $dtts->setTimezone($tzo);
3744 $w = $dtts->format("w");
3745 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3746
3747 $ret = str_replace('__A__', $dayweek, $ret);
3748 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3749 }
3750
3751 return $ret;
3752}
3753
3754
3775function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3776{
3777 if ($timestamp === '') {
3778 return array();
3779 }
3780
3781 $datetimeobj = new DateTime();
3782 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3783 if ($forcetimezone) {
3784 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3785 }
3786 $arrayinfo = array(
3787 'year' => ((int) date_format($datetimeobj, 'Y')),
3788 'mon' => ((int) date_format($datetimeobj, 'm')),
3789 'mday' => ((int) date_format($datetimeobj, 'd')),
3790 'wday' => ((int) date_format($datetimeobj, 'w')),
3791 'yday' => ((int) date_format($datetimeobj, 'z')),
3792 'hours' => ((int) date_format($datetimeobj, 'H')),
3793 'minutes' => ((int) date_format($datetimeobj, 'i')),
3794 'seconds' => ((int) date_format($datetimeobj, 's')),
3795 '0' => $timestamp
3796 );
3797
3798 return $arrayinfo;
3799}
3800
3822function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3823{
3824 global $conf;
3825 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3826
3827 if ($gm === 'auto') {
3828 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3829 }
3830 //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3831
3832 // Clean parameters
3833 if ($hour == -1 || empty($hour)) {
3834 $hour = 0;
3835 }
3836 if ($minute == -1 || empty($minute)) {
3837 $minute = 0;
3838 }
3839 if ($second == -1 || empty($second)) {
3840 $second = 0;
3841 }
3842
3843 // Check parameters
3844 if ($check) {
3845 if (!$month || !$day) {
3846 return '';
3847 }
3848 if ($day > 31) {
3849 return '';
3850 }
3851 if ($month > 12) {
3852 return '';
3853 }
3854 if ($hour < 0 || $hour > 24) {
3855 return '';
3856 }
3857 if ($minute < 0 || $minute > 60) {
3858 return '';
3859 }
3860 if ($second < 0 || $second > 60) {
3861 return '';
3862 }
3863 }
3864
3865 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3866 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3867 $localtz = new DateTimeZone($default_timezone);
3868 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3869 // We use dol_tz_string first because it is more reliable.
3870 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3871 try {
3872 $localtz = new DateTimeZone($default_timezone);
3873 } catch (Exception $e) {
3874 dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3875 $default_timezone = @date_default_timezone_get();
3876 }
3877 } elseif (strrpos($gm, "tz,") !== false) {
3878 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3879 try {
3880 $localtz = new DateTimeZone($timezone);
3881 } catch (Exception $e) {
3882 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3883 }
3884 }
3885
3886 if (empty($localtz)) {
3887 $localtz = new DateTimeZone('UTC');
3888 }
3889 //var_dump($localtz);
3890 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3891 $dt = new DateTime('now', $localtz);
3892 $dt->setDate((int) $year, (int) $month, (int) $day);
3893 $dt->setTime((int) $hour, (int) $minute, (int) $second);
3894 $date = $dt->getTimestamp(); // should include daylight saving time
3895 //var_dump($date);
3896 return $date;
3897}
3898
3899
3910function dol_now($mode = 'auto')
3911{
3912 $ret = 0;
3913
3914 if ($mode === 'auto') {
3915 $mode = 'gmt';
3916 }
3917
3918 if ($mode == 'gmt') {
3919 $ret = time(); // Time for now at greenwich.
3920 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3921 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3922 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3923 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3924 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3925 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3926 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3927 // $ret=dol_now('gmt')+($tzsecond*3600);
3928 //}
3929 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3930 // Time for now with user timezone added
3931 //print 'time: '.time();
3932 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3933 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3934 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3935 }
3936
3937 return $ret;
3938}
3939
3940
3949function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3950{
3951 global $conf, $langs;
3952 $level = 1024;
3953
3954 if (!empty($conf->dol_optimize_smallscreen)) {
3955 $shortunit = 1;
3956 }
3957
3958 // Set value text
3959 if (empty($shortvalue) || $size < ($level * 10)) {
3960 $ret = $size;
3961 $textunitshort = $langs->trans("b");
3962 $textunitlong = $langs->trans("Bytes");
3963 } else {
3964 $ret = round($size / $level, 0);
3965 $textunitshort = $langs->trans("Kb");
3966 $textunitlong = $langs->trans("KiloBytes");
3967 }
3968 // Use long or short text unit
3969 if (empty($shortunit)) {
3970 $ret .= ' '.$textunitlong;
3971 } else {
3972 $ret .= ' '.$textunitshort;
3973 }
3974
3975 return $ret;
3976}
3977
3988function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3989{
3990 global $langs;
3991
3992 if (empty($url)) {
3993 return '';
3994 }
3995
3996 $linkstart = '<a href="';
3997 if (!preg_match('/^http/i', $url)) {
3998 $linkstart .= 'http://';
3999 }
4000 $linkstart .= $url;
4001 $linkstart .= '"';
4002 if ($target) {
4003 $linkstart .= ' target="'.$target.'"';
4004 }
4005 $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
4006 $linkstart .= '>';
4007
4008 $link = '';
4009 if (!preg_match('/^http/i', $url)) {
4010 $link .= 'http://';
4011 }
4012 $link .= dol_trunc($url, $max);
4013
4014 $linkend = '</a>';
4015
4016 if ($morecss == 'float') { // deprecated
4017 return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
4018 } else {
4019 return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
4020 }
4021}
4022
4036function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0, $morecss = 'paddingrightonly')
4037{
4038 global $user, $langs, $hookmanager;
4039
4040 //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
4041 //$showinvalid = 1; $email = 'rrrrr';
4042
4043 $newemail = dol_escape_htmltag($email);
4044
4045 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
4046 $withpicto = 0;
4047 }
4048
4049 if (empty($email)) {
4050 return '&nbsp;';
4051 }
4052
4053 if ($addlink == 1) {
4054 $newemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="';
4055 if (!preg_match('/^mailto:/i', $email)) {
4056 $newemail .= 'mailto:';
4057 }
4058 $newemail .= $email;
4059 $newemail .= '" target="_blank">';
4060
4061 $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4062
4063 if ($max > 0) {
4064 $newemail .= dol_trunc($email, $max);
4065 } else {
4066 $newemail .= $email;
4067 }
4068 $newemail .= '</a>';
4069 if ($showinvalid && !isValidEmail($email)) {
4070 $langs->load("errors");
4071 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
4072 }
4073
4074 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4075 $type = 'AC_EMAIL';
4076 $linktoaddaction = '';
4077 if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
4078 $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>';
4079 }
4080 if ($linktoaddaction) {
4081 $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
4082 }
4083 }
4084 } elseif ($addlink === 'thirdparty') {
4085 $tmpnewemail = '<a class="'.($morecss ? $morecss : '').'" style="text-overflow: ellipsis;" href="'.DOL_URL_ROOT.'/societe/card.php?socid='.$socid.'&action=presend&mode=init#formmailbeforetitle">';
4086 $tmpnewemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
4087 if ($withpicto == 1) {
4088 $tmpnewemail .= $newemail;
4089 }
4090 $tmpnewemail .= '</a>';
4091
4092 $newemail = $tmpnewemail;
4093 } else {
4094 $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4095
4096 if ($showinvalid && !isValidEmail($email)) {
4097 $langs->load("errors");
4098 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
4099 }
4100 }
4101
4102 //$rep = '<div class="nospan" style="margin-right: 10px">';
4103 //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
4104 //$rep .= '</div>';
4105 $rep = $newemail;
4106
4107 if ($hookmanager) {
4108 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
4109
4110 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
4111 if ($reshook > 0) {
4112 $rep = '';
4113 }
4114 $rep .= $hookmanager->resPrint;
4115 }
4116
4117 return $rep;
4118}
4119
4125function getArrayOfSocialNetworks()
4126{
4127 global $conf, $db;
4128
4129 $socialnetworks = array();
4130 // Enable caching of array
4131 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
4132 $cachekey = 'socialnetworks_' . $conf->entity;
4133 $dataretrieved = dol_getcache($cachekey);
4134 if (!is_null($dataretrieved)) {
4135 $socialnetworks = $dataretrieved;
4136 } else {
4137 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
4138 $sql .= " WHERE entity=".$conf->entity;
4139 $resql = $db->query($sql);
4140 if ($resql) {
4141 while ($obj = $db->fetch_object($resql)) {
4142 $socialnetworks[$obj->code] = array(
4143 'rowid' => $obj->rowid,
4144 'label' => $obj->label,
4145 'url' => $obj->url,
4146 'icon' => $obj->icon,
4147 'active' => $obj->active,
4148 );
4149 }
4150 }
4151 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
4152 }
4153 return $socialnetworks;
4154}
4155
4166function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
4167{
4168 global $hookmanager, $langs, $user;
4169
4170 $htmllink = $value;
4171
4172 if (empty($value)) {
4173 return '&nbsp;';
4174 }
4175
4176 if (!empty($type)) {
4177 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
4178 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
4179 $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
4180 if ($type == 'skype') {
4181 $htmllink .= dol_escape_htmltag($value);
4182 $htmllink .= '&nbsp; <a href="skype:';
4183 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4184 $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
4185 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
4186 $htmllink .= '</a><a href="skype:';
4187 $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
4188 $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
4189 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
4190 $htmllink .= '</a>';
4191 if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
4192 $addlink = 'AC_SKYPE';
4193 $link = '';
4194 if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
4195 $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>';
4196 }
4197 $htmllink .= ($link ? ' '.$link : '');
4198 }
4199 } else {
4200 $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
4201 if (getDolGlobalString($networkconstname)) {
4202 $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
4203 if (preg_match('/^https?:\/\//i', $link)) {
4204 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4205 } elseif ($link) {
4206 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4207 }
4208 } elseif (!empty($dictsocialnetworks[$type]['url'])) {
4209 $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
4210 if ($tmpvirginurl) {
4211 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4212 $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
4213
4214 $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
4215 if ($tmpvirginurl3) {
4216 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4217 $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
4218 }
4219
4220 $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
4221 if ($tmpvirginurl2) {
4222 $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4223 $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
4224 }
4225 }
4226 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
4227 if (preg_match('/^https?:\/\//i', $link)) {
4228 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4229 } else {
4230 $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
4231 }
4232 } else {
4233 $htmllink .= dol_escape_htmltag($value);
4234 }
4235 }
4236 $htmllink .= '</div>';
4237 } else {
4238 $langs->load("errors");
4239 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
4240 }
4241
4242 if ($hookmanager) {
4243 $parameters = array(
4244 'value' => $value,
4245 'cid' => $cid,
4246 'socid' => $socid,
4247 'type' => $type,
4248 'dictsocialnetworks' => $dictsocialnetworks,
4249 );
4250
4251 $reshook = $hookmanager->executeHooks('printSocialNetworks', $parameters);
4252 if ($reshook > 0) {
4253 $htmllink = '';
4254 }
4255 $htmllink .= $hookmanager->resPrint;
4256 }
4257
4258 return $htmllink;
4259}
4260
4270function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
4271{
4272 global $mysoc;
4273
4274 if (empty($profID) || empty($profIDtype)) {
4275 return '';
4276 }
4277 if (empty($countrycode)) {
4278 $countrycode = $mysoc->country_code;
4279 }
4280 $newProfID = $profID;
4281 $id = substr($profIDtype, -1);
4282 $ret = '';
4283 if (strtoupper($countrycode) == 'FR') {
4284 // France
4285 // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
4286
4287 if ($id == 1 && dol_strlen($newProfID) == 9) {
4288 // SIREN (ex: 123 123 123)
4289 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
4290 }
4291 if ($id == 2 && dol_strlen($newProfID) == 14) {
4292 // SIRET (ex: 123 123 123 12345)
4293 $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
4294 }
4295 if ($id == 3 && dol_strlen($newProfID) == 5) {
4296 // NAF/APE (ex: 69.20Z)
4297 $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
4298 }
4299 if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
4300 // TVA intracommunautaire (ex: FR12 123 123 123)
4301 $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
4302 }
4303 }
4304 if (!empty($addcpButton)) {
4305 $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
4306 } else {
4307 $ret = $newProfID;
4308 }
4309 return $ret;
4310}
4311
4327function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0, $morecss = 'paddingright')
4328{
4329 global $conf, $user, $langs, $mysoc, $hookmanager;
4330
4331 // Clean phone parameter
4332 $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
4333 if (empty($phone)) {
4334 return '';
4335 }
4336 if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
4337 $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
4338 }
4339 if (empty($countrycode) && is_object($mysoc)) {
4340 $countrycode = $mysoc->country_code;
4341 }
4342
4343 // Short format for small screens
4344 if (!empty($conf->dol_optimize_smallscreen) && $separ != 'hidenum') {
4345 $separ = '';
4346 }
4347
4348 $newphone = $phone;
4349 $newphonewa = $phone;
4350 if (strtoupper($countrycode) == "FR") {
4351 // France
4352 if (dol_strlen($phone) == 10) {
4353 $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);
4354 } elseif (dol_strlen($phone) == 7) {
4355 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
4356 } elseif (dol_strlen($phone) == 9) {
4357 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
4358 } elseif (dol_strlen($phone) == 11) {
4359 $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);
4360 } elseif (dol_strlen($phone) == 12) {
4361 $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);
4362 } elseif (dol_strlen($phone) == 13) {
4363 $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);
4364 }
4365 } elseif (strtoupper($countrycode) == "CA") {
4366 if (dol_strlen($phone) == 10) {
4367 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
4368 }
4369 } elseif (strtoupper($countrycode) == "PT") {//Portugal
4370 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4371 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4372 }
4373 } elseif (strtoupper($countrycode) == "SR") {//Suriname
4374 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4375 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4376 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4377 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4378 }
4379 } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4380 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4381 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4382 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4383 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4384 }
4385 } elseif (strtoupper($countrycode) == "ES") {//Espagne
4386 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4387 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4388 }
4389 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4390 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4391 $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);
4392 }
4393 } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4394 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4395 $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);
4396 }
4397 } elseif (strtoupper($countrycode) == "TR") {//Turquie
4398 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4399 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4400 }
4401 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4402 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4403 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4404 }
4405 } elseif (strtoupper($countrycode) == "MX") {//Mexique
4406 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4407 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4408 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4409 $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);
4410 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4411 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4412 }
4413 } elseif (strtoupper($countrycode) == "ML") {//Mali
4414 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4415 $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);
4416 }
4417 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4418 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4419 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4420 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4421 $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);
4422 }
4423 } elseif (strtoupper($countrycode) == "MU") {
4424 //Maurice
4425 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4426 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4427 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4428 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4429 }
4430 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4431 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4432 $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);
4433 }
4434 } elseif (strtoupper($countrycode) == "SY") {//Syrie
4435 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4436 $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);
4437 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4438 $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);
4439 }
4440 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4441 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4442 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4443 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4444 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4445 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4446 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4447 }
4448 } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4449 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4450 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4451 }
4452 } elseif (strtoupper($countrycode) == "BE") {//Belgique
4453 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4454 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4455 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4456 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4457 }
4458 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4459 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4460 $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);
4461 }
4462 } elseif (strtoupper($countrycode) == "CO") {//Colombie
4463 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4464 $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);
4465 }
4466 } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4467 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4468 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 1).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4469 }
4470 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4471 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4472 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4473 }
4474 } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4475 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4476 $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);
4477 }
4478 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4479 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4480 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4481 }
4482 } elseif (strtoupper($countrycode) == "CH") {//Suisse
4483 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4484 $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);
4485 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4486 $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);
4487 }
4488 } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4489 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4490 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4491 }
4492 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4493 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 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) == "GP") {//Guadeloupe
4497 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 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) == "MQ") {//Martinique
4501 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4502 $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);
4503 }
4504 } elseif (strtoupper($countrycode) == "IT") {//Italie
4505 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4506 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4507 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4508 $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);
4509 }
4510 } elseif (strtoupper($countrycode) == "AU") {
4511 //Australie
4512 if (dol_strlen($phone) == 12) {
4513 //ex: +61_A_BCDE_FGHI
4514 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4515 }
4516 } elseif (strtoupper($countrycode) == "LU") {
4517 // Luxembourg
4518 if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4519 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4520 } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4521 $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);
4522 } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4523 $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);
4524 } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4525 $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);
4526 }
4527 } elseif (strtoupper($countrycode) == "PE") {
4528 // Peru
4529 if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4530 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4531 } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4532 $newphonewa = '+51'.$newphone;
4533 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3);
4534 } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4535 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4536 } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4537 $newphonewa = $newphone;
4538 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4539 }
4540 } elseif (strtoupper($countrycode) == "IN") {//India
4541 if (dol_strlen($phone) == 13) {
4542 if ($withpicto == 'phone') {//ex: +91_AB_CDEF_GHIJ
4543 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 4).$separ.substr($newphone, 9, 4);
4544 } else {//ex: +91_ABCDE_FGHIJ
4545 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 5).$separ.substr($newphone, 8, 5);
4546 }
4547 }
4548 }
4549
4550 $newphoneastart = $newphoneaend = '';
4551 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4552 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
4553 $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4554 $newphoneaend .= '</a>';
4555 } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4556 if (empty($user->clicktodial_loaded)) {
4557 $user->fetch_clicktodial();
4558 }
4559
4560 // Define urlmask
4561 $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4562 if (!empty($user->clicktodial_url)) {
4563 $urlmask = $user->clicktodial_url;
4564 }
4565
4566 $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4567 $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4568 $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4569 // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4570 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4571 // Those lines are for substitution
4572 $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4573 '__PHONETO__' => urlencode($phone),
4574 '__LOGIN__' => $clicktodial_login,
4575 '__PASS__' => $clicktodial_password);
4576 $url = make_substitutions($url, $substitarray);
4577 if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4578 // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4579 $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4580 $newphoneaend = '</a>';
4581 } else {
4582 // Old method
4583 $newphoneastart = '<a href="'.$url.'"';
4584 if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4585 $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4586 }
4587 $newphoneastart .= '>';
4588 $newphoneaend .= '</a>';
4589 }
4590 }
4591
4592 //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4593 if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4594 $type = 'AC_TEL';
4595 $addlinktoagenda = '';
4596 if ($addlink == 'AC_FAX') {
4597 $type = 'AC_FAX';
4598 }
4599 if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4600 $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>';
4601 }
4602 if ($addlinktoagenda) {
4603 $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4604 }
4605 }
4606 }
4607
4608 if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4609 // Link to Whatsapp
4610 $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4611 $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4612 }
4613
4614 if (empty($titlealt)) {
4615 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4616 }
4617 $rep = '';
4618
4619 if ($hookmanager) {
4620 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4621 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4622 $rep .= $hookmanager->resPrint;
4623 }
4624 if (empty($reshook)) {
4625 $picto = '';
4626 if ($withpicto) {
4627 if ($withpicto == 'fax') {
4628 $picto = 'phoning_fax';
4629 } elseif ($withpicto == 'phone') {
4630 $picto = 'phoning';
4631 } elseif ($withpicto == 'mobile') {
4632 $picto = 'phoning_mobile';
4633 } else {
4634 $picto = '';
4635 }
4636 }
4637 if ($adddivfloat == 1) {
4638 $rep .= '<div class="nospan float'.($morecss ? ' '.$morecss : '').'">';
4639 } elseif (empty($adddivfloat)) {
4640 $rep .= '<span'.($morecss ? ' class="'.$morecss.'"' : '').'>';
4641 }
4642
4643 $rep .= $newphoneastart;
4644 $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4645 if ($separ != 'hidenum') {
4646 $rep .= ($withpicto ? ' ' : '').$newphone;
4647 }
4648 $rep .= $newphoneaend;
4649
4650 if ($adddivfloat == 1) {
4651 $rep .= '</div>';
4652 } elseif (empty($adddivfloat)) {
4653 $rep .= '</span>';
4654 }
4655 }
4656
4657 return $rep;
4658}
4659
4667function dol_print_ip($ip, $mode = 0)
4668{
4669 global $langs;
4670
4671 $ret = '';
4672
4673 if (empty($mode)) {
4674 $ret .= $ip;
4675 }
4676
4677 if ($mode != 2) {
4678 $countrycode = dolGetCountryCodeFromIp($ip);
4679 if ($countrycode) { // If success, countrycode is us, fr, ...
4680 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4681 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4682 } else {
4683 $ret .= ' ('.$countrycode.')';
4684 }
4685 } else {
4686 // Nothing
4687 }
4688 }
4689
4690 return $ret;
4691}
4692
4705function getUserRemoteIP($trusted = 0)
4706{
4707 if ($trusted) { // Return only IP we can rely on (not spoofable by the client)
4708 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of a proxy
4709 // 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)
4710 // if the proxy were added in the list of trusted proxy.
4711 return $ip;
4712 }
4713
4714 // Try to guess the real IP of client (but this may not be reliable)
4715 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4716 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]\s]/', $_SERVER['HTTP_CLIENT_IP'])) {
4717 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4718 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may be the IP of the proxy and not the client
4719 } else {
4720 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4721 }
4722 } else {
4723 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_CLIENT_IP']); // value is clean here but may have been forged by proxy
4724 }
4725 } else {
4726 $ip = preg_replace('/,.*$/', '', $_SERVER['HTTP_X_FORWARDED_FOR']); // value is clean here but may have been forged by proxy
4727 }
4728 return $ip;
4729}
4730
4739function isHTTPS()
4740{
4741 $isSecure = false;
4742 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4743 $isSecure = true;
4744 } 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') {
4745 $isSecure = true;
4746 }
4747 return $isSecure;
4748}
4749
4756function dolGetCountryCodeFromIp($ip)
4757{
4758 global $conf;
4759
4760 $countrycode = '';
4761
4762 if (isModEnabled('geoipmaxmind')) {
4763 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4764 //$ip='24.24.24.24';
4765 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4766 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4767 $geoip = new DolGeoIP('country', $datafile);
4768 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4769 $countrycode = $geoip->getCountryCodeFromIP($ip);
4770 }
4771
4772 return $countrycode;
4773}
4774
4775
4782function dol_user_country()
4783{
4784 global $conf, $langs, $user;
4785
4786 //$ret=$user->xxx;
4787 $ret = '';
4788 if (isModEnabled('geoipmaxmind')) {
4789 $ip = getUserRemoteIP();
4790 $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4791 //$ip='24.24.24.24';
4792 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4793 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4794 $geoip = new DolGeoIP('country', $datafile);
4795 $countrycode = $geoip->getCountryCodeFromIP($ip);
4796 $ret = $countrycode;
4797 }
4798 return $ret;
4799}
4800
4813function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4814{
4815 global $conf, $user, $langs, $hookmanager;
4816
4817 $out = '';
4818
4819 if ($address) {
4820 if ($hookmanager) {
4821 $parameters = array('element' => $element, 'id' => $id);
4822 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4823 $out .= $hookmanager->resPrint;
4824 }
4825 if (empty($reshook)) {
4826 if (empty($charfornl)) {
4827 $out .= nl2br($address);
4828 } else {
4829 $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4830 }
4831
4832 // TODO Remove this block, we can add this using the hook now
4833 $showgmap = $showomap = 0;
4834 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4835 $showgmap = 1;
4836 }
4837 if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4838 $showgmap = 1;
4839 }
4840 if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4841 $showgmap = 1;
4842 }
4843 if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4844 $showgmap = 1;
4845 }
4846 if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4847 $showomap = 1;
4848 }
4849 if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4850 $showomap = 1;
4851 }
4852 if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4853 $showomap = 1;
4854 }
4855 if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4856 $showomap = 1;
4857 }
4858 if ($showgmap) {
4859 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4860 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4861 }
4862 if ($showomap) {
4863 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4864 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4865 }
4866 }
4867 }
4868 if ($noprint) {
4869 return $out;
4870 } else {
4871 print $out;
4872 return null;
4873 }
4874}
4875
4876
4886function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4887{
4888 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4889 return true;
4890 }
4891 if ($acceptuserkey && $address == '__USER_EMAIL__') {
4892 return true;
4893 }
4894 if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4895 return true;
4896 }
4897
4898 return false;
4899}
4900
4910function isValidMXRecord($domain)
4911{
4912 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4913 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4914 return 0;
4915 }
4916 if (function_exists('getmxrr')) {
4917 $mxhosts = array();
4918 $weight = array();
4919 getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4920 if (count($mxhosts) > 1) {
4921 return 1;
4922 }
4923 if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4924 return 1;
4925 }
4926
4927 return 0;
4928 }
4929 }
4930
4931 // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4932 return -1;
4933}
4934
4942function isValidPhone($phone)
4943{
4944 return true;
4945}
4946
4947
4957function dolGetFirstLetters($s, $nbofchar = 1)
4958{
4959 $ret = '';
4960 $tmparray = explode(' ', $s);
4961 foreach ($tmparray as $tmps) {
4962 $ret .= dol_substr($tmps, 0, $nbofchar);
4963 }
4964
4965 return $ret;
4966}
4967
4968
4976function dol_strlen($string, $stringencoding = 'UTF-8')
4977{
4978 if (is_null($string)) {
4979 return 0;
4980 }
4981
4982 if (function_exists('mb_strlen')) {
4983 return mb_strlen($string, $stringencoding);
4984 } else {
4985 return strlen($string);
4986 }
4987}
4988
4999function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
5000{
5001 global $langs;
5002
5003 if (empty($stringencoding)) {
5004 $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
5005 }
5006
5007 $ret = '';
5008 if (empty($trunconbytes)) {
5009 if (function_exists('mb_substr')) {
5010 $ret = mb_substr($string, $start, $length, $stringencoding);
5011 } else {
5012 $ret = substr($string, $start, $length);
5013 }
5014 } else {
5015 if (function_exists('mb_strcut')) {
5016 $ret = mb_strcut($string, $start, $length, $stringencoding);
5017 } else {
5018 $ret = substr($string, $start, $length);
5019 }
5020 }
5021 return $ret;
5022}
5023
5024
5038function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
5039{
5040 global $conf;
5041
5042 if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
5043 return $string;
5044 }
5045
5046 if (empty($stringencoding)) {
5047 $stringencoding = 'UTF-8';
5048 }
5049 // reduce for small screen
5050 if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
5051 $size = round($size / 3);
5052 }
5053
5054 // We go always here
5055 if ($trunc == 'right') {
5056 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5057 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5058 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5059 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
5060 } else {
5061 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
5062 return $string;
5063 }
5064 } elseif ($trunc == 'middle') {
5065 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5066 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5067 $size1 = (int) round($size / 2);
5068 $size2 = (int) round($size / 2);
5069 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
5070 } else {
5071 return $string;
5072 }
5073 } elseif ($trunc == 'left') {
5074 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5075 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
5076 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
5077 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
5078 } else {
5079 return $string;
5080 }
5081 } elseif ($trunc == 'wrap') {
5082 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
5083 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
5084 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
5085 } else {
5086 return $string;
5087 }
5088 } else {
5089 return 'BadParam3CallingDolTrunc';
5090 }
5091}
5092
5100function getPictoForType($key, $morecss = '')
5101{
5102 // Set array with type -> picto
5103 $type2picto = array(
5104 'varchar' => 'font',
5105 'text' => 'font',
5106 'html' => 'code',
5107 'int' => 'sort-numeric-down',
5108 'double' => 'sort-numeric-down',
5109 'price' => 'currency',
5110 'pricecy' => 'multicurrency',
5111 'password' => 'key',
5112 'boolean' => 'check-square',
5113 'date' => 'calendar',
5114 'datetime' => 'calendar',
5115 'duration' => 'hourglass',
5116 'phone' => 'phone',
5117 'mail' => 'email',
5118 'url' => 'url',
5119 'ip' => 'country',
5120 'select' => 'list',
5121 'sellist' => 'list',
5122 'stars' => 'fontawesome_star_fas',
5123 'radio' => 'check-circle',
5124 'checkbox' => 'list',
5125 'chkbxlst' => 'list',
5126 'link' => 'link',
5127 'icon' => "question",
5128 'point' => "country",
5129 'multipts' => 'country',
5130 'linestrg' => "country",
5131 'polygon' => "country",
5132 'separate' => 'minus'
5133 );
5134
5135 if (!empty($type2picto[$key])) {
5136 return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5137 }
5138
5139 return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
5140}
5141
5142
5165function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2, $allowothertags = array())
5166{
5167 global $conf;
5168
5169 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
5170 $url = DOL_URL_ROOT;
5171 $theme = isset($conf->theme) ? $conf->theme : null;
5172 $path = 'theme/'.$theme;
5173 if (empty($picto)) {
5174 $picto = 'generic';
5175 }
5176
5177 // Define fullpathpicto to use into src
5178 if ($pictoisfullpath) {
5179 // Clean parameters
5180 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5181 $picto .= '.png';
5182 }
5183 $fullpathpicto = $picto;
5184 $reg = array();
5185 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5186 $morecss .= ($morecss ? ' ' : '').$reg[1];
5187 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5188 }
5189 } else {
5190 // $picto can not be null since replaced with 'generic' in that case
5191 //$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
5192 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
5193 $pictowithouttext = str_replace('object_', '', $pictowithouttext);
5194 $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
5195
5196 // Fix some values of $pictowithouttext
5197 $pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
5198 if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
5199 $pictowithouttext = $pictoconvertkey[$pictowithouttext];
5200 }
5201
5202 if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
5203 // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
5204 $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
5205 $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
5206
5207 // Compatibility with old fontawesome versions
5208 if ($pictowithouttext == 'file-o') {
5209 $pictowithouttext = 'file';
5210 }
5211
5212 $pictowithouttextarray = explode('_', $pictowithouttext);
5213 $marginleftonlyshort = 0;
5214
5215 if (!empty($pictowithouttextarray[1])) {
5216 // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
5217 $fakey = 'fa-'.$pictowithouttextarray[0];
5218 $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
5219 $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
5220 $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
5221 } else {
5222 $fakey = 'fa-'.$pictowithouttext;
5223 $faprefix = 'fas';
5224 $facolor = '';
5225 $fasize = '';
5226 }
5227
5228 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5229 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5230 $morestyle = '';
5231 $reg = array();
5232 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5233 $morecss .= ($morecss ? ' ' : '').$reg[1];
5234 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5235 }
5236 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5237 $morestyle = $reg[1];
5238 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5239 }
5240 $moreatt = trim($moreatt);
5241
5242 $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5243 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5244 $enabledisablehtml .= '</span>';
5245
5246 return $enabledisablehtml;
5247 }
5248
5249 if (empty($srconly) && in_array($pictowithouttext, array(
5250 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
5251 'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
5252 'back', 'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
5253 'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 'hourglass',
5254 'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
5255 'check-circle', 'check-square', 'circle', 'stop-circle', 'currency', 'multicurrency',
5256 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top',
5257 'chevron-double-left', 'chevron-double-right', 'chevron-double-down', 'chevron-double-top',
5258 'commercial', 'companies',
5259 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
5260 'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
5261 'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
5262 'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
5263 'hands-helping', 'help', 'holiday',
5264 'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
5265 'key', 'knowledgemanagement',
5266 'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
5267 'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
5268 'off', 'on', 'order',
5269 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
5270 'stock', 'resize', 'service', 'stats',
5271 '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',
5272 'discord', 'facebook', 'flickr', 'instagram','linkedin', 'github', 'google', 'jabber', 'meetup', 'microsoft', 'skype', 'slack', 'twitter', 'pinterest', 'reddit', 'snapchat', 'tumblr', 'youtube', 'viadeo', 'google-plus-g', 'whatsapp',
5273 'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
5274 'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
5275 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
5276 'technic', 'ticket',
5277 'error', 'warning',
5278 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
5279 'shapes', 'skill', 'square', 'sort-numeric-down', 'status', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
5280 'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
5281 'uncheck', 'undo', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
5282 'conferenceorbooth', 'eventorganization',
5283 'stamp', 'signature',
5284 'webportal'
5285 ))) {
5286 $fakey = $pictowithouttext;
5287 $facolor = '';
5288 $fasize = '';
5289 $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
5290 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'))) {
5291 $fa = 'far';
5292 }
5293 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'))) {
5294 $fa = 'fab';
5295 }
5296
5297 $arrayconvpictotofa = array(
5298 'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
5299 'asset' => 'money-check-alt', 'autofill' => 'fill',
5300 'back' => 'arrow-left', 'bank_account' => 'university',
5301 'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
5302 'bookcal' => 'calendar-check',
5303 'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
5304 'bom' => 'shapes',
5305 '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',
5306 'chevron-double-left' => 'angle-double-left', 'chevron-double-right' => 'angle-double-right', 'chevron-double-down' => 'angle-double-down', 'chevron-double-top' => 'angle-double-up',
5307 'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
5308 'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
5309 'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
5310 'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
5311 'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
5312 'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
5313 'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
5314 'generic' => 'file', 'holiday' => 'umbrella-beach',
5315 'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
5316 'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
5317 'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
5318 'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
5319 'sign-out' => 'sign-out-alt',
5320 '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',
5321 'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
5322 'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
5323 'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
5324 'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
5325 'other' => 'square',
5326 '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',
5327 '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',
5328 'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
5329 'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
5330 'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
5331 'service' => 'concierge-bell',
5332 'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch',
5333 'status' => 'stop-circle',
5334 'stripe' => 'stripe-s', 'supplier' => 'building',
5335 'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
5336 'title_agenda' => 'calendar-alt',
5337 'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
5338 'jabber' => 'comment-o',
5339 'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
5340 'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
5341 'webportal' => 'door-open'
5342 );
5343 if ($conf->currency == 'EUR') {
5344 $arrayconvpictotofa['currency'] = 'euro-sign';
5345 $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
5346 } else {
5347 $arrayconvpictotofa['currency'] = 'dollar-sign';
5348 $arrayconvpictotofa['multicurrency'] = 'euro-sign';
5349 }
5350 if ($pictowithouttext == 'off') {
5351 $fakey = 'fa-square';
5352 $fasize = '1.3em';
5353 } elseif ($pictowithouttext == 'on') {
5354 $fakey = 'fa-check-square';
5355 $fasize = '1.3em';
5356 } elseif ($pictowithouttext == 'listlight') {
5357 $fakey = 'fa-download';
5358 $marginleftonlyshort = 1;
5359 } elseif ($pictowithouttext == 'printer') {
5360 $fakey = 'fa-print';
5361 $fasize = '1.2em';
5362 } elseif ($pictowithouttext == 'note') {
5363 $fakey = 'fa-sticky-note';
5364 $marginleftonlyshort = 1;
5365 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
5366 $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');
5367 $fakey = 'fa-'.$convertarray[$pictowithouttext];
5368 if (preg_match('/selected/', $pictowithouttext)) {
5369 $facolor = '#888';
5370 }
5371 $marginleftonlyshort = 1;
5372 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
5373 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
5374 } else {
5375 $fakey = 'fa-'.$pictowithouttext;
5376 }
5377
5378 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
5379 $morecss .= ' em092';
5380 }
5381 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
5382 $morecss .= ' em088';
5383 }
5384 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
5385 $morecss .= ' em080';
5386 }
5387
5388 // Define $marginleftonlyshort
5389 $arrayconvpictotomarginleftonly = array(
5390 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
5391 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_grey', 'switch_on_red', 'switch_off',
5392 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
5393 );
5394 if (!array_key_exists($pictowithouttext, $arrayconvpictotomarginleftonly)) {
5395 $marginleftonlyshort = 0;
5396 }
5397
5398 // Add CSS
5399 $arrayconvpictotomorcess = array(
5400 'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
5401 'bank_account' => 'infobox-bank_account',
5402 'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5403 'bookcal' => 'infobox-action',
5404 'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5405 'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5406 'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5407 'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5408 'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5409 'incoterm' => 'infobox-supplier_proposal',
5410 'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5411 'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5412 'order' => 'infobox-commande',
5413 'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5414 'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5415 'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5416 'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5417 'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5418 'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5419 'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5420 'resource' => 'infobox-action',
5421 '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',
5422 'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5423 'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5424 'vat' => 'infobox-bank_account',
5425 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5426 'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5427 );
5428 if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5429 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5430 }
5431
5432 // Define $color
5433 $arrayconvpictotocolor = array(
5434 'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5435 'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5436 'dynamicprice' => '#a69944',
5437 'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5438 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5439 'lock' => '#ddd', 'lot' => '#a69944',
5440 'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5441 'other' => '#ddd', 'world' => '#986c6a',
5442 'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5443 //'shipment'=>'#a69944',
5444 'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5445 'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5446 'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5447 'website' => '#304', 'workstation' => '#a69944'
5448 );
5449 if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5450 $facolor = $arrayconvpictotocolor[$pictowithouttext];
5451 }
5452
5453 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5454 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5455 $morestyle = '';
5456 $reg = array();
5457 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5458 $morecss .= ($morecss ? ' ' : '').$reg[1];
5459 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5460 }
5461 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5462 $morestyle = $reg[1];
5463 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5464 }
5465 $moreatt = trim($moreatt);
5466
5467 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5468 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5469 $enabledisablehtml .= '</span>';
5470
5471 return $enabledisablehtml;
5472 }
5473
5474 if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5475 $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5476 } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5477 $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
5478 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5479 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5480 }
5481
5482 // If we ask an image into $url/$mymodule/img (instead of default path)
5483 $regs = array();
5484 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5485 $picto = $regs[1];
5486 $path = $regs[2]; // $path is $mymodule
5487 }
5488
5489 // Clean parameters
5490 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5491 $picto .= '.png';
5492 }
5493 // If alt path are defined, define url where img file is, according to physical path
5494 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5495 foreach ($conf->file->dol_document_root as $type => $dirroot) {
5496 if ($type == 'main') {
5497 continue;
5498 }
5499 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5500 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5501 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5502 break;
5503 }
5504 }
5505
5506 // $url is '' or '/custom', $path is current theme or
5507 $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5508 }
5509
5510 if ($srconly) {
5511 return $fullpathpicto;
5512 }
5513
5514 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5515 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
5516}
5517
5532function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $allowothertags = array())
5533{
5534 if (strpos($picto, '^') === 0) {
5535 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5536 } else {
5537 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle, '', '', 2, $allowothertags);
5538 }
5539}
5540
5552function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5553{
5554 global $conf;
5555
5556 if (is_numeric($picto)) {
5557 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5558 //$picto = $leveltopicto[$picto];
5559 return '<i class="fa fa-weather-level'.$picto.'"></i>';
5560 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5561 $picto .= '.png';
5562 }
5563
5564 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5565
5566 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5567}
5568
5580function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5581{
5582 global $conf;
5583
5584 if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5585 $picto .= '.png';
5586 }
5587
5588 if ($pictoisfullpath) {
5589 $path = $picto;
5590 } else {
5591 $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5592
5593 if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5594 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5595
5596 if (file_exists($themepath)) {
5597 $path = $themepath;
5598 }
5599 }
5600 }
5601
5602 return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5603}
5604
5618function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5619{
5620 global $langs;
5621
5622 if (empty($titlealt) || $titlealt == 'default') {
5623 if ($numaction == '-1' || $numaction == 'ST_NO') {
5624 $numaction = -1;
5625 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5626 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5627 $numaction = 0;
5628 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5629 } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5630 $numaction = 1;
5631 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5632 } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5633 $numaction = 2;
5634 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5635 } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5636 $numaction = 3;
5637 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5638 } else {
5639 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5640 $numaction = 0;
5641 }
5642 }
5643 if (!is_numeric($numaction)) {
5644 $numaction = 0;
5645 }
5646
5647 return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5648}
5649
5657function img_pdf($titlealt = 'default', $size = 3)
5658{
5659 global $langs;
5660
5661 if ($titlealt == 'default') {
5662 $titlealt = $langs->trans('Show');
5663 }
5664
5665 return img_picto($titlealt, 'pdf'.$size.'.png');
5666}
5667
5675function img_edit_add($titlealt = 'default', $other = '')
5676{
5677 global $langs;
5678
5679 if ($titlealt == 'default') {
5680 $titlealt = $langs->trans('Add');
5681 }
5682
5683 return img_picto($titlealt, 'edit_add.png', $other);
5684}
5692function img_edit_remove($titlealt = 'default', $other = '')
5693{
5694 global $langs;
5695
5696 if ($titlealt == 'default') {
5697 $titlealt = $langs->trans('Remove');
5698 }
5699
5700 return img_picto($titlealt, 'edit_remove.png', $other);
5701}
5702
5711function img_edit($titlealt = 'default', $float = 0, $other = '')
5712{
5713 global $langs;
5714
5715 if ($titlealt == 'default') {
5716 $titlealt = $langs->trans('Modify');
5717 }
5718
5719 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5720}
5721
5730function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5731{
5732 global $langs;
5733
5734 if ($titlealt == 'default') {
5735 $titlealt = $langs->trans('View');
5736 }
5737
5738 $moreatt = ($float ? 'style="float: right" ' : '').$other;
5739
5740 return img_picto($titlealt, 'eye', $moreatt);
5741}
5742
5751function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5752{
5753 global $langs;
5754
5755 if ($titlealt == 'default') {
5756 $titlealt = $langs->trans('Delete');
5757 }
5758
5759 return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5760}
5761
5769function img_printer($titlealt = "default", $other = '')
5770{
5771 global $langs;
5772 if ($titlealt == "default") {
5773 $titlealt = $langs->trans("Print");
5774 }
5775 return img_picto($titlealt, 'printer.png', $other);
5776}
5777
5785function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5786{
5787 global $langs;
5788
5789 if ($titlealt == 'default') {
5790 $titlealt = $langs->trans('Split');
5791 }
5792
5793 return img_picto($titlealt, 'split.png', $other);
5794}
5795
5803function img_help($usehelpcursor = 1, $usealttitle = 1)
5804{
5805 global $langs;
5806
5807 if ($usealttitle) {
5808 if (is_string($usealttitle)) {
5809 $usealttitle = dol_escape_htmltag($usealttitle);
5810 } else {
5811 $usealttitle = $langs->trans('Info');
5812 }
5813 }
5814
5815 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5816}
5817
5824function img_info($titlealt = 'default')
5825{
5826 global $langs;
5827
5828 if ($titlealt == 'default') {
5829 $titlealt = $langs->trans('Informations');
5830 }
5831
5832 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5833}
5834
5843function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5844{
5845 global $langs;
5846
5847 if ($titlealt == 'default') {
5848 $titlealt = $langs->trans('Warning');
5849 }
5850
5851 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5852 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5853}
5854
5861function img_error($titlealt = 'default')
5862{
5863 global $langs;
5864
5865 if ($titlealt == 'default') {
5866 $titlealt = $langs->trans('Error');
5867 }
5868
5869 return img_picto($titlealt, 'error.png');
5870}
5871
5879function img_next($titlealt = 'default', $moreatt = '')
5880{
5881 global $langs;
5882
5883 if ($titlealt == 'default') {
5884 $titlealt = $langs->trans('Next');
5885 }
5886
5887 //return img_picto($titlealt, 'next.png', $moreatt);
5888 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5889}
5890
5898function img_previous($titlealt = 'default', $moreatt = '')
5899{
5900 global $langs;
5901
5902 if ($titlealt == 'default') {
5903 $titlealt = $langs->trans('Previous');
5904 }
5905
5906 //return img_picto($titlealt, 'previous.png', $moreatt);
5907 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5908}
5909
5918function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5919{
5920 global $langs;
5921
5922 if ($titlealt == 'default') {
5923 $titlealt = $langs->trans('Down');
5924 }
5925
5926 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5927}
5928
5937function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5938{
5939 global $langs;
5940
5941 if ($titlealt == 'default') {
5942 $titlealt = $langs->trans('Up');
5943 }
5944
5945 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5946}
5947
5956function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5957{
5958 global $langs;
5959
5960 if ($titlealt == 'default') {
5961 $titlealt = $langs->trans('Left');
5962 }
5963
5964 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5965}
5966
5975function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5976{
5977 global $langs;
5978
5979 if ($titlealt == 'default') {
5980 $titlealt = $langs->trans('Right');
5981 }
5982
5983 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5984}
5985
5993function img_allow($allow, $titlealt = 'default')
5994{
5995 global $langs;
5996
5997 if ($titlealt == 'default') {
5998 $titlealt = $langs->trans('Active');
5999 }
6000
6001 if ($allow == 1) {
6002 return img_picto($titlealt, 'tick.png');
6003 }
6004
6005 return '-';
6006}
6007
6015function img_credit_card($brand, $morecss = null)
6016{
6017 if (is_null($morecss)) {
6018 $morecss = 'fa-2x';
6019 }
6020
6021 if ($brand == 'visa' || $brand == 'Visa') {
6022 $brand = 'cc-visa';
6023 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
6024 $brand = 'cc-mastercard';
6025 } elseif ($brand == 'amex' || $brand == 'American Express') {
6026 $brand = 'cc-amex';
6027 } elseif ($brand == 'discover' || $brand == 'Discover') {
6028 $brand = 'cc-discover';
6029 } elseif ($brand == 'jcb' || $brand == 'JCB') {
6030 $brand = 'cc-jcb';
6031 } elseif ($brand == 'diners' || $brand == 'Diners club') {
6032 $brand = 'cc-diners-club';
6033 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
6034 $brand = 'credit-card';
6035 }
6036
6037 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
6038}
6039
6048function img_mime($file, $titlealt = '', $morecss = '')
6049{
6050 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6051
6052 $mimetype = dol_mimetype($file, '', 1);
6053 //$mimeimg = dol_mimetype($file, '', 2);
6054 $mimefa = dol_mimetype($file, '', 4);
6055
6056 if (empty($titlealt)) {
6057 $titlealt = 'Mime type: '.$mimetype;
6058 }
6059
6060 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
6061 return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
6062}
6063
6064
6072function img_search($titlealt = 'default', $other = '')
6073{
6074 global $langs;
6075
6076 if ($titlealt == 'default') {
6077 $titlealt = $langs->trans('Search');
6078 }
6079
6080 $img = img_picto($titlealt, 'search.png', $other, 0, 1);
6081
6082 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
6083 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6084
6085 return $input;
6086}
6087
6095function img_searchclear($titlealt = 'default', $other = '')
6096{
6097 global $langs;
6098
6099 if ($titlealt == 'default') {
6100 $titlealt = $langs->trans('Search');
6101 }
6102
6103 $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
6104
6105 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
6106 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
6107
6108 return $input;
6109}
6110
6123function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '', $picto = '')
6124{
6125 global $conf, $langs;
6126
6127 if ($infoonimgalt) {
6128 $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
6129 } else {
6130 if (empty($conf->use_javascript_ajax)) {
6131 $textfordropdown = '';
6132 }
6133
6134 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
6135 $fa = 'info-circle';
6136 if ($picto == 'warning') {
6137 $fa = 'exclamation-triangle';
6138 }
6139 $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> ';
6140 $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
6141 $result .= ($nodiv ? '' : '</div>');
6142
6143 if ($textfordropdown) {
6144 $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
6145 $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
6146 jQuery(document).ready(function() {
6147 jQuery(".'.$class.'text").click(function() {
6148 console.log("toggle text");
6149 jQuery(".'.$class.'").toggle();
6150 });
6151 });
6152 </script>';
6153
6154 $result = $tmpresult.$result;
6155 }
6156 }
6157
6158 return $result;
6159}
6160
6161
6173function dol_print_error($db = null, $error = '', $errors = null)
6174{
6175 global $conf, $langs, $user, $argv;
6176 global $dolibarr_main_prod;
6177
6178 $out = '';
6179 $syslog = '';
6180
6181 // If error occurs before the $lang object was loaded
6182 if (!$langs) {
6183 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6184 $langs = new Translate('', $conf);
6185 $langs->load("main");
6186 }
6187
6188 // Load translation files required by the error messages
6189 $langs->loadLangs(array('main', 'errors'));
6190
6191 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6192 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
6193 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
6194 $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";
6195 }
6196 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
6197
6198 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
6199 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
6200 if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
6201 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
6202 }
6203 if ($user instanceof User) {
6204 $out .= "<b>".$langs->trans("Login").":</b> ".$user->login."<br>\n";
6205 }
6206 if (function_exists("phpversion")) {
6207 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
6208 }
6209 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
6210 if (function_exists("php_uname")) {
6211 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
6212 }
6213 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
6214 $out .= "<br>\n";
6215 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
6216 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
6217 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
6218 $out .= "<br>\n";
6219 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
6220 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
6221 } else { // Mode CLI
6222 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
6223 $syslog .= "pid=".dol_getmypid();
6224 }
6225
6226 if (!empty($conf->modules)) {
6227 $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
6228 }
6229
6230 if (is_object($db)) {
6231 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6232 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
6233 $lastqueryerror = $db->lastqueryerror();
6234 if (!utf8_check($lastqueryerror)) {
6235 $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
6236 }
6237 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6238 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6239 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
6240 $out .= "<br>\n";
6241 } else { // Mode CLI
6242 // No dol_escape_htmltag for output, we are in CLI mode
6243 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
6244 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6245 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6246 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
6247 }
6248 $syslog .= ", sql=".$db->lastquery();
6249 $syslog .= ", db_error=".$db->lasterror();
6250 }
6251
6252 if ($error || $errors) {
6253 // Merge all into $errors array
6254 if (is_array($error) && is_array($errors)) {
6255 $errors = array_merge($error, $errors);
6256 } elseif (is_array($error)) { // deprecated, use second parameters
6257 $errors = $error;
6258 } elseif (is_array($errors) && !empty($error)) {
6259 $errors = array_merge(array($error), $errors);
6260 } elseif (!empty($error)) {
6261 $errors = array_merge(array($error), array($errors));
6262 }
6263
6264 $langs->load("errors");
6265
6266 foreach ($errors as $msg) {
6267 if (empty($msg)) {
6268 continue;
6269 }
6270 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
6271 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
6272 } else { // Mode CLI
6273 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
6274 }
6275 $syslog .= ", msg=".$msg;
6276 }
6277 }
6278 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
6279 xdebug_print_function_stack();
6280 $out .= '<b>XDebug information:</b>'."<br>\n";
6281 $out .= 'File: '.xdebug_call_file()."<br>\n";
6282 $out .= 'Line: '.xdebug_call_line()."<br>\n";
6283 $out .= 'Function: '.xdebug_call_function()."<br>\n";
6284 $out .= "<br>\n";
6285 }
6286
6287 // Return a http header with error code if possible
6288 if (!headers_sent()) {
6289 if (function_exists('top_httphead')) { // In CLI context, the method does not exists
6290 top_httphead();
6291 }
6292 //http_response_code(500); // If we use 500, message is not output with some command line tools
6293 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
6294 }
6295
6296 if (empty($dolibarr_main_prod)) {
6297 print $out;
6298 } else {
6299 if (empty($langs->defaultlang)) {
6300 $langs->setDefaultLang();
6301 }
6302 $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.
6303 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
6304 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";
6305 print $langs->trans("DolibarrHasDetectedError").'. ';
6306 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
6307 if (!defined("MAIN_CORE_ERROR")) {
6308 define("MAIN_CORE_ERROR", 1);
6309 }
6310 }
6311
6312 dol_syslog("Error ".$syslog, LOG_ERR);
6313}
6314
6325function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
6326{
6327 global $langs;
6328
6329 if (empty($email)) {
6330 $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
6331 }
6332
6333 $langs->load("errors");
6334 $now = dol_now();
6335
6336 print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
6337 print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
6338 if ($errormessage) {
6339 print '<br><br>'.$errormessage;
6340 }
6341 if (is_array($errormessages) && count($errormessages)) {
6342 foreach ($errormessages as $mesgtoshow) {
6343 print '<br><br>'.$mesgtoshow;
6344 }
6345 }
6346 print '</div></div>';
6347}
6348
6365function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
6366{
6367 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
6368}
6369
6388function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
6389{
6390 global $langs, $form;
6391 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
6392
6393 if ($moreattrib == 'class="right"') {
6394 $prefix .= 'right '; // For backward compatibility
6395 }
6396
6397 $sortorder = strtoupper($sortorder);
6398 $out = '';
6399 $sortimg = '';
6400
6401 $tag = 'th';
6402 if ($thead == 2) {
6403 $tag = 'div';
6404 }
6405
6406 $tmpsortfield = explode(',', $sortfield);
6407 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6408 $tmpfield = explode(',', $field);
6409 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6410
6411 if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6412 $prefix = 'wrapcolumntitle '.$prefix;
6413 }
6414
6415 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6416 // If field is used as sort criteria we use a specific css class liste_titre_sel
6417 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6418 $liste_titre = 'liste_titre';
6419 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6420 $liste_titre = 'liste_titre_sel';
6421 }
6422
6423 $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6424 //$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)).'"' : '');
6425 $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6426 $tagstart .= '>';
6427
6428 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6429 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6430 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6431 $options = preg_replace('/&+/i', '&', $options);
6432 if (!preg_match('/^&/', $options)) {
6433 $options = '&'.$options;
6434 }
6435
6436 $sortordertouseinlink = '';
6437 if ($field1 != $sortfield1) { // We are on another field than current sorted field
6438 if (preg_match('/^DESC/i', $sortorder)) {
6439 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6440 } else { // We reverse the var $sortordertouseinlink
6441 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6442 }
6443 } else { // We are on field that is the first current sorting criteria
6444 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6445 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6446 } else {
6447 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6448 }
6449 }
6450 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6451 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6452 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6453 $out .= '>';
6454 }
6455 if ($tooltip) {
6456 // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6457 if (preg_match('/:\w+$/', $tooltip)) {
6458 $tmptooltip = explode(':', $tooltip);
6459 } else {
6460 $tmptooltip = array($tooltip);
6461 }
6462 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6463 } else {
6464 $out .= $langs->trans($name);
6465 }
6466
6467 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6468 $out .= '</a>';
6469 }
6470
6471 if (empty($thead) && $field) { // If this is a sort field
6472 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6473 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6474 $options = preg_replace('/&+/i', '&', $options);
6475 if (!preg_match('/^&/', $options)) {
6476 $options = '&'.$options;
6477 }
6478
6479 if (!$sortorder || ($field1 != $sortfield1)) {
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",0).'</a>';
6482 } else {
6483 if (preg_match('/^DESC/', $sortorder)) {
6484 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6485 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6486 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6487 }
6488 if (preg_match('/^ASC/', $sortorder)) {
6489 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6490 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6491 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6492 }
6493 }
6494 }
6495
6496 $tagend = '</'.$tag.'>';
6497
6498 $out = $tagstart.$sortimg.$out.$tagend;
6499
6500 return $out;
6501}
6502
6511function print_titre($title)
6512{
6513 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6514
6515 print '<div class="titre">'.$title.'</div>';
6516}
6517
6529function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6530{
6531 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6532}
6533
6547function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6548{
6549 $return = '';
6550
6551 if ($picto == 'setup') {
6552 $picto = 'generic';
6553 }
6554
6555 $return .= "\n";
6556 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // margin bottom must be same than into print_barre_list
6557 $return .= '<tr class="toptitle">';
6558 if ($picto) {
6559 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6560 }
6561 $return .= '<td class="nobordernopadding valignmiddle col-title">';
6562 $return .= '<div class="titre inline-block">';
6563 $return .= '<span class="inline-block valignmiddle">'.$title.'</span>'; // $title is already HTML sanitized content
6564 $return .= '</div>';
6565 $return .= '</td>';
6566 if (dol_strlen($morehtmlcenter)) {
6567 $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6568 }
6569 if (dol_strlen($morehtmlright)) {
6570 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6571 }
6572 $return .= '</tr></table>'."\n";
6573
6574 return $return;
6575}
6576
6600function 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 = '')
6601{
6602 global $conf, $langs;
6603
6604 $savlimit = $limit;
6605 $savtotalnboflines = $totalnboflines;
6606 if (is_numeric($totalnboflines)) {
6607 $totalnboflines = abs($totalnboflines);
6608 }
6609
6610 // Detect if there is a subtitle
6611 $subtitle = '';
6612 $tmparray = preg_split('/<br>/i', $title, 2);
6613 if (!empty($tmparray[1])) {
6614 $title = $tmparray[0];
6615 $subtitle = $tmparray[1];
6616 }
6617
6618 $page = (int) $page;
6619
6620 if ($picto == 'setup') {
6621 $picto = 'title_setup.png';
6622 }
6623 if (($conf->browser->name == 'ie') && $picto == 'generic') {
6624 $picto = 'title.gif';
6625 }
6626 if ($limit < 0) {
6627 $limit = $conf->liste_limit;
6628 }
6629
6630 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6631 $nextpage = 1;
6632 } else {
6633 $nextpage = 0;
6634 }
6635 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-selectlimitsuffix='.$selectlimitsuffix.'-hidenavigation='.$hidenavigation;
6636
6637 print "\n";
6638 print "<!-- Begin print_barre_liste -->\n";
6639 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'">';
6640 print '<tr class="toptitle">'; // margin bottom must be same than into load_fiche_tire
6641
6642 // Left
6643
6644 if ($picto && $title) {
6645 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">';
6646 print img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath);
6647 print '</td>';
6648 }
6649
6650 print '<td class="nobordernopadding valignmiddle col-title">';
6651 print '<div class="titre inline-block">';
6652 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()
6653 if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '' && $totalnboflines > 0) {
6654 print '<span class="opacitymedium colorblack marginleftonly totalnboflines valignmiddle" title="'.$langs->trans("NbRecordQualified").'">('.$totalnboflines.')</span>';
6655 }
6656 print '</div>';
6657 if (!empty($subtitle)) {
6658 print '<br><div class="subtitle inline-block hideonsmartphone">'.$subtitle.'</div>';
6659 }
6660 print '</td>';
6661
6662 // Center
6663 if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6664 print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6665 }
6666
6667 // Right
6668 print '<td class="nobordernopadding valignmiddle right col-right">';
6669 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6670 if ($sortfield) {
6671 $options .= "&sortfield=".urlencode($sortfield);
6672 }
6673 if ($sortorder) {
6674 $options .= "&sortorder=".urlencode($sortorder);
6675 }
6676 // Show navigation bar
6677 $pagelist = '';
6678 if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6679 if ($totalnboflines) { // If we know total nb of lines
6680 // Define nb of extra page links before and after selected page + ... + first or last
6681 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6682
6683 if ($limit > 0) {
6684 $nbpages = ceil($totalnboflines / $limit);
6685 } else {
6686 $nbpages = 1;
6687 }
6688 $cpt = ($page - $maxnbofpage);
6689 if ($cpt < 0) {
6690 $cpt = 0;
6691 }
6692
6693 if ($cpt >= 1) {
6694 if (empty($pagenavastextinput)) {
6695 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6696 if ($cpt > 2) {
6697 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6698 } elseif ($cpt == 2) {
6699 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6700 }
6701 }
6702 }
6703
6704 do {
6705 if ($pagenavastextinput) {
6706 if ($cpt == $page) {
6707 $pagelist .= '<li class="pagination pageplusone valignmiddle"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6708 $pagelist .= '/';
6709 }
6710 } else {
6711 if ($cpt == $page) {
6712 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6713 } else {
6714 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6715 }
6716 }
6717 $cpt++;
6718 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6719
6720 if (empty($pagenavastextinput)) {
6721 if ($cpt < $nbpages) {
6722 if ($cpt < $nbpages - 2) {
6723 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6724 } elseif ($cpt == $nbpages - 2) {
6725 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6726 }
6727 $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6728 }
6729 } else {
6730 //var_dump($page.' '.$cpt.' '.$nbpages);
6731 $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6732 }
6733 } else {
6734 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6735 }
6736 }
6737
6738 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6739 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
6740 }
6741
6742 // js to autoselect page field on focus
6743 if ($pagenavastextinput) {
6744 print ajax_autoselect('.pageplusone');
6745 }
6746
6747 print '</td>';
6748 print '</tr>';
6749
6750 print '</table>'."\n";
6751
6752 // Center
6753 if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6754 print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6755 }
6756
6757 print "<!-- End title -->\n\n";
6758}
6759
6776function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $selectlimitsuffix = '', $beforearrows = '', $hidenavigation = 0)
6777{
6778 global $conf, $langs;
6779
6780 print '<div class="pagination"><ul>';
6781 if ($beforearrows) {
6782 print '<li class="paginationbeforearrows">';
6783 print $beforearrows;
6784 print '</li>';
6785 }
6786
6787 if (empty($hidenavigation)) {
6788 if ((int) $limit > 0 && (empty($selectlimitsuffix) || !is_numeric($selectlimitsuffix))) {
6789 $pagesizechoices = '10:10,15:15,20:20,25:25,50:50,100:100,250:250,500:500,1000:1000';
6790 $pagesizechoices .= ',5000:5000';
6791 //$pagesizechoices .= ',10000:10000'; // Memory trouble on most browsers
6792 //$pagesizechoices .= ',20000:20000'; // Memory trouble on most browsers
6793 //$pagesizechoices .= ',0:'.$langs->trans("All"); // Not yet supported
6794 //$pagesizechoices .= ',2:2';
6795 if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6796 $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6797 }
6798
6799 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6800 print '<li class="pagination">';
6801 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.'">';
6802 print '<datalist id="limitlist">';
6803 } else {
6804 print '<li class="paginationcombolimit valignmiddle">';
6805 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")).'">';
6806 }
6807 $tmpchoice = explode(',', $pagesizechoices);
6808 $tmpkey = $limit.':'.$limit;
6809 if (!in_array($tmpkey, $tmpchoice)) {
6810 $tmpchoice[$tmpkey] = $tmpkey;
6811 }
6812 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6813 if (!in_array($tmpkey, $tmpchoice)) {
6814 $tmpchoice[$tmpkey] = $tmpkey;
6815 }
6816 asort($tmpchoice, SORT_NUMERIC);
6817 foreach ($tmpchoice as $val) {
6818 $selected = '';
6819 $tmp = explode(':', $val);
6820 $key = $tmp[0];
6821 $val = $tmp[1];
6822 if ($key != '' && $val != '') {
6823 if ((int) $key == (int) $limit) {
6824 $selected = ' selected="selected"';
6825 }
6826 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6827 }
6828 }
6829 if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6830 print '</datalist>';
6831 } else {
6832 print '</select>';
6833 print ajax_combobox("limit".(is_numeric($selectlimitsuffix) ? '' : $selectlimitsuffix), array(), 0, 0, 'resolve', '-1', 'limit');
6834 //print ajax_combobox("limit");
6835 }
6836
6837 if ($conf->use_javascript_ajax) {
6838 print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6839 <script>
6840 jQuery(document).ready(function () {
6841 jQuery(".selectlimit").change(function() {
6842 console.log("We change limit so we submit the form");
6843 $(this).parents(\'form:first\').submit();
6844 });
6845 });
6846 </script>
6847 ';
6848 }
6849 print '</li>';
6850 }
6851 if ($page > 0) {
6852 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>';
6853 }
6854 if ($betweenarrows) {
6855 print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6856 print $betweenarrows;
6857 print '<!--</div>-->';
6858 }
6859 if ($nextpage > 0) {
6860 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>';
6861 }
6862 if ($afterarrows) {
6863 print '<li class="paginationafterarrows">';
6864 print $afterarrows;
6865 print '</li>';
6866 }
6867 }
6868 print '</ul></div>'."\n";
6869}
6870
6871
6883function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6884{
6885 $morelabel = '';
6886
6887 if (preg_match('/%/', $rate)) {
6888 $rate = str_replace('%', '', $rate);
6889 $addpercent = true;
6890 }
6891 $reg = array();
6892 if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6893 $morelabel = ' ('.$reg[1].')';
6894 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6895 $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6896 }
6897 if (preg_match('/\*/', $rate)) {
6898 $rate = str_replace('*', '', $rate);
6899 $info_bits |= 1;
6900 }
6901
6902 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6903 if (!preg_match('/\//', $rate)) {
6904 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6905 } else {
6906 // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6907 $ret = $rate.($addpercent ? '%' : '');
6908 }
6909 if (($info_bits & 1) && $usestarfornpr >= 0) {
6910 $ret .= ' *';
6911 }
6912 $ret .= $morelabel;
6913 return $ret;
6914}
6915
6916
6932function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6933{
6934 global $langs, $conf;
6935
6936 // Clean parameters
6937 if (empty($amount)) {
6938 $amount = 0; // To have a numeric value if amount not defined or = ''
6939 }
6940 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6941 if ($rounding == -1) {
6942 $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6943 }
6944 $nbdecimal = $rounding;
6945
6946 if ($outlangs === 'none') {
6947 // Use international separators
6948 $dec = '.';
6949 $thousand = '';
6950 } else {
6951 // Output separators by default (french)
6952 $dec = ',';
6953 $thousand = ' ';
6954
6955 // If $outlangs not forced, we use use language
6956 if (!($outlangs instanceof Translate)) {
6957 $outlangs = $langs;
6958 }
6959
6960 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6961 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6962 }
6963 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6964 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6965 }
6966 if ($thousand == 'None') {
6967 $thousand = '';
6968 } elseif ($thousand == 'Space') {
6969 $thousand = ' ';
6970 }
6971 }
6972 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6973
6974 //print "amount=".$amount."-";
6975 $amount = str_replace(',', '.', $amount); // should be useless
6976 //print $amount."-";
6977 $data = explode('.', $amount);
6978 $decpart = isset($data[1]) ? $data[1] : '';
6979 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6980 //print "decpart=".$decpart."<br>";
6981 $end = '';
6982
6983 // We increase nbdecimal if there is more decimal than asked (to not loose information)
6984 if (dol_strlen($decpart) > $nbdecimal) {
6985 $nbdecimal = dol_strlen($decpart);
6986 }
6987
6988 // If nbdecimal is higher than max to show
6989 $nbdecimalmaxshown = (int) str_replace('...', '', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'));
6990 if ($trunc && $nbdecimal > $nbdecimalmaxshown) {
6991 $nbdecimal = $nbdecimalmaxshown;
6992 if (preg_match('/\.\.\./i', getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN'))) {
6993 // If output is truncated, we show ...
6994 $end = '...';
6995 }
6996 }
6997
6998 // If force rounding
6999 if ((string) $forcerounding != '-1') {
7000 if ($forcerounding === 'MU') {
7001 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_UNIT');
7002 } elseif ($forcerounding === 'MT') {
7003 $nbdecimal = getDolGlobalInt('MAIN_MAX_DECIMALS_TOT');
7004 } elseif ($forcerounding >= 0) {
7005 $nbdecimal = $forcerounding;
7006 }
7007 }
7008
7009 // Format number
7010 $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
7011 if ($form) {
7012 $output = preg_replace('/\s/', '&nbsp;', $output);
7013 $output = preg_replace('/\'/', '&#039;', $output);
7014 }
7015 // Add symbol of currency if requested
7016 $cursymbolbefore = $cursymbolafter = '';
7017 if ($currency_code && is_object($outlangs)) {
7018 if ($currency_code == 'auto') {
7019 $currency_code = $conf->currency;
7020 }
7021
7022 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
7023 $listoflanguagesbefore = array('nl_NL');
7024 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
7025 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
7026 } else {
7027 $tmpcur = $outlangs->getCurrencySymbol($currency_code);
7028 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
7029 }
7030 }
7031 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
7032
7033 return $output;
7034}
7035
7060function price2num($amount, $rounding = '', $option = 0)
7061{
7062 global $langs, $conf;
7063
7064 // Clean parameters
7065 if (is_null($amount)) {
7066 $amount = '';
7067 }
7068
7069 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
7070 // Numbers must be '1234.56'
7071 // Decimal delimiter for PHP and database SQL requests must be '.'
7072 $dec = ',';
7073 $thousand = ' ';
7074 if (is_null($langs)) { // $langs is not defined, we use english values.
7075 $dec = '.';
7076 $thousand = ',';
7077 } else {
7078 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
7079 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
7080 }
7081 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
7082 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
7083 }
7084 }
7085 if ($thousand == 'None') {
7086 $thousand = '';
7087 } elseif ($thousand == 'Space') {
7088 $thousand = ' ';
7089 }
7090 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
7091
7092 // Convert value to universal number format (no thousand separator, '.' as decimal separator)
7093 if ($option != 1) { // If not a PHP number or unknown, we change or clean format
7094 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
7095 if (!is_numeric($amount)) {
7096 $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
7097 }
7098
7099 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
7100 $amount = str_replace($thousand, '', $amount);
7101 }
7102
7103 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7104 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
7105 // So if number was already a good number, it is converted into local Dolibarr setup.
7106 if (is_numeric($amount)) {
7107 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7108 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7109 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7110 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7111 $amount = number_format($amount, $nbofdec, $dec, $thousand);
7112 }
7113 //print "QQ".$amount."<br>\n";
7114
7115 // Now make replace (the main goal of function)
7116 if ($thousand != ',' && $thousand != '.') {
7117 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7118 }
7119
7120 $amount = str_replace(' ', '', $amount); // To avoid spaces
7121 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7122 $amount = str_replace($dec, '.', $amount);
7123
7124 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7125 }
7126 //print ' XX'.$amount.' '.$rounding;
7127
7128 // Now, $amount is a real PHP float number. We make a rounding if required.
7129 if ($rounding) {
7130 $nbofdectoround = '';
7131 if ($rounding == 'MU') {
7132 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
7133 } elseif ($rounding == 'MT') {
7134 $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
7135 } elseif ($rounding == 'MS') {
7136 $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
7137 } elseif ($rounding == 'CU') {
7138 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
7139 } elseif ($rounding == 'CT') {
7140 $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_TOT'), 8); // TODO Use param of currency
7141 } elseif (is_numeric($rounding)) {
7142 $nbofdectoround = (int) $rounding;
7143 }
7144
7145 //print " RR".$amount.' - '.$nbofdectoround.'<br>';
7146 if (dol_strlen($nbofdectoround)) {
7147 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
7148 } else {
7149 return 'ErrorBadParameterProvidedToFunction';
7150 }
7151 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
7152
7153 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
7154 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
7155 if (is_numeric($amount)) {
7156 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
7157 $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
7158 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
7159 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
7160 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
7161 }
7162 //print "TT".$amount.'<br>';
7163
7164 // Always make replace because each math function (like round) replace
7165 // with local values and we want a number that has a SQL string format x.y
7166 if ($thousand != ',' && $thousand != '.') {
7167 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
7168 }
7169
7170 $amount = str_replace(' ', '', $amount); // To avoid spaces
7171 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
7172 $amount = str_replace($dec, '.', $amount);
7173
7174 $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
7175 }
7176
7177 return $amount;
7178}
7179
7192function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
7193{
7194 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
7195
7196 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
7197 $dimension *= 1000000;
7198 $unit -= 6;
7199 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
7200 $dimension *= 1000;
7201 $unit -= 3;
7202 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
7203 $dimension /= 1000000;
7204 $unit += 6;
7205 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
7206 $dimension /= 1000;
7207 $unit += 3;
7208 }
7209 // Special case when we want output unit into pound or ounce
7210 /* TODO
7211 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
7212 {
7213 $dimension = // convert dimension from standard unit into ounce or pound
7214 $unit = $forceunitoutput;
7215 }
7216 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
7217 {
7218 $dimension = // convert dimension from standard unit into ounce or pound
7219 $unit = $forceunitoutput;
7220 }*/
7221
7222 $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
7223 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
7224 $ret .= ' '.measuringUnitString(0, $type, (string) $unit, $use_short_label, $outputlangs);
7225
7226 return $ret;
7227}
7228
7229
7242function get_localtax($vatrate, $local, $thirdparty_buyer = null, $thirdparty_seller = null, $vatnpr = 0)
7243{
7244 global $db, $conf, $mysoc;
7245
7246 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
7247 $thirdparty_seller = $mysoc;
7248 }
7249
7250 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);
7251
7252 $vatratecleaned = $vatrate;
7253 $reg = array();
7254 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', (string) $vatrate, $reg)) { // If vat is "xx (yy)"
7255 $vatratecleaned = trim($reg[1]);
7256 $vatratecode = $reg[2];
7257 }
7258
7259 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
7260 {
7261 return 0;
7262 }*/
7263
7264 // Some test to guess with no need to make database access
7265 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
7266 if ($local == 1) {
7267 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
7268 return 0;
7269 }
7270 if ($thirdparty_seller->id == $mysoc->id) {
7271 if (!$thirdparty_buyer->localtax1_assuj) {
7272 return 0;
7273 }
7274 } else {
7275 if (!$thirdparty_seller->localtax1_assuj) {
7276 return 0;
7277 }
7278 }
7279 }
7280
7281 if ($local == 2) {
7282 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
7283 if (!$mysoc->localtax2_assuj) {
7284 return 0; // If main vat is 0, IRPF may be different than 0.
7285 }
7286 if ($thirdparty_seller->id == $mysoc->id) {
7287 if (!$thirdparty_buyer->localtax2_assuj) {
7288 return 0;
7289 }
7290 } else {
7291 if (!$thirdparty_seller->localtax2_assuj) {
7292 return 0;
7293 }
7294 }
7295 }
7296 } else {
7297 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
7298 return 0;
7299 }
7300 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
7301 return 0;
7302 }
7303 }
7304
7305 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
7306 if (in_array($mysoc->country_code, array('ES'))) {
7307 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
7308 }
7309
7310 // Search local taxes
7311 if (getDolGlobalString('MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY')) {
7312 if ($local == 1) {
7313 if ($thirdparty_seller != $mysoc) {
7314 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7315 return $thirdparty_seller->localtax1_value;
7316 }
7317 } else { // i am the seller
7318 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
7319 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX1');
7320 }
7321 }
7322 }
7323 if ($local == 2) {
7324 if ($thirdparty_seller != $mysoc) {
7325 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
7326 // TODO We should also return value defined on thirdparty only if defined
7327 return $thirdparty_seller->localtax2_value;
7328 }
7329 } else { // i am the seller
7330 if (in_array($mysoc->country_code, array('ES'))) {
7331 return $thirdparty_buyer->localtax2_value;
7332 } else {
7333 return getDolGlobalString('MAIN_INFO_VALUE_LOCALTAX2');
7334 }
7335 }
7336 }
7337 }
7338
7339 // By default, search value of local tax on line of common tax
7340 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
7341 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7342 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
7343 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7344 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7345 if (!empty($vatratecode)) {
7346 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
7347 } else {
7348 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
7349 }
7350
7351 $resql = $db->query($sql);
7352
7353 if ($resql) {
7354 $obj = $db->fetch_object($resql);
7355 if ($obj) {
7356 if ($local == 1) {
7357 return $obj->localtax1;
7358 } elseif ($local == 2) {
7359 return $obj->localtax2;
7360 }
7361 }
7362 }
7363
7364 return 0;
7365}
7366
7367
7376function isOnlyOneLocalTax($local)
7377{
7378 $tax = get_localtax_by_third($local);
7379
7380 $valors = explode(":", $tax);
7381
7382 if (count($valors) > 1) {
7383 return false;
7384 } else {
7385 return true;
7386 }
7387}
7388
7395function get_localtax_by_third($local)
7396{
7397 global $db, $mysoc;
7398
7399 $sql = " SELECT t.localtax".$local." as localtax";
7400 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
7401 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.entity IN (".getEntity('c_tva').") AND t.taux = (";
7402 $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";
7403 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.entity IN (".getEntity('c_tva').") AND tt.active = 1)";
7404 $sql .= " AND t.localtax".$local."_type <> '0'";
7405 $sql .= " ORDER BY t.rowid DESC";
7406
7407 $resql = $db->query($sql);
7408 if ($resql) {
7409 $obj = $db->fetch_object($resql);
7410 if ($obj) {
7411 return $obj->localtax;
7412 } else {
7413 return '0';
7414 }
7415 }
7416
7417 return 'Error';
7418}
7419
7420
7432function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
7433{
7434 global $db;
7435
7436 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
7437
7438 // Search local taxes
7439 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
7440 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
7441 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7442 if ($firstparamisid) {
7443 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7444 } else {
7445 $vatratecleaned = $vatrate;
7446 $vatratecode = '';
7447 $reg = array();
7448 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
7449 $vatratecleaned = $reg[1];
7450 $vatratecode = $reg[2];
7451 }
7452
7453 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7454 /*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 ??
7455 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
7456 $sql .= " WHERE t.fk_pays = c.rowid";
7457 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7458 $sql .= " AND c.code = '".$db->escape($buyer->country_code)."'";
7459 } else {
7460 $sql .= " AND c.code = '".$db->escape($seller->country_code)."'";
7461 }
7462 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7463 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7464 if ($vatratecode) {
7465 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7466 }
7467 }
7468
7469 $resql = $db->query($sql);
7470 if ($resql) {
7471 $obj = $db->fetch_object($resql);
7472 if ($obj) {
7473 return array(
7474 'rowid' => $obj->rowid,
7475 'code' => $obj->code,
7476 'rate' => $obj->rate,
7477 'localtax1' => $obj->localtax1,
7478 'localtax1_type' => $obj->localtax1_type,
7479 'localtax2' => $obj->localtax2,
7480 'localtax2_type' => $obj->localtax2_type,
7481 'npr' => $obj->npr,
7482 'accountancy_code_sell' => $obj->accountancy_code_sell,
7483 'accountancy_code_buy' => $obj->accountancy_code_buy
7484 );
7485 } else {
7486 return array();
7487 }
7488 } else {
7489 dol_print_error($db);
7490 }
7491
7492 return array();
7493}
7494
7511function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
7512{
7513 global $db, $mysoc;
7514
7515 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
7516
7517 // Search local taxes
7518 $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";
7519 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
7520 if ($firstparamisid) {
7521 $sql .= " WHERE t.rowid = ".(int) $vatrate;
7522 } else {
7523 $vatratecleaned = $vatrate;
7524 $vatratecode = '';
7525 $reg = array();
7526 if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
7527 $vatratecleaned = $reg[1];
7528 $vatratecode = $reg[2];
7529 }
7530
7531 $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
7532 if (!empty($mysoc) && $mysoc->country_code == 'ES') {
7533 $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
7534 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
7535 } else {
7536 $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
7537 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
7538 }
7539 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
7540 if ($vatratecode) {
7541 $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
7542 }
7543 }
7544
7545 $resql = $db->query($sql);
7546 if ($resql) {
7547 $obj = $db->fetch_object($resql);
7548
7549 if ($obj) {
7550 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
7551
7552 if ($local == 1) {
7553 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7554 } elseif ($local == 2) {
7555 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
7556 } else {
7557 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);
7558 }
7559 }
7560 }
7561
7562 return array();
7563}
7564
7575function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
7576{
7577 global $db, $mysoc;
7578
7579 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7580
7581 $ret = 0;
7582 $found = 0;
7583
7584 if ($idprod > 0) {
7585 // Load product
7586 $product = new Product($db);
7587 $product->fetch($idprod);
7588
7589 if (($mysoc->country_code == $thirdpartytouse->country_code)
7590 || (in_array($mysoc->country_code, array('FR', 'MC')) && in_array($thirdpartytouse->country_code, array('FR', 'MC')))
7591 || (in_array($mysoc->country_code, array('MQ', 'GP')) && in_array($thirdpartytouse->country_code, array('MQ', 'GP')))
7592 ) {
7593 // If country of thirdparty to consider is ours
7594 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
7595 $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
7596 if ($result > 0) {
7597 $ret = $product->vatrate_supplier;
7598 if ($product->default_vat_code_supplier) {
7599 $ret .= ' ('.$product->default_vat_code_supplier.')';
7600 }
7601 $found = 1;
7602 }
7603 }
7604 if (!$found) {
7605 $ret = $product->tva_tx; // Default sales vat of product
7606 if ($product->default_vat_code) {
7607 $ret .= ' ('.$product->default_vat_code.')';
7608 }
7609 $found = 1;
7610 }
7611 } else {
7612 // TODO Read default product vat according to product and an other countrycode.
7613 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7614 }
7615 }
7616
7617 if (!$found) {
7618 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
7619 // 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).
7620 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
7621 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7622 $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
7623 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7624 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7625 $sql .= $db->plimit(1);
7626
7627 $resql = $db->query($sql);
7628 if ($resql) {
7629 $obj = $db->fetch_object($resql);
7630 if ($obj) {
7631 $ret = $obj->vat_rate;
7632 if ($obj->default_vat_code) {
7633 $ret .= ' ('.$obj->default_vat_code.')';
7634 }
7635 }
7636 $db->free($resql);
7637 } else {
7638 dol_print_error($db);
7639 }
7640 } else {
7641 // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
7642 // '1.23'
7643 // or '1.23 (CODE)'
7644 $defaulttx = '';
7645 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
7646 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
7647 }
7648 /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
7649 $defaultcode = $reg[1];
7650 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
7651 }*/
7652
7653 $ret = $defaulttx;
7654 }
7655 }
7656
7657 dol_syslog("get_product_vat_for_country: ret=".$ret);
7658 return $ret;
7659}
7660
7670function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
7671{
7672 global $db, $mysoc;
7673
7674 if (!class_exists('Product')) {
7675 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7676 }
7677
7678 $ret = 0;
7679 $found = 0;
7680
7681 if ($idprod > 0) {
7682 // Load product
7683 $product = new Product($db);
7684 $result = $product->fetch($idprod);
7685
7686 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
7687 /* Not defined yet, so we don't use this
7688 if ($local==1) $ret=$product->localtax1_tx;
7689 elseif ($local==2) $ret=$product->localtax2_tx;
7690 $found=1;
7691 */
7692 } else {
7693 // TODO Read default product vat according to product and another countrycode.
7694 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
7695 }
7696 }
7697
7698 if (!$found) {
7699 // If vat of product for the country not found or not defined, we return higher vat of country.
7700 $sql = "SELECT taux as vat_rate, localtax1, localtax2";
7701 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
7702 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
7703 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7704 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
7705 $sql .= $db->plimit(1);
7706
7707 $resql = $db->query($sql);
7708 if ($resql) {
7709 $obj = $db->fetch_object($resql);
7710 if ($obj) {
7711 if ($local == 1) {
7712 $ret = $obj->localtax1;
7713 } elseif ($local == 2) {
7714 $ret = $obj->localtax2;
7715 }
7716 }
7717 } else {
7718 dol_print_error($db);
7719 }
7720 }
7721
7722 dol_syslog("get_product_localtax_for_country: ret=".$ret);
7723 return $ret;
7724}
7725
7743function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7744{
7745 global $conf, $db;
7746
7747 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
7748
7749 // Note: possible values for tva_assuj are 0/1 or franchise/reel
7750 $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;
7751
7752 $seller_country_code = $thirdparty_seller->country_code;
7753 $seller_in_cee = isInEEC($thirdparty_seller);
7754
7755 $buyer_country_code = $thirdparty_buyer->country_code;
7756 $buyer_in_cee = isInEEC($thirdparty_buyer);
7757
7758 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 : ''));
7759
7760 // 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)
7761 // we use the buyer VAT.
7762 if (getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) {
7763 if ($seller_in_cee && $buyer_in_cee) {
7764 $isacompany = $thirdparty_buyer->isACompany();
7765 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7766 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7767 if (!isValidVATID($thirdparty_buyer)) {
7768 $isacompany = 0;
7769 }
7770 }
7771
7772 if (!$isacompany) {
7773 //print 'VATRULE 0';
7774 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
7775 }
7776 }
7777 }
7778
7779 // If seller does not use VAT, default VAT is 0. End of rule.
7780 if (!$seller_use_vat) {
7781 //print 'VATRULE 1';
7782 return 0;
7783 }
7784
7785 // 'VATRULE 2' - Force VAT if a buyer department is defined on vat rates dictionary
7786 if (!empty($thirdparty_buyer->state_id)) {
7787 $sql = "SELECT d.rowid, t.taux as vat_default_rate, t.code as vat_default_code ";
7788 $sql .= " FROM ".$db->prefix()."c_tva as t";
7789 $sql .= " INNER JOIN ".$db->prefix()."c_departements as d ON t.fk_department_buyer = d.rowid";
7790 $sql .= " WHERE d.rowid = ".((int) $thirdparty_buyer->state_id);
7791 $sql .= " AND t.active > 0";
7792 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7793 $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
7794
7795 $res = $db->query($sql);
7796 if ($res) {
7797 if ($db->num_rows($res)) {
7798 $obj = $db->fetch_object($res);
7799 return $obj->vat_default_rate.' ('.$obj->vat_default_code.')';
7800 }
7801 $db->free($res);
7802 }
7803 }
7804
7805 // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
7806 if (($seller_country_code == $buyer_country_code)
7807 || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))
7808 || (in_array($seller_country_code, array('MQ', 'GP')) && in_array($buyer_country_code, array('MQ', 'GP')))
7809 ) { // Warning ->country_code not always defined
7810 //print 'VATRULE 3';
7811 $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7812
7813 if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
7814 // Special case for india.
7815 //print 'VATRULE 3b';
7816 $reg = array();
7817 if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
7818 // we must revert the C+S into I
7819 $tmpvat = str_replace("C+S", "I", $tmpvat);
7820 } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
7821 // we must revert the I into C+S
7822 $tmpvat = str_replace("I", "C+S", $tmpvat);
7823 }
7824 }
7825
7826 return $tmpvat;
7827 }
7828
7829 // 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.
7830 // 'VATRULE 4' - Not supported
7831
7832 // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
7833 // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
7834 if (($seller_in_cee && $buyer_in_cee)) {
7835 $isacompany = $thirdparty_buyer->isACompany();
7836 if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
7837 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
7838 if (!isValidVATID($thirdparty_buyer)) {
7839 $isacompany = 0;
7840 }
7841 }
7842
7843 if (!$isacompany) {
7844 //print 'VATRULE 5';
7845 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7846 } else {
7847 //print 'VATRULE 6';
7848 return 0;
7849 }
7850 }
7851
7852 // 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
7853 // I don't see any use case that need this rule.
7854 if (getDolGlobalString('MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC') && empty($buyer_in_cee)) {
7855 $isacompany = $thirdparty_buyer->isACompany();
7856 if (!$isacompany) {
7857 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
7858 //print 'VATRULE extra';
7859 }
7860 }
7861
7862 // Otherwise the VAT proposed by default=0. End of rule.
7863 // Rem: This means that at least one of the 2 is outside the European Community and the country differs
7864 //print 'VATRULE 7';
7865 return 0;
7866}
7867
7868
7879function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
7880{
7881 global $db;
7882
7883 if ($idprodfournprice > 0) {
7884 if (!class_exists('ProductFournisseur')) {
7885 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7886 }
7887 $prodprice = new ProductFournisseur($db);
7888 $prodprice->fetch_product_fournisseur_price($idprodfournprice);
7889 return $prodprice->fourn_tva_npr;
7890 } elseif ($idprod > 0) {
7891 if (!class_exists('Product')) {
7892 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7893 }
7894 $prod = new Product($db);
7895 $prod->fetch($idprod);
7896 return $prod->tva_npr;
7897 }
7898
7899 return 0;
7900}
7901
7915function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
7916{
7917 global $mysoc;
7918
7919 if (!is_object($thirdparty_seller)) {
7920 return -1;
7921 }
7922 if (!is_object($thirdparty_buyer)) {
7923 return -1;
7924 }
7925
7926 if ($local == 1) { // Localtax 1
7927 if ($mysoc->country_code == 'ES') {
7928 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
7929 return 0;
7930 }
7931 } else {
7932 // Si vendeur non assujeti a Localtax1, localtax1 par default=0
7933 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
7934 return 0;
7935 }
7936 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
7937 return 0;
7938 }
7939 }
7940 } elseif ($local == 2) { //I Localtax 2
7941 // Si vendeur non assujeti a Localtax2, localtax2 par default=0
7942 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
7943 return 0;
7944 }
7945 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
7946 return 0;
7947 }
7948 }
7949
7950 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
7951 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
7952 }
7953
7954 return 0;
7955}
7956
7965function yn($yesno, $format = 1, $color = 0)
7966{
7967 global $langs;
7968
7969 $result = 'unknown';
7970 $classname = '';
7971 if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // To set to 'no' before the test because of the '== 0'
7972 $result = $langs->trans('yes');
7973 if ($format == 1 || $format == 3) {
7974 $result = $langs->trans("Yes");
7975 }
7976 if ($format == 2) {
7977 $result = '<input type="checkbox" value="1" checked disabled>';
7978 }
7979 if ($format == 3) {
7980 $result = '<input type="checkbox" value="1" checked disabled> '.$result;
7981 }
7982 if ($format == 4 || !is_numeric($format)) {
7983 $result = img_picto(is_numeric($format) ? '' : $format, 'check');
7984 }
7985
7986 $classname = 'ok';
7987 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
7988 $result = $langs->trans("no");
7989 if ($format == 1 || $format == 3) {
7990 $result = $langs->trans("No");
7991 }
7992 if ($format == 2) {
7993 $result = '<input type="checkbox" value="0" disabled>';
7994 }
7995 if ($format == 3) {
7996 $result = '<input type="checkbox" value="0" disabled> '.$result;
7997 }
7998 if ($format == 4 || !is_numeric($format)) {
7999 $result = img_picto(is_numeric($format) ? '' : $format, 'uncheck');
8000 }
8001
8002 if ($color == 2) {
8003 $classname = 'ok';
8004 } else {
8005 $classname = 'error';
8006 }
8007 }
8008 if ($color) {
8009 return '<span class="'.$classname.'">'.$result.'</span>';
8010 }
8011 return $result;
8012}
8013
8032function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
8033{
8034 if (empty($modulepart) && is_object($object)) {
8035 if (!empty($object->module)) {
8036 $modulepart = $object->module;
8037 } elseif (!empty($object->element)) {
8038 $modulepart = $object->element;
8039 }
8040 }
8041
8042 $path = '';
8043
8044 // Define $arrayforoldpath that is module path using a hierarchy on more than 1 level.
8045 $arrayforoldpath = array('cheque' => 2, 'category' => 2, 'holiday' => 2, 'supplier_invoice' => 2, 'invoice_supplier' => 2, 'mailing' => 2, 'supplier_payment' => 2);
8046 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8047 $arrayforoldpath['product'] = 2;
8048 }
8049
8050 if (empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8051 $level = $arrayforoldpath[$modulepart];
8052 }
8053 if (!empty($level) && array_key_exists($modulepart, $arrayforoldpath)) {
8054 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
8055 if (empty($num) && is_object($object)) {
8056 $num = $object->id;
8057 }
8058 if (empty($alpha)) {
8059 $num = preg_replace('/([^0-9])/i', '', $num);
8060 } else {
8061 $num = preg_replace('/^.*\-/i', '', $num);
8062 }
8063 $num = substr("000".$num, -$level);
8064 if ($level == 1) {
8065 $path = substr($num, 0, 1);
8066 }
8067 if ($level == 2) {
8068 $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
8069 }
8070 if ($level == 3) {
8071 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
8072 }
8073 } else {
8074 // We will enhance here a common way of forging path for document storage.
8075 // In a future, we may distribute directories on several levels depending on setup and object.
8076 // Here, $object->id, $object->ref and $modulepart are required.
8077 if (in_array($modulepart, array('societe', 'thirdparty')) && $object instanceOf Societe) {
8078 // 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
8079 $path = dol_sanitizeFileName((string) $object->id);
8080 } else {
8081 $path = dol_sanitizeFileName(empty($object->ref) ? (string) ((is_object($object) && property_exists($object, 'id')) ? $object->id : '') : $object->ref);
8082 }
8083 }
8084
8085 if (empty($withoutslash) && !empty($path)) {
8086 $path .= '/';
8087 }
8088
8089 return $path;
8090}
8091
8100function dol_mkdir($dir, $dataroot = '', $newmask = '')
8101{
8102 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
8103
8104 $dir = dol_sanitizePathName($dir, '_', 0);
8105
8106 $dir_osencoded = dol_osencode($dir);
8107 if (@is_dir($dir_osencoded)) {
8108 return 0;
8109 }
8110
8111 $nberr = 0;
8112 $nbcreated = 0;
8113
8114 $ccdir = '';
8115 if (!empty($dataroot)) {
8116 // Remove data root from loop
8117 $dir = str_replace($dataroot.'/', '', $dir);
8118 $ccdir = $dataroot.'/';
8119 }
8120
8121 $cdir = explode("/", $dir);
8122 $num = count($cdir);
8123 for ($i = 0; $i < $num; $i++) {
8124 if ($i > 0) {
8125 $ccdir .= '/'.$cdir[$i];
8126 } else {
8127 $ccdir .= $cdir[$i];
8128 }
8129 $regs = array();
8130 if (preg_match("/^.:$/", $ccdir, $regs)) {
8131 continue; // If the Windows path is incomplete, continue with next directory
8132 }
8133
8134 // Attention, is_dir() can fail event if the directory exists
8135 // (i.e. according the open_basedir configuration)
8136 if ($ccdir) {
8137 $ccdir_osencoded = dol_osencode($ccdir);
8138 if (!@is_dir($ccdir_osencoded)) {
8139 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' is not found (does not exists or is outside open_basedir PHP setting).", LOG_DEBUG);
8140
8141 umask(0);
8142 $dirmaskdec = octdec((string) $newmask);
8143 if (empty($newmask)) {
8144 $dirmaskdec = octdec(getDolGlobalString('MAIN_UMASK', '0755'));
8145 }
8146 $dirmaskdec |= octdec('0111'); // Set x bit required for directories
8147 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
8148 // If the is_dir has returned a false information, we arrive here
8149 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' (no permission to write into parent or directory already exists).", LOG_WARNING);
8150 $nberr++;
8151 } else {
8152 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
8153 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8154 $nbcreated++;
8155 }
8156 } else {
8157 $nberr = 0; // At this point in the code, the previous failures can be ignored -> set $nberr to 0
8158 }
8159 }
8160 }
8161 return ($nberr ? -$nberr : $nbcreated);
8162}
8163
8164
8172function dolChmod($filepath, $newmask = '')
8173{
8174 global $conf;
8175
8176 if (!empty($newmask)) {
8177 @chmod($filepath, octdec($newmask));
8178 } elseif (getDolGlobalString('MAIN_UMASK')) {
8179 @chmod($filepath, octdec($conf->global->MAIN_UMASK));
8180 }
8181}
8182
8183
8189function picto_required()
8190{
8191 return '<span class="fieldrequired">*</span>';
8192}
8193
8194
8211function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
8212{
8213 if (is_null($stringtoclean)) {
8214 return '';
8215 }
8216
8217 if ($removelinefeed == 2) {
8218 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
8219 }
8220 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
8221
8222 // 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)
8223 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8224
8225 $temp = str_replace('< ', '__ltspace__', $temp);
8226 $temp = str_replace('<:', '__lttwopoints__', $temp);
8227
8228 if ($strip_tags) {
8229 $temp = strip_tags($temp);
8230 } else {
8231 // 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).
8232 $pattern = "/<[^<>]+>/";
8233 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
8234 // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
8235 // pass 2 - $temp after pass 2: 0000-021
8236 $tempbis = $temp;
8237 do {
8238 $temp = $tempbis;
8239 $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
8240 $tempbis = preg_replace($pattern, '', $tempbis);
8241 //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
8242 } while ($tempbis != $temp);
8243
8244 $temp = $tempbis;
8245
8246 // 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).
8247 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
8248 }
8249
8250 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
8251
8252 // Remove also carriage returns
8253 if ($removelinefeed == 1) {
8254 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
8255 }
8256
8257 // And double spaces
8258 if ($removedoublespaces) {
8259 while (strpos($temp, " ") !== false) {
8260 $temp = str_replace(" ", " ", $temp);
8261 }
8262 }
8263
8264 $temp = str_replace('__ltspace__', '< ', $temp);
8265 $temp = str_replace('__lttwopoints__', '<:', $temp);
8266
8267 return trim($temp);
8268}
8269
8288function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array(), $allowlink = 0, $allowscript = 0, $allowstyle = 0, $allowphp = 0)
8289{
8290 if (empty($allowed_tags)) {
8291 $allowed_tags = array(
8292 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li",
8293 "ol", "p", "q", "s", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
8294 "header", "footer", "nav", "section", "menu", "menuitem" // html5 tags
8295 );
8296 }
8297 $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
8298 if ($allowiframe) {
8299 if (!in_array('iframe', $allowed_tags)) {
8300 $allowed_tags[] = "iframe";
8301 }
8302 }
8303 if ($allowlink) {
8304 if (!in_array('link', $allowed_tags)) {
8305 $allowed_tags[] = "link";
8306 }
8307 }
8308 if ($allowscript) {
8309 if (!in_array('script', $allowed_tags)) {
8310 $allowed_tags[] = "script";
8311 }
8312 }
8313 if ($allowstyle) {
8314 if (!in_array('style', $allowed_tags)) {
8315 $allowed_tags[] = "style";
8316 }
8317 }
8318
8319 $allowed_tags_string = implode("><", $allowed_tags);
8320 $allowed_tags_string = '<'.$allowed_tags_string.'>';
8321
8322 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
8323
8324 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
8325
8326 //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
8327 $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
8328
8329 if ($allowphp) {
8330 $allowed_tags[] = "commentphp";
8331 $stringtoclean = preg_replace('/^<\?php([^"]+)\?>$/i', '<commentphp>\1__</commentphp>', $stringtoclean); // Note: <?php ... > is allowed only if on the same line
8332 $stringtoclean = preg_replace('/"<\?php([^"]+)\?>"/i', '"<commentphp>\1</commentphp>"', $stringtoclean); // Note: "<?php ... >" is allowed only if on the same line
8333 }
8334
8335 $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
8336 $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
8337
8338 // Remove all HTML tags
8339 $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
8340
8341 if ($cleanalsosomestyles) { // Clean for remaining html tags
8342 $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
8343 }
8344 if ($removeclassattribute) { // Clean for remaining html tags
8345 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
8346 }
8347
8348 // Remove 'javascript:' that we should not find into a text
8349 // 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)).
8350 if ($cleanalsojavascript) {
8351 $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);
8352 }
8353
8354 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
8355
8356 if ($allowphp) {
8357 $temp = preg_replace('/<commentphp>(.*)<\/commentphp>/', '<?php\1?>', $temp); // Restore php code
8358 }
8359
8360 $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
8361
8362
8363 return $temp;
8364}
8365
8366
8379function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = null)
8380{
8381 if (is_null($allowed_attributes)) {
8382 $allowed_attributes = array(
8383 "allow", "allowfullscreen", "alt", "async", "class", "contenteditable", "crossorigin", "data-html", "frameborder", "height", "href", "id", "name", "property", "rel", "src", "style", "target", "title", "type", "width",
8384 // HTML5
8385 "header", "footer", "nav", "section", "menu", "menuitem"
8386 );
8387 }
8388 // Always add content and http-equiv for meta tags, required to force encoding and keep html content in utf8 by load/saveHTML functions.
8389 if (!in_array("content", $allowed_attributes)) {
8390 $allowed_attributes[] = "content";
8391 }
8392 if (!in_array("http-equiv", $allowed_attributes)) {
8393 $allowed_attributes[] = "http-equiv";
8394 }
8395
8396 if (class_exists('DOMDocument') && !empty($stringtoclean)) {
8397 $stringtoclean = '<?xml encoding="UTF-8"><html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>'.$stringtoclean.'</body></html>';
8398
8399 // Warning: loadHTML does not support HTML5 on old libxml versions.
8400 $dom = new DOMDocument('', 'UTF-8');
8401 // If $stringtoclean is wrong, it will generates warnings. So we disable warnings and restore them later.
8402 $savwarning = error_reporting();
8403 error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE);
8404 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOXMLDECL);
8405 error_reporting($savwarning);
8406
8407 if ($dom instanceof DOMDocument) {
8408 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
8409 $el = $els->item($i);
8410 if (!$el instanceof DOMElement) {
8411 continue;
8412 }
8413 $attrs = $el->attributes;
8414 for ($ii = $attrs->length - 1; $ii >= 0; $ii--) {
8415 //var_dump($attrs->item($ii));
8416 if (!empty($attrs->item($ii)->name)) {
8417 if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
8418 // Delete attribute if not into allowed_attributes @phan-suppress-next-line PhanUndeclaredMethod
8419 $els->item($i)->removeAttribute($attrs->item($ii)->name);
8420 } elseif (in_array($attrs->item($ii)->name, array('style'))) {
8421 // If attribute is 'style'
8422 $valuetoclean = $attrs->item($ii)->value;
8423
8424 if (isset($valuetoclean)) {
8425 do {
8426 $oldvaluetoclean = $valuetoclean;
8427 $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
8428 $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
8429 if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
8430 $valuetoclean = preg_replace('/display\s*:/mi', '', $valuetoclean);
8431 $valuetoclean = preg_replace('/z-index\s*:/mi', '', $valuetoclean);
8432 $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*:/mi', '', $valuetoclean);
8433 }
8434
8435 // We do not allow logout|passwordforgotten.php and action= into the content of a "style" tag
8436 $valuetoclean = preg_replace('/(logout|passwordforgotten)\.php/mi', '', $valuetoclean);
8437 $valuetoclean = preg_replace('/action=/mi', '', $valuetoclean);
8438 } while ($oldvaluetoclean != $valuetoclean);
8439 }
8440
8441 $attrs->item($ii)->value = $valuetoclean;
8442 }
8443 }
8444 }
8445 }
8446 }
8447
8448 $dom->encoding = 'UTF-8';
8449
8450 $return = $dom->saveHTML(); // This may add a LF at end of lines, so we will trim later
8451 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
8452
8453 $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
8454 $return = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]*'.preg_quote('></head><body>', '/').'/', '', $return);
8455 $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', trim($return));
8456
8457 return trim($return);
8458 } else {
8459 return $stringtoclean;
8460 }
8461}
8462
8474function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
8475{
8476 $temp = $stringtoclean;
8477 foreach ($disallowed_tags as $tagtoremove) {
8478 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
8479 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
8480 }
8481
8482 if ($cleanalsosomestyles) {
8483 $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
8484 }
8485
8486 return $temp;
8487}
8488
8489
8499function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
8500{
8501 if ($nboflines == 1) {
8502 if (dol_textishtml($text)) {
8503 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
8504 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
8505 } else {
8506 if (isset($text)) {
8507 $firstline = preg_replace('/[\n\r].*/', '', $text);
8508 } else {
8509 $firstline = '';
8510 }
8511 }
8512 return $firstline.(isset($firstline) && isset($text) && (strlen($firstline) != strlen($text)) ? '...' : '');
8513 } else {
8514 $ishtml = 0;
8515 if (dol_textishtml($text)) {
8516 $text = preg_replace('/\n/', '', $text);
8517 $ishtml = 1;
8518 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8519 } else {
8520 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8521 }
8522
8523 $text = strtr($text, $repTable);
8524 if ($charset == 'UTF-8') {
8525 $pattern = '/(<br[^>]*>)/Uu';
8526 } else {
8527 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8528 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8529 }
8530 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8531
8532 $firstline = '';
8533 $i = 0;
8534 $countline = 0;
8535 $lastaddediscontent = 1;
8536 while ($countline < $nboflines && isset($a[$i])) {
8537 if (preg_match('/<br[^>]*>/', $a[$i])) {
8538 if (array_key_exists($i + 1, $a) && !empty($a[$i + 1])) {
8539 $firstline .= ($ishtml ? "<br>\n" : "\n");
8540 // Is it a br for a new line of after a printed line ?
8541 if (!$lastaddediscontent) {
8542 $countline++;
8543 }
8544 $lastaddediscontent = 0;
8545 }
8546 } else {
8547 $firstline .= $a[$i];
8548 $lastaddediscontent = 1;
8549 $countline++;
8550 }
8551 $i++;
8552 }
8553
8554 $adddots = (isset($a[$i]) && (!preg_match('/<br[^>]*>/', $a[$i]) || (array_key_exists($i + 1, $a) && !empty($a[$i + 1]))));
8555 //unset($a);
8556 $ret = $firstline.($adddots ? '...' : '');
8557 //exit;
8558 return $ret;
8559 }
8560}
8561
8562
8574function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
8575{
8576 if (is_null($stringtoencode)) {
8577 return '';
8578 }
8579
8580 if (!$nl2brmode) {
8581 return nl2br($stringtoencode, $forxml);
8582 } else {
8583 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
8584 return $ret;
8585 }
8586}
8587
8597function dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox = 0, $check = 'restricthtml')
8598{
8599 if (empty($nouseofiframesandbox) && getDolGlobalString('MAIN_SECURITY_USE_SANDBOX_FOR_HTMLWITHNOJS')) {
8600 // TODO using sandbox on inline html content is not possible yet with current browsers
8601 //$s = '<iframe class="iframewithsandbox" sandbox><html><body>';
8602 //$s .= $stringtoencode;
8603 //$s .= '</body></html></iframe>';
8604 return $stringtoencode;
8605 } else {
8606 $out = $stringtoencode;
8607
8608 // First clean HTML content
8609 do {
8610 $oldstringtoclean = $out;
8611
8612 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML') && $check != 'restricthtmlallowunvalid') {
8613 try {
8614 libxml_use_internal_errors(false); // Avoid to fill memory with xml errors
8615 if (LIBXML_VERSION < 20900) {
8616 // Avoid load of external entities (security problem).
8617 // Required only if LIBXML_VERSION < 20900
8618 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
8619 libxml_disable_entity_loader(true);
8620 }
8621
8622 $dom = new DOMDocument();
8623 // Add a trick to solve pb with text without parent tag
8624 // like '<h1>Foo</h1><p>bar</p>' that wrongly ends up, without the trick, with '<h1>Foo<p>bar</p></h1>'
8625 // like 'abc' that wrongly ends up, without the trick, with '<p>abc</p>'
8626
8627 if (dol_textishtml($out)) {
8628 $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>';
8629 } else {
8630 $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>';
8631 }
8632
8633 $dom->loadHTML($out, LIBXML_HTML_NODEFDTD | LIBXML_ERR_NONE | LIBXML_HTML_NOIMPLIED | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOXMLDECL);
8634
8635 $dom->encoding = 'UTF-8';
8636
8637 $out = trim($dom->saveHTML());
8638
8639 // Remove the trick added to solve pb with text in utf8 and text without parent tag
8640 $out = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $out);
8641 $out = preg_replace('/^'.preg_quote('<html><head><', '/').'[^<>]+'.preg_quote('></head><body><div class="tricktoremove">', '/').'/', '', $out);
8642 $out = preg_replace('/'.preg_quote('</div></body></html>', '/').'$/', '', trim($out));
8643 // $out = preg_replace('/^<\?xml encoding="UTF-8"><div class="tricktoremove">/', '', $out);
8644 // $out = preg_replace('/<\/div>$/', '', $out);
8645 // var_dump('rrrrrrrrrrrrrrrrrrrrrrrrrrrrr'.$out);
8646 } catch (Exception $e) {
8647 // If error, invalid HTML string with no way to clean it
8648 //print $e->getMessage();
8649 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8650 }
8651 }
8652
8653 if (!empty($out) && getDolGlobalString('MAIN_RESTRICTHTML_ONLY_VALID_HTML_TIDY') && !in_array($check, array('restricthtmlallowunvalid', 'restricthtmlallowlinkscript'))) {
8654 // Tidy can't be used for restricthtmlallowunvalid and restricthtmlallowlinkscript
8655 // TODO Try to implement a hack for restricthtmlallowlinkscript by renaming tag <link> and <script> ?
8656 try {
8657 // Try cleaning using tidy
8658 if (extension_loaded('tidy') && class_exists("tidy")) {
8659 //print "aaa".$out."\n";
8660
8661 // See options at https://tidy.sourceforge.net/docs/quickref.html
8662 $config = array(
8663 'clean' => false,
8664 // 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;
8665 'quote-marks' => false,
8666 'doctype' => 'strict',
8667 'show-body-only' => true,
8668 "indent-attributes" => false,
8669 "vertical-space" => false,
8670 //'ident' => false, // Not always supported
8671 "wrap" => 0
8672 // HTML5 tags
8673 //'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',
8674 //'new-blocklevel-tags' => 'footer header section menu menuitem'
8675 //'new-empty-tags' => 'command embed keygen source track wbr',
8676 //'new-inline-tags' => 'audio command datalist embed keygen mark menuitem meter output progress source time video wbr',
8677 );
8678
8679 // Tidy
8680 $tidy = new tidy();
8681 $out = $tidy->repairString($out, $config, 'utf8');
8682
8683 //print "xxx".$out;exit;
8684 }
8685 } catch (Exception $e) {
8686 // If error, invalid HTML string with no way to clean it
8687 //print $e->getMessage();
8688 $out = 'InvalidHTMLStringCantBeCleaned '.$e->getMessage();
8689 }
8690 }
8691
8692 // Clear ZERO WIDTH NO-BREAK SPACE, ZERO WIDTH SPACE, ZERO WIDTH JOINER
8693 $out = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $out);
8694
8695 // Clean some html entities that are useless so text is cleaner
8696 $out = preg_replace('/&(tab|newline);/i', ' ', $out);
8697
8698 // Ckeditor uses the numeric entity for apostrophe so we force it to text entity (all other special chars are
8699 // encoded using text entities) so we can then exclude all numeric entities.
8700 $out = preg_replace('/&#39;/i', '&apos;', $out);
8701
8702 // 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).
8703 // 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
8704 // using a non conventionnal way to be encoded, to not have them sanitized just after)
8705 if (function_exists('realCharForNumericEntities')) { // May not exist when main.inc.php not loaded, for example in a CLI context
8706 $out = preg_replace_callback(
8707 '/&#(x?[0-9][0-9a-f]+;?)/i',
8712 static function ($m) {
8713 return realCharForNumericEntities($m);
8714 },
8715 $out
8716 );
8717 }
8718
8719 // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
8720 $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'.
8721
8722 // Keep only some html tags and remove also some 'javascript:' strings
8723 if ($check == 'restricthtmlallowlinkscript') {
8724 $out = dol_string_onlythesehtmltags($out, 0, 1, 0, 0, array(), 1, 1, 1, getDolGlobalInt("UNSECURED_restricthtmlallowlinkscript_ALLOW_PHP"));
8725 } elseif ($check == 'restricthtmlallowclass' || $check == 'restricthtmlallowunvalid') {
8726 $out = dol_string_onlythesehtmltags($out, 0, 0, 1);
8727 } elseif ($check == 'restricthtmlallowiframe') {
8728 $out = dol_string_onlythesehtmltags($out, 0, 0, 1, 1);
8729 } else {
8730 $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
8731 }
8732
8733 // Keep only some html attributes and exclude non expected HTML attributes and clean content of some attributes (keep only alt=, title=...).
8734 if (getDolGlobalString('MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES')) {
8736 }
8737
8738 // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
8739 $out = preg_replace('/&apos;/i', "&#39;", $out);
8740
8741 // Now remove js
8742 // 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
8743 $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)>
8744 $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);
8745 $out = preg_replace('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus(in|out)?|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', '', $out);
8746 $out = preg_replace('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', '', $out);
8747 $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);
8748 $out = preg_replace('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', '', $out);
8749 // More not into the previous list
8750 $out = preg_replace('/on(repeat|begin|finish|beforeinput)[a-z]*\s*=/i', '', $out);
8751 } while ($oldstringtoclean != $out);
8752
8753 // Check the limit of external links that are automatically executed in a Rich text content. We count:
8754 // '<img' to avoid <img src="http...">, we can only accept "<img src="data:..."
8755 // 'url(' to avoid inline style like background: url(http...
8756 // '<link' to avoid <link href="http...">
8757 $reg = array();
8758 $tmpout = preg_replace('/<img src="data:/mi', '<__IMG_SRC_DATA__ src="data:', $out);
8759 preg_match_all('/(<img|url\‍(|<link)/i', $tmpout, $reg);
8760 $nblinks = count($reg[0]);
8761 if ($nblinks > getDolGlobalInt("MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT", 1000)) {
8762 $out = 'ErrorTooManyLinksIntoHTMLString';
8763 }
8764
8765 if (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 2 || $check == 'restricthtmlnolink') {
8766 if ($nblinks > 0) {
8767 $out = 'ErrorHTMLLinksNotAllowed';
8768 }
8769 } elseif (getDolGlobalInt('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS') == 1) {
8770 $nblinks = 0;
8771 // Loop on each url in src= and url(
8772 $pattern = '/src=["\']?(http[^"\']+)|url\‍(["\']?(http[^\‍)]+)/';
8773
8774 $matches = array();
8775 if (preg_match_all($pattern, $out, $matches)) {
8776 // URLs are into $matches[1]
8777 $urls = $matches[1];
8778
8779 // Affiche les URLs
8780 foreach ($urls as $url) {
8781 $nblinks++;
8782 echo "Found url = ".$url . "\n";
8783 }
8784 if ($nblinks > 0) {
8785 $out = 'ErrorHTMLExternalLinksNotAllowed';
8786 }
8787 }
8788 }
8789
8790 return $out;
8791 }
8792}
8793
8814function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
8815{
8816 if (is_null($stringtoencode)) {
8817 return '';
8818 }
8819
8820 $newstring = $stringtoencode;
8821 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
8822 $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.
8823 if ($removelasteolbr) {
8824 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
8825 }
8826 $newstring = preg_replace('/[\x{200B}-\x{200D}\x{FEFF}]/u', ' ', $newstring);
8827 $newstring = strtr($newstring, array('&' => '__PROTECTand__', '<' => '__PROTECTlt__', '>' => '__PROTECTgt__', '"' => '__PROTECTdquot__'));
8828 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
8829 $newstring = strtr($newstring, array('__PROTECTand__' => '&', '__PROTECTlt__' => '<', '__PROTECTgt__' => '>', '__PROTECTdquot__' => '"'));
8830 } else {
8831 if ($removelasteolbr) {
8832 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
8833 }
8834 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
8835 }
8836 // Other substitutions that htmlentities does not do
8837 //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
8838 return $newstring;
8839}
8840
8848function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
8849{
8850 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
8851 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
8852 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
8853 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
8854 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
8855 return $ret;
8856}
8857
8864function dol_htmlcleanlastbr($stringtodecode)
8865{
8866 $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
8867 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
8868 return $ret;
8869}
8870
8880function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
8881{
8882 $newstring = $a;
8883 if ($keepsomeentities) {
8884 $newstring = strtr($newstring, array('&amp;' => '__andamp__', '&lt;' => '__andlt__', '&gt;' => '__andgt__', '"' => '__dquot__'));
8885 }
8886 $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
8887 if ($keepsomeentities) {
8888 $newstring = strtr($newstring, array('__andamp__' => '&amp;', '__andlt__' => '&lt;', '__andgt__' => '&gt;', '__dquot__' => '"'));
8889 }
8890 return $newstring;
8891}
8892
8904function dol_htmlentities($string, $flags = ENT_QUOTES | ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
8905{
8906 return htmlentities($string, $flags, $encoding, $double_encode);
8907}
8908
8920function dol_string_is_good_iso($s, $clean = 0)
8921{
8922 $len = dol_strlen($s);
8923 $out = '';
8924 $ok = 1;
8925 for ($scursor = 0; $scursor < $len; $scursor++) {
8926 $ordchar = ord($s[$scursor]);
8927 //print $scursor.'-'.$ordchar.'<br>';
8928 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
8929 $ok = 0;
8930 break;
8931 } elseif ($ordchar > 126 && $ordchar < 160) {
8932 $ok = 0;
8933 break;
8934 } elseif ($clean) {
8935 $out .= $s[$scursor];
8936 }
8937 }
8938 if ($clean) {
8939 return $out;
8940 }
8941 return $ok;
8942}
8943
8952function dol_nboflines($s, $maxchar = 0)
8953{
8954 if ($s == '') {
8955 return 0;
8956 }
8957 $arraystring = explode("\n", $s);
8958 $nb = count($arraystring);
8959
8960 return $nb;
8961}
8962
8963
8973function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
8974{
8975 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
8976 if (dol_textishtml($text)) {
8977 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
8978 }
8979
8980 $text = strtr($text, $repTable);
8981 if ($charset == 'UTF-8') {
8982 $pattern = '/(<br[^>]*>)/Uu';
8983 } else {
8984 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
8985 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
8986 }
8987 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8988
8989 $nblines = (int) floor((count($a) + 1) / 2);
8990 // count possible auto line breaks
8991 if ($maxlinesize) {
8992 foreach ($a as $line) {
8993 if (dol_strlen($line) > $maxlinesize) {
8994 //$line_dec = html_entity_decode(strip_tags($line));
8995 $line_dec = html_entity_decode($line);
8996 if (dol_strlen($line_dec) > $maxlinesize) {
8997 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
8998 $nblines += substr_count($line_dec, '\n');
8999 }
9000 }
9001 }
9002 }
9003
9004 unset($a);
9005 return $nblines;
9006}
9007
9016function dol_textishtml($msg, $option = 0)
9017{
9018 if (is_null($msg)) {
9019 return false;
9020 }
9021
9022 if ($option == 1) {
9023 if (preg_match('/<(html|link|script)/i', $msg)) {
9024 return true;
9025 } elseif (preg_match('/<body/i', $msg)) {
9026 return true;
9027 } elseif (preg_match('/<\/textarea/i', $msg)) {
9028 return true;
9029 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9030 return true;
9031 } elseif (preg_match('/<br/i', $msg)) {
9032 return true;
9033 }
9034 return false;
9035 } else {
9036 // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
9037 $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
9038 if (preg_match('/<(html|link|script|body)/i', $msg)) {
9039 return true;
9040 } elseif (preg_match('/<\/textarea/i', $msg)) {
9041 return true;
9042 } elseif (preg_match('/<(b|em|i|u)(\s+[^>]+)?>/i', $msg)) {
9043 return true;
9044 } elseif (preg_match('/<(br|hr)\/>/i', $msg)) {
9045 return true;
9046 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)>/i', $msg)) {
9047 return true;
9048 } elseif (preg_match('/<(br|hr|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
9049 return true;
9050 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
9051 return true; // must accept <img src="http://example.com/aaa.png" />
9052 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
9053 return true; // must accept <a href="http://example.com/aaa.png" />
9054 } elseif (preg_match('/<h[0-9]>/i', $msg)) {
9055 return true;
9056 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
9057 // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
9058 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
9059 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
9060 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
9061 }
9062
9063 return false;
9064 }
9065}
9066
9081function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
9082{
9083 if (!empty($invert)) {
9084 $tmp = $text1;
9085 $text1 = $text2;
9086 $text2 = $tmp;
9087 }
9088
9089 $ret = '';
9090 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
9091 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
9092 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
9093 return $ret;
9094}
9095
9096
9097
9111function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null, $include = null)
9112{
9113 global $db, $conf, $mysoc, $user, $extrafields;
9114
9115 $substitutionarray = array();
9116
9117 if ((empty($exclude) || !in_array('user', $exclude)) && (empty($include) || in_array('user', $include)) && $user instanceof User) {
9118 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
9119 // this will include signature content first and then replace var found into content of signature
9120 //var_dump($onlykey);
9121 $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()
9122 $usersignature = $user->signature;
9123 $substitutionarray = array_merge($substitutionarray, array(
9124 '__SENDEREMAIL_SIGNATURE__' => (string) ((!getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc('SignatureFromTheSelectedSenderProfile', 30) : $emailsendersignature) : ''),
9125 '__USER_SIGNATURE__' => (string) (($usersignature && !getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN')) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($usersignature), 30) : $usersignature) : '')
9126 ));
9127
9128 if (is_object($user) && ($user instanceof User)) {
9129 $substitutionarray = array_merge($substitutionarray, array(
9130 '__USER_ID__' => (string) $user->id,
9131 '__USER_LOGIN__' => (string) $user->login,
9132 '__USER_EMAIL__' => (string) $user->email,
9133 '__USER_PHONE__' => (string) dol_print_phone($user->office_phone, '', 0, 0, '', " ", '', '', -1),
9134 '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile, '', 0, 0, '', " ", '', '', -1),
9135 '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile, '', 0, 0, '', " ", '', '', -1),
9136 '__USER_FAX__' => (string) $user->office_fax,
9137 '__USER_LASTNAME__' => (string) $user->lastname,
9138 '__USER_FIRSTNAME__' => (string) $user->firstname,
9139 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
9140 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
9141 '__USER_JOB__' => (string) $user->job,
9142 '__USER_REMOTE_IP__' => (string) getUserRemoteIP(),
9143 '__USER_VCARD_URL__' => (string) $user->getOnlineVirtualCardUrl('', 'external')
9144 ));
9145 }
9146 }
9147 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc) && (empty($include) || in_array('mycompany', $include))) {
9148 $substitutionarray = array_merge($substitutionarray, array(
9149 '__MYCOMPANY_NAME__' => $mysoc->name,
9150 '__MYCOMPANY_EMAIL__' => $mysoc->email,
9151 '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone, '', 0, 0, '', " ", '', '', -1),
9152 '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax, '', 0, 0, '', " ", '', '', -1),
9153 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
9154 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
9155 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
9156 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
9157 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
9158 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
9159 '__MYCOMPANY_PROFID7__' => $mysoc->idprof7,
9160 '__MYCOMPANY_PROFID8__' => $mysoc->idprof8,
9161 '__MYCOMPANY_PROFID9__' => $mysoc->idprof9,
9162 '__MYCOMPANY_PROFID10__' => $mysoc->idprof10,
9163 '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
9164 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
9165 '__MYCOMPANY_ADDRESS__' => $mysoc->address,
9166 '__MYCOMPANY_ZIP__' => $mysoc->zip,
9167 '__MYCOMPANY_TOWN__' => $mysoc->town,
9168 '__MYCOMPANY_STATE__' => $mysoc->state,
9169 '__MYCOMPANY_COUNTRY__' => $mysoc->country,
9170 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
9171 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
9172 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
9173 ));
9174 }
9175
9176 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)) && (empty($include) || in_array('object', $include))) {
9177 if ($onlykey) {
9178 $substitutionarray['__ID__'] = '__ID__';
9179 $substitutionarray['__REF__'] = '__REF__';
9180 $substitutionarray['__NEWREF__'] = '__NEWREF__';
9181 $substitutionarray['__LABEL__'] = '__LABEL__';
9182 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
9183 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
9184 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
9185 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
9186 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
9187
9188 if (isModEnabled("societe")) { // Most objects are concerned
9189 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
9190 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
9191 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
9192 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
9193 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
9194 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
9195 //$substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = '__THIRDPARTY_EMAIL_URLENCODED__'; // We hide this one
9196 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
9197 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
9198 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
9199 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
9200 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
9201 $substitutionarray['__THIRDPARTY_STATE__'] = '__THIRDPARTY_STATE__';
9202 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
9203 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
9204 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
9205 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
9206 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
9207 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
9208 $substitutionarray['__THIRDPARTY_IDPROF7__'] = '__THIRDPARTY_IDPROF7__';
9209 $substitutionarray['__THIRDPARTY_IDPROF8__'] = '__THIRDPARTY_IDPROF8__';
9210 $substitutionarray['__THIRDPARTY_IDPROF9__'] = '__THIRDPARTY_IDPROF9__';
9211 $substitutionarray['__THIRDPARTY_IDPROF10__'] = '__THIRDPARTY_IDPROF10__';
9212 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
9213 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
9214 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
9215 }
9216 if (isModEnabled('member') && (!is_object($object) || $object->element == 'adherent') && (empty($exclude) || !in_array('member', $exclude)) && (empty($include) || in_array('member', $include))) {
9217 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
9218 $substitutionarray['__MEMBER_TITLE__'] = '__MEMBER_TITLE__';
9219 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
9220 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
9221 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
9222 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
9223 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
9224 }
9225 // add substitution variables for ticket
9226 if (isModEnabled('ticket') && (!is_object($object) || $object->element == 'ticket') && (empty($exclude) || !in_array('ticket', $exclude)) && (empty($include) || in_array('ticket', $include))) {
9227 $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
9228 $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
9229 $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
9230 $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
9231 $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
9232 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
9233 $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
9234 $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
9235 $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
9236 }
9237
9238 if (isModEnabled('recruitment') && (!is_object($object) || $object->element == 'recruitmentcandidature') && (empty($exclude) || !in_array('recruitment', $exclude)) && (empty($include) || in_array('recruitment', $include))) {
9239 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
9240 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
9241 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
9242 }
9243 if (isModEnabled('project') && (empty($exclude) || !in_array('project', $exclude)) && (empty($include) || in_array('project', $include))) { // Most objects
9244 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
9245 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
9246 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
9247 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
9248 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
9249 }
9250 if (isModEnabled('contract') && (!is_object($object) || $object->element == 'contract') && (empty($exclude) || !in_array('contract', $exclude)) && (empty($include) || in_array('contract', $include))) {
9251 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
9252 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
9253 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
9254 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
9255 }
9256 if (isModEnabled("propal") && (!is_object($object) || $object->element == 'propal') && (empty($exclude) || !in_array('propal', $exclude)) && (empty($include) || in_array('propal', $include))) {
9257 $substitutionarray['__ONLINE_SIGN_URL__'] = 'ToOfferALinkForOnlineSignature';
9258 }
9259 if (isModEnabled("intervention") && (!is_object($object) || $object->element == 'fichinter') && (empty($exclude) || !in_array('intervention', $exclude)) && (empty($include) || in_array('intervention', $include))) {
9260 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = 'ToOfferALinkForOnlineSignature';
9261 }
9262 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
9263 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
9264 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
9265 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
9266 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
9267 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
9268 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
9269
9270 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
9271 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
9272 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
9273 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
9274 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
9275
9276 if (isModEnabled("shipping") && (!is_object($object) || $object->element == 'shipping')) {
9277 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
9278 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
9279 $substitutionarray['__SHIPPINGMETHOD__'] = 'Shipping method';
9280 }
9281 if (isModEnabled("reception") && (!is_object($object) || $object->element == 'reception')) {
9282 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shipping tracking number of shipment';
9283 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
9284 }
9285 } else {
9286 '@phan-var-force Adherent|Delivery $object';
9287 $substitutionarray['__ID__'] = $object->id;
9288 $substitutionarray['__REF__'] = $object->ref;
9289 $substitutionarray['__NEWREF__'] = $object->newref;
9290 $substitutionarray['__LABEL__'] = (isset($object->label) ? $object->label : (isset($object->title) ? $object->title : null));
9291 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9292 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9293 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
9294 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
9295
9296 $substitutionarray['__DATE_CREATION__'] = (isset($object->date_creation) ? dol_print_date($object->date_creation, 'day', false, $outputlangs) : '');
9297 $substitutionarray['__DATE_MODIFICATION__'] = (isset($object->date_modification) ? dol_print_date($object->date_modification, 'day', false, $outputlangs) : '');
9298 $substitutionarray['__DATE_VALIDATION__'] = (isset($object->date_validation) ? dol_print_date($object->date_validation, 'day', false, $outputlangs) : '');
9299
9300 // handle date_delivery: in customer order/supplier order, the property name is delivery_date, in shipment/reception it is date_delivery
9301 $date_delivery = null;
9302 if (property_exists($object, 'date_delivery')) {
9303 $date_delivery = $object->date_delivery;
9304 } elseif (property_exists($object, 'delivery_date')) {
9305 $date_delivery = $object->delivery_date;
9306 }
9307 $substitutionarray['__DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9308 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%d") : '');
9309 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%A") : '');
9310 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%m") : '');
9311 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%b") : '');
9312 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%Y") : '');
9313 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%H") : '');
9314 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%M") : '');
9315 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($date_delivery) ? dol_print_date($date_delivery, "%S") : '');
9316
9317 // For backward compatibility (deprecated)
9318 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
9319 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
9320
9321 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($date_delivery) ? dol_print_date($date_delivery, 'day', false, $outputlangs) : '');
9322 $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 : '')) : '');
9323 $substitutionarray['__EXPIRATION_DATE__'] = (isset($object->fin_validite) ? dol_print_date($object->fin_validite, 'daytext') : '');
9324
9325 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
9326 '@phan-var-force Adherent $object';
9327 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
9328
9329 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
9330 if (method_exists($object, 'getCivilityLabel')) {
9331 $substitutionarray['__MEMBER_TITLE__'] = $object->getCivilityLabel();
9332 }
9333 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
9334 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
9335 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
9336 if (method_exists($object, 'getFullName')) {
9337 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
9338 }
9339 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
9340 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
9341 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
9342 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
9343 $substitutionarray['__MEMBER_STATE__'] = (isset($object->state) ? $object->state : '');
9344 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
9345 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
9346 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
9347 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
9348 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
9349 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
9350 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
9351 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
9352 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
9353 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
9354 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'day');
9355
9356 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
9357 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'day') : '');
9358 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START_RFC__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
9359 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'day') : '');
9360 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END_RFC__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
9361 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'day');
9362 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_RFC__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
9363 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'day');
9364 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START_RFC__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
9365 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'day');
9366 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END_RFC__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
9367 }
9368
9369 if (is_object($object) && $object->element == 'societe') {
9370 '@phan-var-force Societe $object';
9371 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
9372 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
9373 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
9374 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
9375 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
9376 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
9377 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object) ? $object->email : '');
9378 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
9379 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
9380 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
9381 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
9382 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
9383 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object) ? $object->state : '');
9384 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
9385 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
9386 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
9387 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
9388 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
9389 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
9390 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
9391 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
9392 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
9393 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
9394 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
9395 } elseif (is_object($object->thirdparty)) {
9396 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
9397 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
9398 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
9399 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
9400 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
9401 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
9402 $substitutionarray['__THIRDPARTY_EMAIL_URLENCODED__'] = urlencode(is_object($object->thirdparty) ? $object->thirdparty->email : '');
9403 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
9404 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
9405 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
9406 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
9407 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
9408 $substitutionarray['__THIRDPARTY_STATE__'] = (is_object($object->thirdparty) ? $object->thirdparty->state : '');
9409 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
9410 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
9411 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
9412 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
9413 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
9414 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
9415 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
9416 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
9417 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
9418 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
9419 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
9420 }
9421
9422 if (is_object($object) && $object->element == 'recruitmentcandidature') {
9423 '@phan-var-force RecruitmentCandidature $object';
9424 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
9425 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9426 $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9427 }
9428 if (is_object($object) && $object->element == 'conferenceorboothattendee') {
9429 '@phan-var-force ConferenceOrBoothAttendee $object';
9430 $substitutionarray['__ATTENDEE_FULLNAME__'] = $object->getFullName($outputlangs);
9431 $substitutionarray['__ATTENDEE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
9432 $substitutionarray['__ATTENDEE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
9433 }
9434
9435 if (is_object($object) && $object->element == 'project') {
9436 '@phan-var-force Project $object';
9437 $substitutionarray['__PROJECT_ID__'] = $object->id;
9438 $substitutionarray['__PROJECT_REF__'] = $object->ref;
9439 $substitutionarray['__PROJECT_NAME__'] = $object->title;
9440 } elseif (is_object($object)) {
9441 $project = null;
9442 if (!empty($object->project)) {
9443 $project = $object->project;
9444 } elseif (!empty($object->projet)) { // Deprecated, for backward compatibility
9445 $project = $object->projet;
9446 }
9447 if (!is_null($project) && is_object($project)) {
9448 $substitutionarray['__PROJECT_ID__'] = $project->id;
9449 $substitutionarray['__PROJECT_REF__'] = $project->ref;
9450 $substitutionarray['__PROJECT_NAME__'] = $project->title;
9451 } else {
9452 // can substitute variables for project : uses lazy load in "make_substitutions" method
9453 $project_id = 0;
9454 if (!empty($object->fk_project) && $object->fk_project > 0) {
9455 $project_id = $object->fk_project;
9456 } elseif (!empty($object->fk_projet) && $object->fk_projet > 0) {
9457 $project_id = $object->fk_project;
9458 }
9459 if ($project_id > 0) {
9460 // path:class:method:id
9461 $substitutionarray['__PROJECT_ID__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9462 $substitutionarray['__PROJECT_REF__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9463 $substitutionarray['__PROJECT_NAME__@lazyload'] = '/projet/class/project.class.php:Project:fetchAndSetSubstitution:' . $project_id;
9464 }
9465 }
9466 }
9467
9468 if (is_object($object) && $object->element == 'facture') {
9469 '@phan-var-force Facture $object';
9470 $substitutionarray['__INVOICE_SITUATION_NUMBER__'] = isset($object->situation_counter) ? $object->situation_counter : '';
9471 }
9472 if (is_object($object) && $object->element == 'shipping') {
9473 '@phan-var-force Expedition $object';
9474 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
9475 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
9476 $substitutionarray['__SHIPPINGMETHOD__'] = $object->shipping_method;
9477 }
9478 if (is_object($object) && $object->element == 'reception') {
9479 '@phan-var-force Reception $object';
9480 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
9481 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
9482 }
9483
9484 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
9485 '@phan-var-force Contrat $object';
9486 $dateplannedstart = '';
9487 $datenextexpiration = '';
9488 foreach ($object->lines as $line) {
9489 if ($line->date_start > $dateplannedstart) {
9490 $dateplannedstart = $line->date_start;
9491 }
9492 if ($line->statut == 4 && $line->date_end && (!$datenextexpiration || $line->date_end < $datenextexpiration)) {
9493 $datenextexpiration = $line->date_end;
9494 }
9495 }
9496 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'day');
9497 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE_RFC__'] = dol_print_date($dateplannedstart, 'dayrfc');
9498 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
9499
9500 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'day');
9501 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE_RFC__'] = dol_print_date($datenextexpiration, 'dayrfc');
9502 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
9503 }
9504 // add substitution variables for ticket
9505 if (is_object($object) && $object->element == 'ticket') {
9506 '@phan-var-force Ticket $object';
9507 $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
9508 $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
9509 $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
9510 $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
9511 $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
9512 $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
9513 $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
9514 $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
9515 $userstat = new User($db);
9516 if ($object->fk_user_assign > 0) {
9517 $userstat->fetch($object->fk_user_assign);
9518 $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9519 }
9520
9521 if ($object->fk_user_create > 0) {
9522 $userstat->fetch($object->fk_user_create);
9523 $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
9524 }
9525 }
9526
9527 // Create dynamic tags for __EXTRAFIELD_FIELD__
9528 if ($object->table_element && $object->id > 0) {
9529 if (!is_object($extrafields)) {
9530 $extrafields = new ExtraFields($db);
9531 }
9532 $extrafields->fetch_name_optionals_label($object->table_element, true);
9533
9534 if ($object->fetch_optionals() > 0) {
9535 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
9536 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
9537 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
9538 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
9539 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
9540 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
9541 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
9542 $datetime = $object->array_options['options_'.$key];
9543 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
9544 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
9545 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
9546 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
9547 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
9548 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
9549 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
9550 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
9551 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]); // For compatibility
9552 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATTED__'] = price($object->array_options['options_'.$key]);
9553 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] != 'separator') {
9554 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = !empty($object->array_options['options_'.$key]) ? $object->array_options['options_'.$key] : '';
9555 }
9556 }
9557 }
9558 }
9559 }
9560
9561 // Complete substitution array with the url to make online payment
9562 if (empty($substitutionarray['__REF__'])) {
9563 $paymenturl = '';
9564 } else {
9565 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
9566 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
9567 $outputlangs->loadLangs(array('paypal', 'other'));
9568
9569 $amounttouse = 0;
9570 $typeforonlinepayment = 'free';
9571 if (is_object($object) && $object->element == 'commande') {
9572 $typeforonlinepayment = 'order';
9573 }
9574 if (is_object($object) && $object->element == 'facture') {
9575 $typeforonlinepayment = 'invoice';
9576 }
9577 if (is_object($object) && $object->element == 'member') {
9578 $typeforonlinepayment = 'member';
9579 if (!empty($object->last_subscription_amount)) {
9580 $amounttouse = $object->last_subscription_amount;
9581 }
9582 }
9583 if (is_object($object) && $object->element == 'contrat') {
9584 $typeforonlinepayment = 'contract';
9585 }
9586 if (is_object($object) && $object->element == 'fichinter') {
9587 $typeforonlinepayment = 'ficheinter';
9588 }
9589
9590 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__'], $amounttouse);
9591 $paymenturl = $url;
9592 }
9593
9594 if ($object->id > 0) {
9595 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
9596 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
9597
9598 // Show structured communication
9599 if (getDolGlobalString('INVOICE_PAYMENT_ENABLE_STRUCTURED_COMMUNICATION') && $object->element == 'facture') {
9600 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions_be.lib.php';
9601 $substitutionarray['__PAYMENT_STRUCTURED_COMMUNICATION__'] = dolBECalculateStructuredCommunication($object->ref, $object->type);
9602 }
9603
9604 if (getDolGlobalString('PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'propal') {
9605 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9606 } else {
9607 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
9608 }
9609 if (getDolGlobalString('ORDER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'commande') {
9610 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
9611 } else {
9612 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
9613 }
9614 if (getDolGlobalString('INVOICE_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'facture') {
9615 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
9616 } else {
9617 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
9618 }
9619 if (getDolGlobalString('CONTRACT_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'contrat') {
9620 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
9621 } else {
9622 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
9623 }
9624 if (getDolGlobalString('FICHINTER_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'fichinter') {
9625 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = $object->getLastMainDocLink($object->element);
9626 } else {
9627 $substitutionarray['__DIRECTDOWNLOAD_URL_FICHINTER__'] = '';
9628 }
9629 if (getDolGlobalString('SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD') && is_object($object) && $object->element == 'supplier_proposal') {
9630 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
9631 } else {
9632 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
9633 }
9634
9635 if (is_object($object) && $object->element == 'propal') {
9636 '@phan-var-force Propal $object';
9637 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
9638 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9639 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref, 1, $object);
9640 }
9641 if (is_object($object) && $object->element == 'commande') {
9642 '@phan-var-force Commande $object';
9643 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
9644 }
9645 if (is_object($object) && $object->element == 'facture') {
9646 '@phan-var-force Facture $object';
9647 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
9648 }
9649 if (is_object($object) && $object->element == 'contrat') {
9650 '@phan-var-force Contrat $object';
9651 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
9652 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9653 $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'contract', $object->ref, 1, $object);
9654 }
9655 if (is_object($object) && $object->element == 'fichinter') {
9656 '@phan-var-force Fichinter $object';
9657 $substitutionarray['__URL_FICHINTER__'] = DOL_MAIN_URL_ROOT."/fichinter/card.php?id=".$object->id;
9658 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
9659 $substitutionarray['__ONLINE_SIGN_FICHINTER_URL__'] = getOnlineSignatureUrl(0, 'fichinter', $object->ref, 1, $object);
9660 }
9661 if (is_object($object) && $object->element == 'supplier_proposal') {
9662 '@phan-var-force SupplierProposal $object';
9663 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
9664 }
9665 if (is_object($object) && $object->element == 'invoice_supplier') {
9666 '@phan-var-force FactureFournisseur $object';
9667 $substitutionarray['__URL_SUPPLIER_INVOICE__'] = DOL_MAIN_URL_ROOT."/fourn/facture/card.php?id=".$object->id;
9668 }
9669 if (is_object($object) && $object->element == 'shipping') {
9670 '@phan-var-force Expedition $object';
9671 $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
9672 }
9673 }
9674
9675 if (is_object($object) && $object->element == 'action') {
9676 '@phan-var-force ActionComm $object';
9677 $substitutionarray['__EVENT_LABEL__'] = $object->label;
9678 $substitutionarray['__EVENT_TYPE__'] = $outputlangs->trans("Action".$object->type_code);
9679 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, 'day', 'auto', $outputlangs);
9680 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, 'hour', 'auto', $outputlangs);
9681 }
9682 }
9683 }
9684 if ((empty($exclude) || !in_array('objectamount', $exclude)) && (empty($include) || in_array('objectamount', $include))) {
9685 '@phan-var-force Facture|FactureRec $object';
9686 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
9687
9688 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', false, $outputlangs) : null) : '';
9689 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', false, $outputlangs) : null) : '';
9690 $substitutionarray['__DATE_YMD_TEXT__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'daytext', false, $outputlangs) : null) : '';
9691 $substitutionarray['__DATE_DUE_YMD_TEXT__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'daytext', false, $outputlangs) : null) : '';
9692
9693 $already_payed_all = 0;
9694 if (is_object($object) && ($object instanceof Facture)) {
9695 $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
9696 }
9697
9698 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
9699 $substitutionarray['__AMOUNT_EXCL_TAX_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, '', true) : '';
9700 $substitutionarray['__AMOUNT_EXCL_TAX_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ht, $outputlangs, $conf->currency, true) : '';
9701
9702 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
9703 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
9704 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
9705
9706 $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
9707
9708 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9709 $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)) : '';
9710 $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)) : '';
9711
9712 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9713 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
9714 }
9715 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9716 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
9717 }
9718
9719 // Amount keys formatted in a currency
9720 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9721 $substitutionarray['__AMOUNT_FORMATTED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9722 $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) : '';
9723 $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)) : '';
9724 if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
9725 $substitutionarray['__AMOUNT_TAX2_FORMATTED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9726 }
9727 if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
9728 $substitutionarray['__AMOUNT_TAX3_FORMATTED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
9729 }
9730 // Amount keys formatted in a currency (with the typo error for backward compatibility)
9731 if ($onlykey != 2) {
9732 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = $substitutionarray['__AMOUNT_EXCL_TAX_FORMATTED__'];
9733 $substitutionarray['__AMOUNT_FORMATED__'] = $substitutionarray['__AMOUNT_FORMATTED__'];
9734 $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = $substitutionarray['__AMOUNT_REMAIN_FORMATTED__'];
9735 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = $substitutionarray['__AMOUNT_VAT_FORMATTED__'];
9736 if ($mysoc instanceof Societe && $mysoc->useLocalTax(1)) {
9737 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = $substitutionarray['__AMOUNT_TAX2_FORMATTED__'];
9738 }
9739 if ($mysoc instanceof Societe && $mysoc->useLocalTax(2)) {
9740 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = $substitutionarray['__AMOUNT_TAX3_FORMATTED__'];
9741 }
9742 }
9743
9744 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
9745 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
9746 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
9747 $substitutionarray['__MULTICURRENCY_CODE__'] = (is_object($object) && isset($object->multicurrency_code)) ? $object->multicurrency_code : '';
9748 // TODO Add other keys for foreign multicurrency
9749
9750 // For backward compatibility
9751 if ($onlykey != 2) {
9752 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
9753 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
9754 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
9755 }
9756 }
9757
9758
9759 if ((empty($exclude) || !in_array('date', $exclude)) && (empty($include) || in_array('date', $include))) {
9760 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
9761
9762 $now = dol_now();
9763
9764 $tmp = dol_getdate($now, true);
9765 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9766 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
9767 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
9768 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
9769
9770 $daytext = $outputlangs->trans('Day'.$tmp['wday']);
9771
9772 $substitutionarray = array_merge($substitutionarray, array(
9773 '__NOW_TMS__' => (string) $now, // Must be the string that represent the int
9774 '__NOW_TMS_YMD__' => dol_print_date($now, 'day', 'auto', $outputlangs),
9775 '__DAY__' => (string) $tmp['mday'],
9776 '__DAY_TEXT__' => $daytext, // Monday
9777 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
9778 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
9779 '__MONTH__' => (string) $tmp['mon'],
9780 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
9781 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
9782 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
9783 '__YEAR__' => (string) $tmp['year'],
9784 '__PREVIOUS_DAY__' => (string) $tmp2['day'],
9785 '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
9786 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
9787 '__NEXT_DAY__' => (string) $tmp4['day'],
9788 '__NEXT_MONTH__' => (string) $tmp5['month'],
9789 '__NEXT_MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp5['month'])),
9790 '__NEXT_MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp5['month'])),
9791 '__NEXT_MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp5['month'])),
9792 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
9793 ));
9794 }
9795
9796 if (isModEnabled('multicompany')) {
9797 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
9798 }
9799 if ((empty($exclude) || !in_array('system', $exclude)) && (empty($include) || in_array('user', $include))) {
9800 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
9801 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
9802 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
9803 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
9804 }
9805
9806 // Note: The lazyload variables are replaced only during the call by make_substitutions, and only if necessary
9807
9808 return $substitutionarray;
9809}
9810
9827function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
9828{
9829 global $conf, $db, $langs;
9830
9831 if (!is_array($substitutionarray)) {
9832 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
9833 }
9834
9835 if (empty($outputlangs)) {
9836 $outputlangs = $langs;
9837 }
9838
9839 // Is initial text HTML or simple text ?
9840 $msgishtml = 0;
9841 if (dol_textishtml($text, 1)) {
9842 $msgishtml = 1;
9843 }
9844
9845 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
9846 if (is_object($outputlangs)) {
9847 $reg = array();
9848 while (preg_match('/__\‍(([^\‍)]+)\‍)__/', $text, $reg)) {
9849 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
9850 $tmp = explode('|', $reg[1]);
9851 if (!empty($tmp[1])) {
9852 $outputlangs->load($tmp[1]);
9853 }
9854
9855 $value = $outputlangs->transnoentitiesnoconv($reg[1]);
9856
9857 if (empty($converttextinhtmlifnecessary)) {
9858 // convert $newval into HTML is necessary
9859 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9860 } else {
9861 if (! $msgishtml) {
9862 $valueishtml = dol_textishtml($value, 1);
9863 //var_dump("valueishtml=".$valueishtml);
9864
9865 if ($valueishtml) {
9866 $text = dol_htmlentitiesbr($text);
9867 $msgishtml = 1;
9868 }
9869 } else {
9870 $value = dol_nl2br("$value");
9871 }
9872
9873 $text = preg_replace('/__\‍('.preg_quote($reg[1], '/').'\‍)__/', $value, $text);
9874 }
9875 }
9876 }
9877
9878 // Make substitution for constant keys.
9879 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
9880 $reg = array();
9881 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
9882 $keyfound = $reg[1];
9883 if (isASecretKey($keyfound)) {
9884 $value = '*****forbidden*****';
9885 } else {
9886 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
9887 }
9888
9889 if (empty($converttextinhtmlifnecessary)) {
9890 // convert $newval into HTML is necessary
9891 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
9892 } else {
9893 if (! $msgishtml) {
9894 $valueishtml = dol_textishtml($value, 1);
9895
9896 if ($valueishtml) {
9897 $text = dol_htmlentitiesbr($text);
9898 $msgishtml = 1;
9899 }
9900 } else {
9901 $value = dol_nl2br("$value");
9902 }
9903
9904 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
9905 }
9906 }
9907
9908 // Make substitution for array $substitutionarray
9909 foreach ($substitutionarray as $key => $value) {
9910 if (!isset($value)) {
9911 continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
9912 }
9913
9914 if (($key == '__USER_SIGNATURE__' || $key == '__SENDEREMAIL_SIGNATURE__') && (getDolGlobalString('MAIN_MAIL_DO_NOT_USE_SIGN'))) {
9915 $value = ''; // Protection
9916 }
9917
9918 if (empty($converttextinhtmlifnecessary)) {
9919 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9920 } else {
9921 if (! $msgishtml) {
9922 $valueishtml = dol_textishtml($value, 1);
9923
9924 if ($valueishtml) {
9925 $text = dol_htmlentitiesbr($text);
9926 $msgishtml = 1;
9927 }
9928 } else {
9929 $value = dol_nl2br("$value");
9930 }
9931 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
9932 }
9933 }
9934
9935 // TODO Implement the lazyload substitution
9936 /*
9937 add a loop to scan $substitutionarray:
9938 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.
9939 If no, we don't need to make replacement, so we do nothing.
9940 If yes, we can make the substitution:
9941
9942 include_once $path;
9943 $tmpobj = new $class($db);
9944 $valuetouseforsubstitution = $tmpobj->$method($id, '__XXX__');
9945 And make the replacement of "__XXX__@lazyload" with $valuetouseforsubstitution
9946 */
9947 $memory_object_list = array();
9948 foreach ($substitutionarray as $key => $value) {
9949 $lazy_load_arr = array();
9950 if (preg_match('/(__[A-Z\_]+__)@lazyload$/', $key, $lazy_load_arr)) {
9951 if (isset($lazy_load_arr[1]) && !empty($lazy_load_arr[1])) {
9952 $key_to_substitute = $lazy_load_arr[1];
9953 if (preg_match('/' . preg_quote($key_to_substitute, '/') . '/', $text)) {
9954 $param_arr = explode(':', $value);
9955 // path:class:method:id
9956 if (count($param_arr) == 4) {
9957 $path = $param_arr[0];
9958 $class = $param_arr[1];
9959 $method = $param_arr[2];
9960 $id = (int) $param_arr[3];
9961
9962 // load class file and init object list in memory
9963 if (!isset($memory_object_list[$class])) {
9964 if (dol_is_file(DOL_DOCUMENT_ROOT . $path)) {
9965 require_once DOL_DOCUMENT_ROOT . $path;
9966 if (class_exists($class)) {
9967 $memory_object_list[$class] = array(
9968 'list' => array(),
9969 );
9970 }
9971 }
9972 }
9973
9974 // fetch object and set substitution
9975 if (isset($memory_object_list[$class]) && isset($memory_object_list[$class]['list'])) {
9976 if (method_exists($class, $method)) {
9977 if (!isset($memory_object_list[$class]['list'][$id])) {
9978 $tmpobj = new $class($db);
9979 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9980 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute);
9981 $memory_object_list[$class]['list'][$id] = $tmpobj;
9982 } else {
9983 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9984 $tmpobj = $memory_object_list[$class]['list'][$id];
9985 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
9986 $valuetouseforsubstitution = $tmpobj->$method($id, $key_to_substitute, true);
9987 }
9988
9989 $text = str_replace("$key_to_substitute", "$valuetouseforsubstitution", $text); // We must keep the " to work when value is 123.5 for example
9990 }
9991 }
9992 }
9993 }
9994 }
9995 }
9996 }
9997
9998 return $text;
9999}
10000
10013function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
10014{
10015 global $conf, $user;
10016
10017 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
10018
10019 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
10020
10021 // Check if there is external substitution to do, requested by plugins
10022 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
10023
10024 foreach ($dirsubstitutions as $reldir) {
10025 $dir = dol_buildpath($reldir, 0);
10026
10027 // Check if directory exists
10028 if (!dol_is_dir($dir)) {
10029 continue;
10030 }
10031
10032 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
10033 foreach ($substitfiles as $substitfile) {
10034 $reg = array();
10035 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
10036 $module = $reg[1];
10037
10038 dol_syslog("Library ".$substitfile['name']." found into ".$dir);
10039 // Include the user's functions file
10040 require_once $dir.$substitfile['name'];
10041 // Call the user's function, and only if it is defined
10042 $function_name = $module."_".$callfunc;
10043 if (function_exists($function_name)) {
10044 $function_name($substitutionarray, $outputlangs, $object, $parameters);
10045 }
10046 }
10047 }
10048 }
10049 if (getDolGlobalString('ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS')) {
10050 // to list all tags in odt template
10051 $tags = '';
10052 foreach ($substitutionarray as $key => $value) {
10053 $tags .= '{'.$key.'} => '.$value."\n";
10054 }
10055 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
10056 }
10057}
10058
10068function print_date_range($date_start, $date_end, $format = '', $outputlangs = null)
10069{
10070 print get_date_range($date_start, $date_end, $format, $outputlangs);
10071}
10072
10083function get_date_range($date_start, $date_end, $format = '', $outputlangs = null, $withparenthesis = 1)
10084{
10085 global $langs;
10086
10087 $out = '';
10088
10089 if (!is_object($outputlangs)) {
10090 $outputlangs = $langs;
10091 }
10092
10093 if ($date_start && $date_end) {
10094 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10095 }
10096 if ($date_start && !$date_end) {
10097 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10098 }
10099 if (!$date_start && $date_end) {
10100 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
10101 }
10102
10103 return $out;
10104}
10105
10114function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
10115{
10116 global $conf;
10117
10118 $ret = '';
10119 // If order not defined, we use the setup
10120 if ($nameorder < 0) {
10121 $nameorder = (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION') ? 1 : 0);
10122 }
10123 if ($nameorder == 1) {
10124 $ret .= $firstname;
10125 if ($firstname && $lastname) {
10126 $ret .= ' ';
10127 }
10128 $ret .= $lastname;
10129 } elseif ($nameorder == 2 || $nameorder == 3) {
10130 $ret .= $firstname;
10131 if (empty($ret) && $nameorder == 3) {
10132 $ret .= $lastname;
10133 }
10134 } else { // 0, 4 or 5
10135 $ret .= $lastname;
10136 if (empty($ret) && $nameorder == 5) {
10137 $ret .= $firstname;
10138 }
10139 if ($nameorder == 0) {
10140 if ($firstname && $lastname) {
10141 $ret .= ' ';
10142 }
10143 $ret .= $firstname;
10144 }
10145 }
10146 return $ret;
10147}
10148
10149
10162function setEventMessage($mesgs, $style = 'mesgs', $noduplicate = 0, $attop = 0)
10163{
10164 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
10165 if (!is_array($mesgs)) {
10166 $mesgs = trim((string) $mesgs);
10167 // If mesgs is a not an empty string
10168 if ($mesgs) {
10169 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesgs, $_SESSION['dol_events'][$style])) {
10170 return;
10171 }
10172 if ($attop) {
10173 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10174 } else {
10175 $_SESSION['dol_events'][$style][] = $mesgs;
10176 }
10177 }
10178 } else {
10179 // If mesgs is an array
10180 foreach ($mesgs as $mesg) {
10181 $mesg = trim((string) $mesg);
10182 if ($mesg) {
10183 if (!empty($noduplicate) && isset($_SESSION['dol_events'][$style]) && in_array($mesg, $_SESSION['dol_events'][$style])) {
10184 return;
10185 }
10186 if ($attop) {
10187 array_unshift($_SESSION['dol_events'][$style], $mesgs);
10188 } else {
10189 $_SESSION['dol_events'][$style][] = $mesg;
10190 }
10191 }
10192 }
10193 }
10194}
10195
10209function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '', $noduplicate = 0, $attop = 0)
10210{
10211 if (empty($mesg) && empty($mesgs)) {
10212 dol_syslog("Try to add a message in stack, but value to add is empty message" . getCallerInfoString(), LOG_WARNING);
10213 } else {
10214 if ($messagekey) {
10215 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
10216 // TODO
10217 $mesg .= '';
10218 }
10219 if (empty($messagekey) || empty($_COOKIE["DOLUSER_HIDEMESSAGE".$messagekey])) {
10220 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
10221 dol_print_error(null, 'Bad parameter style='.$style.' for setEventMessages');
10222 }
10223 if (empty($mesgs)) {
10224 setEventMessage($mesg, $style, $noduplicate, $attop);
10225 } else {
10226 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
10227 setEventMessage($mesg, $style, $noduplicate, $attop); // Add message string if not already into array
10228 }
10229 setEventMessage($mesgs, $style, $noduplicate, $attop);
10230 }
10231 }
10232 }
10233}
10234
10244function dol_htmloutput_events($disabledoutputofmessages = 0)
10245{
10246 // Show mesgs
10247 if (isset($_SESSION['dol_events']['mesgs'])) {
10248 if (empty($disabledoutputofmessages)) {
10249 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
10250 }
10251 unset($_SESSION['dol_events']['mesgs']);
10252 }
10253 // Show errors
10254 if (isset($_SESSION['dol_events']['errors'])) {
10255 if (empty($disabledoutputofmessages)) {
10256 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
10257 }
10258 unset($_SESSION['dol_events']['errors']);
10259 }
10260
10261 // Show warnings
10262 if (isset($_SESSION['dol_events']['warnings'])) {
10263 if (empty($disabledoutputofmessages)) {
10264 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
10265 }
10266 unset($_SESSION['dol_events']['warnings']);
10267 }
10268}
10269
10284function get_htmloutput_mesg($mesgstring = '', $mesgarray = [], $style = 'ok', $keepembedded = 0)
10285{
10286 global $conf, $langs;
10287
10288 $ret = 0;
10289 $return = '';
10290 $out = '';
10291 $divstart = $divend = '';
10292
10293 // If inline message with no format, we add it.
10294 if ((empty($conf->use_javascript_ajax) || getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
10295 $divstart = '<div class="'.$style.' clearboth">';
10296 $divend = '</div>';
10297 }
10298
10299 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
10300 $langs->load("errors");
10301 $out .= $divstart;
10302 if (is_array($mesgarray) && count($mesgarray)) {
10303 foreach ($mesgarray as $message) {
10304 $ret++;
10305 $out .= $langs->trans($message);
10306 if ($ret < count($mesgarray)) {
10307 $out .= "<br>\n";
10308 }
10309 }
10310 }
10311 if ($mesgstring) {
10312 $ret++;
10313 $out .= $langs->trans($mesgstring);
10314 }
10315 $out .= $divend;
10316 }
10317
10318 if ($out) {
10319 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && empty($keepembedded)) {
10320 $return = '<script nonce="'.getNonce().'">
10321 $(document).ready(function() {
10322 var block = '.(getDolGlobalString('MAIN_USE_JQUERY_BLOCKUI') ? "true" : "false").'
10323 if (block) {
10324 $.dolEventValid("","'.dol_escape_js($out).'");
10325 } else {
10326 /* jnotify(message, preset of message type, keepmessage) */
10327 $.jnotify("'.dol_escape_js($out).'",
10328 "'.($style == "ok" ? 3000 : $style).'",
10329 '.($style == "ok" ? "false" : "true").',
10330 { remove: function (){} } );
10331 }
10332 });
10333 </script>';
10334 } else {
10335 $return = $out;
10336 }
10337 }
10338
10339 return $return;
10340}
10341
10353function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10354{
10355 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10356}
10357
10371function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
10372{
10373 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
10374 return;
10375 }
10376
10377 $iserror = 0;
10378 $iswarning = 0;
10379 if (is_array($mesgarray)) {
10380 foreach ($mesgarray as $val) {
10381 if ($val && preg_match('/class="error"/i', $val)) {
10382 $iserror++;
10383 break;
10384 }
10385 if ($val && preg_match('/class="warning"/i', $val)) {
10386 $iswarning++;
10387 break;
10388 }
10389 }
10390 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
10391 $iserror++;
10392 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
10393 $iswarning++;
10394 }
10395 if ($style == 'error') {
10396 $iserror++;
10397 }
10398 if ($style == 'warning') {
10399 $iswarning++;
10400 }
10401
10402 if ($iserror || $iswarning) {
10403 // Remove div from texts
10404 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
10405 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
10406 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
10407 // Remove div from texts array
10408 if (is_array($mesgarray)) {
10409 $newmesgarray = array();
10410 foreach ($mesgarray as $val) {
10411 if (is_string($val)) {
10412 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
10413 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
10414 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
10415 $newmesgarray[] = $tmpmesgstring;
10416 } else {
10417 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
10418 }
10419 }
10420 $mesgarray = $newmesgarray;
10421 }
10422 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
10423 } else {
10424 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
10425 }
10426}
10427
10439function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
10440{
10441 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
10442}
10443
10464function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
10465{
10466 // Clean parameters
10467 $order = strtolower($order);
10468
10469 if (is_array($array)) {
10470 $sizearray = count($array);
10471 if ($sizearray > 0) {
10472 $temp = array();
10473 foreach (array_keys($array) as $key) {
10474 if (is_object($array[$key])) {
10475 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
10476 } else {
10477 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable,PhanTypeArraySuspicious,PhanTypeMismatchDimFetch
10478 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
10479 }
10480 if ($natsort == -1) {
10481 $temp[$key] = '___'.$temp[$key]; // We add a string at begin of value to force an alpha order when using asort.
10482 }
10483 }
10484
10485 if (empty($natsort) || $natsort == -1) {
10486 if ($order == 'asc') {
10487 asort($temp);
10488 } else {
10489 arsort($temp);
10490 }
10491 } else {
10492 if ($case_sensitive) {
10493 natsort($temp);
10494 } else {
10495 natcasesort($temp); // natecasesort is not sensible to case
10496 }
10497 if ($order != 'asc') {
10498 $temp = array_reverse($temp, true);
10499 }
10500 }
10501
10502 $sorted = array();
10503
10504 foreach (array_keys($temp) as $key) {
10505 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
10506 }
10507
10508 return $sorted;
10509 }
10510 }
10511 return $array;
10512}
10513
10514
10522function utf8_check($str)
10523{
10524 $str = (string) $str; // Sometimes string is an int.
10525
10526 // We must use here a binary strlen function (so not dol_strlen)
10527 $strLength = strlen($str);
10528 for ($i = 0; $i < $strLength; $i++) {
10529 if (ord($str[$i]) < 0x80) {
10530 continue; // 0bbbbbbb
10531 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
10532 $n = 1; // 110bbbbb
10533 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
10534 $n = 2; // 1110bbbb
10535 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
10536 $n = 3; // 11110bbb
10537 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
10538 $n = 4; // 111110bb
10539 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
10540 $n = 5; // 1111110b
10541 } else {
10542 return false; // Does not match any model
10543 }
10544 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
10545 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
10546 return false;
10547 }
10548 }
10549 }
10550 return true;
10551}
10552
10560function utf8_valid($str)
10561{
10562 /* 2 other methods to test if string is utf8
10563 $validUTF8 = mb_check_encoding($messagetext, 'UTF-8');
10564 $validUTF8b = ! (false === mb_detect_encoding($messagetext, 'UTF-8', true));
10565 */
10566 return preg_match('//u', $str) ? true : false;
10567}
10568
10569
10576function ascii_check($str)
10577{
10578 if (function_exists('mb_check_encoding')) {
10579 //if (mb_detect_encoding($str, 'ASCII', true) return false;
10580 if (!mb_check_encoding($str, 'ASCII')) {
10581 return false;
10582 }
10583 } else {
10584 if (preg_match('/[^\x00-\x7f]/', $str)) {
10585 return false; // Contains a byte > 7f
10586 }
10587 }
10588
10589 return true;
10590}
10591
10592
10600function dol_osencode($str)
10601{
10602 $tmp = ini_get("unicode.filesystem_encoding");
10603 if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
10604 $tmp = 'iso-8859-1'; // By default for windows
10605 }
10606 if (empty($tmp)) {
10607 $tmp = 'utf-8'; // By default for other
10608 }
10609 if (getDolGlobalString('MAIN_FILESYSTEM_ENCODING')) {
10610 $tmp = getDolGlobalString('MAIN_FILESYSTEM_ENCODING');
10611 }
10612
10613 if ($tmp == 'iso-8859-1') {
10614 return mb_convert_encoding($str, 'ISO-8859-1', 'UTF-8');
10615 }
10616 return $str;
10617}
10618
10619
10635function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '', $useCache = true)
10636{
10637 global $conf;
10638
10639 // If key empty
10640 if ($key == '') {
10641 return 0;
10642 }
10643
10644 // Check in cache
10645 if ($useCache && isset($conf->cache['codeid'][$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
10646 return $conf->cache['codeid'][$tablename][$key][$fieldid]; // Found in cache
10647 }
10648
10649 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
10650
10651 $sql = "SELECT ".$fieldid." as valuetoget";
10652 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
10653 if ($fieldkey == 'id' || $fieldkey == 'rowid') {
10654 $sql .= " WHERE ".$fieldkey." = ".((int) $key);
10655 } else {
10656 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
10657 }
10658 if (!empty($entityfilter)) {
10659 $sql .= " AND entity IN (".getEntity($tablename).")";
10660 }
10661 if ($filters) {
10662 $sql .= $filters;
10663 }
10664
10665 $resql = $db->query($sql);
10666 if ($resql) {
10667 $obj = $db->fetch_object($resql);
10668 $valuetoget = '';
10669 if ($obj) {
10670 $valuetoget = $obj->valuetoget;
10671 $conf->cache['codeid'][$tablename][$key][$fieldid] = $valuetoget;
10672 } else {
10673 $conf->cache['codeid'][$tablename][$key][$fieldid] = '';
10674 }
10675 $db->free($resql);
10676
10677 return $valuetoget;
10678 } else {
10679 return -1;
10680 }
10681}
10682
10692function isStringVarMatching($var, $regextext, $matchrule = 1)
10693{
10694 if ($matchrule == 1) {
10695 if ($var == 'mainmenu') {
10696 global $mainmenu;
10697 return (preg_match('/^'.$regextext.'/', $mainmenu));
10698 } elseif ($var == 'leftmenu') {
10699 global $leftmenu;
10700 return (preg_match('/^'.$regextext.'/', $leftmenu));
10701 } else {
10702 return 'This variable is not accessible with dol_eval';
10703 }
10704 } else {
10705 return 'This value for matchrule is not implemented';
10706 }
10707}
10708
10709
10719function verifCond($strToEvaluate, $onlysimplestring = '1')
10720{
10721 //print $strToEvaluate."<br>\n";
10722 $rights = true;
10723 if (isset($strToEvaluate) && $strToEvaluate !== '') {
10724 //var_dump($strToEvaluate);
10725 //$rep = dol_eval($strToEvaluate, 1, 0, '1'); // to show the error
10726 $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
10727 $rights = (bool) $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
10728 //var_dump($rights);
10729 }
10730 return $rights;
10731}
10732
10747function dol_eval($s, $returnvalue = 1, $hideerrors = 1, $onlysimplestring = '1')
10748{
10749 // Only this global variables can be read by eval function and returned to caller
10750 global $conf; // Read of const is done with getDolGlobalString() but we need $conf->currency for example
10751 global $db, $langs, $user, $website, $websitepage;
10752 global $action, $mainmenu, $leftmenu;
10753 global $mysoc;
10754 global $objectoffield; // To allow the use of $objectoffield in computed fields
10755
10756 // Old variables used
10757 global $object;
10758 global $obj; // To get $obj used into list when dol_eval() is used for computed fields and $obj is not yet $object
10759
10760 $isObBufferActive = false; // When true, the ObBuffer must be cleaned in the exception handler
10761 if (!in_array($onlysimplestring, array('0', '1', '2'))) {
10762 return "Bad call of dol_eval. Parameter onlysimplestring must be '0' (deprecated), '1' or '2'";
10763 }
10764
10765 try {
10766 // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
10767 if ($onlysimplestring == '1' || $onlysimplestring == '2') {
10768 // We must accept with 1: '1 && getDolGlobalInt("doesnotexist1") && getDolGlobalString("MAIN_FEATURES_LEVEL")'
10769 // We must accept with 1: '$user->hasRight("cabinetmed", "read") && !$object->canvas=="patient@cabinetmed"'
10770 // 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"
10771
10772 // Check if there is dynamic call (first we check chars are all into a whitelist chars)
10773 $specialcharsallowed = '^$_+-.*>&|=!?():"\',/@';
10774 if ($onlysimplestring == '2') {
10775 $specialcharsallowed .= '<[]';
10776 }
10777 if (getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL')) {
10778 $specialcharsallowed .= getDolGlobalString('MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL');
10779 }
10780 if (preg_match('/[^a-z0-9\s'.preg_quote($specialcharsallowed, '/').']/i', $s)) {
10781 if ($returnvalue) {
10782 return 'Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s;
10783 } else {
10784 dol_syslog('Bad string syntax to evaluate (found chars that are not chars for a simple one line clean eval string): '.$s, LOG_WARNING);
10785 return '';
10786 }
10787 }
10788
10789 // Check if there is a < or <= without spaces before/after
10790 if (preg_match('/<=?[^\s]/', $s)) {
10791 if ($returnvalue) {
10792 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s;
10793 } else {
10794 dol_syslog('Bad string syntax to evaluate (mode '.$onlysimplestring.', found a < or <= without space before and after): '.$s, LOG_WARNING);
10795 return '';
10796 }
10797 }
10798
10799 // Check if there is dynamic call (first we use black list patterns)
10800 if (preg_match('/\$[\w]*\s*\‍(/', $s)) {
10801 if ($returnvalue) {
10802 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;
10803 } else {
10804 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);
10805 return '';
10806 }
10807 }
10808
10809 // Now we check if we try dynamic call
10810 // First we remove white list pattern of using parenthesis then testing if one open parenthesis exists
10811 $savescheck = '';
10812 $scheck = $s;
10813 while ($scheck && $savescheck != $scheck) {
10814 $savescheck = $scheck;
10815 $scheck = preg_replace('/->[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...->method(...'
10816 $scheck = preg_replace('/::[a-zA-Z0-9_]+\‍(/', '->__METHOD__', $scheck); // accept parenthesis in '...::method(...'
10817 $scheck = preg_replace('/^\‍(+/', '__PARENTHESIS__ ', $scheck); // accept parenthesis in '(...'. Must replace with "__PARENTHESIS__ with a space after "to allow following substitutions
10818 $scheck = preg_replace('/\&\&\s+\‍(/', '__ANDPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '&& (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
10819 $scheck = preg_replace('/\|\|\s+\‍(/', '__ORPARENTHESIS__ ', $scheck); // accept parenthesis in '... (' like in '|| (...'. Must replace with "__PARENTHESIS__ with a space after" to allow following substitutions
10820 $scheck = preg_replace('/^!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in 'function(' and '!function('
10821 $scheck = preg_replace('/\s!?[a-zA-Z0-9_]+\‍(/', '__FUNCTION__', $scheck); // accept parenthesis in '... function(' and '... !function('
10822 $scheck = preg_replace('/^!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '!('
10823 $scheck = preg_replace('/\s!\‍(/', '__NOTANDPARENTHESIS__', $scheck); // accept parenthesis in '... !('
10824 $scheck = preg_replace('/(\^|\')\‍(/', '__REGEXSTART__', $scheck); // To allow preg_match('/^(aaa|bbb)/'... or isStringVarMatching('leftmenu', '(aaa|bbb)')
10825 }
10826 //print 'scheck='.$scheck." : ".strpos($scheck, '(')."<br>\n";
10827
10828 // Now test if it remains 1 one parenthesis.
10829 if (strpos($scheck, '(') !== false) {
10830 if ($returnvalue) {
10831 return 'Bad string syntax to evaluate (mode '.$onlysimplestring.', found call of a function or method without using the direct name of the function): '.$s;
10832 } else {
10833 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);
10834 return '';
10835 }
10836 }
10837
10838 // TODO
10839 // We can exclude $ char that are not:
10840 // $db, $langs, $leftmenu, $topmenu, $user, $langs, $objectoffield, $object...,
10841 }
10842 if (is_array($s) || $s === 'Array') {
10843 if ($returnvalue) {
10844 return 'Bad string syntax to evaluate (value is Array): '.var_export($s, true);
10845 } else {
10846 dol_syslog('Bad string syntax to evaluate (value is Array): '.var_export($s, true), LOG_WARNING);
10847 return '';
10848 }
10849 }
10850
10851 if (!getDolGlobalString('MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL') && strpos($s, '::') !== false) {
10852 if ($returnvalue) {
10853 return 'Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s;
10854 } else {
10855 dol_syslog('Bad string syntax to evaluate (double : char is forbidden without setting MAIN_ALLOW_DOUBLE_COLON_IN_DOL_EVAL): '.$s, LOG_WARNING);
10856 return '';
10857 }
10858 }
10859
10860 if (strpos($s, '`') !== false) {
10861 if ($returnvalue) {
10862 return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
10863 } else {
10864 dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s, LOG_WARNING);
10865 return '';
10866 }
10867 }
10868
10869 // Disallow also concat
10870 if (getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL')) {
10871 if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
10872 if ($returnvalue) {
10873 return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
10874 } else {
10875 dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s, LOG_WARNING);
10876 return '';
10877 }
10878 }
10879 }
10880
10881 // We block use of php exec or php file functions
10882 $forbiddenphpstrings = array('$$', '$_', '}[');
10883 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
10884
10885 // 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)
10886 // 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
10887 // like we can do with array_map and its callable parameter: dol_eval('json_encode(array_map(implode("",["ex","ec"]), ["id"]))', 1, 1, '0')
10888 $forbiddenphpfunctions = array();
10889 // @phpcs:ignore
10890 $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
10891
10892 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
10893 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
10894 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func", "call_user_func_array"));
10895
10896 $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"));
10897 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort", "preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
10898 $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"));
10899 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
10900 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
10901
10902 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
10903
10904 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
10905 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
10906 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
10907 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace", "mb_ereg_replace_callback")); // function with eval capabilities
10908 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
10909 $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
10910 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
10911 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
10912
10913 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
10914
10915 $forbiddenphpregex = 'global\s*\$';
10916 $forbiddenphpregex .= '|';
10917 $forbiddenphpregex .= '\b('.implode('|', $forbiddenphpfunctions).')\b';
10918
10919 $forbiddenphpmethodsregex = '->('.implode('|', $forbiddenphpmethods).')';
10920
10921 do {
10922 $oldstringtoclean = $s;
10923 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
10924 $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
10925 $s = preg_replace('/'.$forbiddenphpmethodsregex.'/i', '__forbiddenstring__', $s);
10926 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\‍(/i', '', $s); // Remove $function( call and $mycall->mymethod(
10927 } while ($oldstringtoclean != $s);
10928
10929
10930 if (strpos($s, '__forbiddenstring__') !== false) {
10931 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
10932 if ($returnvalue) {
10933 return 'Bad string syntax to evaluate: '.$s;
10934 } else {
10935 dol_syslog('Bad string syntax to evaluate: '.$s);
10936 return '';
10937 }
10938 }
10939
10940 //print $s."<br>\n";
10941 if ($returnvalue) {
10942 ob_start(); // An evaluation has no reason to output data
10943 $isObBufferActive = true;
10944 $tmps = $hideerrors ? @eval('return ' . $s . ';') : eval('return ' . $s . ';');
10945 $tmpo = ob_get_clean();
10946 $isObBufferActive = false;
10947 if ($tmpo) {
10948 print 'Bad string syntax to evaluate. Some data were output when it should not when evaluating: ' . $s;
10949 }
10950 return $tmps;
10951 } else {
10952 dol_syslog('Do not use anymore dol_eval with param returnvalue=0', LOG_WARNING);
10953 if ($hideerrors) {
10954 @eval($s);
10955 } else {
10956 eval($s);
10957 }
10958 return '';
10959 }
10960 } catch (Error $e) {
10961 if ($isObBufferActive) {
10962 // Clean up buffer which was left behind due to exception.
10963 $tmpo = ob_get_clean();
10964 $isObBufferActive = false;
10965 }
10966 $error = 'dol_eval try/catch error : ';
10967 $error .= $e->getMessage();
10968 dol_syslog($error, LOG_WARNING);
10969 if ($returnvalue) {
10970 return 'Exception during evaluation: '.$s;
10971 } else {
10972 return '';
10973 }
10974 }
10975}
10976
10984function dol_validElement($element)
10985{
10986 return (trim($element) != '');
10987}
10988
10997function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
10998{
10999 if (empty($codelang)) {
11000 return '';
11001 }
11002
11003 if ($codelang == 'auto') {
11004 return '<span class="fa fa-language"></span>';
11005 }
11006
11007 $langtocountryflag = array(
11008 'ar_AR' => '',
11009 'ca_ES' => 'catalonia',
11010 'da_DA' => 'dk',
11011 'fr_CA' => 'mq',
11012 'sv_SV' => 'se',
11013 'sw_SW' => 'unknown',
11014 'AQ' => 'unknown',
11015 'CW' => 'unknown',
11016 'IM' => 'unknown',
11017 'JE' => 'unknown',
11018 'MF' => 'unknown',
11019 'BL' => 'unknown',
11020 'SX' => 'unknown'
11021 );
11022
11023 if (isset($langtocountryflag[$codelang])) {
11024 $flagImage = $langtocountryflag[$codelang];
11025 } else {
11026 $tmparray = explode('_', $codelang);
11027 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
11028 }
11029
11030 $morecss = '';
11031 $reg = array();
11032 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
11033 $morecss = $reg[1];
11034 $moreatt = "";
11035 }
11036
11037 // return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
11038 return '<span class="flag-sprite '.strtolower($flagImage).($morecss ? ' '.$morecss : '').'"'.($moreatt ? ' '.$moreatt : '').(!$notitlealt ? ' title="'.$codelang.'"' : '').'></span>';
11039}
11040
11048function getLanguageCodeFromCountryCode($countrycode)
11049{
11050 global $mysoc;
11051
11052 if (empty($countrycode)) {
11053 return null;
11054 }
11055
11056 if (strtoupper($countrycode) == 'MQ') {
11057 return 'fr_CA';
11058 }
11059 if (strtoupper($countrycode) == 'SE') {
11060 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
11061 }
11062 if (strtoupper($countrycode) == 'CH') {
11063 if ($mysoc->country_code == 'FR') {
11064 return 'fr_CH';
11065 }
11066 if ($mysoc->country_code == 'DE') {
11067 return 'de_CH';
11068 }
11069 if ($mysoc->country_code == 'IT') {
11070 return 'it_CH';
11071 }
11072 }
11073
11074 // Locale list taken from:
11075 // http://stackoverflow.com/questions/3191664/
11076 // list-of-all-locales-and-their-short-codes
11077 $locales = array(
11078 'af-ZA',
11079 'am-ET',
11080 'ar-AE',
11081 'ar-BH',
11082 'ar-DZ',
11083 'ar-EG',
11084 'ar-IQ',
11085 'ar-JO',
11086 'ar-KW',
11087 'ar-LB',
11088 'ar-LY',
11089 'ar-MA',
11090 'ar-OM',
11091 'ar-QA',
11092 'ar-SA',
11093 'ar-SY',
11094 'ar-TN',
11095 'ar-YE',
11096 //'as-IN', // Moved after en-IN
11097 'ba-RU',
11098 'be-BY',
11099 'bg-BG',
11100 'bn-BD',
11101 //'bn-IN', // Moved after en-IN
11102 'bo-CN',
11103 'br-FR',
11104 'ca-ES',
11105 'co-FR',
11106 'cs-CZ',
11107 'cy-GB',
11108 'da-DK',
11109 'de-AT',
11110 'de-CH',
11111 'de-DE',
11112 'de-LI',
11113 'de-LU',
11114 'dv-MV',
11115 'el-GR',
11116 'en-AU',
11117 'en-BZ',
11118 'en-CA',
11119 'en-GB',
11120 'en-IE',
11121 'en-IN',
11122 'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
11123 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
11124 'en-JM',
11125 'en-MY',
11126 'en-NZ',
11127 'en-PH',
11128 'en-SG',
11129 'en-TT',
11130 'en-US',
11131 'en-ZA',
11132 'en-ZW',
11133 'es-AR',
11134 'es-BO',
11135 'es-CL',
11136 'es-CO',
11137 'es-CR',
11138 'es-DO',
11139 'es-EC',
11140 'es-ES',
11141 'es-GT',
11142 'es-HN',
11143 'es-MX',
11144 'es-NI',
11145 'es-PA',
11146 'es-PE',
11147 'es-PR',
11148 'es-PY',
11149 'es-SV',
11150 'es-US',
11151 'es-UY',
11152 'es-VE',
11153 'et-EE',
11154 'eu-ES',
11155 'fa-IR',
11156 'fi-FI',
11157 'fo-FO',
11158 'fr-BE',
11159 'fr-CA',
11160 'fr-CH',
11161 'fr-FR',
11162 'fr-LU',
11163 'fr-MC',
11164 'fy-NL',
11165 'ga-IE',
11166 'gd-GB',
11167 'gl-ES',
11168 'gu-IN',
11169 'he-IL',
11170 'hi-IN',
11171 'hr-BA',
11172 'hr-HR',
11173 'hu-HU',
11174 'hy-AM',
11175 'id-ID',
11176 'ig-NG',
11177 'ii-CN',
11178 'is-IS',
11179 'it-CH',
11180 'it-IT',
11181 'ja-JP',
11182 'ka-GE',
11183 'kk-KZ',
11184 'kl-GL',
11185 'km-KH',
11186 'kn-IN',
11187 'ko-KR',
11188 'ky-KG',
11189 'lb-LU',
11190 'lo-LA',
11191 'lt-LT',
11192 'lv-LV',
11193 'mi-NZ',
11194 'mk-MK',
11195 'ml-IN',
11196 'mn-MN',
11197 'mr-IN',
11198 'ms-BN',
11199 'ms-MY',
11200 'mt-MT',
11201 'nb-NO',
11202 'ne-NP',
11203 'nl-BE',
11204 'nl-NL',
11205 'nn-NO',
11206 'oc-FR',
11207 'or-IN',
11208 'pa-IN',
11209 'pl-PL',
11210 'ps-AF',
11211 'pt-BR',
11212 'pt-PT',
11213 'rm-CH',
11214 'ro-MD',
11215 'ro-RO',
11216 'ru-RU',
11217 'rw-RW',
11218 'sa-IN',
11219 'se-FI',
11220 'se-NO',
11221 'se-SE',
11222 'si-LK',
11223 'sk-SK',
11224 'sl-SI',
11225 'sq-AL',
11226 'sv-FI',
11227 'sv-SE',
11228 'sw-KE',
11229 'ta-IN',
11230 'te-IN',
11231 'th-TH',
11232 'tk-TM',
11233 'tn-ZA',
11234 'tr-TR',
11235 'tt-RU',
11236 'ug-CN',
11237 'uk-UA',
11238 'ur-PK',
11239 'vi-VN',
11240 'wo-SN',
11241 'xh-ZA',
11242 'yo-NG',
11243 'zh-CN',
11244 'zh-HK',
11245 'zh-MO',
11246 'zh-SG',
11247 'zh-TW',
11248 'zu-ZA',
11249 );
11250
11251 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
11252 if (in_array($buildprimarykeytotest, $locales)) {
11253 return strtolower($countrycode).'_'.strtoupper($countrycode);
11254 }
11255
11256 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
11257 foreach ($locales as $locale) {
11258 $locale_language = locale_get_primary_language($locale);
11259 $locale_region = locale_get_region($locale);
11260 if (strtoupper($countrycode) == $locale_region) {
11261 //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
11262 return strtolower($locale_language).'_'.strtoupper($locale_region);
11263 }
11264 }
11265 } else {
11266 dol_syslog("Warning Extension php-intl is not available", LOG_WARNING);
11267 }
11268
11269 return null;
11270}
11271
11302function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add', $filterorigmodule = '')
11303{
11304 global $hookmanager, $db;
11305
11306 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
11307 foreach ($conf->modules_parts['tabs'][$type] as $value) {
11308 $values = explode(':', $value);
11309
11310 $reg = array();
11311 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
11312 $newtab = array();
11313 $postab = $h;
11314 // detect if position set in $values[1] ie : +(2)mytab@mymodule (first tab is 0, second is one, ...)
11315 $str = $values[1];
11316 $posstart = strpos($str, '(');
11317 if ($posstart > 0) {
11318 $posend = strpos($str, ')');
11319 if ($posstart > 0) {
11320 $res1 = substr($str, $posstart + 1, $posend - $posstart - 1);
11321 if (is_numeric($res1)) {
11322 $postab = (int) $res1;
11323 $values[1] = '+' . substr($str, $posend + 1);
11324 }
11325 }
11326 }
11327 if (count($values) == 6) {
11328 // new declaration with permissions:
11329 // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11330 // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
11331 if ($values[0] != $type) {
11332 continue;
11333 }
11334
11335 if (verifCond($values[4], '2')) {
11336 if ($values[3]) {
11337 if ($filterorigmodule) { // If a filter of module origin has been requested
11338 if (strpos($values[3], '@')) { // This is an external module
11339 if ($filterorigmodule != 'external') {
11340 continue;
11341 }
11342 } else { // This looks a core module
11343 if ($filterorigmodule != 'core') {
11344 continue;
11345 }
11346 }
11347 }
11348 $langs->load($values[3]);
11349 }
11350 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11351 // If label is "SUBSTITUION_..."
11352 $substitutionarray = array();
11353 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11354 $label = make_substitutions($reg[1], $substitutionarray);
11355 } else {
11356 // If label is "Label,Class,File,Method", we call the method to show content inside the badge
11357 $labeltemp = explode(',', $values[2]);
11358 $label = $langs->trans($labeltemp[0]);
11359
11360 if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
11361 dol_include_once($labeltemp[2]);
11362 $classtoload = $labeltemp[1];
11363 if (class_exists($classtoload)) {
11364 $obj = new $classtoload($db);
11365 $function = $labeltemp[3];
11366 if ($obj && $function && method_exists($obj, $function)) {
11367 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11368 $nbrec = $obj->$function($object->id, $obj);
11369 if (!empty($nbrec)) {
11370 $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
11371 }
11372 }
11373 }
11374 }
11375 }
11376
11377 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
11378 $newtab[1] = $label;
11379 $newtab[2] = str_replace('+', '', $values[1]);
11380 $h++;
11381 } else {
11382 continue;
11383 }
11384 } elseif (count($values) == 5) { // case deprecated
11385 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
11386
11387 if ($values[0] != $type) {
11388 continue;
11389 }
11390 if ($values[3]) {
11391 if ($filterorigmodule) { // If a filter of module origin has been requested
11392 if (strpos($values[3], '@')) { // This is an external module
11393 if ($filterorigmodule != 'external') {
11394 continue;
11395 }
11396 } else { // This looks a core module
11397 if ($filterorigmodule != 'core') {
11398 continue;
11399 }
11400 }
11401 }
11402 $langs->load($values[3]);
11403 }
11404 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
11405 $substitutionarray = array();
11406 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey' => $values[2]));
11407 $label = make_substitutions($reg[1], $substitutionarray);
11408 } else {
11409 $label = $langs->trans($values[2]);
11410 }
11411
11412 $newtab[0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
11413 $newtab[1] = $label;
11414 $newtab[2] = str_replace('+', '', $values[1]);
11415 $h++;
11416 }
11417 // set tab at its position
11418 $head = array_merge(array_slice($head, 0, $postab), array($newtab), array_slice($head, $postab));
11419 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
11420 if ($values[0] != $type) {
11421 continue;
11422 }
11423 $tabname = str_replace('-', '', $values[1]);
11424 foreach ($head as $key => $val) {
11425 $condition = (!empty($values[3]) ? verifCond($values[3], '2') : 1);
11426 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
11427 if ($head[$key][2] == $tabname && $condition) {
11428 unset($head[$key]);
11429 break;
11430 }
11431 }
11432 }
11433 }
11434 }
11435
11436 // No need to make a return $head. Var is modified as a reference
11437 if (!empty($hookmanager)) {
11438 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head, 'filterorigmodule' => $filterorigmodule);
11439 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters, $object);
11440 if ($reshook > 0) { // Hook ask to replace completely the array
11441 $head = $hookmanager->resArray;
11442 } else { // Hook
11443 $head = array_merge($head, $hookmanager->resArray);
11444 }
11445 $h = count($head);
11446 }
11447}
11448
11460function printCommonFooter($zone = 'private')
11461{
11462 global $conf, $hookmanager, $user, $langs;
11463 global $debugbar;
11464 global $action;
11465 global $micro_start_time;
11466
11467 if ($zone == 'private') {
11468 print "\n".'<!-- Common footer for private page -->'."\n";
11469 } else {
11470 print "\n".'<!-- Common footer for public page -->'."\n";
11471 }
11472
11473 // A div to store page_y POST parameter so we can read it using javascript
11474 print "\n<!-- A div to store page_y POST parameter -->\n";
11475 print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
11476
11477 $parameters = array();
11478 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
11479 if (empty($reshook)) {
11480 if (getDolGlobalString('MAIN_HTML_FOOTER')) {
11481 print getDolGlobalString('MAIN_HTML_FOOTER') . "\n";
11482 }
11483
11484 print "\n";
11485 if (!empty($conf->use_javascript_ajax)) {
11486 print "\n<!-- A script section to add menuhider handler on backoffice, manage focus and mandatory fields, tuning info, ... -->\n";
11487 print '<script>'."\n";
11488 print 'jQuery(document).ready(function() {'."\n";
11489
11490 if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
11491 print "\n";
11492 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
11493 print 'jQuery("li.menuhider").click(function(event) {';
11494 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
11495 print ' console.log("We click on .menuhider");'."\n";
11496 print ' $("body").toggleClass("sidebar-collapse")'."\n";
11497 print '});'."\n";
11498 }
11499
11500 // Management of focus and mandatory for fields
11501 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"])))) {
11502 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
11503 $relativepathstring = $_SERVER["PHP_SELF"];
11504 // Clean $relativepathstring
11505 if (constant('DOL_URL_ROOT')) {
11506 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
11507 }
11508 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
11509 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
11510 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
11511
11512 if (!empty($user->default_values[$relativepathstring]['focus'])) {
11513 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
11514 $qualified = 0;
11515 if ($defkey != '_noquery_') {
11516 $tmpqueryarraytohave = explode('&', $defkey);
11517 $foundintru = 0;
11518 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11519 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11520 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11521 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11522 $foundintru = 1;
11523 }
11524 }
11525 if (!$foundintru) {
11526 $qualified = 1;
11527 }
11528 //var_dump($defkey.'-'.$qualified);
11529 } else {
11530 $qualified = 1;
11531 }
11532
11533 if ($qualified) {
11534 print 'console.log("set the focus by executing jQuery(...).focus();")'."\n";
11535 foreach ($defval as $paramkey => $paramval) {
11536 // Set focus on field
11537 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
11538 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; // TODO KO with ckeditor
11539 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really useful, but we keep it in case of.
11540 }
11541 }
11542 }
11543 }
11544 if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
11545 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
11546 $qualified = 0;
11547 if ($defkey != '_noquery_') {
11548 $tmpqueryarraytohave = explode('&', $defkey);
11549 $foundintru = 0;
11550 foreach ($tmpqueryarraytohave as $tmpquerytohave) {
11551 $tmpquerytohaveparam = explode('=', $tmpquerytohave);
11552 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
11553 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
11554 $foundintru = 1;
11555 }
11556 }
11557 if (!$foundintru) {
11558 $qualified = 1;
11559 }
11560 //var_dump($defkey.'-'.$qualified);
11561 } else {
11562 $qualified = 1;
11563 }
11564
11565 if ($qualified) {
11566 print 'console.log("set the js code to manage fields that are set as mandatory");'."\n";
11567
11568 foreach ($defval as $paramkey => $paramval) {
11569 // Solution 1: Add handler on submit to check if mandatory fields are empty
11570 print 'var form = $(\'#'.dol_escape_js($paramkey).'\').closest("form");'."\n";
11571 print "form.on('submit', function(event) {
11572 var submitter = $(this).find(':submit:focus').get(0);
11573 if (submitter) {
11574 var buttonName = $(submitter).attr('name');
11575 if (buttonName == 'cancel') {
11576 console.log('We click on cancel button so we accept submit with no need to check mandatory fields');
11577 return true;
11578 }
11579 }
11580
11581 console.log('We did not click on cancel button but on something else, we check that field #".dol_escape_js($paramkey)." is not empty');
11582
11583 var tmpvalue = jQuery('#".dol_escape_js($paramkey)."').val();
11584 let tmptypefield = jQuery('#".dol_escape_js($paramkey)."').prop('nodeName').toLowerCase(); // Get the tag name (div, section, footer...)
11585
11586 if (tmptypefield == 'textarea') {
11587 // We must instead check the content of ckeditor
11588 var tmpeditor = CKEDITOR.instances['".dol_escape_js($paramkey)."'];
11589 if (tmpeditor) {
11590 tmpvalue = tmpeditor.getData();
11591 console.log('For textarea tmpvalue is '+tmpvalue);
11592 }
11593 }
11594
11595 let tmpvalueisempty = false;
11596 if (tmpvalue === null || tmpvalue === undefined || tmpvalue === '' || tmpvalue === -1) {
11597 tmpvalueisempty = true;
11598 }
11599 if (tmpvalue === '0' && (tmptypefield == 'select' || tmptypefield == 'input')) {
11600 tmpvalueisempty = true;
11601 }
11602 if (tmpvalueisempty && (buttonName == 'save')) {
11603 console.log('field has type '+tmptypefield+' and is empty, we cancel the submit');
11604 event.preventDefault(); // Stop submission of form to allow custom code to decide.
11605 event.stopPropagation(); // Stop other handlers.
11606 alert('".dol_escape_js($langs->trans("ErrorFieldRequired", $paramkey).' ('.$langs->trans("CustomMandatoryFieldRule").')')."');
11607 return false;
11608 }
11609 console.log('field has type '+tmptypefield+' and is defined to '+tmpvalue);
11610 return true;
11611 });
11612 \n";
11613
11614 // Solution 2: Add property 'required' on input
11615 // so browser will check value and try to focus on it when submitting the form.
11616 //print 'setTimeout(function() {'; // If we want to wait that ckeditor beuatifier has finished its job.
11617 //print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11618 //print 'jQuery("textarea[id=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
11619 //print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";*/
11620 //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";
11621 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
11622 //print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
11623 // Add 'field required' class on closest td for all input elements : input, textarea and select
11624 //print '}, 500);'; // 500 milliseconds delay
11625
11626 // Now set the class "fieldrequired"
11627 print 'jQuery(\':input[name="' . dol_escape_js($paramkey) . '"]\').closest("tr").find("td:first").addClass("fieldrequired");'."\n";
11628 }
11629
11630
11631 // If we submit using the cancel button, we remove the required attributes
11632 print 'jQuery("input[name=\'cancel\']").click(function() {
11633 console.log("We click on cancel button so removed all required attribute");
11634 jQuery("input, textarea, select").each(function(){this.removeAttribute(\'required\');});
11635 });'."\n";
11636 }
11637 }
11638 }
11639 }
11640
11641 print '});'."\n";
11642
11643 // End of tuning
11644 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || getDolGlobalString('MAIN_SHOW_TUNING_INFO')) {
11645 print "\n";
11646 print "/* JS CODE TO ENABLE to add memory info */\n";
11647 print 'window.console && console.log("';
11648 if (getDolGlobalString('MEMCACHED_SERVER')) {
11649 print 'MEMCACHED_SERVER=' . getDolGlobalString('MEMCACHED_SERVER').' - ';
11650 }
11651 print 'MAIN_OPTIMIZE_SPEED=' . getDolGlobalString('MAIN_OPTIMIZE_SPEED', 'off');
11652 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
11653 $micro_end_time = microtime(true);
11654 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
11655 }
11656
11657 if (function_exists("memory_get_usage")) {
11658 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
11659 }
11660 if (function_exists("memory_get_peak_usage")) {
11661 print ' - Real mem peak: '.memory_get_peak_usage(true);
11662 }
11663 if (function_exists("zend_loader_file_encoded")) {
11664 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
11665 }
11666 print '");'."\n";
11667 }
11668
11669 print "\n".'</script>'."\n";
11670
11671 // Google Analytics
11672 // TODO Add a hook here
11673 if (isModEnabled('google') && getDolGlobalString('MAIN_GOOGLE_AN_ID')) {
11674 $tmptagarray = explode(',', getDolGlobalString('MAIN_GOOGLE_AN_ID'));
11675 foreach ($tmptagarray as $tmptag) {
11676 print "\n";
11677 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
11678 print '
11679 <!-- Global site tag (gtag.js) - Google Analytics -->
11680 <script nonce="'.getNonce().'" async src="https://www.googletagmanager.com/gtag/js?id='.trim($tmptag).'"></script>
11681 <script>
11682 window.dataLayer = window.dataLayer || [];
11683 function gtag(){dataLayer.push(arguments);}
11684 gtag(\'js\', new Date());
11685
11686 gtag(\'config\', \''.trim($tmptag).'\');
11687 </script>';
11688 print "\n";
11689 }
11690 }
11691 }
11692
11693 // Add Xdebug coverage of code
11694 if (defined('XDEBUGCOVERAGE')) {
11695 print_r(xdebug_get_code_coverage());
11696 }
11697
11698 // Add DebugBar data
11699 if ($user->hasRight('debugbar', 'read') && $debugbar instanceof DebugBar\DebugBar) {
11700 if (isset($debugbar['time'])) {
11701 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11702 $debugbar['time']->stopMeasure('pageaftermaster');
11703 }
11704 print '<!-- Output debugbar data -->'."\n";
11705 $renderer = $debugbar->getJavascriptRenderer();
11706 print $renderer->render();
11707 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
11708 print "\n";
11709 print "<!-- Start of log output\n";
11710 //print '<div class="hidden">'."\n";
11711 foreach ($conf->logbuffer as $logline) {
11712 print $logline."<br>\n";
11713 }
11714 //print '</div>'."\n";
11715 print "End of log output -->\n";
11716 }
11717 }
11718}
11719
11729function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
11730{
11731 if (is_null($string)) {
11732 return array();
11733 }
11734
11735 if (preg_match('/^\[.*\]$/sm', $delimiter) || preg_match('/^\‍(.*\‍)$/sm', $delimiter)) {
11736 // This is a regex string
11737 $newdelimiter = $delimiter;
11738 } else {
11739 // This is a simple string
11740 // @phan-suppress-next-line PhanPluginSuspiciousParamPositionInternal
11741 $newdelimiter = preg_quote($delimiter, '/');
11742 }
11743
11744 if ($a = preg_split('/'.$newdelimiter.'/', $string)) {
11745 $ka = array();
11746 foreach ($a as $s) { // each part
11747 if ($s) {
11748 if ($pos = strpos($s, $kv)) { // key/value delimiter
11749 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
11750 } else { // key delimiter not found
11751 $ka[] = trim($s);
11752 }
11753 }
11754 }
11755 return $ka;
11756 }
11757
11758 return array();
11759}
11760
11761
11768function dol_set_focus($selector)
11769{
11770 print "\n".'<!-- Set focus onto a specific field -->'."\n";
11771 print '<script nonce="'.getNonce().'">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
11772}
11773
11774
11782function dol_getmypid()
11783{
11784 if (!function_exists('getmypid')) {
11785 return mt_rand(99900000, 99965535);
11786 } else {
11787 return getmypid(); // May be a number on 64 bits (depending on OS)
11788 }
11789}
11790
11791
11813function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
11814{
11815 global $db, $langs;
11816
11817 $value = trim($value);
11818
11819 if ($mode == 0) {
11820 $value = preg_replace('/\*/', '%', $value); // Replace * with %
11821 }
11822 if ($mode == 1) {
11823 $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
11824 }
11825
11826 $value = preg_replace('/\s*\|\s*/', '|', $value);
11827
11828 // Split criteria on ' '.
11829 // For mode 3, the split is done later on the , only and not on the ' '.
11830 if ($mode != -3 && $mode != 3) {
11831 $crits = explode(' ', $value);
11832 } else {
11833 $crits = array($value);
11834 }
11835
11836 $res = '';
11837 if (!is_array($fields)) {
11838 $fields = array($fields);
11839 }
11840
11841 $i1 = 0; // count the nb of "and" criteria added (all fields / criteria)
11842 foreach ($crits as $crit) { // Loop on each AND criteria
11843 $crit = trim($crit);
11844 $i2 = 0; // count the nb of valid criteria added for this this first criteria
11845 $newres = '';
11846 foreach ($fields as $field) {
11847 if ($mode == 1) {
11848 $tmpcrits = explode('|', $crit);
11849 $i3 = 0; // count the nb of valid criteria added for this current field
11850 foreach ($tmpcrits as $tmpcrit) {
11851 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11852 continue;
11853 }
11854 $tmpcrit = trim($tmpcrit);
11855
11856 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11857
11858 $operator = '=';
11859 $newcrit = preg_replace('/([!<>=]+)/', '', $tmpcrit);
11860
11861 $reg = array();
11862 preg_match('/([!<>=]+)/', $tmpcrit, $reg);
11863 if (!empty($reg[1])) {
11864 $operator = $reg[1];
11865 }
11866 if ($newcrit != '') {
11867 $numnewcrit = price2num($newcrit);
11868 if (is_numeric($numnewcrit)) {
11869 $newres .= $field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
11870 } else {
11871 $newres .= '1 = 2'; // force false, we received a corrupted data
11872 }
11873 $i3++; // a criteria was added to string
11874 }
11875 }
11876 $i2++; // a criteria for 1 more field was added to string
11877 } elseif ($mode == 2 || $mode == -2) {
11878 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
11879 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
11880 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
11881 if ($mode == -2) {
11882 $newres .= ' OR '.$field.' IS NULL';
11883 }
11884 $i2++; // a criteria for 1 more field was added to string
11885 } elseif ($mode == 3 || $mode == -3) {
11886 $tmparray = explode(',', $crit);
11887 if (count($tmparray)) {
11888 $listofcodes = '';
11889 foreach ($tmparray as $val) {
11890 $val = trim($val);
11891 if ($val) {
11892 $listofcodes .= ($listofcodes ? ',' : '');
11893 $listofcodes .= "'".$db->escape($val)."'";
11894 }
11895 }
11896 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1, 0, 1).")";
11897 $i2++; // a criteria for 1 more field was added to string
11898 }
11899 if ($mode == -3) {
11900 $newres .= ' OR '.$field.' IS NULL';
11901 }
11902 } elseif ($mode == 4) {
11903 $tmparray = explode(',', $crit);
11904 if (count($tmparray)) {
11905 $listofcodes = '';
11906 foreach ($tmparray as $val) {
11907 $val = trim($val);
11908 if ($val) {
11909 $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
11910 $newres .= ' OR '.$field." = '".$db->escape($val)."'";
11911 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
11912 $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
11913 $newres .= ')';
11914 $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)
11915 }
11916 }
11917 }
11918 } else { // $mode=0
11919 $tmpcrits = explode('|', $crit);
11920 $i3 = 0; // count the nb of valid criteria added for the current couple criteria/field
11921 foreach ($tmpcrits as $tmpcrit) { // loop on each OR criteria
11922 if ($tmpcrit !== '0' && empty($tmpcrit)) {
11923 continue;
11924 }
11925 $tmpcrit = trim($tmpcrit);
11926
11927 if ($tmpcrit == '^$' || strpos($crit, '!') === 0) { // If we search empty, we must combined different OR fields with AND
11928 $newres .= (($i2 > 0 || $i3 > 0) ? ' AND ' : '');
11929 } else {
11930 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
11931 }
11932
11933 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
11934 $newres .= $field." = ".(is_numeric($tmpcrit) ? ((float) $tmpcrit) : '0');
11935 } else {
11936 $tmpcrit2 = $tmpcrit;
11937 $tmpbefore = '%';
11938 $tmpafter = '%';
11939 $tmps = '';
11940
11941 if (preg_match('/^!/', $tmpcrit)) {
11942 $tmps .= $field." NOT LIKE "; // ! as exclude character
11943 $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
11944 } else {
11945 $tmps .= $field." LIKE ";
11946 }
11947 $tmps .= "'";
11948
11949 if (preg_match('/^[\^\$]/', $tmpcrit)) {
11950 $tmpbefore = '';
11951 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
11952 }
11953 if (preg_match('/[\^\$]$/', $tmpcrit)) {
11954 $tmpafter = '';
11955 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
11956 }
11957
11958 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11959 $tmps = "(".$tmps;
11960 }
11961 $newres .= $tmps;
11962 $newres .= $tmpbefore;
11963 $newres .= $db->escape($tmpcrit2);
11964 $newres .= $tmpafter;
11965 $newres .= "'";
11966 if ($tmpcrit2 == '' || preg_match('/^!/', $tmpcrit)) {
11967 $newres .= " OR ".$field." IS NULL)";
11968 }
11969 }
11970
11971 $i3++;
11972 }
11973
11974 $i2++; // a criteria for 1 more field was added to string
11975 }
11976 }
11977
11978 if ($newres) {
11979 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
11980 }
11981 $i1++;
11982 }
11983 $res = ($nofirstand ? "" : " AND ")."(".$res.")";
11984
11985 return $res;
11986}
11987
11994function showDirectDownloadLink($object)
11995{
11996 global $langs;
11997
11998 $out = '';
11999 $url = $object->getLastMainDocLink($object->element);
12000
12001 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
12002 if ($url) {
12003 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
12004 $out .= ajax_autoselect("directdownloadlink", '');
12005 } else {
12006 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
12007 }
12008
12009 return $out;
12010}
12011
12020function getImageFileNameForSize($file, $extName, $extImgTarget = '')
12021{
12022 $dirName = dirname($file);
12023 if ($dirName == '.') {
12024 $dirName = '';
12025 }
12026
12027 if (!in_array($extName, array('', '_small', '_mini'))) {
12028 return 'Bad parameter extName';
12029 }
12030
12031 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove image extension, whatever is its case
12032 $fileName = basename($fileName);
12033
12034 if (empty($extImgTarget)) {
12035 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
12036 }
12037 if (empty($extImgTarget)) {
12038 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
12039 }
12040 if (empty($extImgTarget)) {
12041 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
12042 }
12043 if (empty($extImgTarget)) {
12044 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
12045 }
12046 if (empty($extImgTarget)) {
12047 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
12048 }
12049 if (empty($extImgTarget)) {
12050 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
12051 }
12052
12053 if (!$extImgTarget) {
12054 return $file;
12055 }
12056
12057 $subdir = '';
12058 if ($extName) {
12059 $subdir = 'thumbs/';
12060 }
12061
12062 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
12063}
12064
12065
12075function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
12076{
12077 global $conf, $langs;
12078
12079 if (empty($conf->use_javascript_ajax)) {
12080 return '';
12081 }
12082
12083 $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
12084
12085 if ($alldata == 1) {
12086 if ($isAllowedForPreview) {
12087 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));
12088 } else {
12089 return array();
12090 }
12091 }
12092
12093 // old behavior, return a string
12094 if ($isAllowedForPreview) {
12095 $tmpurl = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($modulepart).'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '');
12096 $title = $langs->transnoentities("Preview");
12097 //$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().
12098 //$tmpurl = 'file='.urlencode("'-alert(document.domain)-'_small.jpg"); // An example of tmpurl that should be blocked by the dol_escape_uri()
12099
12100 // 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,
12101 // 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.
12102 // Using the dol_escape_uri guarantee that we encode for URI so decode retrieve original expected value.
12103 return 'javascript:'.dol_escape_uri('document_preview(\''.dol_escape_js($tmpurl).'\', \''.dol_escape_js(dol_mimetype($relativepath)).'\', \''.dol_escape_js($title).'\')');
12104 } else {
12105 return '';
12106 }
12107}
12108
12115function getLabelSpecialCode($idcode)
12116{
12117 global $langs;
12118
12119 $arrayspecialines = array(1 => 'Transport', 2 => 'EcoTax', 3 => 'Option');
12120 if ($idcode > 10) {
12121 return 'Module ID '.$idcode;
12122 }
12123 if (!empty($arrayspecialines[$idcode])) {
12124 return $langs->trans($arrayspecialines[$idcode]);
12125 }
12126 return '';
12127}
12128
12137function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
12138{
12139 global $langs;
12140 $out = '<script nonce="'.getNonce().'">
12141 jQuery(document).ready(function () {
12142 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
12143 });
12144 </script>';
12145 if ($addlink) {
12146 if ($textonlink === 'image') {
12147 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
12148 } else {
12149 $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
12150 }
12151 }
12152 return $out;
12153}
12154
12162function dolIsAllowedForPreview($file)
12163{
12164 // Check .noexe extension in filename
12165 if (preg_match('/\.noexe$/i', $file)) {
12166 return 0;
12167 }
12168
12169 // Check mime types
12170 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
12171 if (getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) {
12172 $mime_preview[] = 'svg+xml';
12173 }
12174 //$mime_preview[]='vnd.oasis.opendocument.presentation';
12175 //$mime_preview[]='archive';
12176 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
12177 if ($num_mime !== false) {
12178 return 1;
12179 }
12180
12181 // By default, not allowed for preview
12182 return 0;
12183}
12184
12185
12195function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
12196{
12197 $mime = $default;
12198 $imgmime = 'other.png';
12199 $famime = 'file-o';
12200 $srclang = '';
12201
12202 $tmpfile = preg_replace('/\.noexe$/', '', $file);
12203
12204 // Plain text files
12205 if (preg_match('/\.txt$/i', $tmpfile)) {
12206 $mime = 'text/plain';
12207 $imgmime = 'text.png';
12208 $famime = 'file-alt';
12209 } elseif (preg_match('/\.rtx$/i', $tmpfile)) {
12210 $mime = 'text/richtext';
12211 $imgmime = 'text.png';
12212 $famime = 'file-alt';
12213 } elseif (preg_match('/\.csv$/i', $tmpfile)) {
12214 $mime = 'text/csv';
12215 $imgmime = 'text.png';
12216 $famime = 'file-csv';
12217 } elseif (preg_match('/\.tsv$/i', $tmpfile)) {
12218 $mime = 'text/tab-separated-values';
12219 $imgmime = 'text.png';
12220 $famime = 'file-alt';
12221 } elseif (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
12222 $mime = 'text/plain';
12223 $imgmime = 'text.png';
12224 $famime = 'file-alt';
12225 } elseif (preg_match('/\.ini$/i', $tmpfile)) {
12226 $mime = 'text/plain';
12227 $imgmime = 'text.png';
12228 $srclang = 'ini';
12229 $famime = 'file-alt';
12230 } elseif (preg_match('/\.md$/i', $tmpfile)) {
12231 $mime = 'text/plain';
12232 $imgmime = 'text.png';
12233 $srclang = 'md';
12234 $famime = 'file-alt';
12235 } elseif (preg_match('/\.css$/i', $tmpfile)) {
12236 $mime = 'text/css';
12237 $imgmime = 'css.png';
12238 $srclang = 'css';
12239 $famime = 'file-alt';
12240 } elseif (preg_match('/\.lang$/i', $tmpfile)) {
12241 $mime = 'text/plain';
12242 $imgmime = 'text.png';
12243 $srclang = 'lang';
12244 $famime = 'file-alt';
12245 } elseif (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { // Certificate files
12246 $mime = 'text/plain';
12247 $imgmime = 'text.png';
12248 $famime = 'file-alt';
12249 } elseif (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { // XML based (HTML/XML/XAML)
12250 $mime = 'text/html';
12251 $imgmime = 'html.png';
12252 $srclang = 'html';
12253 $famime = 'file-alt';
12254 } elseif (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
12255 $mime = 'text/xml';
12256 $imgmime = 'other.png';
12257 $srclang = 'xml';
12258 $famime = 'file-alt';
12259 } elseif (preg_match('/\.xaml$/i', $tmpfile)) {
12260 $mime = 'text/xml';
12261 $imgmime = 'other.png';
12262 $srclang = 'xaml';
12263 $famime = 'file-alt';
12264 } elseif (preg_match('/\.bas$/i', $tmpfile)) { // Languages
12265 $mime = 'text/plain';
12266 $imgmime = 'text.png';
12267 $srclang = 'bas';
12268 $famime = 'file-code';
12269 } elseif (preg_match('/\.(c)$/i', $tmpfile)) {
12270 $mime = 'text/plain';
12271 $imgmime = 'text.png';
12272 $srclang = 'c';
12273 $famime = 'file-code';
12274 } elseif (preg_match('/\.(cpp)$/i', $tmpfile)) {
12275 $mime = 'text/plain';
12276 $imgmime = 'text.png';
12277 $srclang = 'cpp';
12278 $famime = 'file-code';
12279 } elseif (preg_match('/\.cs$/i', $tmpfile)) {
12280 $mime = 'text/plain';
12281 $imgmime = 'text.png';
12282 $srclang = 'cs';
12283 $famime = 'file-code';
12284 } elseif (preg_match('/\.(h)$/i', $tmpfile)) {
12285 $mime = 'text/plain';
12286 $imgmime = 'text.png';
12287 $srclang = 'h';
12288 $famime = 'file-code';
12289 } elseif (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
12290 $mime = 'text/plain';
12291 $imgmime = 'text.png';
12292 $srclang = 'java';
12293 $famime = 'file-code';
12294 } elseif (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
12295 $mime = 'text/plain';
12296 $imgmime = 'php.png';
12297 $srclang = 'php';
12298 $famime = 'file-code';
12299 } elseif (preg_match('/\.phtml$/i', $tmpfile)) {
12300 $mime = 'text/plain';
12301 $imgmime = 'php.png';
12302 $srclang = 'php';
12303 $famime = 'file-code';
12304 } elseif (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
12305 $mime = 'text/plain';
12306 $imgmime = 'pl.png';
12307 $srclang = 'perl';
12308 $famime = 'file-code';
12309 } elseif (preg_match('/\.sql$/i', $tmpfile)) {
12310 $mime = 'text/plain';
12311 $imgmime = 'text.png';
12312 $srclang = 'sql';
12313 $famime = 'file-code';
12314 } elseif (preg_match('/\.js$/i', $tmpfile)) {
12315 $mime = 'text/x-javascript';
12316 $imgmime = 'jscript.png';
12317 $srclang = 'js';
12318 $famime = 'file-code';
12319 } elseif (preg_match('/\.odp$/i', $tmpfile)) { // Open office
12320 $mime = 'application/vnd.oasis.opendocument.presentation';
12321 $imgmime = 'ooffice.png';
12322 $famime = 'file-powerpoint';
12323 } elseif (preg_match('/\.ods$/i', $tmpfile)) {
12324 $mime = 'application/vnd.oasis.opendocument.spreadsheet';
12325 $imgmime = 'ooffice.png';
12326 $famime = 'file-excel';
12327 } elseif (preg_match('/\.odt$/i', $tmpfile)) {
12328 $mime = 'application/vnd.oasis.opendocument.text';
12329 $imgmime = 'ooffice.png';
12330 $famime = 'file-word';
12331 } elseif (preg_match('/\.mdb$/i', $tmpfile)) { // MS Office
12332 $mime = 'application/msaccess';
12333 $imgmime = 'mdb.png';
12334 $famime = 'file';
12335 } elseif (preg_match('/\.doc[xm]?$/i', $tmpfile)) {
12336 $mime = 'application/msword';
12337 $imgmime = 'doc.png';
12338 $famime = 'file-word';
12339 } elseif (preg_match('/\.dot[xm]?$/i', $tmpfile)) {
12340 $mime = 'application/msword';
12341 $imgmime = 'doc.png';
12342 $famime = 'file-word';
12343 } elseif (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
12344 $mime = 'application/vnd.ms-excel';
12345 $imgmime = 'xls.png';
12346 $famime = 'file-excel';
12347 } elseif (preg_match('/\.xla(m)?$/i', $tmpfile)) {
12348 $mime = 'application/vnd.ms-excel';
12349 $imgmime = 'xls.png';
12350 $famime = 'file-excel';
12351 } elseif (preg_match('/\.xls$/i', $tmpfile)) {
12352 $mime = 'application/vnd.ms-excel';
12353 $imgmime = 'xls.png';
12354 $famime = 'file-excel';
12355 } elseif (preg_match('/\.xls[bmx]$/i', $tmpfile)) {
12356 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
12357 $imgmime = 'xls.png';
12358 $famime = 'file-excel';
12359 } elseif (preg_match('/\.pps[mx]?$/i', $tmpfile)) {
12360 $mime = 'application/vnd.ms-powerpoint';
12361 $imgmime = 'ppt.png';
12362 $famime = 'file-powerpoint';
12363 } elseif (preg_match('/\.ppt[mx]?$/i', $tmpfile)) {
12364 $mime = 'application/x-mspowerpoint';
12365 $imgmime = 'ppt.png';
12366 $famime = 'file-powerpoint';
12367 } elseif (preg_match('/\.pdf$/i', $tmpfile)) { // Other
12368 $mime = 'application/pdf';
12369 $imgmime = 'pdf.png';
12370 $famime = 'file-pdf';
12371 } elseif (preg_match('/\.bat$/i', $tmpfile)) { // Scripts
12372 $mime = 'text/x-bat';
12373 $imgmime = 'script.png';
12374 $srclang = 'dos';
12375 $famime = 'file-code';
12376 } elseif (preg_match('/\.sh$/i', $tmpfile)) {
12377 $mime = 'text/x-sh';
12378 $imgmime = 'script.png';
12379 $srclang = 'bash';
12380 $famime = 'file-code';
12381 } elseif (preg_match('/\.ksh$/i', $tmpfile)) {
12382 $mime = 'text/x-ksh';
12383 $imgmime = 'script.png';
12384 $srclang = 'bash';
12385 $famime = 'file-code';
12386 } elseif (preg_match('/\.bash$/i', $tmpfile)) {
12387 $mime = 'text/x-bash';
12388 $imgmime = 'script.png';
12389 $srclang = 'bash';
12390 $famime = 'file-code';
12391 } elseif (preg_match('/\.ico$/i', $tmpfile)) { // Images
12392 $mime = 'image/x-icon';
12393 $imgmime = 'image.png';
12394 $famime = 'file-image';
12395 } elseif (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
12396 $mime = 'image/jpeg';
12397 $imgmime = 'image.png';
12398 $famime = 'file-image';
12399 } elseif (preg_match('/\.png$/i', $tmpfile)) {
12400 $mime = 'image/png';
12401 $imgmime = 'image.png';
12402 $famime = 'file-image';
12403 } elseif (preg_match('/\.gif$/i', $tmpfile)) {
12404 $mime = 'image/gif';
12405 $imgmime = 'image.png';
12406 $famime = 'file-image';
12407 } elseif (preg_match('/\.bmp$/i', $tmpfile)) {
12408 $mime = 'image/bmp';
12409 $imgmime = 'image.png';
12410 $famime = 'file-image';
12411 } elseif (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
12412 $mime = 'image/tiff';
12413 $imgmime = 'image.png';
12414 $famime = 'file-image';
12415 } elseif (preg_match('/\.svg$/i', $tmpfile)) {
12416 $mime = 'image/svg+xml';
12417 $imgmime = 'image.png';
12418 $famime = 'file-image';
12419 } elseif (preg_match('/\.webp$/i', $tmpfile)) {
12420 $mime = 'image/webp';
12421 $imgmime = 'image.png';
12422 $famime = 'file-image';
12423 } elseif (preg_match('/\.vcs$/i', $tmpfile)) { // Calendar
12424 $mime = 'text/calendar';
12425 $imgmime = 'other.png';
12426 $famime = 'file-alt';
12427 } elseif (preg_match('/\.ics$/i', $tmpfile)) {
12428 $mime = 'text/calendar';
12429 $imgmime = 'other.png';
12430 $famime = 'file-alt';
12431 } elseif (preg_match('/\.torrent$/i', $tmpfile)) { // Other
12432 $mime = 'application/x-bittorrent';
12433 $imgmime = 'other.png';
12434 $famime = 'file-o';
12435 } elseif (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { // Audio
12436 $mime = 'audio';
12437 $imgmime = 'audio.png';
12438 $famime = 'file-audio';
12439 } elseif (preg_match('/\.mp4$/i', $tmpfile)) { // Video
12440 $mime = 'video/mp4';
12441 $imgmime = 'video.png';
12442 $famime = 'file-video';
12443 } elseif (preg_match('/\.ogv$/i', $tmpfile)) {
12444 $mime = 'video/ogg';
12445 $imgmime = 'video.png';
12446 $famime = 'file-video';
12447 } elseif (preg_match('/\.webm$/i', $tmpfile)) {
12448 $mime = 'video/webm';
12449 $imgmime = 'video.png';
12450 $famime = 'file-video';
12451 } elseif (preg_match('/\.avi$/i', $tmpfile)) {
12452 $mime = 'video/x-msvideo';
12453 $imgmime = 'video.png';
12454 $famime = 'file-video';
12455 } elseif (preg_match('/\.divx$/i', $tmpfile)) {
12456 $mime = 'video/divx';
12457 $imgmime = 'video.png';
12458 $famime = 'file-video';
12459 } elseif (preg_match('/\.xvid$/i', $tmpfile)) {
12460 $mime = 'video/xvid';
12461 $imgmime = 'video.png';
12462 $famime = 'file-video';
12463 } elseif (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
12464 $mime = 'video';
12465 $imgmime = 'video.png';
12466 $famime = 'file-video';
12467 } elseif (preg_match('/\.(zip|rar|gz|tgz|xz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { // Archive
12468 // application/xxx where zzz is zip, ...
12469 $mime = 'archive';
12470 $imgmime = 'archive.png';
12471 $famime = 'file-archive';
12472 } elseif (preg_match('/\.(exe|com)$/i', $tmpfile)) { // Exe
12473 $mime = 'application/octet-stream';
12474 $imgmime = 'other.png';
12475 $famime = 'file-o';
12476 } elseif (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { // Lib
12477 $mime = 'library';
12478 $imgmime = 'library.png';
12479 $famime = 'file-o';
12480 } elseif (preg_match('/\.err$/i', $tmpfile)) { // phpcs:ignore
12481 $mime = 'error';
12482 $imgmime = 'error.png';
12483 $famime = 'file-alt';
12484 }
12485
12486 if ($famime == 'file-o') {
12487 // file-o seems to not work in fontawesome 5
12488 $famime = 'file';
12489 }
12490
12491 // Return mimetype string
12492 switch ((int) $mode) {
12493 case 1:
12494 $tmp = explode('/', $mime);
12495 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
12496 case 2:
12497 return $imgmime;
12498 case 3:
12499 return $srclang;
12500 case 4:
12501 return $famime;
12502 }
12503 return $mime;
12504}
12505
12517function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
12518{
12519 global $conf, $db;
12520
12521 $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
12522
12523 $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
12524
12525 if (is_null($dictvalues)) {
12526 $dictvalues = array();
12527
12528 $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
12529 if ($checkentity) {
12530 $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
12531 }
12532
12533 $resql = $db->query($sql);
12534 if ($resql) {
12535 while ($obj = $db->fetch_object($resql)) {
12536 $dictvalues[$obj->$rowidfield] = $obj; // $obj is stdClass
12537 }
12538 } else {
12539 dol_print_error($db);
12540 }
12541
12542 $conf->cache['dictvalues_'.$tablename] = $dictvalues;
12543 }
12544
12545 if (!empty($dictvalues[$id])) {
12546 // Found
12547 $tmp = $dictvalues[$id];
12548 return (property_exists($tmp, $field) ? $tmp->$field : '');
12549 } else {
12550 // Not found
12551 return '';
12552 }
12553}
12554
12561function colorIsLight($stringcolor)
12562{
12563 $stringcolor = str_replace('#', '', $stringcolor);
12564 $res = -1;
12565 if (!empty($stringcolor)) {
12566 $res = 0;
12567 $tmp = explode(',', $stringcolor);
12568 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
12569 $r = $tmp[0];
12570 $g = $tmp[1];
12571 $b = $tmp[2];
12572 } else {
12573 $hexr = $stringcolor[0].$stringcolor[1];
12574 $hexg = $stringcolor[2].$stringcolor[3];
12575 $hexb = $stringcolor[4].$stringcolor[5];
12576 $r = hexdec($hexr);
12577 $g = hexdec($hexg);
12578 $b = hexdec($hexb);
12579 }
12580 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
12581 if ($bright > 0.6) {
12582 $res = 1;
12583 }
12584 }
12585 return $res;
12586}
12587
12596function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
12597{
12598 global $conf;
12599
12600 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
12601 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
12602 if (empty($menuentry['enabled'])) {
12603 return 0; // Entry disabled by condition
12604 }
12605 if ($type_user && $menuentry['module']) {
12606 $tmploops = explode('|', $menuentry['module']);
12607 $found = 0;
12608 foreach ($tmploops as $tmploop) {
12609 if (in_array($tmploop, $listofmodulesforexternal)) {
12610 $found++;
12611 break;
12612 }
12613 }
12614 if (!$found) {
12615 return 0; // Entry is for menus all excluded to external users
12616 }
12617 }
12618 if (!$menuentry['perms'] && $type_user) {
12619 return 0; // No permissions and user is external
12620 }
12621 if (!$menuentry['perms'] && getDolGlobalString('MAIN_MENU_HIDE_UNAUTHORIZED')) {
12622 return 0; // No permissions and option to hide when not allowed, even for internal user, is on
12623 }
12624 if (!$menuentry['perms']) {
12625 return 2; // No permissions and user is external
12626 }
12627 return 1;
12628}
12629
12637function roundUpToNextMultiple($n, $x = 5)
12638{
12639 $result = (ceil($n) % $x === 0) ? ceil($n) : (round(($n + $x / 2) / $x) * $x);
12640 return (int) $result;
12641}
12642
12654function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
12655{
12656 $csstouse = 'badge';
12657 $csstouse .= (!empty($mode) ? ' badge-'.$mode : '');
12658 $csstouse .= (!empty($type) ? ' badge-'.$type : '');
12659 $csstouse .= (empty($params['css']) ? '' : ' '.$params['css']);
12660
12661 $attr = array(
12662 'class' => $csstouse
12663 );
12664
12665 if (empty($html)) {
12666 $html = $label;
12667 }
12668
12669 if (!empty($url)) {
12670 $attr['href'] = $url;
12671 }
12672
12673 if ($mode === 'dot') {
12674 $attr['class'] .= ' classfortooltip';
12675 $attr['title'] = $html;
12676 $attr['aria-label'] = $label;
12677 $html = '';
12678 }
12679
12680 // Override attr
12681 if (!empty($params['attr']) && is_array($params['attr'])) {
12682 foreach ($params['attr'] as $key => $value) {
12683 if ($key == 'class') {
12684 $attr['class'] .= ' '.$value;
12685 } elseif ($key == 'classOverride') {
12686 $attr['class'] = $value;
12687 } else {
12688 $attr[$key] = $value;
12689 }
12690 }
12691 }
12692
12693 // TODO: add hook
12694
12695 // escape all attribute
12696 $attr = array_map('dolPrintHTMLForAttribute', $attr);
12697
12698 $TCompiledAttr = array();
12699 foreach ($attr as $key => $value) {
12700 $TCompiledAttr[] = $key.'="'.$value.'"';
12701 }
12702
12703 $compiledAttributes = !empty($TCompiledAttr) ? implode(' ', $TCompiledAttr) : '';
12704
12705 $tag = !empty($url) ? 'a' : 'span';
12706
12707 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
12708}
12709
12710
12723function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
12724{
12725 global $conf;
12726
12727 $return = '';
12728 $dolGetBadgeParams = array();
12729
12730 if (!empty($params['badgeParams'])) {
12731 $dolGetBadgeParams = $params['badgeParams'];
12732 }
12733
12734 // TODO : add a hook
12735 if ($displayMode == 0) {
12736 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
12737 } elseif ($displayMode == 1) {
12738 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12739 } elseif (getDolGlobalString('MAIN_STATUS_USES_IMAGES')) {
12740 // Use status with images (for backward compatibility)
12741 $return = '';
12742 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
12743 $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>' : '');
12744
12745 // For small screen, we always use the short label instead of long label.
12746 if (!empty($conf->dol_optimize_smallscreen)) {
12747 if ($displayMode == 0) {
12748 $displayMode = 1;
12749 } elseif ($displayMode == 4) {
12750 $displayMode = 2;
12751 } elseif ($displayMode == 6) {
12752 $displayMode = 5;
12753 }
12754 }
12755
12756 // For backward compatibility. Image's filename are still in French, so we use this array to convert
12757 $statusImg = array(
12758 'status0' => 'statut0',
12759 'status1' => 'statut1',
12760 'status2' => 'statut2',
12761 'status3' => 'statut3',
12762 'status4' => 'statut4',
12763 'status5' => 'statut5',
12764 'status6' => 'statut6',
12765 'status7' => 'statut7',
12766 'status8' => 'statut8',
12767 'status9' => 'statut9'
12768 );
12769
12770 if (!empty($statusImg[$statusType])) {
12771 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
12772 } else {
12773 $htmlImg = img_picto($statusLabel, $statusType);
12774 }
12775
12776 if ($displayMode === 2) {
12777 $return = $htmlImg.' '.$htmlLabelShort;
12778 } elseif ($displayMode === 3) {
12779 $return = $htmlImg;
12780 } elseif ($displayMode === 4) {
12781 $return = $htmlImg.' '.$htmlLabel;
12782 } elseif ($displayMode === 5) {
12783 $return = $htmlLabelShort.' '.$htmlImg;
12784 } else { // $displayMode >= 6
12785 $return = $htmlLabel.' '.$htmlImg;
12786 }
12787 } elseif (!getDolGlobalString('MAIN_STATUS_USES_IMAGES') && !empty($displayMode)) {
12788 // Use new badge
12789 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
12790
12791 $dolGetBadgeParams['attr']['class'] = 'badge-status';
12792 if (empty($dolGetBadgeParams['attr']['title'])) {
12793 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
12794 } else { // If a title was forced from $params['badgeParams']['attr']['title'], we set the class to get it as a tooltip.
12795 $dolGetBadgeParams['attr']['class'] .= ' classfortooltip';
12796 // And if we use tooltip, we can output title in HTML @phan-suppress-next-line PhanTypeInvalidDimOffset
12797 $dolGetBadgeParams['attr']['title'] = dol_htmlentitiesbr($dolGetBadgeParams['attr']['title'], 1);
12798 }
12799
12800 if ($displayMode == 3) {
12801 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
12802 } elseif ($displayMode === 5) {
12803 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
12804 } else {
12805 $return = dolGetBadge(((empty($conf->dol_optimize_smallscreen) && $displayMode != 2) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
12806 }
12807 }
12808
12809 return $return;
12810}
12811
12812
12851function dolGetButtonAction($label, $text = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
12852{
12853 global $hookmanager, $action, $object, $langs;
12854
12855 // If $url is an array, we must build a dropdown button or recursively iterate over each value
12856 if (is_array($url)) {
12857 // Loop on $url array to remove entries of disabled modules
12858 foreach ($url as $key => $subbutton) {
12859 if (isset($subbutton['enabled']) && empty($subbutton['enabled'])) {
12860 unset($url[$key]);
12861 }
12862 }
12863
12864 $out = '';
12865
12866 if (array_key_exists('areDropdownButtons', $params) && $params["areDropdownButtons"] === false) { // @phan-suppress-current-line PhanTypeInvalidDimOffset
12867 foreach ($url as $button) {
12868 if (!empty($button['lang'])) {
12869 $langs->load($button['lang']);
12870 }
12871 $label = $langs->trans($button['label']);
12872 $text = $button['text'] ?? '';
12873 $actionType = $button['actionType'] ?? '';
12874 $tmpUrl = DOL_URL_ROOT.$button['url'].(empty($params['backtopage']) ? '' : '&amp;backtopage='.urlencode($params['backtopage']));
12875 $id = $button['id'] ?? '';
12876 $userRight = $button['perm'] ?? 1;
12877 $button['params'] = $button['params'] ?? []; // @phan-suppress-current-line PhanPluginDuplicateExpressionAssignmentOperation
12878
12879 $out .= dolGetButtonAction($label, $text, $actionType, $tmpUrl, $id, $userRight, $button['params']);
12880 }
12881 return $out;
12882 }
12883
12884 if (count($url) > 1) {
12885 $out .= '<div class="dropdown inline-block dropdown-holder">';
12886 $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>';
12887 $out .= '<div class="dropdown-content">';
12888 foreach ($url as $subbutton) {
12889 if (!empty($subbutton['lang'])) {
12890 $langs->load($subbutton['lang']);
12891 }
12892
12893 if (!empty($subbutton['urlraw'])) {
12894 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12895 } else {
12896 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12897 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12898 }
12899
12900 $subbuttonparam = array();
12901 if (!empty($subbutton['attr'])) {
12902 $subbuttonparam['attr'] = $subbutton['attr'];
12903 }
12904 $subbuttonparam['isDropDown'] = (empty($params['isDropDown']) ? ($subbutton['isDropDown'] ?? false) : $params['isDropDown']);
12905
12906 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, $subbutton['id'] ?? '', $subbutton['perm'], $subbuttonparam);
12907 }
12908 $out .= "</div>";
12909 $out .= "</div>";
12910 } else {
12911 foreach ($url as $subbutton) { // Should loop on 1 record only
12912 if (!empty($subbutton['lang'])) {
12913 $langs->load($subbutton['lang']);
12914 }
12915
12916 if (!empty($subbutton['urlraw'])) {
12917 $tmpurl = $subbutton['urlraw']; // Use raw url, no url completion, use only what developer send
12918 } else {
12919 $tmpurl = !empty($subbutton['urlroot']) ? $subbutton['urlroot'] : $subbutton['url'];
12920 $tmpurl = dolCompletUrlForDropdownButton($tmpurl, $params, empty($subbutton['urlroot']));
12921 }
12922
12923 $out .= dolGetButtonAction('', $langs->trans($subbutton['label']), 'default', $tmpurl, '', $subbutton['perm'], $params);
12924 }
12925 }
12926
12927 return $out;
12928 }
12929
12930 // Here, $url is a simple link
12931
12932 if (!empty($params['isDropdown']) || !empty($params['isDropDown'])) { // Use the dropdown-item style (not for action button)
12933 $class = "dropdown-item";
12934 } else {
12935 $class = 'butAction';
12936 if ($actionType == 'danger' || $actionType == 'delete') {
12937 $class = 'butActionDelete';
12938 if (!empty($url) && strpos($url, 'token=') === false) {
12939 $url .= '&token='.newToken();
12940 }
12941 }
12942 }
12943 $attr = array(
12944 'class' => $class,
12945 'href' => empty($url) ? '' : $url,
12946 'title' => $label
12947 );
12948
12949 if (empty($text)) {
12950 $text = $label;
12951 $attr['title'] = ''; // if html not set, leave label on title is redundant
12952 } else {
12953 $attr['title'] = $label;
12954 $attr['aria-label'] = $label;
12955 }
12956
12957 if (empty($userRight) || $userRight < 0) {
12958 $attr['class'] = 'butActionRefused';
12959 $attr['href'] = '';
12960 $attr['title'] = (($label && $text && $label != $text) ? $label : '');
12961 $attr['title'] = ($attr['title'] ? $attr['title'] . (empty($userRight) ? '<br>' : '') : '').(empty($userRight) ? $langs->trans('NotEnoughPermissions') : '');
12962 }
12963
12964 if (!empty($id)) {
12965 $attr['id'] = $id;
12966 }
12967
12968 // Override attr
12969 if (!empty($params['attr']) && is_array($params['attr'])) {
12970 foreach ($params['attr'] as $key => $value) {
12971 if ($key == 'class') {
12972 $attr['class'] .= ' '.$value;
12973 } elseif ($key == 'classOverride') {
12974 $attr['class'] = $value;
12975 } else {
12976 $attr[$key] = $value;
12977 }
12978 }
12979 }
12980
12981 // automatic add tooltip when title is detected
12982 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
12983 $attr['class'] .= ' classfortooltip';
12984 }
12985
12986 // Js Confirm button
12987 if ($userRight && !empty($params['confirm'])) {
12988 if (!is_array($params['confirm'])) {
12989 $params['confirm'] = array();
12990 }
12991
12992 if (empty($params['confirm']['url'])) {
12993 $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
12994 }
12995
12996 // for js disabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
12997 $attr['data-confirm-url'] = $params['confirm']['url'];
12998 $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
12999 $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
13000 $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
13001 $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
13002 $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
13003 $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
13004
13005 $attr['class'] .= ' butActionConfirm';
13006 }
13007
13008 if (isset($attr['href']) && empty($attr['href'])) {
13009 unset($attr['href']);
13010 }
13011
13012 $TCompiledAttr = array();
13013 foreach ($attr as $key => $value) {
13014 if (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr'])) {
13015 // Not recommended
13016 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13017 } elseif ($key == 'href') {
13018 $value = dolPrintHTMLForAttributeUrl($value);
13019 } else {
13020 $value = dolPrintHTMLForAttribute($value);
13021 }
13022
13023 $TCompiledAttr[] = $key.'="'.$value.'"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13024 }
13025
13026 $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
13027
13028 $tag = !empty($attr['href']) ? 'a' : 'span';
13029
13030
13031 $parameters = array(
13032 'TCompiledAttr' => $TCompiledAttr, // array
13033 'compiledAttributes' => $compiledAttributes, // string
13034 'attr' => $attr,
13035 'tag' => $tag,
13036 'label' => $label,
13037 'html' => $text,
13038 'actionType' => $actionType,
13039 'url' => $url,
13040 'id' => $id,
13041 'userRight' => $userRight,
13042 'params' => $params
13043 );
13044
13045 $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
13046 if ($reshook < 0) {
13047 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
13048 }
13049
13050 if (empty($reshook)) {
13051 if (dol_textishtml($text)) { // If content already HTML encoded
13052 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . $text . '</span></' . $tag . '>';
13053 } else {
13054 return '<' . $tag . ' ' . $compiledAttributes . '><span class="textbutton">' . dol_escape_htmltag($text) . '</span></' . $tag . '>';
13055 }
13056 } else {
13057 return $hookmanager->resPrint;
13058 }
13059}
13060
13061
13070function dolCompletUrlForDropdownButton(string $url, array $params, bool $addDolUrlRoot = true)
13071{
13072 if (empty($url)) {
13073 return '';
13074 }
13075
13076 $parsedUrl = parse_url($url);
13077 if ((isset($parsedUrl['scheme']) && in_array($parsedUrl['scheme'], ['javascript', 'mailto', 'tel'])) || strpos($url, '#') === 0) {
13078 return $url;
13079 }
13080
13081 if (!empty($parsedUrl['query'])) {
13082 // Use parse_str() function to parse the string passed via URL
13083 parse_str($parsedUrl['query'], $urlQuery);
13084 if (!isset($urlQuery['backtopage']) && isset($params['backtopage'])) {
13085 $url .= '&amp;backtopage='.urlencode($params['backtopage']);
13086 }
13087 }
13088
13089 if (!isset($parsedUrl['scheme']) && $addDolUrlRoot) {
13090 $url = DOL_URL_ROOT.$url;
13091 }
13092
13093 return $url;
13094}
13095
13096
13103function dolGetButtonTitleSeparator($moreClass = "")
13104{
13105 return '<span class="button-title-separator '.$moreClass.'" ></span>';
13106}
13107
13114function getFieldErrorIcon($fieldValidationErrorMsg)
13115{
13116 $out = '';
13117 if (!empty($fieldValidationErrorMsg)) {
13118 $out .= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
13119 $out .= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
13120 $out .= '</span>';
13121 }
13122
13123 return $out;
13124}
13125
13138function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
13139{
13140 global $langs, $user;
13141
13142 // Actually this conf is used in css too for external module compatibility and smooth transition to this function
13143 if (getDolGlobalString('MAIN_BUTTON_HIDE_UNAUTHORIZED') && (!$user->admin) && $status <= 0) {
13144 return '';
13145 }
13146
13147 $class = 'btnTitle';
13148 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots', 'fa fa-paper-plane'))) {
13149 $class .= ' btnTitlePlus';
13150 }
13151 $useclassfortooltip = 1;
13152
13153 if (!empty($params['morecss'])) {
13154 $class .= ' '.$params['morecss'];
13155 }
13156
13157 $attr = array(
13158 'class' => $class,
13159 'href' => empty($url) ? '' : $url
13160 );
13161
13162 if (!empty($helpText)) {
13163 $attr['title'] = $helpText;
13164 } elseif ($label) { // empty($attr['title']) &&
13165 $attr['title'] = $label;
13166 $useclassfortooltip = 0;
13167 }
13168
13169 if ($status == 2) {
13170 $attr['class'] .= ' btnTitleSelected';
13171 } elseif ($status <= 0) {
13172 $attr['class'] .= ' refused';
13173
13174 $attr['href'] = '';
13175
13176 if ($status == -1) { // disable
13177 $attr['title'] = $langs->transnoentitiesnoconv("FeatureDisabled");
13178 } elseif ($status == 0) { // Not enough permissions
13179 $attr['title'] = $langs->transnoentitiesnoconv("NotEnoughPermissions");
13180 }
13181 }
13182
13183 if (!empty($attr['title']) && $useclassfortooltip) {
13184 $attr['class'] .= ' classfortooltip';
13185 }
13186
13187 if (!empty($id)) {
13188 $attr['id'] = $id;
13189 }
13190
13191 // Override attr
13192 if (!empty($params['attr']) && is_array($params['attr'])) {
13193 foreach ($params['attr'] as $key => $value) {
13194 if ($key == 'class') {
13195 $attr['class'] .= ' '.$value;
13196 } elseif ($key == 'classOverride') {
13197 $attr['class'] = $value;
13198 } else {
13199 $attr[$key] = $value;
13200 }
13201 }
13202 }
13203
13204 if (isset($attr['href']) && empty($attr['href'])) {
13205 unset($attr['href']);
13206 }
13207
13208 // TODO : add a hook
13209
13210 // Generate attributes with escapement
13211 $TCompiledAttr = array();
13212 foreach ($attr as $key => $value) {
13213 $TCompiledAttr[] = $key.'="'.dol_escape_htmltag($value).'"'; // Do not use dolPrintHTMLForAttribute() here, we must accept "javascript:string"
13214 }
13215
13216 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
13217
13218 $tag = (empty($attr['href']) ? 'span' : 'a');
13219
13220 $button = '<'.$tag.' '.$compiledAttributes.'>';
13221 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
13222 if (!empty($params['forcenohideoftext'])) {
13223 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
13224 }
13225 $button .= '</'.$tag.'>';
13226
13227 return $button;
13228}
13229
13239function getElementProperties($elementType)
13240{
13241 global $conf, $db, $hookmanager;
13242
13243 $regs = array();
13244
13245 //$element_type='facture';
13246
13247 $classfile = $classname = $classpath = $subdir = $dir_output = $dir_temp = $parent_element = '';
13248
13249 // Parse element/subelement
13250 $module = $elementType;
13251 $element = $elementType;
13252 $subelement = $elementType;
13253 $table_element = $elementType;
13254
13255 // If we ask a resource form external module (instead of default path)
13256 if (preg_match('/^([^@]+)@([^@]+)$/i', $elementType, $regs)) { // 'myobject@mymodule'
13257 $element = $subelement = $regs[1];
13258 $module = $regs[2];
13259 }
13260
13261 // If we ask a resource for a string with an element and a subelement
13262 // Example 'project_task'
13263 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { // 'myobject_mysubobject' with myobject=mymodule
13264 $module = $element = $regs[1];
13265 $subelement = $regs[2];
13266 }
13267
13268 // Object lines will use parent classpath and module ref
13269 if (substr($elementType, -3) == 'det') {
13270 $module = preg_replace('/det$/', '', $element);
13271 $subelement = preg_replace('/det$/', '', $subelement);
13272 $classpath = $module.'/class';
13273 $classfile = $module;
13274 $classname = preg_replace('/det$/', 'Line', $element);
13275 if (in_array($module, array('expedition', 'propale', 'facture', 'contrat', 'fichinter', 'commandefournisseur'))) {
13276 $classname = preg_replace('/det$/', 'Ligne', $element);
13277 }
13278 }
13279 // For compatibility and to work with non standard path
13280 if ($elementType == "action" || $elementType == "actioncomm") {
13281 $classpath = 'comm/action/class';
13282 $subelement = 'Actioncomm';
13283 $module = 'agenda';
13284 $table_element = 'actioncomm';
13285 } elseif ($elementType == 'cronjob') {
13286 $classpath = 'cron/class';
13287 $module = 'cron';
13288 $table_element = 'cron';
13289 } elseif ($elementType == 'adherent_type') {
13290 $classpath = 'adherents/class';
13291 $classfile = 'adherent_type';
13292 $module = 'adherent';
13293 $subelement = 'adherent_type';
13294 $classname = 'AdherentType';
13295 $table_element = 'adherent_type';
13296 } elseif ($elementType == 'bank_account') {
13297 $classpath = 'compta/bank/class';
13298 $module = 'bank'; // We need $conf->bank->dir_output and not $conf->banque->dir_output
13299 $classfile = 'account';
13300 $classname = 'Account';
13301 } elseif ($elementType == 'category') {
13302 $classpath = 'categories/class';
13303 $module = 'categorie';
13304 $subelement = 'categorie';
13305 $table_element = 'categorie';
13306 } elseif ($elementType == 'contact') {
13307 $classpath = 'contact/class';
13308 $classfile = 'contact';
13309 $module = 'societe';
13310 $subelement = 'contact';
13311 $table_element = 'socpeople';
13312 } elseif ($elementType == 'inventory') {
13313 $module = 'product';
13314 $classpath = 'product/inventory/class';
13315 } elseif ($elementType == 'inventoryline') {
13316 $module = 'product';
13317 $classpath = 'product/inventory/class';
13318 $table_element = 'inventorydet';
13319 $parent_element = 'inventory';
13320 } elseif ($elementType == 'stock' || $elementType == 'entrepot') {
13321 $module = 'stock';
13322 $classpath = 'product/stock/class';
13323 $classfile = 'entrepot';
13324 $classname = 'Entrepot';
13325 $table_element = 'entrepot';
13326 } elseif ($elementType == 'project') {
13327 $classpath = 'projet/class';
13328 $module = 'projet';
13329 $table_element = 'projet';
13330 } elseif ($elementType == 'project_task') {
13331 $classpath = 'projet/class';
13332 $module = 'projet';
13333 $subelement = 'task';
13334 $table_element = 'projet_task';
13335 } elseif ($elementType == 'facture' || $elementType == 'invoice') {
13336 $classpath = 'compta/facture/class';
13337 $module = 'facture';
13338 $subelement = 'facture';
13339 $table_element = 'facture';
13340 } elseif ($elementType == 'facturedet') {
13341 $classpath = 'compta/facture/class';
13342 $classfile = 'facture';
13343 $classname = 'FactureLigne';
13344 $module = 'facture';
13345 $table_element = 'facturedet';
13346 $parent_element = 'facture';
13347 } elseif ($elementType == 'facturerec') {
13348 $classpath = 'compta/facture/class';
13349 $classfile = 'facture-rec';
13350 $module = 'facture';
13351 $classname = 'FactureRec';
13352 } elseif ($elementType == 'commande' || $elementType == 'order') {
13353 $classpath = 'commande/class';
13354 $module = 'commande';
13355 $subelement = 'commande';
13356 $table_element = 'commande';
13357 } elseif ($elementType == 'commandedet') {
13358 $classpath = 'commande/class';
13359 $classfile = 'commande';
13360 $classname = 'OrderLine';
13361 $module = 'commande';
13362 $table_element = 'commandedet';
13363 $parent_element = 'commande';
13364 } elseif ($elementType == 'propal') {
13365 $classpath = 'comm/propal/class';
13366 $table_element = 'propal';
13367 } elseif ($elementType == 'propaldet') {
13368 $classpath = 'comm/propal/class';
13369 $classfile = 'propal';
13370 $subelement = 'propaleligne';
13371 $module = 'propal';
13372 $table_element = 'propaldet';
13373 $parent_element = 'propal';
13374 } elseif ($elementType == 'shipping') {
13375 $classpath = 'expedition/class';
13376 $classfile = 'expedition';
13377 $classname = 'Expedition';
13378 $module = 'expedition';
13379 $table_element = 'expedition';
13380 } elseif ($elementType == 'expeditiondet' || $elementType == 'shippingdet') {
13381 $classpath = 'expedition/class';
13382 $classfile = 'expedition';
13383 $classname = 'ExpeditionLigne';
13384 $module = 'expedition';
13385 $table_element = 'expeditiondet';
13386 $parent_element = 'expedition';
13387 } elseif ($elementType == 'delivery_note') {
13388 $classpath = 'delivery/class';
13389 $subelement = 'delivery';
13390 $module = 'expedition';
13391 } elseif ($elementType == 'delivery') {
13392 $classpath = 'delivery/class';
13393 $subelement = 'delivery';
13394 $module = 'expedition';
13395 } elseif ($elementType == 'deliverydet') {
13396 // @todo
13397 } elseif ($elementType == 'supplier_proposal') {
13398 $classpath = 'supplier_proposal/class';
13399 $module = 'supplier_proposal';
13400 $element = 'supplierproposal';
13401 $classfile = 'supplier_proposal';
13402 $subelement = 'supplierproposal';
13403 } elseif ($elementType == 'supplier_proposaldet') {
13404 $classpath = 'supplier_proposal/class';
13405 $module = 'supplier_proposal';
13406 $classfile = 'supplier_proposal';
13407 $table_element = 'supplier_proposaldet';
13408 $parent_element = 'supplier_proposal';
13409 } elseif ($elementType == 'contract') {
13410 $classpath = 'contrat/class';
13411 $module = 'contrat';
13412 $subelement = 'contrat';
13413 $table_element = 'contract';
13414 } elseif ($elementType == 'contratdet') {
13415 $classpath = 'contrat/class';
13416 $module = 'contrat';
13417 $table_element = 'contratdet';
13418 $parent_element = 'contrat';
13419 } elseif ($elementType == 'mailing') {
13420 $classpath = 'comm/mailing/class';
13421 $module = 'mailing';
13422 $classfile = 'mailing';
13423 $classname = 'Mailing';
13424 $subelement = '';
13425 } elseif ($elementType == 'member' || $elementType == 'adherent') {
13426 $classpath = 'adherents/class';
13427 $module = 'adherent';
13428 $subelement = 'adherent';
13429 $table_element = 'adherent';
13430 } elseif ($elementType == 'usergroup') {
13431 $classpath = 'user/class';
13432 $module = 'user';
13433 } elseif ($elementType == 'mo') {
13434 $classpath = 'mrp/class';
13435 $classfile = 'mo';
13436 $classname = 'Mo';
13437 $module = 'mrp';
13438 $subelement = '';
13439 $table_element = 'mrp_mo';
13440 } elseif ($elementType == 'mrp_production') {
13441 $classpath = 'mrp/class';
13442 $classfile = 'mo';
13443 $classname = 'MoLine';
13444 $module = 'mrp';
13445 $subelement = '';
13446 $table_element = 'mrp_production';
13447 $parent_element = 'mo';
13448 } elseif ($elementType == 'cabinetmed_cons') {
13449 $classpath = 'cabinetmed/class';
13450 $module = 'cabinetmed';
13451 $subelement = 'cabinetmedcons';
13452 $table_element = 'cabinetmedcons';
13453 } elseif ($elementType == 'fichinter') {
13454 $classpath = 'fichinter/class';
13455 $module = 'ficheinter';
13456 $subelement = 'fichinter';
13457 $table_element = 'fichinter';
13458 } elseif ($elementType == 'dolresource' || $elementType == 'resource') {
13459 $classpath = 'resource/class';
13460 $module = 'resource';
13461 $subelement = 'dolresource';
13462 $table_element = 'resource';
13463 } elseif ($elementType == 'opensurvey_sondage') {
13464 $classpath = 'opensurvey/class';
13465 $module = 'opensurvey';
13466 $subelement = 'opensurveysondage';
13467 } elseif ($elementType == 'order_supplier' || $elementType == 'commande_fournisseur') {
13468 $classpath = 'fourn/class';
13469 $module = 'fournisseur';
13470 $classfile = 'fournisseur.commande';
13471 $element = 'order_supplier';
13472 $subelement = '';
13473 $classname = 'CommandeFournisseur';
13474 $table_element = 'commande_fournisseur';
13475 } elseif ($elementType == 'commande_fournisseurdet') {
13476 $classpath = 'fourn/class';
13477 $module = 'fournisseur';
13478 $classfile = 'fournisseur.commande';
13479 $element = 'commande_fournisseurdet';
13480 $subelement = '';
13481 $classname = 'CommandeFournisseurLigne';
13482 $table_element = 'commande_fournisseurdet';
13483 $parent_element = 'commande_fournisseur';
13484 } elseif ($elementType == 'invoice_supplier' || $elementType == 'facture_fourn') {
13485 $classpath = 'fourn/class';
13486 $module = 'fournisseur';
13487 $classfile = 'fournisseur.facture';
13488 $element = 'invoice_supplier';
13489 $subelement = '';
13490 $classname = 'FactureFournisseur';
13491 $table_element = 'facture_fourn';
13492 } elseif ($elementType == 'facture_fourn_det') {
13493 $classpath = 'fourn/class';
13494 $module = 'fournisseur';
13495 $classfile = 'fournisseur.facture';
13496 $element = 'facture_fourn_det';
13497 $subelement = '';
13498 $classname = 'SupplierInvoiceLine';
13499 $table_element = 'facture_fourn_det';
13500 $parent_element = 'invoice_supplier';
13501 } elseif ($elementType == "service") {
13502 $classpath = 'product/class';
13503 $subelement = 'product';
13504 $table_element = 'product';
13505 } elseif ($elementType == 'salary') {
13506 $classpath = 'salaries/class';
13507 $module = 'salaries';
13508 } elseif ($elementType == 'payment_salary') {
13509 $classpath = 'salaries/class';
13510 $classfile = 'paymentsalary';
13511 $classname = 'PaymentSalary';
13512 $module = 'salaries';
13513 } elseif ($elementType == 'productlot') {
13514 $module = 'productbatch';
13515 $classpath = 'product/stock/class';
13516 $classfile = 'productlot';
13517 $classname = 'Productlot';
13518 $element = 'productlot';
13519 $subelement = '';
13520 $table_element = 'product_lot';
13521 } elseif ($elementType == 'societeaccount') {
13522 $classpath = 'societe/class';
13523 $classfile = 'societeaccount';
13524 $classname = 'SocieteAccount';
13525 $module = 'societe';
13526 } elseif ($elementType == 'websitepage') {
13527 $classpath = 'website/class';
13528 $classfile = 'websitepage';
13529 $classname = 'Websitepage';
13530 $module = 'website';
13531 $subelement = 'websitepage';
13532 $table_element = 'website_page';
13533 } elseif ($elementType == 'fiscalyear') {
13534 $classpath = 'core/class';
13535 $module = 'accounting';
13536 $subelement = 'fiscalyear';
13537 } elseif ($elementType == 'chargesociales') {
13538 $classpath = 'compta/sociales/class';
13539 $module = 'tax';
13540 $table_element = 'chargesociales';
13541 } elseif ($elementType == 'tva') {
13542 $classpath = 'compta/tva/class';
13543 $module = 'tax';
13544 $subdir = '/vat';
13545 $table_element = 'tva';
13546 } elseif ($elementType == 'emailsenderprofile') {
13547 $module = '';
13548 $classpath = 'core/class';
13549 $classfile = 'emailsenderprofile';
13550 $classname = 'EmailSenderProfile';
13551 $table_element = 'c_email_senderprofile';
13552 $subelement = '';
13553 } elseif ($elementType == 'conferenceorboothattendee') {
13554 $classpath = 'eventorganization/class';
13555 $classfile = 'conferenceorboothattendee';
13556 $classname = 'ConferenceOrBoothAttendee';
13557 $module = 'eventorganization';
13558 } elseif ($elementType == 'conferenceorbooth') {
13559 $classpath = 'eventorganization/class';
13560 $classfile = 'conferenceorbooth';
13561 $classname = 'ConferenceOrBooth';
13562 $module = 'eventorganization';
13563 } elseif ($elementType == 'ccountry') {
13564 $module = '';
13565 $classpath = 'core/class';
13566 $classfile = 'ccountry';
13567 $classname = 'Ccountry';
13568 $table_element = 'c_country';
13569 $subelement = '';
13570 } elseif ($elementType == 'ecmfiles') {
13571 $module = 'ecm';
13572 $classpath = 'ecm/class';
13573 $classfile = 'ecmfiles';
13574 $classname = 'Ecmfiles';
13575 $table_element = 'ecmfiles';
13576 $subelement = '';
13577 } elseif ($elementType == 'knowledgerecord') {
13578 $module = '';
13579 $classpath = 'knowledgemanagement/class';
13580 $classfile = 'knowledgerecord';
13581 $classname = 'KnowledgeRecord';
13582 $table_element = 'knowledgemanagement_knowledgerecord';
13583 $subelement = '';
13584 }
13585
13586 if (empty($classfile)) {
13587 $classfile = strtolower($subelement);
13588 }
13589 if (empty($classname)) {
13590 $classname = ucfirst($subelement);
13591 }
13592 if (empty($classpath)) {
13593 $classpath = $module.'/class';
13594 }
13595
13596 //print 'getElementProperties subdir='.$subdir;
13597
13598 // Set dir_output
13599 if ($module && isset($conf->$module)) { // The generic case
13600 if (!empty($conf->$module->multidir_output[$conf->entity])) {
13601 $dir_output = $conf->$module->multidir_output[$conf->entity];
13602 } elseif (!empty($conf->$module->output[$conf->entity])) {
13603 $dir_output = $conf->$module->output[$conf->entity];
13604 } elseif (!empty($conf->$module->dir_output)) {
13605 $dir_output = $conf->$module->dir_output;
13606 }
13607 if (!empty($conf->$module->multidir_temp[$conf->entity])) {
13608 $dir_temp = $conf->$module->multidir_temp[$conf->entity];
13609 } elseif (!empty($conf->$module->temp[$conf->entity])) {
13610 $dir_temp = $conf->$module->temp[$conf->entity];
13611 } elseif (!empty($conf->$module->dir_temp)) {
13612 $dir_temp = $conf->$module->dir_temp;
13613 }
13614 }
13615
13616 // Overwrite value for special cases
13617 if ($element == 'order_supplier') {
13618 $dir_output = $conf->fournisseur->commande->dir_output;
13619 $dir_temp = $conf->fournisseur->commande->dir_temp;
13620 } elseif ($element == 'invoice_supplier') {
13621 $dir_output = $conf->fournisseur->facture->dir_output;
13622 $dir_temp = $conf->fournisseur->facture->dir_temp;
13623 }
13624 $dir_output .= $subdir;
13625 $dir_temp .= $subdir;
13626
13627 $elementProperties = array(
13628 'module' => $module,
13629 'element' => $element,
13630 'table_element' => $table_element,
13631 'subelement' => $subelement,
13632 'classpath' => $classpath,
13633 'classfile' => $classfile,
13634 'classname' => $classname,
13635 'dir_output' => $dir_output,
13636 'dir_temp' => $dir_temp,
13637 'parent_element' => $parent_element,
13638 );
13639
13640
13641 // Add hook
13642 if (!is_object($hookmanager)) {
13643 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
13644 $hookmanager = new HookManager($db);
13645 }
13646 $hookmanager->initHooks(array('elementproperties'));
13647
13648
13649 // Hook params
13650 $parameters = array(
13651 'elementType' => $elementType,
13652 'elementProperties' => $elementProperties
13653 );
13654
13655 $reshook = $hookmanager->executeHooks('getElementProperties', $parameters);
13656
13657 if ($reshook) {
13658 $elementProperties = $hookmanager->resArray;
13659 } elseif (!empty($hookmanager->resArray) && is_array($hookmanager->resArray)) { // resArray is always an array but for sécurity against misconfigured external modules
13660 $elementProperties = array_replace($elementProperties, $hookmanager->resArray);
13661 }
13662
13663 // context of elementproperties doesn't need to exist out of this function so delete it to avoid elementproperties context is equal to all
13664 if (($key = array_search('elementproperties', $hookmanager->contextarray)) !== false) {
13665 unset($hookmanager->contextarray[$key]);
13666 }
13667
13668 return $elementProperties;
13669}
13670
13683function fetchObjectByElement($element_id, $element_type, $element_ref = '', $useCache = 0, $maxCacheByType = 10)
13684{
13685 global $db, $conf;
13686
13687 $ret = 0;
13688
13689 $element_prop = getElementProperties($element_type);
13690
13691 if ($element_prop['module'] == 'product' || $element_prop['module'] == 'service') {
13692 // For example, for an extrafield 'product' (shared for both product and service) that is a link to an object,
13693 // this is called with $element_type = 'product' when we need element properties of a service, we must return a product. If we create the
13694 // extrafield for a service, it is not supported and not found when editing the product/service card. So we must keep 'product' for extrafields
13695 // of service and we will return properties of a product.
13696 $ismodenabled = (isModEnabled('product') || isModEnabled('service'));
13697 } elseif ($element_prop['module'] == 'societeaccount') {
13698 $ismodenabled = isModEnabled('website') || isModEnabled('webportal');
13699 } else {
13700 $ismodenabled = isModEnabled($element_prop['module']);
13701 }
13702 //var_dump('element_type='.$element_type);
13703 //var_dump($element_prop);
13704 //var_dump($element_prop['module'].' '.$ismodenabled);
13705 if (is_array($element_prop) && (empty($element_prop['module']) || $ismodenabled)) {
13706 if ($useCache === 1
13707 && !empty($conf->cache['fetchObjectByElement'][$element_type])
13708 && !empty($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13709 && is_object($conf->cache['fetchObjectByElement'][$element_type][$element_id])
13710 ) {
13711 return $conf->cache['fetchObjectByElement'][$element_type][$element_id];
13712 }
13713
13714 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13715
13716 if (class_exists($element_prop['classname'])) {
13717 $className = $element_prop['classname'];
13718 $objecttmp = new $className($db);
13719 '@phan-var-force CommonObject $objecttmp';
13720
13721 if ($element_id > 0 || !empty($element_ref)) {
13722 $ret = $objecttmp->fetch($element_id, $element_ref);
13723 if ($ret >= 0) {
13724 if (empty($objecttmp->module)) {
13725 $objecttmp->module = $element_prop['module'];
13726 }
13727
13728 if ($useCache > 0) {
13729 if (!isset($conf->cache['fetchObjectByElement'][$element_type])) {
13730 $conf->cache['fetchObjectByElement'][$element_type] = [];
13731 }
13732
13733 // Manage cache limit
13734 if (! empty($conf->cache['fetchObjectByElement'][$element_type]) && is_array($conf->cache['fetchObjectByElement'][$element_type]) && count($conf->cache['fetchObjectByElement'][$element_type]) >= $maxCacheByType) {
13735 array_shift($conf->cache['fetchObjectByElement'][$element_type]);
13736 }
13737
13738 $conf->cache['fetchObjectByElement'][$element_type][$element_id] = $objecttmp;
13739 }
13740
13741 return $objecttmp;
13742 }
13743 } else {
13744 return $objecttmp; // returned an object without fetch
13745 }
13746 } else {
13747 dol_syslog($element_prop['classname'].' doesn\'t exists in /'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
13748 return -1;
13749 }
13750 }
13751
13752 return $ret;
13753}
13754
13761function isAFileWithExecutableContent($filename)
13762{
13763 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)) {
13764 return true;
13765 }
13766
13767 return false;
13768}
13769
13777function newToken()
13778{
13779 return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
13780}
13781
13789function currentToken()
13790{
13791 return isset($_SESSION['token']) ? $_SESSION['token'] : '';
13792}
13793
13799function getNonce()
13800{
13801 global $conf;
13802
13803 if (empty($conf->cache['nonce'])) {
13804 $conf->cache['nonce'] = dolGetRandomBytes(8);
13805 }
13806
13807 return $conf->cache['nonce'];
13808}
13809
13810
13824function startSimpleTable($header, $link = "", $arguments = "", $emptyColumns = 0, $number = -1, $pictofulllist = '')
13825{
13826 global $langs;
13827
13828 print '<div class="div-table-responsive-no-min">';
13829 print '<table class="noborder centpercent">';
13830 print '<tr class="liste_titre">';
13831
13832 print ($emptyColumns < 1) ? '<th>' : '<th colspan="'.($emptyColumns + 1).'">';
13833
13834 print '<span class="valignmiddle">'.$langs->trans($header).'</span>';
13835
13836 if (!empty($link)) {
13837 if (!empty($arguments)) {
13838 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
13839 } else {
13840 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
13841 }
13842 }
13843
13844 if ($number > -1) {
13845 print '<span class="badge marginleftonlyshort">'.$number.'</span>';
13846 } elseif (!empty($link)) {
13847 print '<span class="badge marginleftonlyshort">...</span>';
13848 }
13849
13850 if (!empty($link)) {
13851 print '</a>';
13852 }
13853
13854 print '</th>';
13855
13856 if ($number < 0 && !empty($link)) {
13857 print '<th class="right">';
13858 print '</th>';
13859 }
13860
13861 print '</tr>';
13862}
13863
13872function finishSimpleTable($addLineBreak = false)
13873{
13874 print '</table>';
13875 print '</div>';
13876
13877 if ($addLineBreak) {
13878 print '<br>';
13879 }
13880}
13881
13893function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
13894{
13895 global $langs;
13896
13897 if ($num === 0) {
13898 print '<tr class="oddeven">';
13899 print '<td colspan="'.$tableColumnCount.'"><span class="opacitymedium">'.$langs->trans($noneWord).'</span></td>';
13900 print '</tr>';
13901 return;
13902 }
13903
13904 if ($nbofloop === 0) {
13905 // don't show a summary line
13906 return;
13907 }
13908
13909 /* Case already handled above, commented to satisfy phpstan.
13910 if ($num === 0) {
13911 $colspan = $tableColumnCount;
13912 } else
13913 */
13914 if ($num > $nbofloop) {
13915 $colspan = $tableColumnCount;
13916 } else {
13917 $colspan = $tableColumnCount - 1;
13918 }
13919
13920 if ($extraRightColumn) {
13921 $colspan--;
13922 }
13923
13924 print '<tr class="liste_total">';
13925
13926 if ($nbofloop > 0 && $num > $nbofloop) {
13927 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
13928 } else {
13929 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
13930 print '<td class="right centpercent">'.price($total).'</td>';
13931 }
13932
13933 if ($extraRightColumn) {
13934 print '<td></td>';
13935 }
13936
13937 print '</tr>';
13938}
13939
13948function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
13949{
13950 if ($method == -1) {
13951 $method = 0;
13952 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_FREAD')) {
13953 $method = 1;
13954 }
13955 if (getDolGlobalString('MAIN_FORCE_READFILE_WITH_STREAM_COPY')) {
13956 $method = 2;
13957 }
13958 }
13959
13960 // Be sure we don't have output buffering enabled to have readfile working correctly
13961 while (ob_get_level()) {
13962 ob_end_flush();
13963 }
13964
13965 // Solution 0
13966 if ($method == 0) {
13967 readfile($fullpath_original_file_osencoded);
13968 } elseif ($method == 1) {
13969 // Solution 1
13970 $handle = fopen($fullpath_original_file_osencoded, "rb");
13971 while (!feof($handle)) {
13972 print fread($handle, 8192);
13973 }
13974 fclose($handle);
13975 } elseif ($method == 2) {
13976 // Solution 2
13977 $handle1 = fopen($fullpath_original_file_osencoded, "rb");
13978 $handle2 = fopen("php://output", "wb");
13979 stream_copy_to_stream($handle1, $handle2);
13980 fclose($handle1);
13981 fclose($handle2);
13982 }
13983}
13984
13994function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
13995{
13996 global $langs;
13997
13998 $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
13999
14000 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'">';
14001 if ($texttoshow === 'none') {
14002 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14003 $result .= '<span class="clipboardCPValueToPrint"></span>';
14004 } elseif ($texttoshow) {
14005 $result .= '<'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14006 $result .= '<span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span>';
14007 } else {
14008 $result .= '<'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'>';
14009 }
14010 $result .= '<span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft pictomodule" title="'.dolPrintHTML($langs->trans("ClickToCopyToClipboard")).'"></span>';
14011 $result .= img_picto('', 'tick', 'class="clipboardCPTick hidden paddingleft pictomodule"');
14012 $result .= '<span class="clipboardCPText"></span>';
14013 $result .= '</span>';
14014
14015 return $result;
14016}
14017
14018
14025function jsonOrUnserialize($stringtodecode)
14026{
14027 $result = json_decode($stringtodecode);
14028 if ($result === null) {
14029 $result = unserialize($stringtodecode);
14030 }
14031
14032 return $result;
14033}
14034
14035
14052function forgeSQLFromUniversalSearchCriteria($filter, &$errorstr = '', $noand = 0, $nopar = 0, $noerror = 0)
14053{
14054 global $db, $user;
14055
14056 if (is_null($filter) || !is_string($filter) || $filter === '') {
14057 return '';
14058 }
14059 if (!preg_match('/^\‍(.*\‍)$/', $filter)) { // If $filter does not start and end with ()
14060 $filter = '(' . $filter . ')';
14061 }
14062
14063 $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'
14064 $firstandlastparenthesis = 0;
14065
14066 if (!dolCheckFilters($filter, $errorstr, $firstandlastparenthesis)) {
14067 if ($noerror) {
14068 return '1 = 2';
14069 } else {
14070 return 'Filter syntax error - '.$errorstr; // Bad balance of parenthesis, we return an error message or force a SQL not found
14071 }
14072 }
14073
14074 // Test the filter syntax
14075 $t = preg_replace_callback('/'.$regexstring.'/i', 'dolForgeDummyCriteriaCallback', $filter);
14076 $t = str_ireplace(array('and', 'or', ' '), '', $t); // Remove the only strings allowed between each () criteria
14077 // If the string result contains something else than '()', the syntax was wrong
14078
14079 if (preg_match('/[^\‍(\‍)]/', $t)) {
14080 $tmperrorstr = 'Bad syntax of the search string';
14081 $errorstr = 'Bad syntax of the search string: '.$filter;
14082 if ($noerror) {
14083 return '1 = 2';
14084 } else {
14085 dol_syslog("forgeSQLFromUniversalSearchCriteria Filter error - ".$errorstr, LOG_WARNING);
14086 return 'Filter error - '.$tmperrorstr; // Bad syntax of the search string, we return an error message or force a SQL not found
14087 }
14088 }
14089
14090 $ret = ($noand ? "" : " AND ").($nopar ? "" : '(').preg_replace_callback('/'.$regexstring.'/i', 'dolForgeSQLCriteriaCallback', $filter).($nopar ? "" : ')');
14091
14092 if (is_object($db)) {
14093 $ret = str_replace('__NOW__', "'".$db->idate(dol_now())."'", $ret);
14094 }
14095 if (is_object($user)) {
14096 $ret = str_replace('__USER_ID__', (string) $user->id, $ret);
14097 }
14098
14099 return $ret;
14100}
14101
14109function dolForgeExplodeAnd($sqlfilters)
14110{
14111 $arrayofandtags = array();
14112 $nbofchars = dol_strlen($sqlfilters);
14113
14114 $error = '';
14115 $parenthesislevel = 0;
14116 $result = dolCheckFilters($sqlfilters, $error, $parenthesislevel);
14117 if (!$result) {
14118 return array();
14119 }
14120 if ($parenthesislevel >= 1) {
14121 $sqlfilters = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $sqlfilters));
14122 }
14123
14124 $i = 0;
14125 $s = '';
14126 $countparenthesis = 0;
14127 while ($i < $nbofchars) {
14128 $char = dol_substr($sqlfilters, $i, 1);
14129
14130 if ($char == '(') {
14131 $countparenthesis++;
14132 } elseif ($char == ')') {
14133 $countparenthesis--;
14134 }
14135
14136 if ($countparenthesis == 0) {
14137 $char2 = dol_substr($sqlfilters, $i + 1, 1);
14138 $char3 = dol_substr($sqlfilters, $i + 2, 1);
14139 if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
14140 // We found a AND
14141 $s = trim($s);
14142 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14143 $s = '('.$s.')';
14144 }
14145 $arrayofandtags[] = $s;
14146 $s = '';
14147 $i += 2;
14148 } else {
14149 $s .= $char;
14150 }
14151 } else {
14152 $s .= $char;
14153 }
14154 $i++;
14155 }
14156 if ($s) {
14157 $s = trim($s);
14158 if (!preg_match('/^\‍(.*\‍)$/', $s)) {
14159 $s = '('.$s.')';
14160 }
14161 $arrayofandtags[] = $s;
14162 }
14163
14164 return $arrayofandtags;
14165}
14166
14176function dolCheckFilters($sqlfilters, &$error = '', &$parenthesislevel = 0)
14177{
14178 //$regexstring='\‍(([^:\'\‍(\‍)]+:[^:\'\‍(\‍)]+:[^:\‍(\‍)]+)\‍)';
14179 //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
14180 $tmp = $sqlfilters;
14181
14182 $nb = dol_strlen($tmp);
14183 $counter = 0;
14184 $parenthesislevel = 0;
14185
14186 $error = '';
14187
14188 $i = 0;
14189 while ($i < $nb) {
14190 $char = dol_substr($tmp, $i, 1);
14191
14192 if ($char == '(') {
14193 if ($i == $parenthesislevel && $parenthesislevel == $counter) {
14194 // We open a parenthesis and it is the first char
14195 $parenthesislevel++;
14196 }
14197 $counter++;
14198 } elseif ($char == ')') {
14199 $nbcharremaining = ($nb - $i - 1);
14200 if ($nbcharremaining >= $counter) {
14201 $parenthesislevel = min($parenthesislevel, $counter - 1);
14202 }
14203 if ($parenthesislevel > $counter && $nbcharremaining >= $counter) {
14204 $parenthesislevel = $counter;
14205 }
14206 $counter--;
14207 }
14208
14209 if ($counter < 0) {
14210 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14211 $parenthesislevel = 0;
14212 dol_syslog($error, LOG_WARNING);
14213 return false;
14214 }
14215
14216 $i++;
14217 }
14218
14219 if ($counter > 0) {
14220 $error = "Wrong balance of parenthesis in sqlfilters=".$sqlfilters;
14221 $parenthesislevel = 0;
14222 dol_syslog($error, LOG_WARNING);
14223 return false;
14224 }
14225
14226 return true;
14227}
14228
14236function dolForgeDummyCriteriaCallback($matches)
14237{
14238 //dol_syslog("Convert matches ".$matches[1]);
14239 if (empty($matches[1])) {
14240 return '';
14241 }
14242 $tmp = explode(':', $matches[1]);
14243 if (count($tmp) < 3) {
14244 return '';
14245 }
14246
14247 return '()'; // An empty criteria
14248}
14249
14258function dolForgeSQLCriteriaCallback($matches)
14259{
14260 global $db;
14261
14262 //dol_syslog("Convert matches ".$matches[1]);
14263 if (empty($matches[1])) {
14264 return '';
14265 }
14266 $tmp = explode(':', $matches[1], 3);
14267 if (count($tmp) < 3) {
14268 return '';
14269 }
14270
14271 $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
14272
14273 $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
14274
14275 $realOperator = [
14276 'NOTLIKE' => 'NOT LIKE',
14277 'ISNOT' => 'IS NOT',
14278 'NOTIN' => 'NOT IN',
14279 '!=' => '<>',
14280 ];
14281
14282 if (array_key_exists($operator, $realOperator)) {
14283 $operator = $realOperator[$operator];
14284 }
14285
14286 $tmpescaped = $tmp[2];
14287
14288 //print "Case: ".$operator." ".$operand." ".$tmpescaped."\n";
14289
14290 $regbis = array();
14291
14292 if ($operator == 'IN' || $operator == 'NOT IN') { // IN is allowed for list of ID or code only
14293 //if (!preg_match('/^\‍(.*\‍)$/', $tmpescaped)) {
14294 $tmpescaped2 = '(';
14295 // Explode and sanitize each element in list
14296 $tmpelemarray = explode(',', $tmpescaped);
14297 foreach ($tmpelemarray as $tmpkey => $tmpelem) {
14298 $reg = array();
14299 $tmpelem = trim($tmpelem);
14300 if (preg_match('/^\'(.*)\'$/', $tmpelem, $reg)) {
14301 $tmpelemarray[$tmpkey] = "'".$db->escape($db->sanitize($reg[1], 2, 1, 1, 1))."'";
14302 } else {
14303 // TODO Replace with $tmpelemarray[$tmpkey] = (float) $tmpelem;
14304 // or allow subrequests.
14305 $tmpelemarray[$tmpkey] = $db->escape($db->sanitize($tmpelem, 1, 1, 1, 1));
14306 }
14307 }
14308 $tmpescaped2 .= implode(',', $tmpelemarray);
14309 $tmpescaped2 .= ')';
14310
14311 $tmpescaped = $tmpescaped2;
14312 } elseif ($operator == 'LIKE' || $operator == 'NOT LIKE') {
14313 if (preg_match('/^\'([^\']*)\'$/', $tmpescaped, $regbis)) {
14314 $tmpescaped = $regbis[1];
14315 }
14316 //$tmpescaped = "'".$db->escape($db->escapeforlike($regbis[1]))."'";
14317 $tmpescaped = "'".$db->escape($tmpescaped)."'"; // We do not escape the _ and % so the LIKE will work as expected
14318 } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
14319 // TODO Retrieve type of field for $operand field name.
14320 // So we can complete format. For example we could complete a year with month and day.
14321 $tmpescaped = "'".$db->escape($regbis[1])."'";
14322 } else {
14323 if (strtoupper($tmpescaped) == 'NULL') {
14324 $tmpescaped = 'NULL';
14325 } elseif (ctype_digit((string) $tmpescaped)) { // if only 0-9 chars, no .
14326 $tmpescaped = (int) $tmpescaped;
14327 } elseif (is_numeric((string) $tmpescaped)) { // it can be a float with a .
14328 $tmpescaped = (float) $tmpescaped;
14329 } else {
14330 $tmpescaped = preg_replace('/[^a-z0-9_]/i', '', $tmpescaped); // it can be a name of field or a substitution variable like '__NOW__'
14331 }
14332 }
14333
14334 return '('.$db->escape($operand).' '.strtoupper($operator).' '.$tmpescaped.')';
14335}
14336
14337
14347function getTimelineIcon($actionstatic, &$histo, $key)
14348{
14349 global $langs;
14350
14351 $out = '<!-- timeline icon -->'."\n";
14352 $iconClass = 'fa fa-comments';
14353 $img_picto = '';
14354 $colorClass = '';
14355 $pictoTitle = '';
14356
14357 if ($histo[$key]['percent'] == -1) {
14358 $colorClass = 'timeline-icon-not-applicble';
14359 $pictoTitle = $langs->trans('StatusNotApplicable');
14360 } elseif ($histo[$key]['percent'] == 0) {
14361 $colorClass = 'timeline-icon-todo';
14362 $pictoTitle = $langs->trans('StatusActionToDo').' (0%)';
14363 } elseif ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100) {
14364 $colorClass = 'timeline-icon-in-progress';
14365 $pictoTitle = $langs->trans('StatusActionInProcess').' ('.$histo[$key]['percent'].'%)';
14366 } elseif ($histo[$key]['percent'] >= 100) {
14367 $colorClass = 'timeline-icon-done';
14368 $pictoTitle = $langs->trans('StatusActionDone').' (100%)';
14369 }
14370
14371 if ($actionstatic->code == 'AC_TICKET_CREATE') {
14372 $iconClass = 'fa fa-ticket';
14373 } elseif ($actionstatic->code == 'AC_TICKET_MODIFY') {
14374 $iconClass = 'fa fa-pencilxxx';
14375 } elseif (preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14376 $iconClass = 'fa fa-comments';
14377 } elseif (preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
14378 $iconClass = 'fa fa-mask';
14379 } elseif (getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14380 if ($actionstatic->type_picto) {
14381 $img_picto = img_picto('', $actionstatic->type_picto);
14382 } else {
14383 if ($actionstatic->type_code == 'AC_RDV') {
14384 $iconClass = 'fa fa-handshake';
14385 } elseif ($actionstatic->type_code == 'AC_TEL') {
14386 $iconClass = 'fa fa-phone';
14387 } elseif ($actionstatic->type_code == 'AC_FAX') {
14388 $iconClass = 'fa fa-fax';
14389 } elseif ($actionstatic->type_code == 'AC_EMAIL') {
14390 $iconClass = 'fa fa-envelope';
14391 } elseif ($actionstatic->type_code == 'AC_INT') {
14392 $iconClass = 'fa fa-shipping-fast';
14393 } elseif ($actionstatic->type_code == 'AC_OTH_AUTO') {
14394 $iconClass = 'fa fa-robot';
14395 } elseif (!preg_match('/_AUTO/', $actionstatic->type_code)) {
14396 $iconClass = 'fa fa-robot';
14397 }
14398 }
14399 }
14400
14401 $out .= '<i class="'.$iconClass.' '.$colorClass.'" title="'.$pictoTitle.'">'.$img_picto.'</i>'."\n";
14402 return $out;
14403}
14404
14412{
14413 global $conf, $db;
14414
14415 $documents = array();
14416
14417 $sql = 'SELECT ecm.rowid as id, ecm.src_object_type, ecm.src_object_id, ecm.filepath, ecm.filename';
14418 $sql .= ' FROM '.MAIN_DB_PREFIX.'ecm_files ecm';
14419 $sql .= " WHERE ecm.filepath = 'agenda/".((int) $object->id)."'";
14420 //$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
14421 $sql .= ' ORDER BY ecm.position ASC';
14422
14423 $resql = $db->query($sql);
14424 if ($resql) {
14425 if ($db->num_rows($resql)) {
14426 while ($obj = $db->fetch_object($resql)) {
14427 $documents[$obj->id] = $obj;
14428 }
14429 }
14430 }
14431
14432 return $documents;
14433}
14434
14435
14453function show_actions_messaging($conf, $langs, $db, $filterobj, $objcon = null, $noprint = 0, $actioncode = '', $donetodo = 'done', $filters = array(), $sortfield = 'a.datep,a.id', $sortorder = 'DESC')
14454{
14455 global $user, $conf;
14456 global $form;
14457
14458 global $param, $massactionbutton;
14459
14460 require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php';
14461
14462 // Check parameters
14463 if (!is_object($filterobj) && !is_object($objcon)) {
14464 dol_print_error(null, 'BadParameter');
14465 }
14466
14467 $histo = array();
14468 '@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';
14469
14470 $numaction = 0;
14471 $now = dol_now();
14472
14473 $sortfield_list = explode(',', $sortfield);
14474 $sortfield_label_list = array('a.id' => 'id', 'a.datep' => 'dp', 'a.percent' => 'percent');
14475 $sortfield_new_list = array();
14476 foreach ($sortfield_list as $sortfield_value) {
14477 $sortfield_new_list[] = $sortfield_label_list[trim($sortfield_value)];
14478 }
14479 $sortfield_new = implode(',', $sortfield_new_list);
14480
14481 $sql = null;
14482 $sql2 = null;
14483
14484 if (isModEnabled('agenda')) {
14485 // Search histo on actioncomm
14486 if (is_object($objcon) && $objcon->id > 0) {
14487 $sql = "SELECT DISTINCT a.id, a.label as label,";
14488 } else {
14489 $sql = "SELECT a.id, a.label as label,";
14490 }
14491 $sql .= " a.datep as dp,";
14492 $sql .= " a.note as message,";
14493 $sql .= " a.datep2 as dp2,";
14494 $sql .= " a.percent as percent, 'action' as type,";
14495 $sql .= " a.fk_element, a.elementtype,";
14496 $sql .= " a.fk_contact,";
14497 $sql .= " a.email_from as msg_from,";
14498 $sql .= " c.code as acode, c.libelle as alabel, c.picto as apicto,";
14499 $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";
14500 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14501 $sql .= ", sp.lastname, sp.firstname";
14502 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14503 $sql .= ", m.lastname, m.firstname";
14504 } elseif (is_object($filterobj) && in_array(get_class($filterobj), array('Commande', 'CommandeFournisseur', 'Product', 'Ticket', 'BOM', 'Contrat', 'Facture', 'FactureFournisseur'))) {
14505 $sql .= ", o.ref";
14506 }
14507 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
14508 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as u on u.rowid = a.fk_user_action";
14509 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_actioncomm as c ON a.fk_action = c.id";
14510
14511 $force_filter_contact = $filterobj instanceof User;
14512
14513 if (is_object($objcon) && $objcon->id > 0) {
14514 $force_filter_contact = true;
14515 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."actioncomm_resources as r ON a.id = r.fk_actioncomm";
14516 $sql .= " AND r.element_type = '".$db->escape($objcon->table_element)."' AND r.fk_element = ".((int) $objcon->id);
14517 }
14518
14519 if ((is_object($filterobj) && get_class($filterobj) == 'Societe') || (is_object($filterobj) && get_class($filterobj) == 'Contact')) {
14520 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as sp ON a.fk_contact = sp.rowid";
14521 } elseif (is_object($filterobj) && get_class($filterobj) == 'Dolresource') {
14522 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."element_resources as er";
14523 $sql .= " ON er.resource_type = 'dolresource'";
14524 $sql .= " AND er.element_id = a.id";
14525 $sql .= " AND er.resource_id = ".((int) $filterobj->id);
14526 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14527 $sql .= ", ".MAIN_DB_PREFIX."adherent as m";
14528 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14529 $sql .= ", ".MAIN_DB_PREFIX."commande_fournisseur as o";
14530 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14531 $sql .= ", ".MAIN_DB_PREFIX."product as o";
14532 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14533 $sql .= ", ".MAIN_DB_PREFIX."ticket as o";
14534 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14535 $sql .= ", ".MAIN_DB_PREFIX."bom_bom as o";
14536 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14537 $sql .= ", ".MAIN_DB_PREFIX."contrat as o";
14538 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14539 $sql .= ", ".MAIN_DB_PREFIX."facture as o";
14540 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14541 $sql .= ", ".MAIN_DB_PREFIX."facture_fourn as o";
14542 }
14543
14544 $sql .= " WHERE a.entity IN (".getEntity('agenda').")";
14545 if (!$force_filter_contact) {
14546 if (is_object($filterobj) && in_array(get_class($filterobj), array('Societe', 'Client', 'Fournisseur')) && $filterobj->id) {
14547 $sql .= " AND a.fk_soc = ".((int) $filterobj->id);
14548 } elseif (is_object($filterobj) && get_class($filterobj) == 'Project' && $filterobj->id) {
14549 $sql .= " AND a.fk_project = ".((int) $filterobj->id);
14550 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14551 $sql .= " AND a.fk_element = m.rowid AND a.elementtype = 'member'";
14552 if ($filterobj->id) {
14553 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14554 }
14555 } elseif (is_object($filterobj) && get_class($filterobj) == 'Commande') {
14556 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order'";
14557 if ($filterobj->id) {
14558 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14559 }
14560 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14561 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'order_supplier'";
14562 if ($filterobj->id) {
14563 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14564 }
14565 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14566 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'product'";
14567 if ($filterobj->id) {
14568 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14569 }
14570 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14571 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'ticket'";
14572 if ($filterobj->id) {
14573 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14574 }
14575 } elseif (is_object($filterobj) && get_class($filterobj) == 'BOM') {
14576 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'bom'";
14577 if ($filterobj->id) {
14578 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14579 }
14580 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contrat') {
14581 $sql .= " AND a.fk_element = o.rowid AND a.elementtype = 'contract'";
14582 if ($filterobj->id) {
14583 $sql .= " AND a.fk_element = ".((int) $filterobj->id);
14584 }
14585 } elseif (is_object($filterobj) && get_class($filterobj) == 'Contact' && $filterobj->id) {
14586 $sql .= " AND a.fk_contact = sp.rowid";
14587 if ($filterobj->id) {
14588 $sql .= " AND a.fk_contact = ".((int) $filterobj->id);
14589 }
14590 } elseif (is_object($filterobj) && get_class($filterobj) == 'Facture') {
14591 $sql .= " AND a.fk_element = o.rowid";
14592 if ($filterobj->id) {
14593 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice'";
14594 }
14595 } elseif (is_object($filterobj) && get_class($filterobj) == 'FactureFournisseur') {
14596 $sql .= " AND a.fk_element = o.rowid";
14597 if ($filterobj->id) {
14598 $sql .= " AND a.fk_element = ".((int) $filterobj->id)." AND a.elementtype = 'invoice_supplier'";
14599 }
14600 }
14601 } else {
14602 $sql .= " AND u.rowid = ". ((int) $filterobj->id);
14603 }
14604
14605 // Condition on actioncode
14606 if (!empty($actioncode) && $actioncode != '-1') {
14607 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE')) {
14608 if ($actioncode == 'AC_NON_AUTO') {
14609 $sql .= " AND c.type != 'systemauto'";
14610 } elseif ($actioncode == 'AC_ALL_AUTO') {
14611 $sql .= " AND c.type = 'systemauto'";
14612 } else {
14613 if ($actioncode == 'AC_OTH') {
14614 $sql .= " AND c.type != 'systemauto'";
14615 } elseif ($actioncode == 'AC_OTH_AUTO') {
14616 $sql .= " AND c.type = 'systemauto'";
14617 }
14618 }
14619 } else {
14620 if ($actioncode == 'AC_NON_AUTO') {
14621 $sql .= " AND c.type != 'systemauto'";
14622 } elseif ($actioncode == 'AC_ALL_AUTO') {
14623 $sql .= " AND c.type = 'systemauto'";
14624 } else {
14625 $sql .= " AND c.code = '".$db->escape($actioncode)."'";
14626 }
14627 }
14628 }
14629 if ($donetodo == 'todo') {
14630 $sql .= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14631 } elseif ($donetodo == 'done') {
14632 $sql .= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14633 }
14634 if (is_array($filters) && $filters['search_agenda_label']) {
14635 $sql .= natural_search('a.label', $filters['search_agenda_label']);
14636 }
14637 }
14638
14639 // Add also event from emailings. TODO This should be replaced by an automatic event ? May be it's too much for very large emailing.
14640 if (isModEnabled('mailing') && !empty($objcon->email)
14641 && (empty($actioncode) || $actioncode == 'AC_OTH_AUTO' || $actioncode == 'AC_EMAILING')) {
14642 $langs->load("mails");
14643
14644 $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";
14645 $sql2 .= ", null as fk_element, '' as elementtype, null as contact_id";
14646 $sql2 .= ", 'AC_EMAILING' as acode, '' as alabel, '' as apicto";
14647 $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
14648 if (is_object($filterobj) && get_class($filterobj) == 'Societe') {
14649 $sql2 .= ", '' as lastname, '' as firstname";
14650 } elseif (is_object($filterobj) && get_class($filterobj) == 'Adherent') {
14651 $sql2 .= ", '' as lastname, '' as firstname";
14652 } elseif (is_object($filterobj) && get_class($filterobj) == 'CommandeFournisseur') {
14653 $sql2 .= ", '' as ref";
14654 } elseif (is_object($filterobj) && get_class($filterobj) == 'Product') {
14655 $sql2 .= ", '' as ref";
14656 } elseif (is_object($filterobj) && get_class($filterobj) == 'Ticket') {
14657 $sql2 .= ", '' as ref";
14658 }
14659 $sql2 .= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc, ".MAIN_DB_PREFIX."user as u";
14660 $sql2 .= " WHERE mc.email = '".$db->escape($objcon->email)."'"; // Search is done on email.
14661 $sql2 .= " AND mc.statut = 1";
14662 $sql2 .= " AND u.rowid = m.fk_user_valid";
14663 $sql2 .= " AND mc.fk_mailing=m.rowid";
14664 }
14665
14666 if ($sql || $sql2) { // May not be defined if module Agenda is not enabled and mailing module disabled too
14667 if (!empty($sql) && !empty($sql2)) {
14668 $sql = $sql." UNION ".$sql2;
14669 } elseif (empty($sql) && !empty($sql2)) {
14670 $sql = $sql2;
14671 }
14672
14673 //TODO Add navigation with this limits...
14674 $offset = 0;
14675 $limit = 1000;
14676
14677 // Complete request and execute it with limit
14678 $sql .= $db->order($sortfield_new, $sortorder);
14679 if ($limit) {
14680 $sql .= $db->plimit($limit + 1, $offset);
14681 }
14682
14683 dol_syslog("function.lib::show_actions_messaging", LOG_DEBUG);
14684 $resql = $db->query($sql);
14685 if ($resql) {
14686 $i = 0;
14687 $num = $db->num_rows($resql);
14688
14689 $imaxinloop = ($limit ? min($num, $limit) : $num);
14690 while ($i < $imaxinloop) {
14691 $obj = $db->fetch_object($resql);
14692
14693 if ($obj->type == 'action') {
14694 $contactaction = new ActionComm($db);
14695 $contactaction->id = $obj->id;
14696 $result = $contactaction->fetchResources();
14697 if ($result < 0) {
14698 dol_print_error($db);
14699 setEventMessage("actions.lib::show_actions_messaging Error fetch resource", 'errors');
14700 }
14701
14702 //if ($donetodo == 'todo') $sql.= " AND ((a.percent >= 0 AND a.percent < 100) OR (a.percent = -1 AND a.datep > '".$db->idate($now)."'))";
14703 //elseif ($donetodo == 'done') $sql.= " AND (a.percent = 100 OR (a.percent = -1 AND a.datep <= '".$db->idate($now)."'))";
14704 $tododone = '';
14705 if (($obj->percent >= 0 and $obj->percent < 100) || ($obj->percent == -1 && $obj->dp > $now)) {
14706 $tododone = 'todo';
14707 }
14708
14709 $histo[$numaction] = array(
14710 'type' => $obj->type,
14711 'tododone' => $tododone,
14712 'id' => $obj->id,
14713 'datestart' => $db->jdate($obj->dp),
14714 'dateend' => $db->jdate($obj->dp2),
14715 'note' => $obj->label,
14716 'message' => dol_htmlentitiesbr($obj->message),
14717 'percent' => $obj->percent,
14718
14719 'userid' => $obj->user_id,
14720 'login' => $obj->user_login,
14721 'userfirstname' => $obj->user_firstname,
14722 'userlastname' => $obj->user_lastname,
14723 'userphoto' => $obj->user_photo,
14724 'msg_from' => $obj->msg_from,
14725
14726 'contact_id' => $obj->fk_contact,
14727 'socpeopleassigned' => $contactaction->socpeopleassigned,
14728 'lastname' => (empty($obj->lastname) ? '' : $obj->lastname),
14729 'firstname' => (empty($obj->firstname) ? '' : $obj->firstname),
14730 'fk_element' => $obj->fk_element,
14731 'elementtype' => $obj->elementtype,
14732 // Type of event
14733 'acode' => $obj->acode,
14734 'alabel' => $obj->alabel,
14735 'libelle' => $obj->alabel, // deprecated
14736 'apicto' => $obj->apicto
14737 );
14738 } else {
14739 $histo[$numaction] = array(
14740 'type' => $obj->type,
14741 'tododone' => 'done',
14742 'id' => $obj->id,
14743 'datestart' => $db->jdate($obj->dp),
14744 'dateend' => $db->jdate($obj->dp2),
14745 'note' => $obj->label,
14746 'message' => dol_htmlentitiesbr($obj->message),
14747 'percent' => $obj->percent,
14748 'acode' => $obj->acode,
14749
14750 'userid' => $obj->user_id,
14751 'login' => $obj->user_login,
14752 'userfirstname' => $obj->user_firstname,
14753 'userlastname' => $obj->user_lastname,
14754 'userphoto' => $obj->user_photo
14755 );
14756 }
14757
14758 $numaction++;
14759 $i++;
14760 }
14761 } else {
14762 dol_print_error($db);
14763 }
14764 }
14765
14766 // Set $out to show events
14767 $out = '';
14768
14769 if (!isModEnabled('agenda')) {
14770 $langs->loadLangs(array("admin", "errors"));
14771 $out = info_admin($langs->trans("WarningModuleXDisabledSoYouMayMissEventHere", $langs->transnoentitiesnoconv("Module2400Name")), 0, 0, 'warning');
14772 }
14773
14774 if (isModEnabled('agenda') || (isModEnabled('mailing') && !empty($objcon->email))) {
14775 $delay_warning = getDolGlobalInt('MAIN_DELAY_ACTIONS_TODO') * 24 * 60 * 60;
14776
14777 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
14778 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
14779 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
14780 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
14781
14782 $formactions = new FormActions($db);
14783
14784 $actionstatic = new ActionComm($db);
14785 $userstatic = new User($db);
14786 $contactstatic = new Contact($db);
14787 $userGetNomUrlCache = array();
14788 $contactGetNomUrlCache = array();
14789
14790 $out .= '<div class="filters-container" >';
14791 $out .= '<form name="listactionsfilter" class="listactionsfilter" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
14792 $out .= '<input type="hidden" name="token" value="'.newToken().'">';
14793
14794 if ($objcon && get_class($objcon) == 'Contact' &&
14795 (is_null($filterobj) || get_class($filterobj) == 'Societe')) {
14796 $out .= '<input type="hidden" name="id" value="'.$objcon->id.'" />';
14797 } else {
14798 $out .= '<input type="hidden" name="id" value="'.$filterobj->id.'" />';
14799 }
14800 if (($filterobj && get_class($filterobj) == 'Societe')) {
14801 $out .= '<input type="hidden" name="socid" value="'.$filterobj->id.'" />';
14802 } else {
14803 $out .= '<input type="hidden" name="userid" value="'.$filterobj->id.'" />';
14804 }
14805
14806 $out .= "\n";
14807
14808 $out .= '<div class="div-table-responsive-no-min">';
14809 $out .= '<table class="noborder borderbottom centpercent">';
14810
14811 $out .= '<tr class="liste_titre">';
14812
14813 // Action column
14814 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14815 $out .= '<th class="liste_titre width50 middle">';
14816 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14817 $out .= $searchpicto;
14818 $out .= '</th>';
14819 }
14820
14821 // Date
14822 $out .= getTitleFieldOfList('Date', 0, $_SERVER["PHP_SELF"], 'a.datep', '', $param, '', $sortfield, $sortorder, 'nowraponall nopaddingleftimp ')."\n";
14823
14824 $out .= '<th class="liste_titre hideonsmartphone"><strong class="hideonsmartphone">'.$langs->trans("Search").' : </strong></th>';
14825 if ($donetodo) {
14826 $out .= '<th class="liste_titre"></th>';
14827 }
14828 // Type of event
14829 $out .= '<th class="liste_titre">';
14830 $out .= '<span class="fas fa-square inline-block fawidth30 hideonsmartphone" style="color: #ddd;" title="'.$langs->trans("ActionType").'"></span>';
14831 $out .= $formactions->select_type_actions($actioncode, "actioncode", '', getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? -1 : 1, 0, 0, 1, 'selecttype minwidth100', $langs->trans("Type"));
14832 $out .= '</th>';
14833 // Label
14834 $out .= '<th class="liste_titre maxwidth100onsmartphone">';
14835 $out .= '<input type="text" class="maxwidth100onsmartphone" name="search_agenda_label" value="'.$filters['search_agenda_label'].'" placeholder="'.$langs->trans("Label").'">';
14836 $out .= '</th>';
14837
14838 // Action column
14839 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
14840 $out .= '<th class="liste_titre width50 middle">';
14841 $searchpicto = $form->showFilterAndCheckAddButtons($massactionbutton ? 1 : 0, 'checkforselect', 1);
14842 $out .= $searchpicto;
14843 $out .= '</th>';
14844 }
14845
14846 $out .= '</tr>';
14847
14848 $out .= '</table>';
14849
14850 $out .= '</form>';
14851 $out .= '</div>';
14852
14853 $out .= "\n";
14854
14855 $out .= '<ul class="timeline">';
14856
14857 if ($donetodo) {
14858 $tmp = '';
14859 if ($filterobj instanceof Societe) {
14860 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14861 }
14862 if ($filterobj instanceof User) {
14863 $tmp .= '<a href="'.DOL_URL_ROOT.'/comm/action/list.php?mode=show_list&socid='.$filterobj->id.'&status=done">';
14864 }
14865 $tmp .= ($donetodo != 'done' ? $langs->trans("ActionsToDoShort") : '');
14866 $tmp .= ($donetodo != 'done' && $donetodo != 'todo' ? ' / ' : '');
14867 $tmp .= ($donetodo != 'todo' ? $langs->trans("ActionsDoneShort") : '');
14868 //$out.=$langs->trans("ActionsToDoShort").' / '.$langs->trans("ActionsDoneShort");
14869 if ($filterobj instanceof Societe) {
14870 $tmp .= '</a>';
14871 }
14872 if ($filterobj instanceof User) {
14873 $tmp .= '</a>';
14874 }
14875 $out .= getTitleFieldOfList($tmp);
14876 }
14877
14878 require_once DOL_DOCUMENT_ROOT.'/comm/action/class/cactioncomm.class.php';
14879 $caction = new CActionComm($db);
14880 $arraylist = $caction->liste_array(1, 'code', '', (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') ? 1 : 0), '', 1);
14881
14882 $actualCycleDate = false;
14883
14884 // Loop on each event to show it
14885 foreach ($histo as $key => $value) {
14886 $actionstatic->fetch($histo[$key]['id']); // TODO Do we need this, we already have a lot of data of line into $histo
14887
14888 $actionstatic->type_picto = $histo[$key]['apicto'];
14889 $actionstatic->type_code = $histo[$key]['acode'];
14890
14891 $labeltype = $actionstatic->type_code;
14892 if (!getDolGlobalString('AGENDA_USE_EVENT_TYPE') && empty($arraylist[$labeltype])) {
14893 $labeltype = 'AC_OTH';
14894 }
14895 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
14896 $labeltype = $langs->trans("Message");
14897 } else {
14898 if (!empty($arraylist[$labeltype])) {
14899 $labeltype = $arraylist[$labeltype];
14900 }
14901 if ($actionstatic->type_code == 'AC_OTH_AUTO' && ($actionstatic->type_code != $actionstatic->code) && $labeltype && !empty($arraylist[$actionstatic->code])) {
14902 $labeltype .= ' - '.$arraylist[$actionstatic->code]; // Use code in priority on type_code
14903 }
14904 }
14905
14906 $url = DOL_URL_ROOT.'/comm/action/card.php?id='.$histo[$key]['id'];
14907
14908 $tmpa = dol_getdate($histo[$key]['datestart'], false);
14909
14910 if (isset($tmpa['year']) && isset($tmpa['yday']) && $actualCycleDate !== $tmpa['year'].'-'.$tmpa['yday']) {
14911 $actualCycleDate = $tmpa['year'].'-'.$tmpa['yday'];
14912 $out .= '<!-- timeline time label -->';
14913 $out .= '<li class="time-label">';
14914 $out .= '<span class="timeline-badge-date">';
14915 $out .= dol_print_date($histo[$key]['datestart'], 'daytext', 'tzuserrel', $langs);
14916 $out .= '</span>';
14917 $out .= '</li>';
14918 $out .= '<!-- /.timeline-label -->';
14919 }
14920
14921
14922 $out .= '<!-- timeline item -->'."\n";
14923 $out .= '<li class="timeline-code-'.(!empty($actionstatic->code) ? strtolower($actionstatic->code) : "none").'">';
14924
14925 //$timelineicon = getTimelineIcon($actionstatic, $histo, $key);
14926 $typeicon = $actionstatic->getTypePicto('pictofixedwidth timeline-icon-not-applicble', $labeltype);
14927 //$out .= $timelineicon;
14928 //var_dump($timelineicon);
14929 $out .= $typeicon;
14930
14931 $out .= '<div class="timeline-item">'."\n";
14932
14933 $out .= '<span class="time timeline-header-action2">';
14934
14935 if (isset($histo[$key]['type']) && $histo[$key]['type'] == 'mailing') {
14936 $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").' ';
14937 $out .= $histo[$key]['id'];
14938 $out .= '</a> ';
14939 } else {
14940 $out .= $actionstatic->getNomUrl(1, -1, 'valignmiddle').' ';
14941 }
14942
14943 if ($user->hasRight('agenda', 'allactions', 'create') ||
14944 (($actionstatic->authorid == $user->id || $actionstatic->userownerid == $user->id) && $user->hasRight('agenda', 'myactions', 'create'))) {
14945 $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).'">';
14946 //$out .= '<i class="fa fa-pencil" title="'.$langs->trans("Modify").'" ></i>';
14947 $out .= img_picto($langs->trans("Modify"), 'edit', 'class="edita"');
14948 $out .= '</a>';
14949 }
14950
14951 $out .= '</span>';
14952
14953 // Date
14954 $out .= '<span class="time"><i class="fa fa-clock-o valignmiddle"></i> <span class="valignmiddle">';
14955 $out .= dol_print_date($histo[$key]['datestart'], 'dayhour', 'tzuserrel');
14956 if ($histo[$key]['dateend'] && $histo[$key]['dateend'] != $histo[$key]['datestart']) {
14957 $tmpa = dol_getdate($histo[$key]['datestart'], true);
14958 $tmpb = dol_getdate($histo[$key]['dateend'], true);
14959 if ($tmpa['mday'] == $tmpb['mday'] && $tmpa['mon'] == $tmpb['mon'] && $tmpa['year'] == $tmpb['year']) {
14960 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'hour', 'tzuserrel');
14961 } else {
14962 $out .= '-'.dol_print_date($histo[$key]['dateend'], 'dayhour', 'tzuserrel');
14963 }
14964 }
14965 $late = 0;
14966 if ($histo[$key]['percent'] == 0 && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14967 $late = 1;
14968 }
14969 if ($histo[$key]['percent'] == 0 && !$histo[$key]['datestart'] && $histo[$key]['dateend'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14970 $late = 1;
14971 }
14972 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && $histo[$key]['dateend'] && $histo[$key]['dateend'] < ($now - $delay_warning)) {
14973 $late = 1;
14974 }
14975 if ($histo[$key]['percent'] > 0 && $histo[$key]['percent'] < 100 && !$histo[$key]['dateend'] && $histo[$key]['datestart'] && $histo[$key]['datestart'] < ($now - $delay_warning)) {
14976 $late = 1;
14977 }
14978 if ($late) {
14979 $out .= img_warning($langs->trans("Late")).' ';
14980 }
14981 $out .= "</span></span>\n";
14982
14983 // Ref
14984 $out .= '<h3 class="timeline-header">';
14985
14986 // Author of event
14987 $out .= '<div class="messaging-author inline-block tdoverflowmax150 valignmiddle marginrightonly">';
14988 if ($histo[$key]['userid'] > 0) {
14989 if (!isset($userGetNomUrlCache[$histo[$key]['userid']])) { // is in cache ?
14990 $userstatic->fetch($histo[$key]['userid']);
14991 $userGetNomUrlCache[$histo[$key]['userid']] = $userstatic->getNomUrl(-1, '', 0, 0, 16, 0, 'firstelselast', '');
14992 }
14993 $out .= $userGetNomUrlCache[$histo[$key]['userid']];
14994 } elseif (!empty($histo[$key]['msg_from']) && $actionstatic->code == 'TICKET_MSG') {
14995 if (!isset($contactGetNomUrlCache[$histo[$key]['msg_from']])) {
14996 if ($contactstatic->fetch(0, null, '', $histo[$key]['msg_from']) > 0) {
14997 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $contactstatic->getNomUrl(-1, '', 16);
14998 } else {
14999 $contactGetNomUrlCache[$histo[$key]['msg_from']] = $histo[$key]['msg_from'];
15000 }
15001 }
15002 $out .= $contactGetNomUrlCache[$histo[$key]['msg_from']];
15003 }
15004 $out .= '</div>';
15005
15006 // Title
15007 $out .= ' <div class="messaging-title inline-block">';
15008 //$out .= $actionstatic->getTypePicto();
15009 if (empty($conf->dol_optimize_smallscreen) && $actionstatic->type_code != 'AC_OTH_AUTO') {
15010 $out .= $labeltype.' - ';
15011 }
15012
15013 $libelle = '';
15014 if (!empty($actionstatic->code) && preg_match('/^TICKET_MSG/', $actionstatic->code)) {
15015 $out .= $langs->trans('TicketNewMessage');
15016 } elseif (!empty($actionstatic->code) && preg_match('/^TICKET_MSG_PRIVATE/', $actionstatic->code)) {
15017 $out .= $langs->trans('TicketNewMessage').' <em>('.$langs->trans('Private').')</em>';
15018 } elseif (isset($histo[$key]['type'])) {
15019 if ($histo[$key]['type'] == 'action') {
15020 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15021 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : $histo[$key]['alabel']);
15022 $libelle = $histo[$key]['note'];
15023 $actionstatic->id = $histo[$key]['id'];
15024 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15025 } elseif ($histo[$key]['type'] == 'mailing') {
15026 $out .= '<a href="'.DOL_URL_ROOT.'/comm/mailing/card.php?id='.$histo[$key]['id'].'">'.img_object($langs->trans("ShowEMailing"), "email").' ';
15027 $transcode = $langs->transnoentitiesnoconv("Action".$histo[$key]['acode']);
15028 $libelle = ($transcode != "Action".$histo[$key]['acode'] ? $transcode : 'Send mass mailing');
15029 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15030 } else {
15031 $libelle .= $histo[$key]['note'];
15032 $out .= dol_escape_htmltag(dol_trunc($libelle, 120));
15033 }
15034 }
15035
15036 if (isset($histo[$key]['elementtype']) && !empty($histo[$key]['fk_element'])) {
15037 if (isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']]) && isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']])) {
15038 $link = $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']];
15039 } else {
15040 if (!isset($conf->cache['elementlinkcache'][$histo[$key]['elementtype']])) {
15041 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']] = array();
15042 }
15043 $link = dolGetElementUrl($histo[$key]['fk_element'], $histo[$key]['elementtype'], 1);
15044 $conf->cache['elementlinkcache'][$histo[$key]['elementtype']][$histo[$key]['fk_element']] = $link;
15045 }
15046 if ($link) {
15047 $out .= ' - '.$link;
15048 }
15049 }
15050
15051 $out .= '</div>';
15052
15053 $out .= '</h3>';
15054
15055 // Message
15056 if (!empty($histo[$key]['message'] && $histo[$key]['message'] != $libelle)
15057 && $actionstatic->code != 'AC_TICKET_CREATE'
15058 && $actionstatic->code != 'AC_TICKET_MODIFY'
15059 ) {
15060 $out .= '<div class="timeline-body wordbreak small">';
15061 $truncateLines = getDolGlobalInt('MAIN_TRUNCATE_TIMELINE_MESSAGE', 3);
15062 $truncatedText = dolGetFirstLineOfText($histo[$key]['message'], $truncateLines);
15063 if ($truncateLines > 0 && strlen($histo[$key]['message']) > strlen($truncatedText)) {
15064 $out .= '<div class="readmore-block --closed" >';
15065 $out .= ' <div class="readmore-block__excerpt">';
15066 $out .= dolPrintHTML($truncatedText);
15067 $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>';
15068 $out .= ' </div>';
15069 $out .= ' <div class="readmore-block__full-text" >';
15070 $out .= dolPrintHTML($histo[$key]['message']);
15071 $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>';
15072 $out .= ' </div>';
15073 $out .= '</div>';
15074 } else {
15075 $out .= dolPrintHTML($histo[$key]['message']);
15076 }
15077
15078 $out .= '</div>';
15079 }
15080
15081 // Timeline footer
15082 $footer = '';
15083
15084 // Contact for this action
15085 if (isset($histo[$key]['socpeopleassigned']) && is_array($histo[$key]['socpeopleassigned']) && count($histo[$key]['socpeopleassigned']) > 0) {
15086 $contactList = '';
15087 foreach ($histo[$key]['socpeopleassigned'] as $cid => $Tab) {
15088 if (empty($conf->cache['contact'][$cid])) {
15089 $contact = new Contact($db);
15090 $contact->fetch($cid);
15091 $conf->cache['contact'][$cid] = $contact;
15092 } else {
15093 $contact = $conf->cache['contact'][$cid];
15094 }
15095
15096 if ($contact) {
15097 $contactList .= !empty($contactList) ? ', ' : '';
15098 $contactList .= $contact->getNomUrl(1);
15099 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15100 if (!empty($contact->phone_pro)) {
15101 $contactList .= '('.dol_print_phone($contact->phone_pro).')';
15102 }
15103 }
15104 }
15105 }
15106
15107 $footer .= $langs->trans('ActionOnContact').' : '.$contactList;
15108 } elseif (empty($objcon->id) && isset($histo[$key]['contact_id']) && $histo[$key]['contact_id'] > 0) {
15109 if (empty($conf->cache['contact'][$histo[$key]['contact_id']])) {
15110 $contact = new Contact($db);
15111 $result = $contact->fetch($histo[$key]['contact_id']);
15112 $conf->cache['contact'][$histo[$key]['contact_id']] = $contact;
15113 } else {
15114 $contact = $conf->cache['contact'][$histo[$key]['contact_id']];
15115 $result = ($contact instanceof Contact) ? $contact->id : 0;
15116 }
15117
15118 if ($result > 0) {
15119 $footer .= $contact->getNomUrl(1);
15120 if (isset($histo[$key]['acode']) && $histo[$key]['acode'] == 'AC_TEL') {
15121 if (!empty($contact->phone_pro)) {
15122 $footer .= '('.dol_print_phone($contact->phone_pro).')';
15123 }
15124 }
15125 }
15126 }
15127
15128 $documents = getActionCommEcmList($actionstatic);
15129 if (!empty($documents)) {
15130 $footer .= '<div class="timeline-documents-container">';
15131 foreach ($documents as $doc) {
15132 $footer .= '<span id="document_'.$doc->id.'" class="timeline-documents" ';
15133 $footer .= ' data-id="'.$doc->id.'" ';
15134 $footer .= ' data-path="'.$doc->filepath.'"';
15135 $footer .= ' data-filename="'.dol_escape_htmltag($doc->filename).'" ';
15136 $footer .= '>';
15137
15138 $filePath = DOL_DATA_ROOT.'/'.$doc->filepath.'/'.$doc->filename;
15139 $mime = dol_mimetype($filePath);
15140 $file = $actionstatic->id.'/'.$doc->filename;
15141 $thumb = $actionstatic->id.'/thumbs/'.substr($doc->filename, 0, strrpos($doc->filename, '.')).'_mini'.substr($doc->filename, strrpos($doc->filename, '.'));
15142 $doclink = dol_buildpath('document.php', 1).'?modulepart=actions&attachment=0&file='.urlencode($file).'&entity='.$conf->entity;
15143 $viewlink = dol_buildpath('viewimage.php', 1).'?modulepart=actions&file='.urlencode($thumb).'&entity='.$conf->entity;
15144
15145 $mimeAttr = ' mime="'.$mime.'" ';
15146 $class = '';
15147 if (in_array($mime, array('image/png', 'image/jpeg', 'application/pdf'))) {
15148 $class .= ' documentpreview';
15149 }
15150
15151 $footer .= '<a href="'.$doclink.'" class="btn-link '.$class.'" target="_blank" rel="noopener noreferrer" '.$mimeAttr.' >';
15152 $footer .= img_mime($filePath).' '.$doc->filename;
15153 $footer .= '</a>';
15154
15155 $footer .= '</span>';
15156 }
15157 $footer .= '</div>';
15158 }
15159
15160 if (!empty($footer)) {
15161 $out .= '<div class="timeline-footer">'.$footer.'</div>';
15162 }
15163
15164 $out .= '</div>'."\n"; // end timeline-item
15165
15166 $out .= '</li>';
15167 $out .= '<!-- END timeline item -->';
15168 }
15169
15170 $out .= "</ul>\n";
15171
15172 $out .= '<script>
15173 jQuery(document).ready(function () {
15174 $(document).on("click", "[data-read-more-action]", function(e){
15175 let readMoreBloc = $(this).closest(".readmore-block");
15176 if(readMoreBloc.length > 0){
15177 e.preventDefault();
15178 if($(this).attr("data-read-more-action") == "close"){
15179 readMoreBloc.addClass("--closed").removeClass("--open");
15180 $("html, body").animate({
15181 scrollTop: readMoreBloc.offset().top - 200
15182 }, 100);
15183 }else{
15184 readMoreBloc.addClass("--open").removeClass("--closed");
15185 }
15186 }
15187 });
15188 });
15189 </script>';
15190
15191
15192 if (empty($histo)) {
15193 $out .= '<span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span>';
15194 }
15195 }
15196
15197 if ($noprint) {
15198 return $out;
15199 } else {
15200 print $out;
15201 return null;
15202 }
15203}
15204
15216function buildParamDate($prefix, $timestamp = null, $hourTime = '', $gm = 'auto')
15217{
15218 if ($timestamp === null) {
15219 $timestamp = GETPOSTDATE($prefix, $hourTime, $gm);
15220 }
15221 $TParam = array(
15222 $prefix . 'day' => intval(dol_print_date($timestamp, '%d')),
15223 $prefix . 'month' => intval(dol_print_date($timestamp, '%m')),
15224 $prefix . 'year' => intval(dol_print_date($timestamp, '%Y')),
15225 );
15226 if ($hourTime === 'getpost' || ($timestamp !== null && dol_print_date($timestamp, '%H:%M:%S') !== '00:00:00')) {
15227 $TParam = array_merge($TParam, array(
15228 $prefix . 'hour' => intval(dol_print_date($timestamp, '%H')),
15229 $prefix . 'min' => intval(dol_print_date($timestamp, '%M')),
15230 $prefix . 'sec' => intval(dol_print_date($timestamp, '%S'))
15231 ));
15232 }
15233
15234 return '&' . http_build_query($TParam);
15235}
15236
15255function recordNotFound($message = '', $printheader = 1, $printfooter = 1, $showonlymessage = 0, $params = null)
15256{
15257 global $conf, $db, $langs, $hookmanager;
15258 global $action, $object;
15259
15260 if (!is_object($langs)) {
15261 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
15262 $langs = new Translate('', $conf);
15263 $langs->setDefaultLang();
15264 }
15265
15266 $langs->load("errors");
15267
15268 if ($printheader) {
15269 if (function_exists("llxHeader")) {
15270 llxHeader('');
15271 } elseif (function_exists("llxHeaderVierge")) {
15272 llxHeaderVierge('');
15273 }
15274 }
15275
15276 print '<div class="error">';
15277 if (empty($message)) {
15278 print $langs->trans("ErrorRecordNotFound");
15279 } else {
15280 print $langs->trans($message);
15281 }
15282 print '</div>';
15283 print '<br>';
15284
15285 if (empty($showonlymessage)) {
15286 if (empty($hookmanager)) {
15287 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
15288 $hookmanager = new HookManager($db);
15289 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
15290 $hookmanager->initHooks(array('main'));
15291 }
15292
15293 $parameters = array('message' => $message, 'params' => $params);
15294 $reshook = $hookmanager->executeHooks('getErrorRecordNotFound', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
15295 print $hookmanager->resPrint;
15296 }
15297
15298 if ($printfooter && function_exists("llxFooter")) {
15299 llxFooter();
15300 }
15301 exit(0);
15302}
$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.