dolibarr 21.0.4
main.inc.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Xavier Dutoit <doli@sydesy.com>
4 * Copyright (C) 2004-2021 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) 2005-2021 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2011-2014 Philippe Grand <philippe.grand@atoo-net.com>
9 * Copyright (C) 2008 Matteli
10 * Copyright (C) 2011-2016 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
13 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
14 * Copyright (C) 2020 Demarest Maxime <maxime@indelog.fr>
15 * Copyright (C) 2020-2024 Charlene Benke <charlene@patas-monkey.com>
16 * Copyright (C) 2021-2024 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
18 * Copyright (C) 2023 Joachim Küter <git-jk@bloxera.com>
19 * Copyright (C) 2023 Eric Seigne <eric.seigne@cap-rel.fr>
20 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 3 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34 */
35
42//@ini_set('memory_limit', '128M'); // This may be useless if memory is hard limited by your PHP
43
44// For optional tuning. Enabled if environment variable MAIN_SHOW_TUNING_INFO is defined.
45$micro_start_time = 0;
46if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) {
47 list($usec, $sec) = explode(" ", microtime());
48 $micro_start_time = ((float) $usec + (float) $sec);
49 // Add Xdebug code coverage
50 //define('XDEBUGCOVERAGE',1);
51 if (defined('XDEBUGCOVERAGE')) {
52 xdebug_start_code_coverage();
53 }
54}
55
63{
64 $arrayofcommonemoji = array(
65 'misc' => array('2600', '26FF'), // Miscellaneous Symbols
66 'ding' => array('2700', '27BF'), // Dingbats
67 '????' => array('9989', '9989'), // Variation Selectors
68 'vars' => array('FE00', 'FE0F'), // Variation Selectors
69 'pict' => array('1F300', '1F5FF'), // Miscellaneous Symbols and Pictographs
70 'emot' => array('1F600', '1F64F'), // Emoticons
71 'tran' => array('1F680', '1F6FF'), // Transport and Map Symbols
72 'flag' => array('1F1E0', '1F1FF'), // Flags (note: may be 1F1E6 instead of 1F1E0)
73 'supp' => array('1F900', '1F9FF'), // Supplemental Symbols and Pictographs
74 );
75
76 return $arrayofcommonemoji;
77}
78
87{
88 $newstringnumentity = preg_replace('/;$/', '', $matches[1]);
89 //print ' $newstringnumentity='.$newstringnumentity;
90
91 if (preg_match('/^x/i', $newstringnumentity)) { // if numeric is hexadecimal
92 $newstringnumentity = hexdec(preg_replace('/^x/i', '', $newstringnumentity));
93 } else {
94 $newstringnumentity = (int) $newstringnumentity;
95 }
96
97 // The numeric values we don't want as entities because they encode ascii char, and why using html entities on ascii except for haking ?
98 if (($newstringnumentity >= 65 && $newstringnumentity <= 90) || ($newstringnumentity >= 97 && $newstringnumentity <= 122)) {
99 return chr((int) $newstringnumentity);
100 }
101
102 // The numeric values we want in UTF8 instead of entities because it is emoji
103 $arrayofemojis = getArrayOfEmoji();
104 foreach ($arrayofemojis as $valarray) {
105 if ($newstringnumentity >= hexdec($valarray[0]) && $newstringnumentity <= hexdec($valarray[1])) {
106 // This is a known emoji
107 return html_entity_decode($matches[0], ENT_COMPAT | ENT_HTML5, 'UTF-8');
108 }
109 }
110
111 return '&#'.$matches[1]; // Value will be unchanged because regex was /&#( )/
112}
113
123function testSqlAndScriptInject($val, $type)
124{
125 // Decode string first because a lot of things are obfuscated by encoding or multiple encoding.
126 // So <svg o&#110;load='console.log(&quot;123&quot;)' become <svg onload='console.log(&quot;123&quot;)'
127 // So "&colon;&apos;" become ":'" (due to ENT_HTML5)
128 // So "&Tab;&NewLine;" become ""
129 // So "&lpar;&rpar;" become "()"
130
131 // Loop to decode until no more things to decode.
132 //print "before decoding $val\n";
133 do {
134 $oldval = $val;
135 $val = html_entity_decode($val, ENT_QUOTES | ENT_HTML5); // Decode '&colon;', '&apos;', '&Tab;', '&NewLine', ...
136 // Sometimes we have entities without the ; at end so html_entity_decode does not work but entities is still interpreted by browser.
137 $val = preg_replace_callback(
138 '/&#(x?[0-9][0-9a-f]+;?)/i',
143 static function ($m) {
144 // Decode '&#110;', ...
146 },
147 $val
148 );
149
150 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
151 $val = preg_replace('/<!--[^>]*-->/', '', $val);
152 $val = preg_replace('/[\r\n\t]/', '', $val);
153 } while ($oldval != $val);
154 //print "type = ".$type." after decoding: ".$val."\n";
155
156 $inj = 0;
157
158 // We check 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)
159 // We should use dol_string_nounprintableascii but function is not yet loaded/available
160 // Example of valid UTF8 chars:
161 // utf8 or utf8mb3: '\x09', '\x0A', '\x0D', '\x7E'
162 // utf8 or utf8mb3: '\xE0\xA0\x80'
163 // utf8mb4: '\xF0\x9D\x84\x9E' (so this may be refused by the database insert if pagecode is utf8=utf8mb3)
164 $newval = preg_replace('/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/u', '', $val); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
165
166 // Note that $newval may also be completely empty '' when non valid UTF8 are found.
167 if ($newval != $val) {
168 // If $val has changed after removing non valid UTF8 chars, it means we have an evil string.
169 $inj += 1;
170 }
171 //print 'inj='.$inj.'-type='.$type.'-val='.$val.'-newval='.$newval."\n";
172
173 // For SQL Injection (only GET are used to scan for such injection strings)
174 if ($type == 1 || $type == 3) {
175 // Note the \s+ is replaced into \s* because some spaces may have been modified in previous loop
176 $inj += preg_match('/delete\s*from/i', $val);
177 $inj += preg_match('/create\s*table/i', $val);
178 $inj += preg_match('/insert\s*into/i', $val);
179 $inj += preg_match('/select\s*from/i', $val);
180 $inj += preg_match('/into\s*(outfile|dumpfile)/i', $val);
181 $inj += preg_match('/user\s*\‍(/i', $val); // avoid to use function user() or mysql_user() that return current database login
182 $inj += preg_match('/information_schema/i', $val); // avoid to use request that read information_schema database
183 $inj += preg_match('/<svg/i', $val); // <svg can be allowed in POST
184 $inj += preg_match('/update[^&=\w].*set.+=/i', $val); // the [^&=\w] test is to avoid error when request is like action=update&...set... or &updatemodule=...set...
185 $inj += preg_match('/union.+select/i', $val);
186 }
187 if ($type == 3) {
188 // Note the \s+ is replaced into \s* because some spaces may have been modified in previous loop
189 $inj += preg_match('/select|update|delete|truncate|replace|group\s*by|concat|count|from|union/i', $val);
190 }
191 if ($type != 2) { // Not common key strings, so we can check them both on GET and POST
192 $inj += preg_match('/updatexml\‍(/i', $val);
193 $inj += preg_match('/(\.\.%2f)+/i', $val);
194 $inj += preg_match('/\s@@/', $val);
195 }
196 // For XSS Injection done by closing textarea to execute content into a textarea field
197 $inj += preg_match('/<\/textarea/i', $val);
198 // For XSS Injection done by adding javascript with script
199 // This is all cases a browser consider text is javascript:
200 // When it found '<script', 'javascript:', '<style', 'onload\s=' on body tag, '="&' on a tag size with old browsers
201 // All examples on page: http://ha.ckers.org/xss.html#XSScalc
202 // More on https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
203 $inj += preg_match('/<audio/i', $val);
204 $inj += preg_match('/<embed/i', $val);
205 $inj += preg_match('/<iframe/i', $val);
206 $inj += preg_match('/<object/i', $val);
207 $inj += preg_match('/<script/i', $val);
208 $inj += preg_match('/Set\.constructor/i', $val); // ECMA script 6
209 if (!defined('NOSTYLECHECK')) {
210 $inj += preg_match('/<style/i', $val);
211 }
212 $inj += preg_match('/base\s+href/si', $val);
213 $inj += preg_match('/=data:/si', $val);
214
215 // 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
216 $inj += preg_match('/on(mouse|content|drag|key|load|touch|pointer|select|transition)[a-z]*\s*=/i', $val); // onmousexxx can be set on img or any html tag like <img title='...' onmouseover=alert(1)>
217 $inj += preg_match('/on(abort|after|animation|auxclick|before|blur|bounce|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', $val);
218 $inj += preg_match('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', $val);
219 $inj += preg_match('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', $val);
220 $inj += preg_match('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', $val);
221 $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', $val);
222 // More not into the previous list
223 $inj += preg_match('/on(repeat|begin|finish)[a-z]*\s*=/i', $val);
224
225 // We refuse html into html because some hacks try to obfuscate evil strings by inserting HTML into HTML.
226 // Example: <img on<a>error=alert(1) or <img onerror<>=alert(1) to bypass test on onerror=
227 $tmpval = preg_replace('/<[^<]*>/', '', $val);
228
229 // 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
230 $inj += preg_match('/on(mouse|drag|key|load|touch|pointer|select|transition)[a-z]*\s*=/i', $tmpval); // onmousexxx can be set on img or any html tag like <img title='...' onmouseover=alert(1)>
231 $inj += preg_match('/on(abort|after|animation|auxclick|before|blur|bounce|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)[a-z]*\s*=/i', $tmpval);
232 $inj += preg_match('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)[a-z]*\s*=/i', $tmpval);
233 $inj += preg_match('/on(lostpointercapture|offline|online|pagehide|pageshow)[a-z]*\s*=/i', $tmpval);
234 $inj += preg_match('/on(paste|pause|play|playing|progress|ratechange|reset|resize|scroll|search|seeked|seeking|show|stalled|start|submit|suspend)[a-z]*\s*=/i', $tmpval);
235 $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)[a-z]*\s*=/i', $tmpval);
236 // More not into the previous list
237 $inj += preg_match('/on(repeat|begin|finish)[a-z]*\s*=/i', $tmpval);
238
239 //$inj += preg_match('/on[A-Z][a-z]+\*=/', $val); // To lock event handlers onAbort(), ...
240 $inj += preg_match('/&#58;|&#0000058|&#x3A/i', $val); // refused string ':' encoded (no reason to have it encoded) to lock 'javascript:...'
241 $inj += preg_match('/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*:/i', $val);
242 $inj += preg_match('/vbscript\s*:/i', $val);
243 // For XSS Injection done by adding javascript closing html tags like with onmousemove, etc... (closing a src or href tag with not cleaned param)
244 if ($type == 1 || $type == 3) {
245 $val = str_replace('enclosure="', 'enclosure=X', $val); // We accept enclosure=" for the export/import module
246 $inj += preg_match('/"/i', $val); // We refused " in GET parameters value.
247 }
248 if ($type == 2) {
249 $inj += preg_match('/[:;"\'<>\?\‍(\‍){}\$%]/', $val); // PHP_SELF is a file system (or url path without parameters). It can contains spaces.
250 }
251
252 return $inj;
253}
254
263function analyseVarsForSqlAndScriptsInjection(&$var, $type, $stopcode = 1)
264{
265 if (is_array($var)) {
266 foreach ($var as $key => $value) { // Warning, $key may also be used for attacks
267 // Exclude check for some variable keys
268 if ($type === 0 && defined('NOSCANPOSTFORINJECTION') && is_array(constant('NOSCANPOSTFORINJECTION')) && in_array($key, constant('NOSCANPOSTFORINJECTION'))) {
269 continue;
270 }
271
272 if (analyseVarsForSqlAndScriptsInjection($key, $type, $stopcode) && analyseVarsForSqlAndScriptsInjection($value, $type, $stopcode)) {
273 //$var[$key] = $value; // This is useless
274 } else {
275 http_response_code(403);
276
277 // Get remote IP: PS: We do not use getRemoteIP(), function is not yet loaded and we need a value that can't be spoofed
278 $ip = (empty($_SERVER['REMOTE_ADDR']) ? 'unknown' : $_SERVER['REMOTE_ADDR']);
279
280 if ($stopcode) {
281 $errormessage = 'Access refused to '.htmlentities($ip, ENT_COMPAT, 'UTF-8').' by SQL or Script injection protection in main.inc.php:analyseVarsForSqlAndScriptsInjection type='.htmlentities((string) $type, ENT_COMPAT, 'UTF-8');
282 //$errormessage .= ' paramkey='.htmlentities($key, ENT_COMPAT, 'UTF-8'); // Disabled to avoid text injection
283
284 $errormessage2 = 'page='.htmlentities((empty($_SERVER["REQUEST_URI"]) ? '' : $_SERVER["REQUEST_URI"]), ENT_COMPAT, 'UTF-8');
285 $errormessage2 .= ' paramtype='.htmlentities((string) $type, ENT_COMPAT, 'UTF-8');
286 $errormessage2 .= ' paramkey='.htmlentities($key, ENT_COMPAT, 'UTF-8');
287 $errormessage2 .= ' paramvalue='.htmlentities($value, ENT_COMPAT, 'UTF-8');
288
289 print $errormessage;
290 print "<br>\n";
291 print 'Try to go back, fix data of your form and resubmit it. You can contact also your technical support.';
292
293 print "\n".'<!--'."\n";
294 print $errormessage2;
295 print "\n".'-->';
296
297 // Add entry into the PHP server error log
298 if (function_exists('error_log')) {
299 error_log($errormessage.' '.substr($errormessage2, 2000));
300 }
301
302 // Note: No addition into security audit table is done because we don't want to execute code in such a case.
303 // Detection of too many such requests can be done with a fail2ban rule on 403 error code or into the PHP server error log.
304
305
306 if (class_exists('PHPUnit\Framework\TestSuite')) {
307 $message = $errormessage.' '.substr($errormessage2, 2000);
308 throw new Exception("Security injection exception: $message");
309 }
310 exit;
311 } else {
312 return false;
313 }
314 }
315 }
316 return true;
317 } else {
318 return (testSqlAndScriptInject($var, $type) <= 0);
319 }
320}
321
322// To disable the WAF for GET and POST and PHP_SELF, uncomment this
323//define('NOSCANPHPSELFFORINJECTION', 1);
324//define('NOSCANGETFORINJECTION', 1);
325//define('NOSCANPOSTFORINJECTION', 1 or 2);
326
327// Check consistency of NOREQUIREXXX DEFINES
328if ((defined('NOREQUIREDB') || defined('NOREQUIRETRAN')) && !defined('NOREQUIREMENU')) {
329 print 'If define NOREQUIREDB or NOREQUIRETRAN are set, you must also set NOREQUIREMENU or not set them.';
330 exit;
331}
332if (defined('NOREQUIREUSER') && !defined('NOREQUIREMENU')) {
333 print 'If define NOREQUIREUSER is set, you must also set NOREQUIREMENU or not set it.';
334 exit;
335}
336
337// Sanity check on URL
338if (!defined('NOSCANPHPSELFFORINJECTION') && !empty($_SERVER["PHP_SELF"])) {
339 $morevaltochecklikepost = array($_SERVER["PHP_SELF"]);
340 analyseVarsForSqlAndScriptsInjection($morevaltochecklikepost, 2);
341}
342// Sanity check on GET parameters
343if (!defined('NOSCANGETFORINJECTION') && !empty($_SERVER["QUERY_STRING"])) {
344 // Note: QUERY_STRING is url encoded, but $_GET and $_POST are already decoded
345 // Because the analyseVarsForSqlAndScriptsInjection is designed for already url decoded value, we must decode QUERY_STRING
346 // Another solution is to provide $_GET as parameter with analyseVarsForSqlAndScriptsInjection($_GET, 1);
347 $morevaltochecklikeget = array(urldecode($_SERVER["QUERY_STRING"]));
348 analyseVarsForSqlAndScriptsInjection($morevaltochecklikeget, 1);
349}
350// Sanity check on POST
351if (!defined('NOSCANPOSTFORINJECTION') || is_array(constant('NOSCANPOSTFORINJECTION'))) {
353}
354
355// This is to make Dolibarr working with Plesk
356if (!empty($_SERVER['DOCUMENT_ROOT']) && substr($_SERVER['DOCUMENT_ROOT'], -6) !== 'htdocs') {
357 set_include_path($_SERVER['DOCUMENT_ROOT'].'/htdocs');
358}
359
360// Include the conf.php and functions.lib.php and security.lib.php. This defined the constants like DOL_DOCUMENT_ROOT, DOL_DATA_ROOT, DOL_URL_ROOT...
361require_once 'filefunc.inc.php';
378// If there is a POST parameter to tell to save automatically some POST parameters into cookies, we do it.
379// This is used for example by form of boxes to save personalization of some options.
380// DOL_AUTOSET_COOKIE=cookiename:val1,val2 and cookiename_val1=aaa cookiename_val2=bbb will set cookie_name with value json_encode(array('val1'=> , ))
381if (GETPOST("DOL_AUTOSET_COOKIE")) {
382 $tmpautoset = explode(':', GETPOST("DOL_AUTOSET_COOKIE"), 2);
383 $tmplist = explode(',', $tmpautoset[1]);
384 $cookiearrayvalue = array();
385 foreach ($tmplist as $tmpkey) {
386 $postkey = $tmpautoset[0].'_'.$tmpkey;
387 //var_dump('tmpkey='.$tmpkey.' postkey='.$postkey.' value='.GETPOST($postkey);
388 if (GETPOST($postkey)) {
389 $cookiearrayvalue[$tmpkey] = GETPOST($postkey);
390 }
391 }
392 $cookiename = $tmpautoset[0];
393 $cookievalue = json_encode($cookiearrayvalue);
394 //var_dump('setcookie cookiename='.$cookiename.' cookievalue='.$cookievalue);
395 if (PHP_VERSION_ID < 70300) {
396 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : (time() + (86400 * 354)), '/', '', !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // keep cookie 1 year and add tag httponly
397 } else {
398 // Only available for php >= 7.3
399 $cookieparams = array(
400 'expires' => empty($cookievalue) ? 0 : (time() + (86400 * 354)),
401 'path' => '/',
402 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
403 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
404 'httponly' => true,
405 'samesite' => 'Lax' // None || Lax || Strict
406 );
407 setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
408 }
409 if (empty($cookievalue)) {
410 unset($_COOKIE[$cookiename]);
411 }
412}
413
414// Set the handler of session
415// if (ini_get('session.save_handler') == 'user')
416if (!empty($php_session_save_handler) && $php_session_save_handler == 'db') {
417 require_once 'core/lib/phpsessionin'.$php_session_save_handler.'.lib.php';
418}
419
420// Init session. Name of session is specific to Dolibarr instance.
421// Must be done after the include of filefunc.inc.php so global variables of conf file are defined (like $dolibarr_main_instance_unique_id or $dolibarr_main_force_https).
422// Note: the function dol_getprefix() is defined into functions.lib.php but may have been defined to return a different key to manage another area to protect.
423$prefix = dol_getprefix('');
424$sessionname = 'DOLSESSID_'.$prefix;
425$sessiontimeout = 'DOLSESSTIMEOUT_'.$prefix;
426if (!empty($_COOKIE[$sessiontimeout])) {
427 ini_set('session.gc_maxlifetime', $_COOKIE[$sessiontimeout]);
428}
429
430// This create lock, released by session_write_close() or end of page.
431// We need this lock as long as we read/write $_SESSION ['vars']. We can remove lock when finished.
432if (!defined('NOSESSION')) {
433 if (PHP_VERSION_ID < 70300) {
434 session_set_cookie_params(0, '/', null, !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // Add tag secure and httponly on session cookie (same as setting session.cookie_httponly into php.ini). Must be called before the session_start.
435 } else {
436 // Only available for php >= 7.3
437 $sessioncookieparams = array(
438 'lifetime' => 0,
439 'path' => '/',
440 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
441 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
442 'httponly' => true,
443 'samesite' => 'Lax' // None || Lax || Strict
444 );
445 session_set_cookie_params($sessioncookieparams);
446 }
447 session_name($sessionname);
448 dol_session_start(); // This call the open and read of session handler
449 //exit; // this exist generates a call to write and close
450}
451
452
453// Init the 6 global objects, this include will make the 'new Xxx()' and set properties for: $conf, $db, $langs, $user, $mysoc, $hookmanager
454require_once 'master.inc.php';
455
456// Uncomment this and set session.save_handler = user to use local session storing
457// include DOL_DOCUMENT_ROOT.'/core/lib/phpsessionindb.inc.php
458
459// If software has been locked. Only login $conf->global->MAIN_ONLY_LOGIN_ALLOWED is allowed.
460if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
461 $ok = 0;
462 if ((!session_id() || !isset($_SESSION["dol_login"])) && !isset($_POST["username"]) && !empty($_SERVER["GATEWAY_INTERFACE"])) {
463 $ok = 1; // We let working pages if not logged and inside a web browser (login form, to allow login by admin)
464 } elseif (isset($_POST["username"]) && in_array($_POST["username"], explode(';', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')))) {
465 $ok = 1; // We let working pages that is a login submission (login submit, to allow login by admin)
466 } elseif (defined('NOREQUIREDB')) {
467 $ok = 1; // We let working pages that don't need database access (xxx.css.php)
468 } elseif (defined('EVEN_IF_ONLY_LOGIN_ALLOWED')) {
469 $ok = 1; // We let working pages that ask to work even if only login enabled (logout.php)
470 } elseif (session_id() && isset($_SESSION["dol_login"]) && in_array($_SESSION["dol_login"], explode(';', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')))) {
471 $ok = 1; // We let working if user is allowed admin
472 }
473 if (!$ok) {
474 if (session_id() && isset($_SESSION["dol_login"]) && !in_array($_SESSION["dol_login"], explode(';', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')))) {
475 print 'Sorry, your application is offline.'."\n";
476 print 'You are logged with user "'.$_SESSION["dol_login"].'" and only administrator users (' . str_replace(';', ', ', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')).') is allowed to connect for the moment.'."\n";
477 $nexturl = DOL_URL_ROOT.'/user/logout.php?token='.newToken();
478 print 'Please try later or <a href="'.$nexturl.'">click here to disconnect and change login user</a>...'."\n";
479 } else {
480 print 'Sorry, your application is offline. Only administrator users (' . str_replace(';', ', ', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')).') is allowed to connect for the moment.'."\n";
481 $nexturl = DOL_URL_ROOT.'/';
482 print 'Please try later or <a href="'.$nexturl.'">click here to change login user</a>...'."\n";
483 }
484 exit;
485 }
486}
487
488
489// Activate end of page function
490register_shutdown_function('dol_shutdown');
491
492// Load debugbar
493if (isModEnabled('debugbar') && !GETPOST('dol_use_jmobile') && empty($_SESSION['dol_use_jmobile'])) {
494 global $debugbar;
495 include_once DOL_DOCUMENT_ROOT.'/debugbar/class/DebugBar.php';
496 $debugbar = new DolibarrDebugBar();
497 $renderer = $debugbar->getJavascriptRenderer();
498 if (!getDolGlobalString('MAIN_HTML_HEADER')) {
499 $conf->global->MAIN_HTML_HEADER = '';
500 }
501 $conf->global->MAIN_HTML_HEADER .= $renderer->renderHead();
502
503 '@phan-var-force array{time:DebugBar\DataCollector\TimeDataCollector} $debugbar';
504 $debugbar['time']->startMeasure('pageaftermaster', 'Page generation (after environment init)');
505}
506
507// Detection browser
508if (isset($_SERVER["HTTP_USER_AGENT"])) {
509 $tmp = getBrowserInfo($_SERVER["HTTP_USER_AGENT"]);
510 $conf->browser->name = $tmp['browsername'];
511 $conf->browser->os = $tmp['browseros'];
512 $conf->browser->version = $tmp['browserversion'];
513 $conf->browser->ua = $tmp['browserua'];
514 $conf->browser->layout = $tmp['layout']; // 'classic', 'phone', 'tablet'
515 //var_dump($conf->browser);
516
517 if ($conf->browser->layout == 'phone') {
518 $conf->dol_no_mouse_hover = 1;
519 }
520}
521
522// If theme is forced
523if (GETPOST('theme', 'aZ09')) {
524 $conf->theme = GETPOST('theme', 'aZ09');
525 $conf->css = "/theme/".$conf->theme."/style.css.php";
526}
527
528// Set global MAIN_OPTIMIZEFORTEXTBROWSER (must be before login part)
529if (GETPOSTINT('textbrowser') || (!empty($conf->browser->name) && $conf->browser->name == 'textbrowser')) { // If we must enable text browser
530 $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = 2;
531}
532
533// Force HTTPS if required ($conf->file->main_force_https is 0/1 or 'https dolibarr root url')
534// $_SERVER["HTTPS"] is 'on' when link is https, otherwise $_SERVER["HTTPS"] is empty or 'off'
535if (!empty($conf->file->main_force_https) && !isHTTPS() && !defined('NOHTTPSREDIRECT')) {
536 $newurl = '';
537 if (is_numeric($conf->file->main_force_https)) {
538 if ($conf->file->main_force_https == '1' && !empty($_SERVER["SCRIPT_URI"])) { // If SCRIPT_URI supported by server
539 if (preg_match('/^http:/i', $_SERVER["SCRIPT_URI"]) && !preg_match('/^https:/i', $_SERVER["SCRIPT_URI"])) { // If link is http
540 $newurl = preg_replace('/^http:/i', 'https:', $_SERVER["SCRIPT_URI"]);
541 }
542 } else {
543 // If HTTPS is not defined in DOL_MAIN_URL_ROOT,
544 // Check HTTPS environment variable (Apache/mod_ssl only)
545 $newurl = preg_replace('/^http:/i', 'https:', DOL_MAIN_URL_ROOT).$_SERVER["REQUEST_URI"];
546 }
547 } else {
548 // Check HTTPS environment variable (Apache/mod_ssl only)
549 $newurl = $conf->file->main_force_https.$_SERVER["REQUEST_URI"];
550 }
551 // Start redirect
552 if ($newurl) {
553 header_remove(); // Clean header already set to be sure to remove any header like "Set-Cookie: DOLSESSID_..." from non HTTPS answers
554 dol_syslog("main.inc: dolibarr_main_force_https is on, we make a redirect to ".$newurl);
555 header("Location: ".$newurl);
556 exit;
557 } else {
558 dol_syslog("main.inc: dolibarr_main_force_https is on but we failed to forge new https url so no redirect is done", LOG_WARNING);
559 }
560}
561
562if (!defined('NOLOGIN') && !defined('NOIPCHECK') && !empty($dolibarr_main_restrict_ip)) {
563 $listofip = explode(',', $dolibarr_main_restrict_ip);
564 $found = false;
565 foreach ($listofip as $ip) {
566 $ip = trim($ip);
567 if ($ip == $_SERVER['REMOTE_ADDR']) {
568 $found = true;
569 break;
570 }
571 }
572 if (!$found) {
573 print 'Access refused by IP protection. Your detected IP is '.$_SERVER['REMOTE_ADDR'];
574 exit;
575 }
576}
577
578// Loading of additional presentation includes
579if (!defined('NOREQUIREHTML')) {
580 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; // Need 660ko memory (800ko in 2.2)
581}
582if (!defined('NOREQUIREAJAX')) {
583 require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; // Need 22ko memory
584}
585
586// If install or upgrade process not done or not completely finished, we call the install page.
587if (getDolGlobalString('MAIN_NOT_INSTALLED') || getDolGlobalString('MAIN_NOT_UPGRADED')) {
588 dol_syslog("main.inc: A previous install or upgrade was not complete. Redirect to install page.", LOG_WARNING);
589 header("Location: ".DOL_URL_ROOT."/install/index.php");
590 exit;
591}
592// If an upgrade process is required, we call the install page.
593$checkifupgraderequired = false;
594if (getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') && getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') != DOL_VERSION) {
595 $checkifupgraderequired = true;
596}
597if (!getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') && getDolGlobalString('MAIN_VERSION_LAST_INSTALL') && getDolGlobalString('MAIN_VERSION_LAST_INSTALL') != DOL_VERSION) {
598 $checkifupgraderequired = true;
599}
600if ($checkifupgraderequired) {
601 $versiontocompare = getDolGlobalString('MAIN_VERSION_LAST_UPGRADE', getDolGlobalString('MAIN_VERSION_LAST_INSTALL'));
602 require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
603 $dolibarrversionlastupgrade = preg_split('/[.-]/', $versiontocompare);
604 $dolibarrversionprogram = preg_split('/[.-]/', DOL_VERSION);
605 $rescomp = versioncompare($dolibarrversionprogram, $dolibarrversionlastupgrade);
606 if ($rescomp > 0) { // Programs have a version higher than database.
607 if (!getDolGlobalString('MAIN_NO_UPGRADE_REDIRECT_ON_LEVEL_3_CHANGE') || $rescomp < 3) {
608 // We did not add "&& $rescomp < 3" because we want upgrade process for build upgrades
609 dol_syslog("main.inc: database version ".$versiontocompare." is lower than programs version ".DOL_VERSION.". Redirect to install/upgrade page.", LOG_WARNING);
610 if (php_sapi_name() === "cli") {
611 print "main.inc: database version ".$versiontocompare." is lower than programs version ".DOL_VERSION.". Try to run upgrade process.\n";
612 } else {
613 header("Location: ".DOL_URL_ROOT."/install/index.php");
614 }
615 exit;
616 }
617 }
618}
619
620// Creation of a token against CSRF vulnerabilities
621if (!defined('NOTOKENRENEWAL') && !defined('NOSESSION')) {
622 // No token renewal on .css.php, .js.php and .json.php (even if the NOTOKENRENEWAL was not provided)
623 if (!preg_match('/\.(css|js|json)\.php$/', $_SERVER["PHP_SELF"])) {
624 // Rolling token at each call ($_SESSION['token'] contains token of previous page)
625 if (isset($_SESSION['newtoken'])) {
626 $_SESSION['token'] = $_SESSION['newtoken'];
627 }
628
629 if (!isset($_SESSION['newtoken']) || getDolGlobalInt('MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL')) {
630 // Note: Using MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL is not recommended: if a user succeed in entering a data from
631 // a public page with a link that make a token regeneration, it can make use of the backoffice no more possible !
632 // Save in $_SESSION['newtoken'] what will be next token. Into forms, we will add param token = $_SESSION['newtoken']
633 $token = dol_hash(uniqid((string) mt_rand(), false), 'md5'); // Generates a hash of a random number. We don't need a secured hash, just a changing random value.
634 $_SESSION['newtoken'] = $token;
635 dol_syslog("NEW TOKEN generated by : ".$_SERVER['PHP_SELF'], LOG_DEBUG);
636 }
637 }
638}
639
640//dol_syslog("CSRF info: ".defined('NOCSRFCHECK')." - ".$dolibarr_nocsrfcheck." - ".$conf->global->MAIN_SECURITY_CSRF_WITH_TOKEN." - ".$_SERVER['REQUEST_METHOD']." - ".GETPOST('token', 'alpha'));
641
642// Check validity of token, only if option MAIN_SECURITY_CSRF_WITH_TOKEN enabled or if constant CSRFCHECK_WITH_TOKEN is set into page
643if ((!defined('NOCSRFCHECK') && empty($dolibarr_nocsrfcheck) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) || defined('CSRFCHECK_WITH_TOKEN')) {
644 $tmpaction = GETPOST('action', 'aZ09');
645 // Array of action code where CSRFCHECK with token will be forced (so token must be provided on url request)
646 $sensitiveget = false;
647 if ((GETPOSTISSET('massaction') || $tmpaction) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 3) {
648 // All GET actions (except the listed exceptions that are usually post for pre-actions and not real action) and mass actions are processed as sensitive.
649 // We exclude some action that are not sensitive so legitimate
650 if (GETPOSTISSET('massaction') || (strpos($tmpaction, 'display') !== 0 && !in_array($tmpaction, array('create', 'create2', 'createsite', 'createcard', 'edit', 'editcontract', 'editvalidator', 'file_manager', 'presend', 'presend_addmessage', 'preview', 'reconcile', 'specimen')))) {
651 $sensitiveget = true;
652 }
653 } elseif (getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 2) {
654 // Few GET actions coded with a &token into url are also processed as sensitive.
655 $arrayofactiontoforcetokencheck = array(
656 'activate',
657 'doprev', 'donext', 'dvprev', 'dvnext',
658 'freezone', 'install',
659 'reopen'
660 );
661 if (in_array($tmpaction, $arrayofactiontoforcetokencheck)) {
662 $sensitiveget = true;
663 }
664 // We also need a valid token for actions matching one of these values
665 if (preg_match('/^(confirm_)?(add|classify|close|confirm|copy|del|disable|enable|remove|set|unset|update|save)/', $tmpaction)) {
666 $sensitiveget = true;
667 }
668 }
669
670 // Check a token is provided for all cases that need a mandatory token
671 // (all POST actions + all sensitive GET actions + all mass actions + all login/actions/logout on pages with CSRFCHECK_WITH_TOKEN set)
672 if (
673 (!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') ||
674 $sensitiveget ||
675 GETPOSTISSET('massaction') ||
676 ((GETPOSTISSET('actionlogin') || GETPOSTISSET('action')) && defined('CSRFCHECK_WITH_TOKEN'))
677 ) {
678 // If token is not provided or empty, error (we are in case it is mandatory)
679 if (!GETPOST('token', 'alpha') || GETPOST('token', 'alpha') == 'notrequired') {
680 top_httphead();
681 if (GETPOSTINT('uploadform')) {
682 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." refused. File size too large or not provided.");
683 $langs->loadLangs(array("errors", "install"));
684 print $langs->trans("ErrorFileSizeTooLarge").' ';
685 print $langs->trans("ErrorGoBackAndCorrectParameters");
686 } else {
687 http_response_code(403);
688 if (defined('CSRFCHECK_WITH_TOKEN')) {
689 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." refused by CSRF protection (CSRFCHECK_WITH_TOKEN protection) in main.inc.php. Token not provided.", LOG_WARNING);
690 print "Access to a page that needs a token (constant CSRFCHECK_WITH_TOKEN is defined) is refused by CSRF protection in main.inc.php. Token not provided.\n";
691 } else {
692 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." refused by CSRF protection (POST method or GET with a sensible value for 'action' parameter) in main.inc.php. Token not provided.", LOG_WARNING);
693 print "Access to this page this way (POST method or GET with a sensible value for 'action' parameter) is refused by CSRF protection in main.inc.php. Token not provided.\n";
694 print "If you access your server behind a proxy using url rewriting and the parameter is provided by caller, you might check that all HTTP header are propagated (or add the line \$dolibarr_nocsrfcheck=1 into your conf.php file or MAIN_SECURITY_CSRF_WITH_TOKEN to 0";
695 if (getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN')) {
696 print " instead of " . getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN');
697 }
698 print " into setup).\n";
699 }
700 }
701 die;
702 }
703 }
704
705 $sessiontokenforthisurl = (empty($_SESSION['token']) ? '' : $_SESSION['token']);
706 // TODO Get the sessiontokenforthisurl into an array of session token (one array per base URL so we can use the CSRF per page and we keep ability for several tabs per url in a browser)
707 if (GETPOSTISSET('token') && GETPOST('token') != 'notrequired' && GETPOST('token', 'alpha') != $sessiontokenforthisurl) {
708 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." refused by CSRF protection (invalid token), so we disable POST and some GET parameters - referrer=".(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']).", action=".GETPOST('action', 'aZ09').", _GET|POST['token']=".GETPOST('token', 'alpha'), LOG_WARNING);
709 //dol_syslog("_SESSION['token']=".$sessiontokenforthisurl, LOG_DEBUG);
710 // Do not output anything on standard output because this create problems when using the BACK button on browsers. So we just set a message into session.
711 if (!defined('NOTOKENRENEWAL')) {
712 // If the page is not a page that disable the token renewal, we report a warning message to explain token has epired.
713 setEventMessages('SecurityTokenHasExpiredSoActionHasBeenCanceledPleaseRetry', null, 'warnings', '', 1);
714 }
715 $savid = null;
716 if (isset($_POST['id'])) {
717 $savid = ((int) $_POST['id']);
718 }
719 unset($_POST);
720 unset($_GET['confirm']);
721 unset($_GET['action']);
722 unset($_GET['confirmmassaction']);
723 unset($_GET['massaction']);
724 unset($_GET['token']); // TODO Make a redirect if we have a token in url to remove it ?
725 if (isset($savid)) {
726 $_POST['id'] = ((int) $savid);
727 }
728 // So rest of code can know something was wrong here
729 $_GET['errorcode'] = 'InvalidToken';
730 }
731
732 // Note: There is another CSRF protection into the filefunc.inc.php
733}
734
735if (!empty($dolibarr_main_demo)) {
736 // Disable modules (this must be after session_start and after conf has been loaded)
737 if (GETPOSTISSET('disablemodules')) {
738 $_SESSION["disablemodules"] = GETPOST('disablemodules', 'alpha');
739 }
740 if (!empty($_SESSION["disablemodules"])) {
741 $modulepartkeys = array('css', 'js', 'tabs', 'triggers', 'login', 'substitutions', 'menus', 'theme', 'sms', 'tpl', 'barcode', 'models', 'societe', 'hooks', 'dir', 'syslog', 'tpllinkable', 'contactelement', 'moduleforexternal', 'websitetemplates');
742
743 $disabled_modules = explode(',', $_SESSION["disablemodules"]);
744 foreach ($disabled_modules as $module) {
745 if ($module) {
746 if (empty($conf->$module)) {
747 $conf->$module = new stdClass(); // To avoid warnings
748 }
749
750 $conf->$module->enabled = false; // Old usage
751 unset($conf->modules[$module]);
752
753 foreach ($modulepartkeys as $modulepartkey) {
754 unset($conf->modules_parts[$modulepartkey][$module]);
755 }
756 if ($module == 'fournisseur') { // Special case
757 $conf->supplier_order->enabled = 0; // Old usage
758 $conf->supplier_invoice->enabled = 0; // Old usage
759 unset($conf->modules['supplier_order']);
760 unset($conf->modules['supplier_invoice']);
761 }
762 }
763 }
764 }
765}
766
767// Set current modulepart
768$modulepart = explode("/", $_SERVER["PHP_SELF"]);
769if (is_array($modulepart) && count($modulepart) > 0) {
770 foreach ($conf->modules as $module) {
771 if (in_array($module, $modulepart)) {
772 $modulepart = $module;
773 break;
774 }
775 }
776}
777if (is_array($modulepart)) {
778 $modulepart = '';
779}
780
781
782/*
783 * Phase authentication / login
784 */
785
786$login = '';
787$error = 0;
788if (!defined('NOLOGIN')) {
789 // $authmode lists the different method of identification to be tested in order of preference.
790 // Example: 'http', 'dolibarr', 'ldap', 'http,forceuser', '...'
791
792 if (defined('MAIN_AUTHENTICATION_MODE')) {
793 $dolibarr_main_authentication = constant('MAIN_AUTHENTICATION_MODE');
794 } else {
795 // Authentication mode
796 if (empty($dolibarr_main_authentication)) {
797 $dolibarr_main_authentication = 'dolibarr';
798 }
799 // Authentication mode: forceuser
800 if ($dolibarr_main_authentication == 'forceuser' && empty($dolibarr_auto_user)) {
801 $dolibarr_auto_user = 'auto';
802 }
803 }
804 // Set authmode
805 $authmode = explode(',', $dolibarr_main_authentication);
806
807 // No authentication mode
808 if (!count($authmode)) {
809 $langs->load('main');
810 dol_print_error(null, $langs->trans("ErrorConfigParameterNotDefined", 'dolibarr_main_authentication'));
811 exit;
812 }
813
814 // If login request was already post, we retrieve login from the session
815 // Call module if not realized that his request.
816 // At the end of this phase, the variable $login is defined.
817 $resultFetchUser = '';
818 $test = true;
819 $dol_authmode = null;
820
821 if (!isset($_SESSION["dol_login"])) {
822 // It is not already authenticated and it requests the login / password
823 include_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
824
825 $dol_dst_observed = GETPOSTINT("dst_observed", 3);
826 $dol_dst_first = GETPOSTINT("dst_first", 3);
827 $dol_dst_second = GETPOSTINT("dst_second", 3);
828 $dol_screenwidth = GETPOSTINT("screenwidth", 3);
829 $dol_screenheight = GETPOSTINT("screenheight", 3);
830 $dol_hide_topmenu = GETPOSTINT('dol_hide_topmenu', 3);
831 $dol_hide_leftmenu = GETPOSTINT('dol_hide_leftmenu', 3);
832 $dol_optimize_smallscreen = GETPOSTINT('dol_optimize_smallscreen', 3);
833 $dol_no_mouse_hover = GETPOSTINT('dol_no_mouse_hover', 3);
834 $dol_use_jmobile = GETPOSTINT('dol_use_jmobile', 3); // 0=default, 1=to say we use app from a webview app, 2=to say we use app from a webview app and keep ajax
835
836 // If in demo mode, we check we go to home page through the public/demo/index.php page
837 if (!empty($dolibarr_main_demo) && $_SERVER['PHP_SELF'] == DOL_URL_ROOT.'/index.php') { // We ask index page
838 if (empty($_SERVER['HTTP_REFERER']) || !preg_match('/public/', $_SERVER['HTTP_REFERER'])) {
839 dol_syslog("Call index page from another url than demo page (call is done from page ".(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']).")");
840 $url = '';
841 $url .= ($url ? '&' : '').($dol_hide_topmenu ? 'dol_hide_topmenu='.$dol_hide_topmenu : '');
842 $url .= ($url ? '&' : '').($dol_hide_leftmenu ? 'dol_hide_leftmenu='.$dol_hide_leftmenu : '');
843 $url .= ($url ? '&' : '').($dol_optimize_smallscreen ? 'dol_optimize_smallscreen='.$dol_optimize_smallscreen : '');
844 $url .= ($url ? '&' : '').($dol_no_mouse_hover ? 'dol_no_mouse_hover='.$dol_no_mouse_hover : '');
845 $url .= ($url ? '&' : '').($dol_use_jmobile ? 'dol_use_jmobile='.$dol_use_jmobile : '');
846 $url = DOL_URL_ROOT.'/public/demo/index.php'.($url ? '?'.$url : '');
847 header("Location: ".$url);
848 exit;
849 }
850 }
851
852 // Hooks for security access
853 $action = '';
854 $hookmanager->initHooks(array('login'));
855 $parameters = array();
856 $reshook = $hookmanager->executeHooks('beforeLoginAuthentication', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
857 if ($reshook < 0) {
858 $test = false;
859 $error++;
860 }
861
862 // Verification security graphic code
863 if ($test && GETPOST('actionlogin', 'aZ09') == 'login' && GETPOST("username", "alpha", 2) && getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA') && !isset($_SESSION['dol_bypass_antispam'])) {
864 $ok = false;
865
866 // Use the captcha handler to validate
867 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
868 $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard');
869
870 // List of directories where we can find captcha handlers
871 $dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), isset($conf->modules_parts['captcha']) && is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array());
872 $fullpathclassfile = '';
873 foreach ($dirModCaptcha as $dir) {
874 $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2);
875 if ($fullpathclassfile) {
876 break;
877 }
878 }
879
880 // The file for captcha check has been found
881 if ($fullpathclassfile) {
882 include_once $fullpathclassfile;
883 $captchaobj = null;
884
885 // Charging the numbering class
886 $classname = "modCaptcha".ucfirst($captcha);
887 if (class_exists($classname)) {
889 $captchaobj = new $classname($db, $conf, $langs, $user);
890 '@phan-var-force ModeleCaptcha $captchaobj';
891
892 if (is_object($captchaobj) && method_exists($captchaobj, 'validateCodeAfterLoginSubmit')) {
893 $ok = $captchaobj->validateCodeAfterLoginSubmit(); // @phan-suppress-current-line PhanUndeclaredMethod
894 } else {
895 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler '.get_class($captchaobj).' does not have any method validateCodeAfterLoginSubmit()';
896 $test = false;
897 $error++;
898 }
899 } else {
900 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler class '.$classname.' was not found after the include';
901 $test = false;
902 $error++;
903 }
904 } else {
905 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha);
906 $test = false;
907 $error++;
908 }
909
910 // Process error of captcha validation
911 if (!$ok) {
912 dol_syslog('Bad value for code, connection refused', LOG_NOTICE);
913 // Load translation files required by page
914 $langs->loadLangs(array('main', 'errors'));
915
916 $_SESSION["dol_loginmesg"] = (empty($_SESSION["dol_loginmesg"]) ? "" : $_SESSION["dol_loginmesg"]."<br>\n").$langs->transnoentitiesnoconv("ErrorBadValueForCode");
917 $test = false;
918
919 // Call trigger for the "security events" log
920 $user->context['audit'] = 'ErrorBadValueForCode - login='.GETPOST("username", "alpha", 2);
921
922 // Call trigger
923 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
924 if ($result < 0) {
925 $error++;
926 }
927 // End call triggers
928
929 // Hooks on failed login
930 $action = '';
931 $hookmanager->initHooks(array('login'));
932 $parameters = array('dol_authmode' => $authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
933 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
934 if ($reshook < 0) {
935 $error++;
936 }
937
938 // Note: exit is done later ($test is false)
939 }
940 }
941
942 $allowedmethodtopostusername = 3;
943 if (defined('MAIN_AUTHENTICATION_POST_METHOD')) {
944 $allowedmethodtopostusername = constant('MAIN_AUTHENTICATION_POST_METHOD'); // Note a value of 2 is not compatible with some authentication methods that put username as GET parameter
945 }
946 // TODO Remove use of $_COOKIE['login_dolibarr'] by replacing line with $usertotest = GETPOST("username", "alpha", $allowedmethodtopostusername); ?
947 $usertotest = (!empty($_COOKIE['login_dolibarr']) ? preg_replace('/[^a-zA-Z0-9_@\-\.]/', '', $_COOKIE['login_dolibarr']) : GETPOST("username", "alpha", $allowedmethodtopostusername));
948 $passwordtotest = GETPOST('password', 'password', $allowedmethodtopostusername);
949 $entitytotest = (GETPOSTINT('entity') ? GETPOSTINT('entity') : (!empty($conf->entity) ? $conf->entity : 1));
950
951 // Define if we received the correct data to go into the test of the login with the checkLoginPassEntity().
952 $goontestloop = false;
953 if (isset($_SERVER["REMOTE_USER"]) && in_array('http', $authmode)) { // For http basic login test
954 $goontestloop = true;
955 }
956 if ($dolibarr_main_authentication == 'forceuser' && !empty($dolibarr_auto_user)) { // For automatic login with a forced user
957 $goontestloop = true;
958 }
959 if (GETPOST("username", "alpha", $allowedmethodtopostusername)) { // For posting the login form
960 $goontestloop = true;
961 }
962 if (GETPOST('openid_mode', 'alpha')) { // For openid_connect ?
963 $goontestloop = true;
964 }
965 if (GETPOST('beforeoauthloginredirect') || GETPOST('afteroauthloginreturn')) { // For oauth login
966 $goontestloop = true;
967 }
968 if (!empty($_COOKIE['login_dolibarr'])) { // TODO For ? Remove this ?
969 $goontestloop = true;
970 }
971
972 if (!is_object($langs)) { // This can occurs when calling page with NOREQUIRETRAN defined, however we need langs for error messages.
973 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
974 $langs = new Translate("", $conf);
975 $langcode = (GETPOST('lang', 'aZ09', 1) ? GETPOST('lang', 'aZ09', 1) : getDolGlobalString('MAIN_LANG_DEFAULT', 'auto'));
976 if (defined('MAIN_LANG_DEFAULT')) {
977 $langcode = constant('MAIN_LANG_DEFAULT');
978 }
979 $langs->setDefaultLang($langcode);
980 }
981
982 // Validation of login/pass/entity
983 // If ok, the variable login will be returned
984 // If error, we will put error message in session under the name dol_loginmesg
985 if ($test && $goontestloop && GETPOST('actionlogin', 'aZ09') != 'disabled' && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {
986 // Loop on each test mode defined into $authmode
987 // $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');
988 $oauthmodetotestarray = array('google');
989 foreach ($oauthmodetotestarray as $oauthmodetotest) {
990 if (in_array($oauthmodetotest.'oauth', $authmode)) { // This is an authmode that is currently qualified. Do we have to remove it ?
991 // If we click on the link to use OAuth authentication or if we goes after callback return, we do nothing
992 // TODO Use: if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {
993 if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn')) {
994 continue;
995 }
996 dol_syslog("User did not click on link for OAuth, or is not on the OAuth return, so we disable check using ".$oauthmodetotest);
997 foreach ($authmode as $tmpkey => $tmpval) {
998 if ($tmpval == $oauthmodetotest.'oauth') {
999 unset($authmode[$tmpkey]);
1000 break;
1001 }
1002 }
1003 }
1004 }
1005
1006 // Check login for all qualified modes in array $authmode.
1007 $login = checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode);
1008 if ($login === '--bad-login-validity--') {
1009 $login = '';
1010 }
1011
1012 if ($login) {
1013 $dol_authmode = $conf->authmode; // This property is defined only when logged, to say what mode was successfully used
1014 $dol_tz = empty($_POST["tz"]) ? (empty($_SESSION["tz"]) ? '' : $_SESSION["tz"]) : $_POST["tz"];
1015 $dol_tz_string = empty($_POST["tz_string"]) ? (empty($_SESSION["tz_string"]) ? '' : $_SESSION["tz_string"]) : $_POST["tz_string"];
1016 $dol_tz_string = preg_replace('/\s*\‍(.+\‍)$/', '', $dol_tz_string);
1017 $dol_tz_string = preg_replace('/,/', '/', $dol_tz_string);
1018 $dol_tz_string = preg_replace('/\s/', '_', $dol_tz_string);
1019 $dol_dst = 0;
1020 // Keep $_POST here. Do not use GETPOSTISSET
1021 $dol_dst_first = empty($_POST["dst_first"]) ? (empty($_SESSION["dst_first"]) ? '' : $_SESSION["dst_first"]) : $_POST["dst_first"];
1022 $dol_dst_second = empty($_POST["dst_second"]) ? (empty($_SESSION["dst_second"]) ? '' : $_SESSION["dst_second"]) : $_POST["dst_second"];
1023 if ($dol_dst_first && $dol_dst_second) {
1024 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1025 $datenow = dol_now();
1026 $datefirst = dol_stringtotime($dol_dst_first);
1027 $datesecond = dol_stringtotime($dol_dst_second);
1028 if ($datenow >= $datefirst && $datenow < $datesecond) {
1029 $dol_dst = 1;
1030 }
1031 }
1032 $dol_screenheight = empty($_POST["screenheight"]) ? (empty($_SESSION["dol_screenheight"]) ? '' : $_SESSION["dol_screenheight"]) : $_POST["screenheight"];
1033 $dol_screenwidth = empty($_POST["screenwidth"]) ? (empty($_SESSION["dol_screenwidth"]) ? '' : $_SESSION["dol_screenwidth"]) : $_POST["screenwidth"];
1034 //print $datefirst.'-'.$datesecond.'-'.$datenow.'-'.$dol_tz.'-'.$dol_tzstring.'-'.$dol_dst.'-'.sdol_screenheight.'-'.sdol_screenwidth; exit;
1035 }
1036
1037 if (!$login) {
1038 dol_syslog('Bad password, connection refused (see a previous notice message for more info)', LOG_NOTICE);
1039 // Load translation files required by page
1040 $langs->loadLangs(array('main', 'errors'));
1041
1042 // Bad password. No authmode has found a good password.
1043 // We set a generic message if not defined inside function checkLoginPassEntity or subfunctions
1044 if (empty($_SESSION["dol_loginmesg"])) {
1045 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadLoginPassword");
1046 }
1047
1048 // Call trigger for the "security events" log
1049 $user->context['audit'] = $langs->trans("ErrorBadLoginPassword").' - login='.GETPOST("username", "alpha", 2);
1050
1051 // Call trigger
1052 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1053 if ($result < 0) {
1054 $error++;
1055 }
1056 // End call triggers
1057
1058 // Hooks on failed login
1059 $action = '';
1060 $hookmanager->initHooks(array('login'));
1061 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1062 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1063 if ($reshook < 0) {
1064 $error++;
1065 }
1066
1067 // Note: exit is done in next chapter
1068 }
1069 }
1070
1071 // End test login / passwords
1072 if (!$login || (in_array('ldap', $authmode) && empty($passwordtotest))) { // With LDAP we refused empty password because some LDAP are "opened" for anonymous access so connection is a success.
1073 // No data to test login, so we show the login page.
1074 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." - action=".GETPOST('action', 'aZ09')." - actionlogin=".GETPOST('actionlogin', 'aZ09')." - showing the login form and exit", LOG_NOTICE);
1075 if (defined('NOREDIRECTBYMAINTOLOGIN')) {
1076 // When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
1077 // See example with selectsearchbox.php. This case is reserved for the selectesearchbox.php so we can
1078 // report a message to ask to login when search ajax component is used after a timeout.
1079 //top_httphead();
1080 return 'ERROR_NOT_LOGGED';
1081 } else {
1082 if (!empty($_SERVER["HTTP_USER_AGENT"]) && $_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
1083 http_response_code(401); // It makes easier to understand if session was broken during security tests
1084 }
1085
1086 // Show login form
1087 dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : '')); // This include http headers
1088 }
1089 exit;
1090 }
1091
1092 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.
1093 if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
1094 dol_syslog('User not found or not valid, connection refused');
1095 session_destroy();
1096 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
1097 session_name($sessionname);
1099
1100 if ($resultFetchUser == 0) {
1101 // Load translation files required by page
1102 $langs->loadLangs(array('main', 'errors'));
1103
1104 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
1105
1106 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
1107 } elseif ($resultFetchUser < 0) {
1108 $_SESSION["dol_loginmesg"] = $user->error;
1109
1110 $user->context['audit'] = $user->error;
1111 } else {
1112 // Load translation files required by the page
1113 $langs->loadLangs(array('main', 'errors'));
1114
1115 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginDateValidity");
1116
1117 $user->context['audit'] = $langs->trans("ErrorLoginDateValidity").' - login='.$login;
1118 }
1119
1120 // Call trigger
1121 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1122 if ($result < 0) {
1123 $error++;
1124 }
1125 // End call triggers
1126
1127
1128 // Hooks on failed login
1129 $action = '';
1130 $hookmanager->initHooks(array('login'));
1131 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1132 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1133 if ($reshook < 0) {
1134 $error++;
1135 }
1136
1137 $paramsurl = array();
1138 if (GETPOSTINT('textbrowser')) {
1139 $paramsurl[] = 'textbrowser='.GETPOSTINT('textbrowser');
1140 }
1141 if (GETPOSTINT('nojs')) {
1142 $paramsurl[] = 'nojs='.GETPOSTINT('nojs');
1143 }
1144 if (GETPOST('lang', 'aZ09')) {
1145 $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
1146 }
1147 header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
1148 exit;
1149 } else {
1150 // User is loaded, we may need to change language for him according to its choice
1151 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1152 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1153 }
1154 }
1155 } else {
1156 // We are already into an authenticated session
1157 $login = $_SESSION["dol_login"];
1158 $entity = isset($_SESSION["dol_entity"]) ? $_SESSION["dol_entity"] : 0;
1159 dol_syslog("- This is an already logged session. _SESSION['dol_login']=".$login." _SESSION['dol_entity']=".$entity, LOG_DEBUG);
1160
1161 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entity > 0 ? $entity : -1));
1162
1163 //var_dump(dol_print_date($user->flagdelsessionsbefore, 'dayhour', 'gmt')." ".dol_print_date($_SESSION["dol_logindate"], 'dayhour', 'gmt'));
1164
1165 if ($resultFetchUser <= 0
1166 || ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"])
1167 || ($user->status != $user::STATUS_ENABLED)
1168 || ($user->isNotIntoValidityDateRange())) {
1169 if ($resultFetchUser <= 0) {
1170 // Account has been removed after login
1171 dol_syslog("Can't load user even if session logged. _SESSION['dol_login']=".$login, LOG_WARNING);
1172 } elseif ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"]) {
1173 // Session is no more valid
1174 dol_syslog("The user has a date for session invalidation = ".$user->flagdelsessionsbefore." and a session date = ".$_SESSION["dol_logindate"].". We must invalidate its sessions.");
1175 } elseif ($user->status != $user::STATUS_ENABLED) {
1176 // User is not enabled
1177 dol_syslog("The user login is disabled");
1178 } else {
1179 // User validity dates are no more valid
1180 dol_syslog("The user login has a validity between [".$user->datestartvalidity." and ".$user->dateendvalidity."], current date is ".dol_now());
1181 }
1182 session_destroy();
1183 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
1184 session_name($sessionname);
1186
1187 if ($resultFetchUser == 0) {
1188 $langs->loadLangs(array('main', 'errors'));
1189
1190 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
1191
1192 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
1193 } elseif ($resultFetchUser < 0) {
1194 $_SESSION["dol_loginmesg"] = $user->error;
1195
1196 $user->context['audit'] = $user->error;
1197 } else {
1198 $langs->loadLangs(array('main', 'errors'));
1199
1200 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorSessionInvalidatedAfterPasswordChange");
1201
1202 $user->context['audit'] = 'ErrorUserSessionWasInvalidated - login='.$login;
1203 }
1204
1205 // Call trigger
1206 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1207 if ($result < 0) {
1208 $error++;
1209 }
1210 // End call triggers
1211
1212 // Hooks on failed login
1213 $action = '';
1214 $hookmanager->initHooks(array('login'));
1215 $parameters = array('dol_authmode' => (string) $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1216 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1217 if ($reshook < 0) {
1218 $error++;
1219 }
1220
1221 $paramsurl = array();
1222 if (GETPOSTINT('textbrowser')) {
1223 $paramsurl[] = 'textbrowser='.GETPOSTINT('textbrowser');
1224 }
1225 if (GETPOSTINT('nojs')) {
1226 $paramsurl[] = 'nojs='.GETPOSTINT('nojs');
1227 }
1228 if (GETPOST('lang', 'aZ09')) {
1229 $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
1230 }
1231
1232 header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
1233 exit;
1234 } else {
1235 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
1236 $hookmanager->initHooks(array('main'));
1237
1238 // Code for search criteria persistence.
1239 if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1240 $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]);
1241 $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server
1242 // Clean $relativepathstring
1243 if (constant('DOL_URL_ROOT')) {
1244 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
1245 }
1246 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
1247 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1248 //var_dump($relativepathstring);
1249
1250 // We click on a link that leave a page we have to save search criteria, contextpage, limit and page and mode. We save them from tmp to no tmp
1251 if (!empty($_SESSION['lastsearch_values_tmp_'.$relativepathstring])) {
1252 $_SESSION['lastsearch_values_'.$relativepathstring] = $_SESSION['lastsearch_values_tmp_'.$relativepathstring];
1253 unset($_SESSION['lastsearch_values_tmp_'.$relativepathstring]);
1254 }
1255 if (!empty($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring])) {
1256 $_SESSION['lastsearch_contextpage_'.$relativepathstring] = $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring];
1257 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
1258 }
1259 if (!empty($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] != $conf->liste_limit) {
1260 $_SESSION['lastsearch_limit_'.$relativepathstring] = $_SESSION['lastsearch_limit_tmp_'.$relativepathstring];
1261 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
1262 }
1263 if (!empty($_SESSION['lastsearch_page_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_page_tmp_'.$relativepathstring] > 0) {
1264 $_SESSION['lastsearch_page_'.$relativepathstring] = $_SESSION['lastsearch_page_tmp_'.$relativepathstring];
1265 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
1266 }
1267 if (!empty($_SESSION['lastsearch_mode_tmp_'.$relativepathstring])) {
1268 $_SESSION['lastsearch_mode_'.$relativepathstring] = $_SESSION['lastsearch_mode_tmp_'.$relativepathstring];
1269 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
1270 }
1271 }
1272 if (!empty($_GET['save_pageforbacktolist']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1273 if (empty($_SESSION['pageforbacktolist'])) {
1274 $pageforbacktolistarray = array();
1275 } else {
1276 $pageforbacktolistarray = $_SESSION['pageforbacktolist'];
1277 }
1278 $tmparray = explode(':', $_GET['save_pageforbacktolist'], 2);
1279 if (!empty($tmparray[0]) && !empty($tmparray[1])) {
1280 $pageforbacktolistarray[$tmparray[0]] = $tmparray[1];
1281 $_SESSION['pageforbacktolist'] = $pageforbacktolistarray;
1282 }
1283 }
1284
1285 $action = '';
1286 $parameters = array();
1287 $reshook = $hookmanager->executeHooks('updateSession', $parameters, $user, $action);
1288 if ($reshook < 0) {
1289 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1290 }
1291 }
1292 }
1293
1294 // Is it a new session that has started ?
1295 // If we are here, this means authentication was successful.
1296 if (!isset($_SESSION["dol_login"])) {
1297 // New session for this login has started.
1298 $error = 0;
1299
1300 // Store value into session (values always stored)
1301 $_SESSION["dol_login"] = $user->login;
1302 $_SESSION["dol_logindate"] = dol_now('gmt');
1303 $_SESSION["dol_authmode"] = isset($dol_authmode) ? $dol_authmode : '';
1304 $_SESSION["dol_tz"] = isset($dol_tz) ? $dol_tz : '';
1305 $_SESSION["dol_tz_string"] = isset($dol_tz_string) ? $dol_tz_string : '';
1306 $_SESSION["dol_dst"] = isset($dol_dst) ? $dol_dst : '';
1307 $_SESSION["dol_dst_observed"] = isset($dol_dst_observed) ? $dol_dst_observed : '';
1308 $_SESSION["dol_dst_first"] = isset($dol_dst_first) ? $dol_dst_first : '';
1309 $_SESSION["dol_dst_second"] = isset($dol_dst_second) ? $dol_dst_second : '';
1310 $_SESSION["dol_screenwidth"] = isset($dol_screenwidth) ? $dol_screenwidth : '';
1311 $_SESSION["dol_screenheight"] = isset($dol_screenheight) ? $dol_screenheight : '';
1312 $_SESSION["dol_company"] = getDolGlobalString("MAIN_INFO_SOCIETE_NOM");
1313 $_SESSION["dol_entity"] = $conf->entity;
1314 // Store value into session (values stored only if defined)
1315 if (!empty($dol_hide_topmenu)) {
1316 $_SESSION['dol_hide_topmenu'] = $dol_hide_topmenu;
1317 }
1318 if (!empty($dol_hide_leftmenu)) {
1319 $_SESSION['dol_hide_leftmenu'] = $dol_hide_leftmenu;
1320 }
1321 if (!empty($dol_optimize_smallscreen)) {
1322 $_SESSION['dol_optimize_smallscreen'] = $dol_optimize_smallscreen;
1323 }
1324 if (!empty($dol_no_mouse_hover)) {
1325 $_SESSION['dol_no_mouse_hover'] = $dol_no_mouse_hover;
1326 }
1327 if (!empty($dol_use_jmobile)) {
1328 $_SESSION['dol_use_jmobile'] = $dol_use_jmobile;
1329 }
1330
1331 dol_syslog("This is a new started user session. _SESSION['dol_login']=".$_SESSION["dol_login"]." Session id=".session_id());
1332
1333 $db->begin();
1334
1335 $user->update_last_login_date();
1336
1337 $loginfo = 'TZ='.$_SESSION["dol_tz"].';TZString='.$_SESSION["dol_tz_string"].';Screen='.$_SESSION["dol_screenwidth"].'x'.$_SESSION["dol_screenheight"];
1338 $loginfo .= ' - authmode='.$dol_authmode.' - entity='.$conf->entity;
1339
1340 // Call triggers for the "security events" log
1341 $user->context['audit'] = $loginfo;
1342 $user->context['authentication_method'] = $dol_authmode;
1343
1344 // Call trigger
1345 $result = $user->call_trigger('USER_LOGIN', $user);
1346 if ($result < 0) {
1347 $error++;
1348 }
1349 // End call triggers
1350
1351 // Hooks on successful login
1352 $action = '';
1353 $hookmanager->initHooks(array('login'));
1354 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginfo' => $loginfo);
1355 $reshook = $hookmanager->executeHooks('afterLogin', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1356 if ($reshook < 0) {
1357 $error++;
1358 }
1359
1360 if ($error) {
1361 $db->rollback();
1362 session_destroy();
1363 dol_print_error($db, 'Error in some triggers USER_LOGIN or in some hooks afterLogin');
1364 exit;
1365 } else {
1366 $db->commit();
1367 }
1368
1369 // Change landing page if defined.
1370 $landingpage = (empty($user->conf->MAIN_LANDING_PAGE) ? (!getDolGlobalString('MAIN_LANDING_PAGE') ? '' : $conf->global->MAIN_LANDING_PAGE) : $user->conf->MAIN_LANDING_PAGE);
1371 if (!empty($landingpage)) { // Example: /index.php
1372 $newpath = dol_buildpath($landingpage, 1);
1373 if ($_SERVER["PHP_SELF"] != $newpath) { // not already on landing page (avoid infinite loop)
1374 header('Location: '.$newpath);
1375 exit;
1376 }
1377 }
1378 }
1379
1380
1381 // If user admin, we force the rights-based modules
1382 if ($user->admin) {
1383 $user->rights->user->user->lire = 1;
1384 $user->rights->user->user->creer = 1;
1385 $user->rights->user->user->password = 1;
1386 $user->rights->user->user->supprimer = 1;
1387 $user->rights->user->self->creer = 1;
1388 $user->rights->user->self->password = 1;
1389
1390 //Required if advanced permissions are used with MAIN_USE_ADVANCED_PERMS
1391 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
1392 if (!$user->hasRight('user', 'user_advance')) {
1393 $user->rights->user->user_advance = new stdClass(); // To avoid warnings
1394 }
1395 if (!$user->hasRight('user', 'self_advance')) {
1396 $user->rights->user->self_advance = new stdClass(); // To avoid warnings
1397 }
1398 if (!$user->hasRight('user', 'group_advance')) {
1399 $user->rights->user->group_advance = new stdClass(); // To avoid warnings
1400 }
1401
1402 $user->rights->user->user_advance->readperms = 1;
1403 $user->rights->user->user_advance->write = 1;
1404 $user->rights->user->self_advance->readperms = 1;
1405 $user->rights->user->self_advance->writeperms = 1;
1406 $user->rights->user->group_advance->read = 1;
1407 $user->rights->user->group_advance->readperms = 1;
1408 $user->rights->user->group_advance->write = 1;
1409 $user->rights->user->group_advance->delete = 1;
1410 }
1411 }
1412
1413 /*
1414 * Overwrite some configs globals (try to avoid this and have code to use instead $user->conf->xxx)
1415 */
1416
1417 // Set liste_limit from user setup
1418 if (isset($user->conf->MAIN_SIZE_LISTE_LIMIT)) { // If a user setup exists
1419 $conf->liste_limit = getDolUserInt('MAIN_SIZE_LISTE_LIMIT'); // Can be 0
1420 }
1421 if ((int) $conf->liste_limit <= 0) {
1422 // Mode automatic. Similar code than into conf.class.php
1423 $conf->liste_limit = 15;
1424 if (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 700) {
1425 $conf->liste_limit = 8;
1426 } elseif (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 910) {
1427 $conf->liste_limit = 10;
1428 } elseif (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] > 1130) {
1429 $conf->liste_limit = 20;
1430 }
1431 }
1432 // Set main_checkbox_left_column from user setup
1433 if (isset($user->conf->MAIN_CHECKBOX_LEFT_COLUMN)) { // If a user setup exists
1434 $conf->main_checkbox_left_column = getDolUserInt('MAIN_CHECKBOX_LEFT_COLUMN'); // Can be 0
1435 }
1436
1437 // Replace conf->css by personalized value if theme not forced
1438 if (!getDolGlobalString('MAIN_FORCETHEME') && getDolUserString('MAIN_THEME')) {
1439 $conf->theme = getDolUserString('MAIN_THEME');
1440 $conf->css = "/theme/".$conf->theme."/style.css.php";
1441 }
1442} else {
1443 // We may have NOLOGIN set, but NOREQUIREUSER not
1444 if (!empty($user) && method_exists($user, 'loadDefaultValues') && !defined('NODEFAULTVALUES')) {
1445 $user->loadDefaultValues(); // Load default values for everybody (works even if $user->id = 0
1446 }
1447}
1448
1449
1450// Case forcing style from url
1451if (GETPOST('theme', 'aZ09')) {
1452 $conf->theme = GETPOST('theme', 'aZ09', 1);
1453 $conf->css = "/theme/".$conf->theme."/style.css.php";
1454}
1455
1456// Set javascript option
1457if (GETPOSTINT('nojs')) { // If javascript was not disabled on URL
1458 $conf->use_javascript_ajax = 0;
1459} else {
1460 if (getDolUserString('MAIN_DISABLE_JAVASCRIPT')) {
1461 $conf->use_javascript_ajax = !getDolUserString('MAIN_DISABLE_JAVASCRIPT') ? 1 : 0;
1462 }
1463}
1464
1465// Set MAIN_OPTIMIZEFORTEXTBROWSER for user (must be after login part)
1466if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1467 $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER');
1468 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') == 1) {
1469 $conf->global->THEME_TOPMENU_DISABLE_IMAGE = 1;
1470 }
1471}
1472//var_dump($conf->global->THEME_TOPMENU_DISABLE_IMAGE);
1473//var_dump($user->conf->THEME_TOPMENU_DISABLE_IMAGE);
1474
1475// set MAIN_OPTIMIZEFORCOLORBLIND for user
1476$conf->global->MAIN_OPTIMIZEFORCOLORBLIND = getDolUserString('MAIN_OPTIMIZEFORCOLORBLIND');
1477
1478// Set terminal output option according to conf->browser.
1479if (GETPOSTINT('dol_hide_leftmenu') || !empty($_SESSION['dol_hide_leftmenu'])) {
1480 $conf->dol_hide_leftmenu = 1;
1481}
1482if (GETPOSTINT('dol_hide_topmenu') || !empty($_SESSION['dol_hide_topmenu'])) {
1483 $conf->dol_hide_topmenu = 1;
1484}
1485if (GETPOSTINT('dol_optimize_smallscreen') || !empty($_SESSION['dol_optimize_smallscreen'])) {
1486 $conf->dol_optimize_smallscreen = 1;
1487}
1488if (GETPOSTINT('dol_no_mouse_hover') || !empty($_SESSION['dol_no_mouse_hover'])) {
1489 $conf->dol_no_mouse_hover = 1;
1490}
1491if (GETPOSTINT('dol_use_jmobile') || !empty($_SESSION['dol_use_jmobile'])) {
1492 $conf->dol_use_jmobile = 1;
1493}
1494// If not on Desktop
1495if (!empty($conf->browser->layout) && $conf->browser->layout != 'classic') {
1496 $conf->dol_no_mouse_hover = 1;
1497}
1498
1499// If on smartphone or optimized for small screen
1500if ((!empty($conf->browser->layout) && $conf->browser->layout == 'phone')
1501 || (!empty($_SESSION['dol_screenwidth']) && $_SESSION['dol_screenwidth'] < 400)
1502 || (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 400
1503 || getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER'))
1504) {
1505 $conf->dol_optimize_smallscreen = 1;
1506
1507 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM') == 1) {
1508 $conf->global->PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE = 0;
1509 }
1510}
1511// Replace themes bugged with jmobile with eldy
1512if (!empty($conf->dol_use_jmobile) && in_array($conf->theme, array('bureau2crea', 'cameleo', 'amarok'))) {
1513 $conf->theme = 'eldy';
1514 $conf->css = "/theme/".$conf->theme."/style.css.php";
1515}
1516
1517if (!defined('NOREQUIRETRAN')) {
1518 if (!GETPOST('lang', 'aZ09')) { // If language was not forced on URL
1519 // If user has chosen its own language
1520 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1521 // If different than current language
1522 //print ">>>".$langs->getDefaultLang()."-".$user->conf->MAIN_LANG_DEFAULT;
1523 if ($langs->getDefaultLang() != $user->conf->MAIN_LANG_DEFAULT) {
1524 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1525 }
1526 }
1527 }
1528}
1529
1530if (!defined('NOLOGIN')) {
1531 // If the login is not recovered, it is identified with an account that does not exist.
1532 // Hacking attempt?
1533 if (!$user->login) {
1535 }
1536
1537 // Check if user is active
1538 if ($user->statut < 1) {
1539 // If not active, we refuse the user
1540 $langs->loadLangs(array("errors", "other"));
1541 dol_syslog("Authentication KO as login is disabled", LOG_NOTICE);
1542 accessforbidden("ErrorLoginDisabled");
1543 }
1544
1545 // Load permissions
1546 $user->loadRights();
1547}
1548
1549dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"].' - action='.GETPOST('action', 'aZ09').', massaction='.GETPOST('massaction', 'aZ09').(defined('NOTOKENRENEWAL') ? ' NOTOKENRENEWAL='.constant('NOTOKENRENEWAL') : ''), LOG_NOTICE);
1550//Another call for easy debug
1551//dol_syslog("Access to ".$_SERVER["PHP_SELF"].' '.$_SERVER["HTTP_REFERER"].' GET='.join(',',array_keys($_GET)).'->'.join(',',$_GET).' POST:'.join(',',array_keys($_POST)).'->'.join(',',$_POST));
1552
1553// Load main languages files
1554if (!defined('NOREQUIRETRAN')) {
1555 // Load translation files required by page
1556 $langs->loadLangs(array('main', 'dict'));
1557}
1558
1559// Define some constants used for style of arrays
1560$bc = array(0 => 'class="impair"', 1 => 'class="pair"');
1561$bcdd = array(0 => 'class="drag drop oddeven"', 1 => 'class="drag drop oddeven"');
1562$bcnd = array(0 => 'class="nodrag nodrop nohover"', 1 => 'class="nodrag nodrop nohoverpair"'); // Used for tr to add new lines
1563$bctag = array(0 => 'class="impair tagtr"', 1 => 'class="pair tagtr"');
1564
1565// Define messages variables
1566$mesg = '';
1567$warning = '';
1568$error = 0;
1569// deprecated, see setEventMessages() and dol_htmloutput_events()
1570$mesgs = array();
1571$warnings = array();
1572$errors = array();
1573
1574// Constants used to defined number of lines in textarea
1575if (empty($conf->browser->firefox)) {
1576 define('ROWS_1', 1);
1577 define('ROWS_2', 2);
1578 define('ROWS_3', 3);
1579 define('ROWS_4', 4);
1580 define('ROWS_5', 5);
1581 define('ROWS_6', 6);
1582 define('ROWS_7', 7);
1583 define('ROWS_8', 8);
1584 define('ROWS_9', 9);
1585} else {
1586 define('ROWS_1', 0);
1587 define('ROWS_2', 1);
1588 define('ROWS_3', 2);
1589 define('ROWS_4', 3);
1590 define('ROWS_5', 4);
1591 define('ROWS_6', 5);
1592 define('ROWS_7', 6);
1593 define('ROWS_8', 7);
1594 define('ROWS_9', 8);
1595}
1596
1597$heightforframes = 50;
1598
1599// Init menu manager
1600if (!defined('NOREQUIREMENU')) {
1601 if (empty($user->socid)) { // If internal user or not defined
1602 $conf->standard_menu = getDolGlobalString('MAIN_MENU_STANDARD_FORCED', getDolGlobalString('MAIN_MENU_STANDARD', 'eldy_menu.php'));
1603 } else {
1604 // If external user
1605 $conf->standard_menu = getDolGlobalString('MAIN_MENUFRONT_STANDARD_FORCED', getDolGlobalString('MAIN_MENUFRONT_STANDARD', 'eldy_menu.php'));
1606 }
1607
1608 // Load the menu manager (only if not already done)
1609 $file_menu = $conf->standard_menu;
1610 if (GETPOST('menu', 'alpha')) {
1611 $file_menu = GETPOST('menu', 'alpha'); // example: menu=eldy_menu.php
1612 }
1613
1614 if (!class_exists('MenuManager')) {
1615 $menufound = 0;
1616 $dirmenus = array_merge(array("/core/menus/"), (array) $conf->modules_parts['menus']);
1617 foreach ($dirmenus as $dirmenu) {
1618 $menufound = dol_include_once($dirmenu."standard/".$file_menu);
1619 if (class_exists('MenuManager')) {
1620 break;
1621 }
1622 }
1623 if (!class_exists('MenuManager')) { // If failed to include, we try with standard eldy_menu.php
1624 dol_syslog("You define a menu manager '".$file_menu."' that can not be loaded.", LOG_WARNING);
1625 $file_menu = 'eldy_menu.php';
1626 include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".$file_menu;
1627 }
1628 }
1629 // @phan-suppress-next-line PhanRedefinedClassReference
1630 $menumanager = new MenuManager($db, empty($user->socid) ? 0 : 1);
1631 // @phan-suppress-next-line PhanRedefinedClassReference
1632 $menumanager->loadMenu();
1633}
1634
1635if (!empty(GETPOST('seteventmessages', 'alpha'))) {
1636 $message = GETPOST('seteventmessages', 'alpha');
1637 $messages = explode(',', $message);
1638 foreach ($messages as $key => $msg) {
1639 $tmp = explode(':', $msg);
1640 setEventMessages($tmp[0], null, !empty($tmp[1]) ? $tmp[1] : 'mesgs');
1641 }
1642}
1643
1644// Functions
1645
1646if (!function_exists("llxHeader")) {
1668 function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
1669 {
1670 global $conf, $hookmanager;
1671
1672 $parameters = array(
1673 'head' => & $head,
1674 'title' => & $title,
1675 'help_url' => & $help_url,
1676 'target' => & $target,
1677 'disablejs' => & $disablejs,
1678 'disablehead' => & $disablehead,
1679 'arrayofjs' => & $arrayofjs,
1680 'arrayofcss' => & $arrayofcss,
1681 'morequerystring' => & $morequerystring,
1682 'morecssonbody' => & $morecssonbody,
1683 'replacemainareaby' => & $replacemainareaby,
1684 'disablenofollow' => & $disablenofollow,
1685 'disablenoindex' => & $disablenoindex
1686
1687 );
1688 $reshook = $hookmanager->executeHooks('llxHeader', $parameters);
1689 if ($reshook > 0) {
1690 print $hookmanager->resPrint;
1691 return;
1692 }
1693
1694 // html header
1695 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
1696
1697 $tmpcsstouse = 'sidebar-collapse'.($morecssonbody ? ' '.$morecssonbody : '');
1698 // If theme MD and classic layer, we open the menulayer by default.
1699 if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1700 global $mainmenu;
1701 if ($mainmenu != 'website') {
1702 $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
1703 }
1704 }
1705
1706 if (getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND')) {
1707 $tmpcsstouse .= ' colorblind-'.strip_tags(getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND'));
1708 }
1709
1710 print '<body id="mainbody" class="'.$tmpcsstouse.'">'."\n";
1711
1712 // top menu and left menu area
1713 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && !GETPOST('dol_openinpopup', 'aZ09')) {
1714 top_menu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
1715 }
1716
1717 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1718 left_menu('', $help_url, '', array(), 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
1719 }
1720
1721 // main area
1722 if ($replacemainareaby) {
1723 print $replacemainareaby;
1724 return;
1725 }
1726
1727 main_area($title);
1728 }
1729}
1730
1731
1739function top_httphead($contenttype = 'text/html', $forcenocache = 0)
1740{
1741 global $db, $conf, $hookmanager;
1742
1743 if ($contenttype == 'text/html') {
1744 header("Content-Type: text/html; charset=".$conf->file->character_set_client);
1745 } else {
1746 header("Content-Type: ".$contenttype);
1747 }
1748
1749 // Security options
1750
1751 // X-Content-Type-Options
1752 header("X-Content-Type-Options: nosniff"); // With the nosniff option, if the server says the content is text/html, the browser will render it as text/html (note that most browsers now force this option to on)
1753
1754 // X-Frame-Options
1755 if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
1756 header("X-Frame-Options: SAMEORIGIN"); // By default, frames allowed only if on same domain (stop some XSS attacks)
1757 } else {
1758 header("X-Frame-Options: ALLOWALL");
1759 }
1760
1761 if (getDolGlobalString('MAIN_SECURITY_FORCE_ACCESS_CONTROL_ALLOW_ORIGIN')) {
1762 $tmpurl = constant('DOL_MAIN_URL_ROOT');
1763 $tmpurl = preg_replace('/^(https?:\/\/[^\/]+)\/.*$/', '\1', $tmpurl);
1764 header('Access-Control-Allow-Origin: '.$tmpurl);
1765 header('Vary: Origin');
1766 }
1767
1768 // X-XSS-Protection
1769 //header("X-XSS-Protection: 1"); // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
1770
1771 // Content-Security-Policy-Report-Only
1772 if (!defined('MAIN_SECURITY_FORCECSPRO')) {
1773 // If CSP not forced from the page
1774
1775 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1776 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1777 // default-src 'self'; style-src: https://cdnjs.cloudflare.com https://fonts.googleapis.com; script-src: https://cdn.transifex.com https://www.googletagmanager.com; object-src https://youtube.com; frame-src https://youtube.com; img-src: *;
1778 // For example, to restrict everything to itself except img that can be on other servers:
1779 // default-src 'self'; img-src *;
1780 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1781 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1782 //
1783 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com;";
1784 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src *; script-src 'self' 'unsafe-inline' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com; style-src 'self' 'unsafe-inline'; connect-src 'self';";
1785 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
1786
1787 if (!is_object($hookmanager)) {
1788 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1789 $hookmanager = new HookManager($db);
1790 }
1791 $hookmanager->initHooks(array("main"));
1792
1793 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'reportonly');
1794 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1795 if ($result > 0) {
1796 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1797 } else {
1798 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1799 }
1800
1801 if (!empty($contentsecuritypolicy)) {
1802 header("Content-Security-Policy-Report-Only: ".$contentsecuritypolicy);
1803 }
1804 } else {
1805 header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSPRO'));
1806 }
1807
1808 // Content-Security-Policy
1809 if (!defined('MAIN_SECURITY_FORCECSP')) {
1810 // If CSP not forced from the page
1811
1812 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1813 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1814 // default-src 'self'; style-src: https://cdnjs.cloudflare.com https://fonts.googleapis.com; script-src: https://cdn.transifex.com https://www.googletagmanager.com; object-src https://youtube.com; frame-src https://youtube.com; img-src: *;
1815 // For example, to restrict everything to itself except img that can be on other servers:
1816 // default-src 'self'; img-src *;
1817 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1818 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1819 //
1820 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com;";
1821 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src *; script-src 'self' 'unsafe-inline' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com; style-src 'self' 'unsafe-inline'; connect-src 'self';";
1822 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
1823
1824 if (!is_object($hookmanager)) {
1825 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1826 $hookmanager = new HookManager($db);
1827 }
1828 $hookmanager->initHooks(array("main"));
1829
1830 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'active');
1831 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1832 if ($result > 0) {
1833 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1834 } else {
1835 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1836 }
1837
1838 if (!empty($contentsecuritypolicy)) {
1839 header("Content-Security-Policy: ".$contentsecuritypolicy);
1840 }
1841 } else {
1842 header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSP'));
1843 }
1844
1845 // Referrer-Policy
1846 // Say if we must provide the referrer when we jump onto another web page.
1847 // Default browser are 'strict-origin-when-cross-origin' (only domain is sent on other domain switching), we want more so we use 'same-origin' so browser doesn't send any referrer at all when going into another web site domain.
1848 // Note that we do not use 'strict-origin' as this breaks feature to restore filters when clicking on "back to page" link on some cases.
1849 if (!defined('MAIN_SECURITY_FORCERP')) {
1850 $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
1851
1852 header("Referrer-Policy: ".$referrerpolicy);
1853 }
1854
1855 if ($forcenocache) {
1856 header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
1857 }
1858
1859 // No need to add this token in header, we use instead the one into the forms.
1860 //header("anti-csrf-token: ".newToken());
1861}
1862
1878function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
1879{
1880 global $db, $conf, $langs, $user, $mysoc, $hookmanager;
1881
1882 top_httphead();
1883
1884 if (empty($conf->css)) {
1885 $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
1886 }
1887
1888 print '<!doctype html>'."\n";
1889
1890 print '<html lang="'.substr($langs->defaultlang, 0, 2).'">'."\n";
1891
1892 //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
1893 if (empty($disablehead)) {
1894 if (!is_object($hookmanager)) {
1895 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1896 $hookmanager = new HookManager($db);
1897 }
1898 $hookmanager->initHooks(array("main"));
1899
1900 $ext = 'layout='.(empty($conf->browser->layout) ? '' : $conf->browser->layout).'&amp;version='.urlencode(DOL_VERSION);
1901
1902 print "<head>\n";
1903
1904 if (GETPOST('dol_basehref', 'alpha')) {
1905 print '<base href="'.dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')).'">'."\n";
1906 }
1907
1908 // Displays meta
1909 print '<meta charset="utf-8">'."\n";
1910 print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
1911 print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
1912 print '<meta name="author" content="Dolibarr Development Team">'."\n";
1913 print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
1914 print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
1915 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
1916 print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
1917 }
1918 // Favicon
1919 $favicon = DOL_URL_ROOT.'/theme/dolibarr_256x256_color.png';
1920 $appletouchicon = DOL_URL_ROOT.'/theme/apple-touch-icon.png';
1921 if (!empty($mysoc->logo_squarred_mini)) {
1922 $favicon = DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_mini);
1923 }
1924 if (getDolGlobalString('MAIN_FAVICON_URL')) {
1925 $favicon = getDolGlobalString('MAIN_FAVICON_URL');
1926 }
1927 if (empty($conf->dol_use_jmobile)) {
1928 print '<link rel="shortcut icon" type="image/x-icon" href="'.$favicon.'"/>'."\n"; // Not required into an Android webview
1929 print '<link rel="apple-touch-icon" href="'.$appletouchicon.'"/>'."\n";
1930 }
1931
1932 // Mobile appli like icon
1933 $manifest = DOL_URL_ROOT.'/theme/'.$conf->theme.'/manifest.json.php';
1934 $parameters = array('manifest' => $manifest);
1935 $resHook = $hookmanager->executeHooks('hookSetManifest', $parameters); // Note that $action and $object may have been modified by some hooks
1936 if ($resHook > 0) {
1937 $manifest = $hookmanager->resPrint; // Replace manifest.json
1938 } else {
1939 $manifest .= $hookmanager->resPrint; // Concat to actual manifest declaration
1940 }
1941 if (!empty($manifest)) {
1942 print '<link rel="manifest" href="'.$manifest.'" />'."\n";
1943 }
1944
1945 if (getDolGlobalString('THEME_ELDY_TOPMENU_BACK1')) {
1946 // TODO: use auto theme color switch
1947 print '<meta name="theme-color" content="rgb(' . getDolGlobalString('THEME_ELDY_TOPMENU_BACK1').')">'."\n";
1948 }
1949
1950 // Auto refresh page
1951 if (GETPOSTINT('autorefresh') > 0) {
1952 print '<meta http-equiv="refresh" content="'.GETPOSTINT('autorefresh').'">';
1953 }
1954
1955 // Displays title
1956 $appli = constant('DOL_APPLICATION_TITLE');
1957 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
1958 if ($applicustom) {
1959 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
1960 }
1961
1962 print '<title>';
1963 $titletoshow = '';
1964 if ($title && preg_match('/showapp/', getDolGlobalString('MAIN_HTML_TITLE'))) {
1965 $titletoshow = dol_htmlentities($appli.' - '.$title);
1966 } elseif ($title) {
1967 $titletoshow = dol_htmlentities($title);
1968 } else {
1969 $titletoshow = dol_htmlentities($appli);
1970 }
1971
1972 $parameters = array('title' => $titletoshow);
1973 $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
1974 if ($result > 0) {
1975 $titletoshow = $hookmanager->resPrint; // Replace Title to show
1976 } else {
1977 $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
1978 }
1979
1980 print $titletoshow;
1981 print '</title>';
1982
1983 print "\n";
1984
1985 if (GETPOSTINT('version')) {
1986 $ext = 'version='.GETPOSTINT('version'); // useful to force no cache on css/js
1987 }
1988 // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
1989 if (GETPOST('dol_resetcache')) {
1990 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1991 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
1992 }
1993
1994 $themeparam = '?lang='.$langs->defaultlang.'&amp;theme='.$conf->theme.(GETPOST('optioncss', 'aZ09') ? '&amp;optioncss='.GETPOST('optioncss', 'aZ09', 1) : '').(empty($user->id) ? '' : ('&amp;userid='.$user->id)).'&amp;entity='.$conf->entity;
1995
1996 $themeparam .= ($ext ? '&amp;'.$ext : '').'&amp;revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV");
1997 if (GETPOSTISSET('dol_hide_topmenu')) {
1998 $themeparam .= '&amp;dol_hide_topmenu='.GETPOSTINT('dol_hide_topmenu');
1999 }
2000 if (GETPOSTISSET('dol_hide_leftmenu')) {
2001 $themeparam .= '&amp;dol_hide_leftmenu='.GETPOSTINT('dol_hide_leftmenu');
2002 }
2003 if (GETPOSTISSET('dol_openinpopup')) {
2004 $themeparam .= '&amp;dol_openinpopup='.GETPOST('dol_openinpopup', 'aZ09');
2005 }
2006 if (GETPOSTISSET('dol_optimize_smallscreen')) {
2007 $themeparam .= '&amp;dol_optimize_smallscreen='.GETPOSTINT('dol_optimize_smallscreen');
2008 }
2009 if (GETPOSTISSET('dol_no_mouse_hover')) {
2010 $themeparam .= '&amp;dol_no_mouse_hover='.GETPOSTINT('dol_no_mouse_hover');
2011 }
2012 if (GETPOSTISSET('dol_use_jmobile')) {
2013 $themeparam .= '&amp;dol_use_jmobile='.GETPOSTINT('dol_use_jmobile');
2014 $conf->dol_use_jmobile = GETPOSTINT('dol_use_jmobile');
2015 }
2016 if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
2017 $themeparam .= '&amp;THEME_DARKMODEENABLED='.GETPOSTINT('THEME_DARKMODEENABLED');
2018 }
2019 if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
2020 $themeparam .= '&amp;THEME_SATURATE_RATIO='.GETPOSTINT('THEME_SATURATE_RATIO');
2021 }
2022
2023 if (getDolGlobalString('MAIN_ENABLE_FONT_ROBOTO')) {
2024 print '<link rel="preconnect" href="https://fonts.gstatic.com">'."\n";
2025 print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">'."\n";
2026 }
2027
2028 if (!defined('DISABLE_JQUERY') && !$disablejs && $conf->use_javascript_ajax) {
2029 print '<!-- Includes CSS for JQuery (Ajax library) -->'."\n";
2030 $jquerytheme = 'base';
2031 if (getDolGlobalString('MAIN_USE_JQUERY_THEME')) {
2032 $jquerytheme = getDolGlobalString('MAIN_USE_JQUERY_THEME');
2033 }
2034 if (constant('JS_JQUERY_UI')) {
2035 print '<link rel="stylesheet" type="text/css" href="'.JS_JQUERY_UI.'css/'.$jquerytheme.'/jquery-ui.min.css'.($ext ? '?'.$ext : '').'">'."\n"; // Forced JQuery
2036 } else {
2037 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/css/'.$jquerytheme.'/jquery-ui.css'.($ext ? '?'.$ext : '').'">'."\n"; // JQuery
2038 }
2039 if (!defined('DISABLE_JQUERY_JNOTIFY')) {
2040 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css'.($ext ? '?'.$ext : '').'">'."\n"; // JNotify
2041 }
2042 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) { // jQuery plugin "mutiselect", "multiple-select", "select2"...
2043 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
2044 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/plugins/'.$tmpplugin.'/dist/css/'.$tmpplugin.'.css'.($ext ? '?'.$ext : '').'">'."\n";
2045 }
2046 }
2047
2048 if (!defined('DISABLE_FONT_AWSOME')) {
2049 print '<!-- Includes CSS for font awesome -->'."\n";
2050 $fontawesome_directory = getDolGlobalString('MAIN_FONTAWESOME_DIRECTORY', '/theme/common/fontawesome-5');
2051 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.$fontawesome_directory.'/css/all.min.css'.($ext ? '?'.$ext : '').'">'."\n";
2052 }
2053
2054 print '<!-- Includes CSS for Dolibarr theme -->'."\n";
2055 // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
2056 $themepath = dol_buildpath($conf->css, 1);
2057 $themesubdir = '';
2058 if (!empty($conf->modules_parts['theme'])) { // This slow down
2059 foreach ($conf->modules_parts['theme'] as $reldir) {
2060 if (file_exists(dol_buildpath($reldir.$conf->css, 0))) {
2061 $themepath = dol_buildpath($reldir.$conf->css, 1);
2062 $themesubdir = $reldir;
2063 break;
2064 }
2065 }
2066 }
2067
2068 //print 'themepath='.$themepath.' themeparam='.$themeparam;exit;
2069 print '<link rel="stylesheet" type="text/css" href="'.$themepath.$themeparam.'">'."\n";
2070 if (getDolGlobalString('MAIN_FIX_FLASH_ON_CHROME')) {
2071 print '<!-- Includes CSS that does not exists as a workaround of flash bug of chrome -->'."\n".'<link rel="stylesheet" type="text/css" href="filethatdoesnotexiststosolvechromeflashbug">'."\n";
2072 }
2073
2074 // LEAFLET AND GEOMAN
2075 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
2076 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.css'.($ext ? '?'.$ext : '')."\">\n";
2077 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.css'.($ext ? '?'.$ext : '')."\">\n";
2078 }
2079
2080 // CSS forced by modules (relative url starting with /)
2081 if (!empty($conf->modules_parts['css'])) {
2082 $arraycss = (array) $conf->modules_parts['css'];
2083 foreach ($arraycss as $modcss => $filescss) {
2084 $filescss = (array) $filescss; // To be sure filecss is an array
2085 foreach ($filescss as $cssfile) {
2086 if (empty($cssfile)) {
2087 dol_syslog("Warning: module ".$modcss." declared a css path file into its descriptor that is empty.", LOG_WARNING);
2088 }
2089 // cssfile is a relative path
2090 $urlforcss = dol_buildpath($cssfile, 1);
2091 if ($urlforcss && $urlforcss != '/') {
2092 print '<!-- Includes CSS added by module '.$modcss.' -->'."\n".'<link rel="stylesheet" type="text/css" href="'.$urlforcss;
2093 // We add params only if page is not static, because some web server setup does not return content type text/css if url has parameters, so browser cache is not used.
2094 if (!preg_match('/\.css$/i', $cssfile)) {
2095 print $themeparam;
2096 }
2097 print '">'."\n";
2098 } else {
2099 dol_syslog("Warning: module ".$modcss." declared a css path file for a file we can't find.", LOG_WARNING);
2100 }
2101 }
2102 }
2103 }
2104 // CSS forced by page in top_htmlhead call (relative url starting with /)
2105 if (is_array($arrayofcss)) {
2106 foreach ($arrayofcss as $cssfile) {
2107 if (preg_match('/^(http|\/\/)/i', $cssfile)) {
2108 $urltofile = $cssfile;
2109 } else {
2110 $urltofile = dol_buildpath($cssfile, 1);
2111 }
2112 print '<!-- Includes CSS added by page -->'."\n".'<link rel="stylesheet" type="text/css" title="default" href="'.$urltofile;
2113 // We add params only if page is not static, because some web server setup does not return content type text/css if url has parameters and browser cache is not used.
2114 if (!preg_match('/\.css$/i', $cssfile)) {
2115 print $themeparam;
2116 }
2117 print '">'."\n";
2118 }
2119 }
2120
2121 // Custom CSS
2122 if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
2123 // If a custom CSS was set, we add link to the custom css php file
2124 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/theme/custom.css.php'.($ext ? '?'.$ext : '').'&amp;revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV").'">'."\n";
2125 }
2126
2127 // Output standard javascript links
2128 if (!defined('DISABLE_JQUERY') && !$disablejs && !empty($conf->use_javascript_ajax)) {
2129 // JQuery. Must be before other includes
2130 print '<!-- Includes JS for JQuery -->'."\n";
2131 if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
2132 print '<script nonce="'.getNonce().'" src="'.JS_JQUERY.'jquery.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2133 } else {
2134 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/js/jquery.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2135 }
2136 if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
2137 print '<script nonce="'.getNonce().'" src="'.JS_JQUERY_UI.'jquery-ui.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2138 } else {
2139 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/js/jquery-ui.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2140 }
2141 // jQuery jnotify
2142 if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
2143 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2144 }
2145 // Table drag and drop lines
2146 if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
2147 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/tablednd/jquery.tablednd.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2148 }
2149 // Chart
2150 if (empty($disableforlogin) && (!getDolGlobalString('MAIN_JS_GRAPH') || getDolGlobalString('MAIN_JS_GRAPH') == 'chart') && !defined('DISABLE_JS_GRAPH')) {
2151 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/nnnick/chartjs/dist/chart.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2152 }
2153
2154 // jQuery jeditable for Edit In Place features
2155 if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !defined('DISABLE_JQUERY_JEDITABLE')) {
2156 print '<!-- JS to manage editInPlace feature -->'."\n";
2157 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2158 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2159 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2160 print '<script>'."\n";
2161 print 'var urlSaveInPlace = \''.DOL_URL_ROOT.'/core/ajax/saveinplace.php\';'."\n";
2162 print 'var urlLoadInPlace = \''.DOL_URL_ROOT.'/core/ajax/loadinplace.php\';'."\n";
2163 print 'var tooltipInPlace = \''.$langs->transnoentities('ClickToEdit').'\';'."\n"; // Added in title attribute of span
2164 print 'var placeholderInPlace = \'&nbsp;\';'."\n"; // If we put another string than $langs->trans("ClickToEdit") here, nothing is shown. If we put empty string, there is error, Why ?
2165 print 'var cancelInPlace = \''.$langs->trans("Cancel").'\';'."\n";
2166 print 'var submitInPlace = \''.$langs->trans('Ok').'\';'."\n";
2167 print 'var indicatorInPlace = \'<img src="'.DOL_URL_ROOT."/theme/".$conf->theme."/img/working.gif".'">\';'."\n";
2168 print 'var withInPlace = 300;'; // width in pixel for default string edit
2169 print '</script>'."\n";
2170 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/editinplace.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2171 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2172 }
2173 // jQuery Timepicker
2174 if (getDolGlobalString('MAIN_USE_JQUERY_TIMEPICKER') || defined('REQUIRE_JQUERY_TIMEPICKER')) {
2175 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/timepicker/jquery-ui-timepicker-addon.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2176 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/timepicker.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
2177 }
2178 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
2179 // jQuery plugin "mutiselect", "multiple-select", "select2", ...
2180 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
2181 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/'.$tmpplugin.'/dist/js/'.$tmpplugin.'.full.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n"; // We include full because we need the support of containerCssClass
2182 }
2183 if (!defined('DISABLE_MULTISELECT')) { // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
2184 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/multiselect/jquery.multi-select.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2185 }
2186 }
2187
2188 if (!$disablejs && !empty($conf->use_javascript_ajax)) {
2189 // CKEditor
2190 if (empty($disableforlogin) && (isModEnabled('fckeditor') && (!getDolGlobalString('FCKEDITOR_EDITORNAME') || getDolGlobalString('FCKEDITOR_EDITORNAME') == 'ckeditor') && !defined('DISABLE_CKEDITOR')) || defined('FORCE_CKEDITOR')) {
2191 print '<!-- Includes JS for CKEditor -->'."\n";
2192 $pathckeditor = DOL_URL_ROOT.'/includes/ckeditor/ckeditor/';
2193 $jsckeditor = 'ckeditor.js';
2194 if (constant('JS_CKEDITOR')) {
2195 // To use external ckeditor 4 js lib
2196 $pathckeditor = constant('JS_CKEDITOR');
2197 }
2198 print '<script nonce="'.getNonce().'">';
2199 print '/* enable ckeditor by main.inc.php */';
2200 print 'var CKEDITOR_BASEPATH = \''.dol_escape_js($pathckeditor).'\';'."\n";
2201 print 'var ckeditorConfig = \''.dol_escape_js(dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js'.($ext ? '?'.$ext : ''), 1)).'\';'."\n"; // $themesubdir='' in standard usage
2202 print 'var ckeditorFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2203 print 'var ckeditorFilebrowserImageBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Type=Image&Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2204 print '</script>'."\n";
2205 print '<script src="'.$pathckeditor.$jsckeditor.($ext ? '?'.$ext : '').'"></script>'."\n";
2206 print '<script>';
2207 if (GETPOST('mode', 'aZ09') == 'Full_inline') {
2208 print 'CKEDITOR.disableAutoInline = false;'."\n";
2209 } else {
2210 print 'CKEDITOR.disableAutoInline = true;'."\n";
2211 }
2212 print '</script>'."\n";
2213 }
2214
2215 // Browser notifications (if NOREQUIREMENU is on, it is mostly a page for popup, so we do not enable notif too. We hide also for public pages).
2216 if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
2217 $enablebrowsernotif = false;
2218 if (isModEnabled('agenda') && getDolGlobalString('AGENDA_REMINDER_BROWSER')) {
2219 $enablebrowsernotif = true;
2220 }
2221 if ($conf->browser->layout == 'phone') {
2222 $enablebrowsernotif = false;
2223 }
2224 if ($enablebrowsernotif) {
2225 print '<!-- Includes JS of Dolibarr (browser layout = '.$conf->browser->layout.')-->'."\n";
2226 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_notification.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
2227 }
2228 }
2229
2230 // Global js function
2231 print '<!-- Includes JS of Dolibarr -->'."\n";
2232 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_head.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
2233
2234 // Leaflet TODO use dolibarr files
2235 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
2236 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2237 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2238 }
2239
2240 // JS forced by modules (relative url starting with /)
2241 if (!empty($conf->modules_parts['js'])) { // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
2242 $arrayjs = (array) $conf->modules_parts['js'];
2243 foreach ($arrayjs as $modjs => $filesjs) {
2244 $filesjs = (array) $filesjs; // To be sure filejs is an array
2245 foreach ($filesjs as $jsfile) {
2246 // jsfile is a relative path
2247 $urlforjs = dol_buildpath($jsfile, 1);
2248 if ($urlforjs && $urlforjs != '/') {
2249 print '<!-- Include JS added by module '.$modjs.'-->'."\n";
2250 print '<script nonce="'.getNonce().'" src="'.$urlforjs.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2251 } else {
2252 dol_syslog("Warning: module ".$modjs." declared a js path file for a file we can't find.", LOG_WARNING);
2253 }
2254 }
2255 }
2256 }
2257 // JS forced by page in top_htmlhead (relative url starting with /)
2258 if (is_array($arrayofjs)) {
2259 print '<!-- Includes JS added by page -->'."\n";
2260 foreach ($arrayofjs as $jsfile) {
2261 if (preg_match('/^(http|\/\/)/i', $jsfile)) {
2262 print '<script nonce="'.getNonce().'" src="'.$jsfile.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2263 } else {
2264 print '<script nonce="'.getNonce().'" src="'.dol_buildpath($jsfile, 1).((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2265 }
2266 }
2267 }
2268 }
2269
2270 //If you want to load custom javascript file from your selected theme directory
2271 if (getDolGlobalString('ALLOW_THEME_JS')) {
2272 $theme_js = dol_buildpath('/theme/'.$conf->theme.'/'.$conf->theme.'.js', 0);
2273 if (file_exists($theme_js)) {
2274 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/'.$conf->theme.'.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2275 }
2276 }
2277
2278 if (!empty($head)) {
2279 print $head."\n";
2280 }
2281 if (getDolGlobalString('MAIN_HTML_HEADER')) {
2282 print getDolGlobalString('MAIN_HTML_HEADER') . "\n";
2283 }
2284
2285 $parameters = array();
2286 $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
2287 print $hookmanager->resPrint; // Replace Title to show
2288
2289 print "</head>\n\n";
2290 }
2291
2292 $conf->headerdone = 1; // To tell header was output
2293}
2294
2295
2312function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $morequerystring = '', $helppagename = '')
2313{
2314 global $user, $conf, $langs, $db, $form;
2315 global $dolibarr_main_authentication, $dolibarr_main_demo;
2316 global $hookmanager, $menumanager;
2317
2318 $searchform = '';
2319
2320 // Instantiate hooks for external modules
2321 $hookmanager->initHooks(array('toprightmenu'));
2322
2323 $toprightmenu = '';
2324
2325 // For backward compatibility with old modules
2326 if (empty($conf->headerdone)) {
2327 $disablenofollow = 0;
2328 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
2329 print '<body id="mainbody">';
2330 }
2331
2332 /*
2333 * Top menu
2334 */
2335 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
2336 if (!isset($form) || !is_object($form)) {
2337 include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
2338 $form = new Form($db);
2339 }
2340
2341 print "\n".'<!-- Start top horizontal -->'."\n";
2342
2343 print '<header id="id-top" class="side-nav-vert'.(GETPOSTINT('dol_invisible_topmenu') ? ' hidden' : '').'">'; // dol_invisible_topmenu differs from dol_hide_topmenu: dol_invisible_topmenu means we output menu but we make it invisible.
2344
2345 // Show menu entries
2346 print '<div id="tmenu_tooltip'.(!getDolGlobalString('MAIN_MENU_INVERT') ? '' : 'invert').'" class="tmenu">'."\n";
2347 // @phan-suppress-next-line PhanRedefinedClassReference
2348 $menumanager->atarget = $target;
2349 // @phan-suppress-next-line PhanRedefinedClassReference
2350 $menumanager->showmenu('top', array('searchform' => $searchform)); // This contains a \n
2351 print "</div>\n";
2352
2353 // Define link to login card
2354 $appli = constant('DOL_APPLICATION_TITLE');
2355 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
2356 if ($applicustom) {
2357 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
2358 } else {
2359 $appli .= " ".DOL_VERSION;
2360 }
2361
2362 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
2363 $appli .= "<br>".$langs->trans("LevelOfFeature").': '.getDolGlobalInt('MAIN_FEATURES_LEVEL');
2364 }
2365
2366 $logouttext = '';
2367 $logouthtmltext = '';
2368 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2369 //$logouthtmltext=$appli.'<br>';
2370 $stringforfirstkey = $langs->trans("KeyboardShortcut");
2371 if ($conf->browser->name == 'chrome') {
2372 $stringforfirstkey .= ' ALT +';
2373 } elseif ($conf->browser->name == 'firefox') {
2374 $stringforfirstkey .= ' ALT + SHIFT +';
2375 } else {
2376 $stringforfirstkey .= ' CTL +';
2377 }
2378 if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') {
2379 $logouthtmltext .= $langs->trans("Logout").'<br>';
2380 $logouttext .= '<a accesskey="l" href="'.DOL_URL_ROOT.'/user/logout.php?token='.newToken().'">';
2381 $logouttext .= img_picto($langs->trans('Logout').' ('.$stringforfirstkey.' l)', 'sign-out', '', 0, 0, 0, '', 'atoplogin valignmiddle');
2382 $logouttext .= '</a>';
2383 } else {
2384 $logouthtmltext .= $langs->trans("NoLogoutProcessWithAuthMode", $_SESSION["dol_authmode"]);
2385 $logouttext .= img_picto($langs->trans('Logout').' ('.$stringforfirstkey.' l)', 'sign-out', '', 0, 0, 0, '', 'atoplogin valignmiddle opacitymedium');
2386 }
2387 }
2388
2389
2390 print '<div class="login_block usedropdown">'."\n";
2391
2392
2393 // Add block for tools
2394 $toprightmenu .= '<div class="login_block_tools valignmiddle">';
2395
2396 $mode = -1;
2397 $toprightmenu .= '<div class="inline-block nowrap" style="padding: 0px;">';
2398
2399 if (getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
2400 // Add search dropdown
2401 $toprightmenu .= top_menu_search();
2402 }
2403
2404 if (getDolGlobalString('MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN')) {
2405 // Add the quick add object dropdown
2406 $toprightmenu .= top_menu_quickadd();
2407 }
2408
2409 // Add bookmark dropdown
2410 $toprightmenu .= top_menu_bookmark();
2411
2412 if (getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE')) {
2413 // Add the import file link
2414 $toprightmenu .= top_menu_importfile();
2415 }
2416
2417 $toprightmenu .= '</div>';
2418
2419 $toprightmenu .= '</div>'."\n"; // end div class="login_block_tools"
2420
2421
2422 // Add block for other tools
2423 $toprightmenu .= '<div class="login_block_other valignmiddle">';
2424
2425 // Execute hook printTopRightMenu (hooks should output string like '<div class="login"><a href="">mylink</a></div>')
2426 $parameters = array();
2427 $result = $hookmanager->executeHooks('printTopRightMenu', $parameters); // Note that $action and $object may have been modified by some hooks
2428 if (is_numeric($result)) {
2429 if ($result == 0) {
2430 $toprightmenu .= $hookmanager->resPrint; // add
2431 } else {
2432 $toprightmenu = $hookmanager->resPrint; // replace
2433 }
2434 } else {
2435 $toprightmenu .= $result; // For backward compatibility
2436 }
2437
2438 // Link to module builder
2439 if (isModEnabled('modulebuilder')) {
2440 $text = '<a href="'.DOL_URL_ROOT.'/modulebuilder/index.php?mainmenu=home&leftmenu=admintools" target="modulebuilder">';
2441 //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
2442 $text .= '<span class="fa fa-bug atoplogin valignmiddle"></span>';
2443 $text .= '</a>';
2444 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2445 $toprightmenu .= $form->textwithtooltip('', $langs->trans("ModuleBuilder"), 2, 1, $text, 'login_block_elem', 2);
2446 }
2447
2448 // Link to print main content area (optioncss=print)
2449 if (!getDolGlobalString('MAIN_PRINT_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2450 $qs = dol_escape_htmltag($_SERVER["QUERY_STRING"]);
2451
2452 if (isset($_POST) && is_array($_POST)) {
2453 foreach ($_POST as $key => $value) {
2454 $key = preg_replace('/[^a-z0-9_\.\-\[\]]/i', '', $key);
2455 if (in_array($key, array('action', 'massaction', 'password'))) {
2456 continue;
2457 }
2458 if (!is_array($value)) {
2459 if ($value !== '') {
2460 $qs .= '&'.urlencode($key).'='.urlencode($value);
2461 }
2462 } else {
2463 foreach ($value as $value2) {
2464 if (($value2 !== '') && (!is_array($value2))) {
2465 $qs .= '&'.urlencode($key).'[]='.urlencode($value2);
2466 }
2467 }
2468 }
2469 }
2470 }
2471 $qs .= (($qs && $morequerystring) ? '&' : '').$morequerystring;
2472 $text = '<a href="'.dol_escape_htmltag($_SERVER["PHP_SELF"]).'?'.$qs.($qs ? '&' : '').'optioncss=print" target="_blank" rel="noopener noreferrer">';
2473 //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
2474 $text .= '<span class="fa fa-print atoplogin valignmiddle"></span>';
2475 $text .= '</a>';
2476 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2477 $toprightmenu .= $form->textwithtooltip('', $langs->trans("PrintContentArea"), 2, 1, $text, 'login_block_elem', 2);
2478 }
2479
2480 // Link to Dolibarr wiki pages
2481 if (!getDolGlobalString('MAIN_HELP_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2482 $langs->load("help");
2483
2484 $helpbaseurl = '';
2485 $helppage = '';
2486 $mode = '';
2487 $helppresent = '';
2488
2489 if (empty($helppagename)) {
2490 $helppagename = 'EN:User_documentation|FR:Documentation_utilisateur|ES:Documentación_usuarios|DE:Benutzerdokumentation';
2491 } else {
2492 $helppresent = 'helppresent';
2493 }
2494
2495 // Get helpbaseurl, helppage and mode from helppagename and langs
2496 $arrayres = getHelpParamFor($helppagename, $langs);
2497 $helpbaseurl = $arrayres['helpbaseurl'];
2498 $helppage = $arrayres['helppage'];
2499 $mode = $arrayres['mode'];
2500
2501 // Link to help pages
2502 if ($helpbaseurl && $helppage) {
2503 $text = '';
2504 $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage').', ';
2505 if ($mode == 'wiki') {
2506 $title .= '<br>'.img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans("PageWiki").' '.dol_escape_htmltag('"'.strtr($helppage, '_', ' ').'"');
2507 if ($helppresent) {
2508 $title .= ' <span class="opacitymedium">('.$langs->trans("DedicatedPageAvailable").')</span>';
2509 } else {
2510 $title .= ' <span class="opacitymedium">('.$langs->trans("HomePage").')</span>';
2511 }
2512 }
2513 $text .= '<a class="help" target="_blank" rel="noopener noreferrer" href="';
2514 if ($mode == 'wiki') {
2515 // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2516 $text .= sprintf($helpbaseurl, urlencode(html_entity_decode($helppage)));
2517 } else {
2518 // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2519 $text .= sprintf($helpbaseurl, $helppage);
2520 }
2521 $text .= '">';
2522 $text .= '<span class="fa fa-question-circle atoplogin valignmiddle'.($helppresent ? ' '.$helppresent : '').'"></span>';
2523 $text .= '<span class="fa fa-long-arrow-alt-up helppresentcircle'.($helppresent ? '' : ' unvisible').'"></span>';
2524 $text .= '</a>';
2525 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2526 $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2);
2527 }
2528
2529 // Version
2530 if (getDolGlobalString('MAIN_SHOWDATABASENAMEINHELPPAGESLINK')) {
2531 $langs->load('admin');
2532 $appli .= '<br>'.$langs->trans("Database").': '.$db->database_name;
2533 }
2534 }
2535
2536 // Version
2537 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalInt('MAIN_HIDE_VERSION') == 0) {
2538 $text = '<span class="aversion"><span class="hideonsmartphone small">'.DOL_VERSION.'</span></span>';
2539 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2540 $toprightmenu .= $form->textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
2541 }
2542
2543 // Logout link
2544 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2545 $toprightmenu .= $form->textwithtooltip('', $logouthtmltext, 2, 1, $logouttext, 'login_block_elem logout-btn', 2);
2546 }
2547
2548 $toprightmenu .= '</div>'; // end div class="login_block_other"
2549
2550
2551 // Add block for user photo and name
2552 $toprightmenu .= '<div class="login_block_user">';
2553
2554 $mode = -1;
2555 $toprightmenu .= '<div class="inline-block login_block_elem login_block_elem_name nowrap centpercent" style="padding: 0px;">';
2556
2557 // Add user dropdown
2558 $toprightmenu .= top_menu_user();
2559
2560 $toprightmenu .= '</div>';
2561
2562 $toprightmenu .= '</div>'."\n";
2563
2564
2565 print $toprightmenu;
2566
2567 print "</div>\n"; // end div class="login_block"
2568
2569 print '</header>';
2570 //print '<header class="header2">&nbsp;</header>';
2571
2572 print '<div style="clear: both;"></div>';
2573 print "<!-- End top horizontal menu -->\n\n";
2574 }
2575
2576 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
2577 print '<!-- Begin div id-container --><div id="id-container" class="id-container">';
2578 }
2579}
2580
2581
2589function top_menu_user($hideloginname = 0, $urllogout = '')
2590{
2591 global $langs, $conf, $db, $hookmanager, $user, $mysoc;
2592 global $dolibarr_main_authentication, $dolibarr_main_demo;
2593 global $menumanager;
2594
2595 // Return empty in some case
2596 if ($conf->browser->name == 'textbrowser') {
2597 return '';
2598 }
2599
2600 $langs->load('companies');
2601
2602 $userImage = $userDropDownImage = '';
2603 if (!empty($user->photo)) {
2604 $userImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'photouserphoto userphoto', 'small', 0, 1);
2605 $userDropDownImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'dropdown-user-image', 'small', 0, 1);
2606 } else {
2607 $nophoto = '/public/theme/common/user_anonymous.png';
2608 if ($user->gender == 'man') {
2609 $nophoto = '/public/theme/common/user_man.png';
2610 }
2611 if ($user->gender == 'woman') {
2612 $nophoto = '/public/theme/common/user_woman.png';
2613 }
2614
2615 $userImage = '<img class="photo photouserphoto userphoto" alt="" src="'.DOL_URL_ROOT.$nophoto.'" aria-hidden="true">';
2616 $userDropDownImage = '<img class="photo dropdown-user-image" alt="" src="'.DOL_URL_ROOT.$nophoto.'">';
2617 }
2618
2619 $dropdownBody = '';
2620 $dropdownBody .= '<span id="topmenulogincompanyinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowCompanyInfos").'</span>';
2621 $dropdownBody .= '<div id="topmenulogincompanyinfo" >';
2622
2623 $dropdownBody .= '<br><b>'.$langs->trans("Company").'</b>: <span>'.dol_escape_htmltag($mysoc->name).'</span>';
2624 $idprofcursor = 0;
2625 while ($idprofcursor < 10) {
2626 $idprofcursor++;
2627 $constkeyforprofid = 'MAIN_INFO_PROFID'.$idprofcursor;
2628 if ($idprofcursor == 1) {
2629 $constkeyforprofid = 'MAIN_INFO_SIREN';
2630 }
2631 if ($idprofcursor == 2) {
2632 $constkeyforprofid = 'MAIN_INFO_SIRET';
2633 }
2634 if ($idprofcursor == 3) {
2635 $constkeyforprofid = 'MAIN_INFO_APE';
2636 }
2637 if ($idprofcursor == 4) {
2638 $constkeyforprofid = 'MAIN_INFO_RCS';
2639 }
2640 $showprofid = (($idprofcursor <= 6) && $langs->transcountry("ProfId".$idprofcursor, $mysoc->country_code) != '-');
2641 if ($idprofcursor > 6 && getDolGlobalString($constkeyforprofid)) {
2642 $showprofid = true;
2643 }
2644 if ($showprofid) {
2645 $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId".$idprofcursor, $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString($constkeyforprofid), '1').'</span>';
2646 }
2647 }
2648 $dropdownBody .= '<br><b>'.$langs->trans("VATIntraShort").'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT').'</span>';
2649 $dropdownBody .= '<br><b>'.$langs->trans("Country").'</b>: <span>'.($mysoc->country_code ? $langs->trans("Country".$mysoc->country_code) : '').'</span>';
2650 if (isModEnabled('multicurrency')) {
2651 $dropdownBody .= '<br><b>'.$langs->trans("Currency").'</b>: <span>'.$conf->currency.'</span>';
2652 }
2653 $dropdownBody .= '</div>';
2654
2655 $dropdownBody .= '<br>';
2656 $dropdownBody .= '<span id="topmenuloginmoreinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowMoreInfos").'</span>';
2657 $dropdownBody .= '<div id="topmenuloginmoreinfo" >';
2658
2659 // login infos
2660 if (!empty($user->admin)) {
2661 $dropdownBody .= '<br><b>'.$langs->trans("Administrator").'</b>: '.yn($user->admin);
2662 }
2663 $company = '';
2664 if (!empty($user->socid)) { // Add third party for external users
2665 $thirdpartystatic = new Societe($db);
2666 $thirdpartystatic->fetch($user->socid);
2667 $companylink = ' '.$thirdpartystatic->getNomUrl(2); // picto only of company
2668 $company = ' ('.$langs->trans("Company").': '.$thirdpartystatic->name.')';
2669 }
2670 $type = ($user->socid ? $langs->trans("External").$company : $langs->trans("Internal"));
2671 $dropdownBody .= '<br><b>'.$langs->trans("Type").':</b> '.$type;
2672 $dropdownBody .= '<br><b>'.$langs->trans("Status").'</b>: '.$user->getLibStatut(0);
2673 $dropdownBody .= '<br>';
2674
2675 $dropdownBody .= '<br><u>'.$langs->trans("Session").'</u>';
2676 $dropdownBody .= '<br><b>'.$langs->trans("IPAddress").'</b>: '.dol_escape_htmltag($_SERVER["REMOTE_ADDR"]);
2677 if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
2678 $dropdownBody .= '<br><b>'.$langs->trans("ConnectedOnMultiCompany").':</b> '.$conf->entity.' (user entity '.$user->entity.')';
2679 }
2680 $dropdownBody .= '<br><b>'.$langs->trans("AuthenticationMode").':</b> '.$_SESSION["dol_authmode"].(empty($dolibarr_main_demo) ? '' : ' (demo)');
2681 $dropdownBody .= '<br><b>'.$langs->trans("ConnectedSince").':</b> '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2682 $dropdownBody .= '<br><b>'.$langs->trans("PreviousConnexion").':</b> '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2683 $dropdownBody .= '<br><b>'.$langs->trans("CurrentTheme").':</b> '.$conf->theme;
2684 // @phan-suppress-next-line PhanRedefinedClassReference
2685 $dropdownBody .= '<br><b>'.$langs->trans("CurrentMenuManager").':</b> '.(isset($menumanager) ? $menumanager->name : 'unknown');
2686 $langFlag = picto_from_langcode($langs->getDefaultLang());
2687 $dropdownBody .= '<br><b>'.$langs->trans("CurrentUserLanguage").':</b> '.($langFlag ? $langFlag.' ' : '').$langs->getDefaultLang();
2688
2689 $tz = (int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst'];
2690 $dropdownBody .= '<br><b>'.$langs->trans("ClientTZ").':</b> '.($tz ? ($tz >= 0 ? '+' : '').$tz : '');
2691 $dropdownBody .= ' ('.$_SESSION['dol_tz_string'].')';
2692 //$dropdownBody .= ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
2693 //if ($_SESSION['dol_dst'] > 0) $dropdownBody .= yn(1);
2694 //else $dropdownBody .= yn(0);
2695
2696 $dropdownBody .= '<br><b>'.$langs->trans("Browser").':</b> '.$conf->browser->name.($conf->browser->version ? ' '.$conf->browser->version : '').' <small class="opacitymedium">('.dol_escape_htmltag($_SERVER['HTTP_USER_AGENT']).')</small>';
2697 $dropdownBody .= '<br><b>'.$langs->trans("Layout").':</b> '.$conf->browser->layout;
2698 $dropdownBody .= '<br><b>'.$langs->trans("Screen").':</b> '.$_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight'];
2699 if ($conf->browser->layout == 'phone') {
2700 $dropdownBody .= '<br><b>'.$langs->trans("Phone").':</b> '.$langs->trans("Yes");
2701 }
2702 if (!empty($_SESSION["disablemodules"])) {
2703 $dropdownBody .= '<br><b>'.$langs->trans("DisabledModules").':</b> <br>'.implode(', ', explode(',', $_SESSION["disablemodules"]));
2704 }
2705 $dropdownBody .= '</div>';
2706
2707 // Execute hook
2708 $parameters = array('user' => $user, 'langs' => $langs);
2709 $result = $hookmanager->executeHooks('printTopRightMenuLoginDropdownBody', $parameters); // Note that $action and $object may have been modified by some hooks
2710 if (is_numeric($result)) {
2711 if ($result == 0) {
2712 $dropdownBody .= $hookmanager->resPrint; // add
2713 } else {
2714 $dropdownBody = $hookmanager->resPrint; // replace
2715 }
2716 }
2717
2718 if (empty($urllogout)) {
2719 $urllogout = DOL_URL_ROOT.'/user/logout.php?token='.newToken();
2720 }
2721
2722 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2723 // accesskey is for Mac: CTRL + key for all browsers
2724 $stringforfirstkey = $langs->trans("KeyboardShortcut");
2725 if ($conf->browser->name == 'chrome') {
2726 $stringforfirstkey .= ' ALT +';
2727 } elseif ($conf->browser->name == 'firefox') {
2728 $stringforfirstkey .= ' ALT + SHIFT +';
2729 } else {
2730 $stringforfirstkey .= ' CTL +';
2731 }
2732
2733 // Defined the links for bottom of card
2734 $profilLink = '<a accesskey="u" href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="button-top-menu-dropdown" title="'.dol_escape_htmltag($langs->trans("YourUserFile").' ('.$stringforfirstkey.' u)').'"><i class="fa fa-user"></i> '.$langs->trans("Card").'</a>';
2735 $urltovirtualcard = '/user/virtualcard.php?id='.((int) $user->id);
2736 $virtuelcardLink = dolButtonToOpenUrlInDialogPopup('publicvirtualcardmenu', $langs->transnoentitiesnoconv("PublicVirtualCardUrl").(is_object($user) ? ' - '.$user->getFullName($langs) : '').' ('.$stringforfirstkey.' v)', img_picto($langs->trans("PublicVirtualCardUrl").' ('.$stringforfirstkey.' v)', 'card', ''), $urltovirtualcard, '', 'button-top-menu-dropdown marginleftonly nohover', "closeTopMenuLoginDropdown()", '', 'v');
2737 $logoutLink = '<a accesskey="l" href="'.$urllogout.'" class="button-top-menu-dropdown" title="'.dol_escape_htmltag($langs->trans("Logout").' ('.$stringforfirstkey.' l)').'"><i class="fa fa-sign-out-alt pictofixedwidth"></i><span class="hideonsmartphone">'.$langs->trans("Logout").'</span></a>';
2738
2739 $profilName = $user->getFullName($langs).' ('.$user->login.')';
2740 if (!empty($user->admin)) {
2741 $profilName = '<i class="far fa-star classfortooltip" title="'.$langs->trans("Administrator").'" ></i> '.$profilName;
2742 }
2743
2744 // Define version to show
2745 $appli = constant('DOL_APPLICATION_TITLE');
2746 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
2747 if ($applicustom) {
2748 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
2749 } else {
2750 $appli .= " ".DOL_VERSION;
2751 }
2752
2753 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2754 $btnUser = '<!-- div for user link -->
2755 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2756 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="dropdown-toggle login-dropdown-a valignmiddle" data-toggle="dropdown">
2757 '.$userImage.(empty($user->photo) ? '<!-- no photo so show also the login --><span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft valignmiddle small">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2758 </a>
2759 <div class="dropdown-menu">
2760 <!-- User image -->
2761 <div class="user-header">
2762 '.$userDropDownImage.'
2763 <p>
2764 '.$profilName.'<br>';
2765 $title = '';
2766 if ($user->datelastlogin) {
2767 $title = $langs->trans("ConnectedSince").' : '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2768 if ($user->datepreviouslogin) {
2769 $title .= '<br>'.$langs->trans("PreviousConnexion").' : '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2770 }
2771 }
2772 $btnUser .= '<small class="classfortooltip" title="'.dol_escape_htmltag($title).'" ><i class="fa fa-user-clock"></i> '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser').'</small><br>';
2773 if ($user->datepreviouslogin) {
2774 $btnUser .= '<small class="classfortooltip" title="'.dol_escape_htmltag($title).'" ><i class="fa fa-user-clock opacitymedium"></i> '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser').'</small><br>';
2775 }
2776
2777 //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
2778 $btnUser .= '
2779 </p>
2780 </div>
2781
2782 <!-- Menu Body user-->
2783 <div class="user-body">'.$dropdownBody.'</div>
2784
2785 <!-- Menu Footer-->
2786 <div class="user-footer">
2787 <div class="pull-left">
2788 '.$profilLink.'
2789 </div>
2790 <div class="pull-left">
2791 '.$virtuelcardLink.'
2792 </div>
2793 <div class="pull-right">
2794 '.$logoutLink.'
2795 </div>
2796 <div class="clearboth"></div>
2797 </div>
2798
2799 </div>
2800 </div>';
2801 } else {
2802 $btnUser = '<!-- div for user link text browser -->
2803 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2804 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="valignmiddle" alt="'.$langs->trans("MyUserCard").'">
2805 '.$userImage.(empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft small valignmiddle">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2806 </a>
2807 </div>';
2808 }
2809
2810 if (!defined('JS_JQUERY_DISABLE_DROPDOWN') && !empty($conf->use_javascript_ajax)) { // This may be set by some pages that use different jquery version to avoid errors
2811 $btnUser .= '
2812 <!-- Code to show/hide the user drop-down -->
2813 <script>
2814 function closeTopMenuLoginDropdown() {
2815 console.log("close login dropdown"); // This is called at each click on page, so we disable the log
2816 // Hide the menus.
2817 jQuery("#topmenu-login-dropdown").removeClass("open");
2818 }
2819 jQuery(document).ready(function() {
2820 jQuery(document).on("click", function(event) {
2821 if (!$(event.target).closest("#topmenu-login-dropdown").length) {
2822 /* console.log("click close login - we click outside"); */
2823 closeTopMenuLoginDropdown();
2824 }
2825 });
2826 ';
2827
2828
2829 //if ($conf->theme != 'md') {
2830 $btnUser .= '
2831 jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
2832 console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
2833 event.preventDefault();
2834 jQuery("#topmenu-login-dropdown").toggleClass("open");
2835 });
2836
2837 jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
2838 console.log("Click on #topmenulogincompanyinfo-btn");
2839 jQuery("#topmenulogincompanyinfo").slideToggle();
2840 });
2841
2842 jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
2843 console.log("Click on #topmenuloginmoreinfo-btn");
2844 jQuery("#topmenuloginmoreinfo").slideToggle();
2845 });';
2846 //}
2847
2848 $btnUser .= '
2849 });
2850 </script>
2851 ';
2852 }
2853
2854 return $btnUser;
2855}
2856
2864{
2865 global $conf, $langs;
2866
2867 // Button disabled on text browser
2868 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2869 return '';
2870 }
2871
2872 $html = '';
2873
2874 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2875 // accesskey is for Mac: CTRL + key for all browsers
2876 $stringforfirstkey = $langs->trans("KeyboardShortcut");
2877 if ($conf->browser->os === 'macintosh') {
2878 $stringforfirstkey .= ' CTL +';
2879 } else {
2880 if ($conf->browser->name == 'chrome') {
2881 $stringforfirstkey .= ' ALT +';
2882 } elseif ($conf->browser->name == 'firefox') {
2883 $stringforfirstkey .= ' ALT + SHIFT +';
2884 } else {
2885 $stringforfirstkey .= ' CTL +';
2886 }
2887 }
2888
2889 if (!empty($conf->use_javascript_ajax)) {
2890 $html .= '<!-- div for quick add link -->
2891 <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
2892 <a accesskey="a" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('QuickAdd').' ('.$stringforfirstkey.' a)"><i class="fa fa-plus-circle"></i></a>
2893 <div class="dropdown-menu">'.printDropdownQuickadd().'</div>
2894 </div>';
2895 if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) { // This may be set by some pages that use different jquery version to avoid errors
2896 $html .= '
2897 <!-- Code to show/hide the user drop-down for the quick add -->
2898 <script>
2899 jQuery(document).ready(function() {
2900 jQuery(document).on("click", function(event) {
2901 if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
2902 /* console.log("click close quick add - we click outside"); */
2903 // Hide the menus.
2904 $("#topmenu-quickadd-dropdown").removeClass("open");
2905 }
2906 });
2907 $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
2908 console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
2909 openQuickAddDropDown(event);
2910 });
2911
2912 // Key map shortcut
2913 $(document).keydown(function(event){
2914 var ostype = \''.dol_escape_js($conf->browser->os).'\';
2915 if (ostype === "macintosh") {
2916 if ( event.which === 65 && event.ctrlKey ) {
2917 console.log(\'control + a : trigger open quick add dropdown\');
2918 openQuickAddDropDown(event);
2919 }
2920 } else {
2921 if ( event.which === 65 && event.ctrlKey && event.shiftKey ) {
2922 console.log(\'control + shift + a : trigger open quick add dropdown\');
2923 openQuickAddDropDown(event);
2924 }
2925 }
2926 });
2927
2928 var openQuickAddDropDown = function(event) {
2929 event.preventDefault();
2930 $("#topmenu-quickadd-dropdown").toggleClass("open");
2931 //$("#top-quickadd-search-input").focus();
2932 }
2933 });
2934 </script>
2935 ';
2936 }
2937 }
2938
2939 return $html;
2940}
2941
2942
2950{
2951 global $conf, $langs;
2952
2953 // Button disabled on text browser
2954 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2955 return '';
2956 }
2957
2958 $html = '';
2959
2960 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2961 // accesskey is for Mac: CTRL + key for all browsers
2962 $stringforfirstkey = $langs->trans("KeyboardShortcut");
2963 if ($conf->browser->os === 'macintosh') {
2964 $stringforfirstkey .= ' CTL +';
2965 } else {
2966 if ($conf->browser->name == 'chrome') {
2967 $stringforfirstkey .= ' ALT +';
2968 } elseif ($conf->browser->name == 'firefox') {
2969 $stringforfirstkey .= ' ALT + SHIFT +';
2970 } else {
2971 $stringforfirstkey .= ' CTL +';
2972 }
2973 }
2974
2975
2976 if (!empty($conf->use_javascript_ajax)) {
2977 $urlforuploadpage = DOL_URL_ROOT.'/core/upload_page.php';
2978 if (!is_numeric(getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE'))) {
2979 $urlforuploadpage = getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE');
2980 }
2981
2982 $html .= '<!-- div for link to upload file -->
2983 <div id="topmenu-uploadfile-dropdown" class="atoplogin dropdown inline-block">
2984 <a accesskey="i" class="dropdown-togglex login-dropdown-a nofocusvisible" data-toggle="dropdown" href="'.$urlforuploadpage.'" title="'.$langs->trans('UploadFile').' ('.$stringforfirstkey.' i)"><i class="fa fa-upload"></i></a>
2985 </div>';
2986 }
2987
2988 return $html;
2989}
2990
2991
2998function printDropdownQuickadd($mode = 0)
2999{
3000 global $user, $langs, $hookmanager;
3001
3002 $items = array(
3003 'items' => array(
3004 array(
3005 "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
3006 "title" => "MenuNewMember@members",
3007 "name" => "Adherent@members",
3008 "picto" => "object_member",
3009 "activation" => isModEnabled('member') && $user->hasRight("adherent", "write"), // vs hooking
3010 "position" => 5,
3011 ),
3012 array(
3013 "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
3014 "title" => "MenuNewThirdParty@companies",
3015 "name" => "ThirdParty@companies",
3016 "picto" => "object_company",
3017 "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
3018 "position" => 10,
3019 ),
3020 array(
3021 "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
3022 "title" => "NewContactAddress@companies",
3023 "name" => "Contact@companies",
3024 "picto" => "object_contact",
3025 "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
3026 "position" => 20,
3027 ),
3028 array(
3029 "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
3030 "title" => "NewPropal@propal",
3031 "name" => "Proposal@propal",
3032 "picto" => "object_propal",
3033 "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
3034 "position" => 30,
3035 ),
3036
3037 array(
3038 "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
3039 "title" => "NewOrder@orders",
3040 "name" => "Order@orders",
3041 "picto" => "object_order",
3042 "activation" => isModEnabled('order') && $user->hasRight("commande", "write"), // vs hooking
3043 "position" => 40,
3044 ),
3045 array(
3046 "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
3047 "title" => "NewBill@bills",
3048 "name" => "Bill@bills",
3049 "picto" => "object_bill",
3050 "activation" => isModEnabled('invoice') && $user->hasRight("facture", "write"), // vs hooking
3051 "position" => 50,
3052 ),
3053 array(
3054 "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
3055 "title" => "NewContractSubscription@contracts",
3056 "name" => "Contract@contracts",
3057 "picto" => "object_contract",
3058 "activation" => isModEnabled('contract') && $user->hasRight("contrat", "write"), // vs hooking
3059 "position" => 60,
3060 ),
3061 array(
3062 "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
3063 "title" => "SupplierProposalNew@supplier_proposal",
3064 "name" => "SupplierProposal@supplier_proposal",
3065 "picto" => "supplier_proposal",
3066 "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
3067 "position" => 70,
3068 ),
3069 array(
3070 "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
3071 "title" => "NewSupplierOrderShort@orders",
3072 "name" => "SupplierOrder@orders",
3073 "picto" => "supplier_order",
3074 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "commande", "write")) || (isModEnabled("supplier_order") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3075 "position" => 80,
3076 ),
3077 array(
3078 "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
3079 "title" => "NewBill@bills",
3080 "name" => "SupplierBill@bills",
3081 "picto" => "supplier_invoice",
3082 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "write")) || (isModEnabled("supplier_invoice") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3083 "position" => 90,
3084 ),
3085 array(
3086 "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
3087 "title" => "NewTicket@ticket",
3088 "name" => "Ticket@ticket",
3089 "picto" => "ticket",
3090 "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
3091 "position" => 100,
3092 ),
3093 array(
3094 "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
3095 "title" => "NewIntervention@interventions",
3096 "name" => "Intervention@interventions",
3097 "picto" => "intervention",
3098 "activation" => isModEnabled('intervention') && $user->hasRight("ficheinter", "creer"), // vs hooking
3099 "position" => 110,
3100 ),
3101 array(
3102 "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
3103 "title" => "NewProduct@products",
3104 "name" => "Product@products",
3105 "picto" => "object_product",
3106 "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
3107 "position" => 400,
3108 ),
3109 array(
3110 "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
3111 "title" => "NewService@products",
3112 "name" => "Service@products",
3113 "picto" => "object_service",
3114 "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
3115 "position" => 410,
3116 ),
3117 array(
3118 "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
3119 "title" => "AddUser@users",
3120 "name" => "User@users",
3121 "picto" => "user",
3122 "activation" => $user->hasRight("user", "user", "write"), // vs hooking
3123 "position" => 500,
3124 ),
3125 ),
3126 );
3127
3128 $dropDownQuickAddHtml = '';
3129
3130 // Define $dropDownQuickAddHtml
3131 if (empty($mode)) {
3132 $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
3133 }
3134 $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
3135
3136 // Allow the $items of the menu to be manipulated by modules
3137 $parameters = array();
3138 $hook_items = $items;
3139 $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks
3140 if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
3141 if ($reshook == 0) {
3142 $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
3143 } else {
3144 $items = $hookmanager->resArray; // replace
3145 }
3146
3147 // Sort menu items by 'position' value
3148 $position = array();
3149 foreach ($items['items'] as $key => $row) {
3150 $position[$key] = $row['position'];
3151 }
3152 $array1_sort_order = SORT_ASC;
3153 array_multisort($position, $array1_sort_order, $items['items']);
3154 }
3155
3156 foreach ($items['items'] as $item) {
3157 if (!$item['activation']) {
3158 continue;
3159 }
3160 $langs->load(explode('@', $item['title'])[1]);
3161 $langs->load(explode('@', $item['name'])[1]);
3162 $dropDownQuickAddHtml .= '
3163 <a class="dropdown-item quickadd-item" href="'.DOL_URL_ROOT.$item['url'].'" title="'.$langs->trans(explode('@', $item['title'])[0]).'">
3164 '. img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
3165 ';
3166 }
3167
3168 if (empty($mode)) {
3169 $dropDownQuickAddHtml .= '</div>';
3170 }
3171 $dropDownQuickAddHtml .= '</div>';
3172
3173 return $dropDownQuickAddHtml;
3174}
3175
3182{
3183 global $langs, $conf, $user;
3184
3185 $html = '';
3186
3187 // Return empty in some case
3188 if (!isModEnabled('bookmark') || !$user->hasRight('bookmark', 'lire')) {
3189 return '';
3190 }
3191 /*
3192 if ($conf->browser->name == 'textbrowser') {
3193 return $html;
3194 }
3195 */
3196
3197 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3198 // accesskey is for Mac: CTRL + key for all browsers
3199 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3200 if ($conf->browser->os === 'macintosh') {
3201 $stringforfirstkey .= ' CTL +';
3202 } else {
3203 if ($conf->browser->name == 'chrome') {
3204 $stringforfirstkey .= ' ALT +';
3205 } elseif ($conf->browser->name == 'firefox') {
3206 $stringforfirstkey .= ' ALT + SHIFT +';
3207 } else {
3208 $stringforfirstkey .= ' CTL +';
3209 }
3210 }
3211
3212 if (!defined('JS_JQUERY_DISABLE_DROPDOWN') && !empty($conf->use_javascript_ajax)) { // This may be set by some pages that use different jquery version to avoid errors
3213 include_once DOL_DOCUMENT_ROOT.'/bookmarks/bookmarks.lib.php';
3214 $langs->load("bookmarks");
3215
3216 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3217 $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
3218 $html .= printDropdownBookmarksList();
3219 $html .= '</div>';
3220 } else {
3221 $html .= '<!-- div for bookmark link -->
3222 <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
3223 <a accesskey="b" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Bookmarks').' ('.$stringforfirstkey.' b)"><i class="fa fa-star"></i></a>
3224 <div class="dropdown-menu">
3226 </div>
3227 </div>';
3228
3229 $html .= '
3230 <!-- Code to show/hide the bookmark drop-down -->
3231 <script>
3232 jQuery(document).ready(function() {
3233 jQuery(document).on("click", function(event) {
3234 if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
3235 /* console.log("close bookmark dropdown - we click outside"); */
3236 // Hide the menus.
3237 $("#topmenu-bookmark-dropdown").removeClass("open");
3238 }
3239 });
3240
3241 jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
3242 console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
3243 openBookMarkDropDown(event);
3244 });
3245
3246 // Key map shortcut
3247 jQuery(document).keydown(function(event) {
3248 var ostype = \''.dol_escape_js($conf->browser->os).'\';
3249 if (ostype === "macintosh") {
3250 if ( event.which === 66 && event.ctrlKey ) {
3251 console.log("Click on control + b : trigger open bookmark dropdown");
3252 openBookMarkDropDown(event);
3253 }
3254 } else {
3255 if ( event.which === 66 && event.ctrlKey && event.shiftKey ) {
3256 console.log("Click on control + shift + b : trigger open bookmark dropdown");
3257 openBookMarkDropDown(event);
3258 }
3259 }
3260 });
3261
3262 var openBookMarkDropDown = function(event) {
3263 event.preventDefault();
3264 jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
3265 jQuery("#top-bookmark-search-input").focus();
3266 }
3267
3268 });
3269 </script>
3270 ';
3271 }
3272 }
3273 return $html;
3274}
3275
3281function top_menu_search()
3282{
3283 global $langs, $conf, $db, $user, $hookmanager; // used by htdocs/core/ajax/selectsearchbox.php
3284
3285 $html = '';
3286
3287 $usedbyinclude = 1;
3288 $arrayresult = array();
3289 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This sets $arrayresult
3290
3291 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3292 // accesskey is for Mac: CTRL + key for all browsers
3293 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3294 if ($conf->browser->name == 'chrome') {
3295 $stringforfirstkey .= ' ALT +';
3296 } elseif ($conf->browser->name == 'firefox') {
3297 $stringforfirstkey .= ' ALT + SHIFT +';
3298 } else {
3299 $stringforfirstkey .= ' CTL +';
3300 }
3301
3302 $searchInput = '<input type="search" name="search_all"'.($stringforfirstkey ? ' title="'.dol_escape_htmltag($stringforfirstkey.' s').'"' : '').' id="top-global-search-input" class="dropdown-search-input search_component_input" placeholder="'.$langs->trans('Search').'" autocomplete="off">';
3303
3304 $defaultAction = '';
3305 $buttonList = '<div class="dropdown-global-search-button-list" >';
3306 // Menu with all searchable items
3307 // @phan-suppress-next-line PhanEmptyForeach // array is really empty
3308 foreach ($arrayresult as $keyItem => $item) {
3309 if (empty($defaultAction)) {
3310 $defaultAction = $item['url'];
3311 }
3312 $buttonList .= '<button class="dropdown-item global-search-item tdoverflowmax300" data-target="'.dol_escape_htmltag($item['url']).'" >';
3313 $buttonList .= $item['text'];
3314 $buttonList .= '</button>';
3315 }
3316 $buttonList .= '</div>';
3317
3318 $dropDownHtml = '<form role="search" id="top-menu-action-search" name="actionsearch" method="GET" action="'.$defaultAction.'">';
3319
3320 $dropDownHtml .= '
3321 <!-- search input -->
3322 <div class="dropdown-header search-dropdown-header">
3323 ' . $searchInput.'
3324 </div>
3325 ';
3326
3327 $dropDownHtml .= '
3328 <!-- Menu Body search -->
3329 <div class="dropdown-body search-dropdown-body">
3330 '.$buttonList.'
3331 </div>
3332 ';
3333
3334 $dropDownHtml .= '</form>';
3335
3336 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3337 // accesskey is for Mac: CTRL + key for all browsers
3338 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3339 if ($conf->browser->name == 'chrome') {
3340 $stringforfirstkey .= ' ALT +';
3341 } elseif ($conf->browser->name == 'firefox') {
3342 $stringforfirstkey .= ' ALT + SHIFT +';
3343 } else {
3344 $stringforfirstkey .= ' CTL +';
3345 }
3346
3347 $html .= '<!-- div for Global Search -->
3348 <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
3349 <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Search').' ('.$stringforfirstkey.' s)">
3350 <i class="fa fa-search" aria-hidden="true" ></i>
3351 </a>
3352 <div class="dropdown-menu dropdown-search">
3353 '.$dropDownHtml.'
3354 </div>
3355 </div>';
3356
3357 $html .= '
3358 <!-- Code to show/hide the user drop-down -->
3359 <script>
3360 jQuery(document).ready(function() {
3361
3362 // prevent submitting form on press ENTER
3363 jQuery("#top-global-search-input").keydown(function (e) {
3364 if (e.keyCode == 13 || e.keyCode == 40) {
3365 var inputs = $(this).parents("form").eq(0).find(":button");
3366 if (inputs[inputs.index(this) + 1] != null) {
3367 inputs[inputs.index(this) + 1].focus();
3368 if (e.keyCode == 13){
3369 inputs[inputs.index(this) + 1].trigger("click");
3370 }
3371
3372 }
3373 e.preventDefault();
3374 return false;
3375 }
3376 });
3377
3378 // arrow key nav
3379 jQuery(document).keydown(function(e) {
3380 // Get the focused element:
3381 var $focused = $(":focus");
3382 if($focused.length && $focused.hasClass("global-search-item")){
3383
3384 // UP - move to the previous line
3385 if (e.keyCode == 38) {
3386 e.preventDefault();
3387 $focused.prev().focus();
3388 }
3389
3390 // DOWN - move to the next line
3391 if (e.keyCode == 40) {
3392 e.preventDefault();
3393 $focused.next().focus();
3394 }
3395 }
3396 });
3397
3398
3399 // submit form action
3400 jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
3401 jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
3402 jQuery("#top-menu-action-search").submit();
3403 });
3404
3405 // close drop down
3406 jQuery(document).on("click", function(event) {
3407 if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
3408 console.log("click close search - we click outside");
3409 // Hide the menus.
3410 jQuery("#topmenu-global-search-dropdown").removeClass("open");
3411 }
3412 });
3413
3414 // Open drop down
3415 jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
3416 console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
3417 openGlobalSearchDropDown();
3418 });
3419
3420 // Key map shortcut
3421 jQuery(document).keydown(function(e){
3422 if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
3423 console.log(\'control + shift + f : trigger open global-search dropdown\');
3424 openGlobalSearchDropDown();
3425 }
3426 if ( e.which === 70 && e.alKey ) {
3427 console.log(\'alt + f : trigger open global-search dropdown\');
3428 openGlobalSearchDropDown();
3429 }
3430 });
3431
3432 var openGlobalSearchDropDown = function() {
3433 jQuery("#topmenu-global-search-dropdown").toggleClass("open");
3434 jQuery("#top-global-search-input").focus();
3435 }
3436
3437 });
3438 </script>
3439 ';
3440
3441 return $html;
3442}
3443
3458function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = array(), $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
3459{
3460 global $user, $conf, $langs, $db, $form;
3461 global $hookmanager, $menumanager;
3462
3463 $searchform = '';
3464
3465 if (!empty($menu_array_before)) {
3466 dol_syslog("Deprecated parameter menu_array_before was used when calling main::left_menu function. Menu entries of module should now be defined into module descriptor and not provided when calling left_menu.", LOG_WARNING);
3467 }
3468
3469 if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
3470 // Instantiate hooks for external modules
3471 $hookmanager->initHooks(array('leftblock'));
3472
3473 print "\n".'<!-- Begin side-nav id-left -->'."\n".'<div class="side-nav"><div id="id-left">'."\n";
3474 print "\n";
3475
3476 if (!is_object($form)) {
3477 $form = new Form($db);
3478 }
3479 $selected = -1;
3480 if (!getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
3481 // Select with select2 is awful on smartphone. TODO Is this still true with select2 v4 ?
3482 if ($conf->browser->layout == 'phone') {
3483 $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
3484 }
3485
3486 $usedbyinclude = 1;
3487 $arrayresult = array();
3488 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
3489
3490 if ($conf->use_javascript_ajax && !getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3491 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3492 // accesskey is for Mac: CTRL + key for all browsers
3493 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3494 if ($conf->browser->name == 'chrome') {
3495 $stringforfirstkey .= ' ALT +';
3496 } elseif ($conf->browser->name == 'firefox') {
3497 $stringforfirstkey .= ' ALT + SHIFT +';
3498 } else {
3499 $stringforfirstkey .= ' CTL +';
3500 }
3501
3502 //$textsearch = $langs->trans("Search");
3503 $textsearch = '<span class="fa fa-search paddingright pictofixedwidth"></span>'.$langs->trans("Search");
3504 $searchform .= $form->selectArrayFilter('searchselectcombo', $arrayresult, $selected, 'accesskey="s"', 1, 0, (getDolGlobalString('MAIN_SEARCHBOX_CONTENT_LOADED_BEFORE_KEY') ? 0 : 1), 'vmenusearchselectcombo', 1, $textsearch, 1, $stringforfirstkey.' s');
3505 } else {
3506 if (is_array($arrayresult)) {
3507 // @phan-suppress-next-line PhanEmptyForeach // array is really empty in else case.
3508 foreach ($arrayresult as $key => $val) {
3509 $searchform .= printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft'.$key, $val['img']);
3510 }
3511 }
3512 }
3513
3514 // Execute hook printSearchForm
3515 $parameters = array('searchform' => $searchform);
3516 $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
3517 if (empty($reshook)) {
3518 $searchform .= $hookmanager->resPrint;
3519 } else {
3520 $searchform = $hookmanager->resPrint;
3521 }
3522
3523 // Force special value for $searchform for text browsers or very old search form
3524 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') || empty($conf->use_javascript_ajax)) {
3525 $urltosearch = DOL_URL_ROOT.'/core/search_page.php?showtitlebefore=1';
3526 $searchform = '<div class="blockvmenuimpair blockvmenusearchphone"><div id="divsearchforms1"><a href="'.$urltosearch.'" accesskey="s" alt="'.dol_escape_htmltag($langs->trans("ShowSearchFields")).'">'.$langs->trans("Search").'...</a></div></div>';
3527 } elseif ($conf->use_javascript_ajax && getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3528 $searchform = '<div class="blockvmenuimpair blockvmenusearchphone"><div id="divsearchforms1"><a href="#" alt="'.dol_escape_htmltag($langs->trans("ShowSearchFields")).'">'.$langs->trans("Search").'...</a></div><div id="divsearchforms2" style="display: none">'.$searchform.'</div>';
3529 $searchform .= '<script>
3530 jQuery(document).ready(function () {
3531 jQuery("#divsearchforms1").click(function(){
3532 jQuery("#divsearchforms2").toggle();
3533 });
3534 });
3535 </script>' . "\n";
3536 $searchform .= '</div>';
3537 }
3538
3539 // Key map shortcut
3540 $searchform .= '<script>
3541 jQuery(document).keydown(function(e){
3542 if( e.which === 70 && e.ctrlKey && e.shiftKey ){
3543 console.log(\'control + shift + f : trigger open global-search dropdown\');
3544 openGlobalSearchDropDown();
3545 }
3546 if( (e.which === 83 || e.which === 115) && e.altKey ){
3547 console.log(\'alt + s : trigger open global-search dropdown\');
3548 openGlobalSearchDropDown();
3549 }
3550 });
3551
3552 var openGlobalSearchDropDown = function() {
3553 jQuery("#searchselectcombo").select2(\'open\');
3554 }
3555 </script>';
3556 }
3557
3558 // Left column
3559 print '<!-- Begin left menu -->'."\n";
3560
3561 print '<div class="vmenu"'.(getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' alt="Left menu"' : '').'>'."\n\n";
3562
3563 // Show left menu with other forms
3564 // @phan-suppress-next-line PhanRedefinedClassReference
3565 $menumanager->menu_array = $menu_array_before;
3566 // @phan-suppress-next-line PhanRedefinedClassReference
3567 $menumanager->menu_array_after = $menu_array_after;
3568 if (getDolGlobalInt('MAIN_MENU_LEFT_DROPDOWN')) {
3569 // @phan-suppress-next-line PhanRedefinedClassReference
3570 $menumanager->showmenu('leftdropdown', array('searchform' => $searchform)); // output menu_array and menu found in database
3571 } else {
3572 // @phan-suppress-next-line PhanRedefinedClassReference
3573 $menumanager->showmenu('left', array('searchform' => $searchform)); // output menu_array and menu found in database
3574 }
3575
3576 // Dolibarr version + help + bug report link
3577 print "\n";
3578 print "<!-- Begin Help Block-->\n";
3579 print '<div id="blockvmenuhelp" class="blockvmenuhelp">'."\n";
3580
3581 // Version
3582 if (getDolGlobalString('MAIN_SHOW_VERSION')) { // Version is already on help picto and on login page.
3583 $doliurl = 'https://www.dolibarr.org';
3584 //local communities
3585 if (preg_match('/fr/i', $langs->defaultlang)) {
3586 $doliurl = 'https://www.dolibarr.fr';
3587 }
3588 if (preg_match('/es/i', $langs->defaultlang)) {
3589 $doliurl = 'https://www.dolibarr.es';
3590 }
3591 if (preg_match('/de/i', $langs->defaultlang)) {
3592 $doliurl = 'https://www.dolibarr.de';
3593 }
3594 if (preg_match('/it/i', $langs->defaultlang)) {
3595 $doliurl = 'https://www.dolibarr.it';
3596 }
3597 if (preg_match('/gr/i', $langs->defaultlang)) {
3598 $doliurl = 'https://www.dolibarr.gr';
3599 }
3600
3601 $appli = constant('DOL_APPLICATION_TITLE');
3602 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
3603 if ($applicustom) {
3604 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
3605 } else {
3606 $appli .= " ".DOL_VERSION;
3607 }
3608
3609 // Clean doliurl if we use a custom application name
3610 if ($applicustom) {
3611 $doliurl = '';
3612 }
3613
3614 print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
3615 if ($doliurl) {
3616 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$doliurl.'">';
3617 } else {
3618 print '<span class="help">';
3619 }
3620 print $appli;
3621 if ($doliurl) {
3622 print '</a>';
3623 } else {
3624 print '</span>';
3625 }
3626 print '</div>'."\n";
3627 }
3628
3629 // Link to bugtrack
3630 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3631 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3632
3633 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK') == 'github') {
3634 $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
3635 $bugbaseurl .= '&title=';
3636 $bugbaseurl .= urlencode("Bug: ");
3637 $bugbaseurl .= '&body=';
3638 $bugbaseurl .= urlencode("# Instructions\n");
3639 $bugbaseurl .= urlencode("*This is a template to help you report good issues. You may use [Github Markdown](https://help.github.com/articles/getting-started-with-writing-and-formatting-on-github/) syntax to format your issue report.*\n");
3640 $bugbaseurl .= urlencode("*Please:*\n");
3641 $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
3642 $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
3643 $bugbaseurl .= urlencode("\n");
3644 $bugbaseurl .= urlencode("\n");
3645 $bugbaseurl .= urlencode("# Bug\n");
3646 $bugbaseurl .= urlencode("[*Short description*]\n");
3647 $bugbaseurl .= urlencode("\n");
3648 $bugbaseurl .= urlencode("## Environment\n");
3649 $bugbaseurl .= urlencode("- **Version**: ".DOL_VERSION."\n");
3650 $bugbaseurl .= urlencode("- **OS**: ".php_uname('s')."\n");
3651 $bugbaseurl .= urlencode("- **Web server**: ".$_SERVER["SERVER_SOFTWARE"]."\n");
3652 $bugbaseurl .= urlencode("- **PHP**: ".php_sapi_name().' '.phpversion()."\n");
3653 $bugbaseurl .= urlencode("- **Database**: ".$db::LABEL.' '.$db->getVersion()."\n");
3654 $bugbaseurl .= urlencode("- **URL(s)**: ".$_SERVER["REQUEST_URI"]."\n");
3655 $bugbaseurl .= urlencode("\n");
3656 $bugbaseurl .= urlencode("## Expected and actual behavior\n");
3657 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3658 $bugbaseurl .= urlencode("\n");
3659 $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
3660 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3661 $bugbaseurl .= urlencode("\n");
3662 $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, dolibarr.log, debugging information…)\n");
3663 $bugbaseurl .= urlencode("[*Files*]\n");
3664 $bugbaseurl .= urlencode("\n");
3665
3666 $bugbaseurl .= urlencode("\n");
3667 $bugbaseurl .= urlencode("## Report\n");
3668 } elseif (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3669 $bugbaseurl = getDolGlobalString('MAIN_BUGTRACK_ENABLELINK');
3670 } else {
3671 $bugbaseurl = "";
3672 }
3673
3674 // Execute hook printBugtrackInfo
3675 $parameters = array('bugbaseurl' => $bugbaseurl);
3676 $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
3677 if (empty($reshook)) {
3678 $bugbaseurl .= $hookmanager->resPrint;
3679 } else {
3680 $bugbaseurl = $hookmanager->resPrint;
3681 }
3682
3683 print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
3684 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$bugbaseurl.'"><i class="fas fa-bug"></i> '.$langs->trans("FindBug").'</a>';
3685 print '</div>';
3686 }
3687
3688 print "</div>\n";
3689 print "<!-- End Help Block-->\n";
3690 print "\n";
3691
3692 print "</div>\n";
3693 print "<!-- End left menu -->\n";
3694 print "\n";
3695
3696 // Execute hook printLeftBlock
3697 $parameters = array();
3698 $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
3699 print $hookmanager->resPrint;
3700
3701 print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
3702 }
3703
3704 print "\n";
3705 print '<!-- Begin right area -->'."\n";
3706
3707 if (empty($leftmenuwithoutmainarea)) {
3708 main_area($title);
3709 }
3710}
3711
3712
3719function main_area($title = '')
3720{
3721 global $conf, $langs, $hookmanager;
3722
3723 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3724 print '<div id="id-right">';
3725 }
3726
3727 print "\n";
3728
3729 print '<!-- Begin div class="fiche" -->'."\n".'<div class="fiche">'."\n";
3730
3731 $hookmanager->initHooks(array('main'));
3732 $parameters = array();
3733 $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
3734 print $hookmanager->resPrint;
3735
3736 if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
3737 print info_admin($langs->trans("WarningYouAreInMaintenanceMode", getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')), 0, 0, '1', 'warning maintenancemode');
3738 }
3739
3740 // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
3741 if (getDolGlobalString('SHOW_SOCINFO_ON_PRINT') && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'aZ09'))) {
3742 $parameters = array();
3743 $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
3744 if (empty($reshook)) {
3745 print '<!-- Begin show mysoc info header -->'."\n";
3746 print '<div id="mysoc-info-header">'."\n";
3747 print '<table class="centpercent div-table-responsive">'."\n";
3748 print '<tbody>';
3749 print '<tr><td rowspan="0" class="width20p">';
3750 if (getDolGlobalString('MAIN_SHOW_LOGO') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalString('MAIN_INFO_SOCIETE_LOGO')) {
3751 print '<img id="mysoc-info-header-logo" style="max-width:100%" alt="" src="'.DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_LOGO'))).'">';
3752 }
3753 print '</td><td rowspan="0" class="width50p"></td></tr>'."\n";
3754 print '<tr><td class="titre bold">'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_NOM')).'</td></tr>'."\n";
3755 print '<tr><td>'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS')).'<br>'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_ZIP')).' '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')).'</td></tr>'."\n";
3756 if (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) {
3757 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Phone").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TEL')).'</td></tr>';
3758 }
3759 if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
3760 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Email").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')).'</td></tr>';
3761 }
3762 if (getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) {
3763 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Web").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_WEB')).'</td></tr>';
3764 }
3765 print '</tbody>';
3766 print '</table>'."\n";
3767 print '</div>'."\n";
3768 print '<!-- End show mysoc info header -->'."\n";
3769 }
3770 }
3771}
3772
3773
3781function getHelpParamFor($helppagename, $langs)
3782{
3783 $helpbaseurl = '';
3784 $helppage = '';
3785 $mode = '';
3786
3787 if (preg_match('/^http/i', $helppagename)) {
3788 // If complete URL
3789 $helpbaseurl = '%s';
3790 $helppage = $helppagename;
3791 $mode = 'local';
3792 } else {
3793 // If WIKI URL
3794 $reg = array();
3795 if (preg_match('/^es/i', $langs->defaultlang)) {
3796 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3797 if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
3798 $helppage = $reg[1];
3799 }
3800 }
3801 if (preg_match('/^fr/i', $langs->defaultlang)) {
3802 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3803 if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
3804 $helppage = $reg[1];
3805 }
3806 }
3807 if (preg_match('/^de/i', $langs->defaultlang)) {
3808 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3809 if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
3810 $helppage = $reg[1];
3811 }
3812 }
3813 if (empty($helppage)) { // If help page not already found
3814 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3815 if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
3816 $helppage = $reg[1];
3817 }
3818 }
3819 $mode = 'wiki';
3820 }
3821 return array('helpbaseurl' => $helpbaseurl, 'helppage' => $helppage, 'mode' => $mode);
3822}
3823
3824
3841function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
3842{
3843 global $langs, $user;
3844
3845 $ret = '';
3846 $ret .= '<form action="'.$urlaction.'" method="post" class="searchform nowraponall tagtr">';
3847 $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
3848 $ret .= '<input type="hidden" name="savelogin" value="'.dol_escape_htmltag($user->login).'">';
3849 if ($showtitlebefore) {
3850 $ret .= '<div class="tagtd left">'.$title.'</div> ';
3851 }
3852 $ret .= '<div class="tagtd">';
3853 $ret .= img_picto('', $img, '', 0, 0, 0, '', 'paddingright width20');
3854 $ret .= '<input type="text" class="flat '.$htmlmorecss.'"';
3855 $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
3856 $ret .= ($accesskey ? ' accesskey="'.$accesskey.'"' : '');
3857 $ret .= ' placeholder="'.strip_tags($title).'"';
3858 $ret .= ($autofocus ? ' autofocus' : '');
3859 $ret .= ' name="'.$htmlinputname.'" id="'.$prefhtmlinputname.$htmlinputname.'" />';
3860 $ret .= '<button type="submit" class="button bordertransp" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
3861 $ret .= '<span class="fa fa-search"></span>';
3862 $ret .= '</button>';
3863 $ret .= '</div>';
3864 $ret .= "</form>\n";
3865 return $ret;
3866}
3867
3868
3869if (!function_exists("llxFooter")) {
3881 function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
3882 {
3883 global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager, $action;
3884 global $delayedhtmlcontent;
3885 global $contextpage, $page, $limit, $mode;
3886 global $dolibarr_distrib;
3887
3888 $ext = 'layout='.urlencode($conf->browser->layout).'&version='.urlencode(DOL_VERSION);
3889
3890 // Hook to add more things on all pages within fiche DIV
3891 $llxfooter = '';
3892 $parameters = array();
3893 $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3894 if (empty($reshook)) {
3895 $llxfooter .= $hookmanager->resPrint;
3896 } elseif ($reshook > 0) {
3897 $llxfooter = $hookmanager->resPrint;
3898 }
3899 if ($llxfooter) {
3900 print $llxfooter;
3901 }
3902
3903 // Global html output events ($mesgs, $errors, $warnings)
3904 dol_htmloutput_events($disabledoutputofmessages);
3905
3906 // Code for search criteria persistence.
3907 // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
3908 if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
3909 // Clean and save data
3910 foreach ($user->lastsearch_values_tmp as $key => $val) {
3911 unset($_SESSION['lastsearch_values_tmp_'.$key]); // Clean array to rebuild it just after
3912 if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
3913 if (empty($val['sortfield'])) {
3914 unset($val['sortfield']);
3915 }
3916 if (empty($val['sortorder'])) {
3917 unset($val['sortorder']);
3918 }
3919 dol_syslog('Save lastsearch_values_tmp_'.$key.'='.json_encode($val, 0)." (systematic recording of last search criteria)");
3920 $_SESSION['lastsearch_values_tmp_'.$key] = json_encode($val);
3921 unset($_SESSION['lastsearch_values_'.$key]);
3922 }
3923 }
3924 }
3925
3926
3927 $relativepathstring = $_SERVER["PHP_SELF"];
3928 // Clean $relativepathstring
3929 if (constant('DOL_URL_ROOT')) {
3930 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
3931 }
3932 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3933 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3934 if (preg_match('/list\.php$/', $relativepathstring)) {
3935 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
3936 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
3937 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
3938 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
3939
3940 if (!empty($contextpage)) {
3941 $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring] = $contextpage;
3942 }
3943 if (!empty($page) && $page > 0) {
3944 $_SESSION['lastsearch_page_tmp_'.$relativepathstring] = $page;
3945 }
3946 if (!empty($limit) && $limit != $conf->liste_limit) {
3947 $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] = $limit;
3948 }
3949 if (!empty($mode)) {
3950 $_SESSION['lastsearch_mode_tmp_'.$relativepathstring] = $mode;
3951 }
3952
3953 unset($_SESSION['lastsearch_contextpage_'.$relativepathstring]);
3954 unset($_SESSION['lastsearch_page_'.$relativepathstring]);
3955 unset($_SESSION['lastsearch_limit_'.$relativepathstring]);
3956 unset($_SESSION['lastsearch_mode_'.$relativepathstring]);
3957 }
3958
3959 // Core error message
3960 if (getDolGlobalString('MAIN_CORE_ERROR')) {
3961 // Ajax version
3962 if ($conf->use_javascript_ajax) {
3963 $title = img_warning().' '.$langs->trans('CoreErrorTitle');
3964 print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
3965 } else {
3966 // html version
3967 $msg = img_warning().' '.$langs->trans('CoreErrorMessage');
3968 print '<div class="error">'.$msg.'</div>';
3969 }
3970
3971 //define("MAIN_CORE_ERROR",0); // Constant was defined and we can't change value of a constant
3972 }
3973
3974 print "\n\n";
3975
3976 print '</div> <!-- End div class="fiche" -->'."\n"; // End div fiche
3977
3978 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3979 print '</div> <!-- End div id-right -->'."\n"; // End div id-right
3980 }
3981
3982 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
3983 print '</div> <!-- End div id-container -->'."\n"; // End div container
3984 }
3985
3986 print "\n";
3987 if ($comment) {
3988 print '<!-- '.$comment.' -->'."\n";
3989 }
3990
3991 printCommonFooter($zone);
3992
3993 if (!empty($delayedhtmlcontent)) {
3994 print $delayedhtmlcontent;
3995 }
3996
3997 if (!empty($conf->use_javascript_ajax)) {
3998 print "\n".'<!-- Includes JS Footer of Dolibarr -->'."\n";
3999 print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang.($ext ? '&'.$ext : '').'"></script>'."\n";
4000 }
4001
4002 // JS wrapper to add log when clicking on download or preview
4003 if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
4004 if (in_array($object->element, array('facture')) && $object->statut > 0) { // Restrict for the moment to element 'facture'
4005 print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
4006 ?>
4007 <script>
4008 jQuery(document).ready(function () {
4009 $('a.documentpreview').click(function() {
4010 console.log("Call /blockedlog/ajax/block-add on a.documentpreview");
4011 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
4012 , {
4013 id:<?php echo $object->id; ?>
4014 , element:'<?php echo dol_escape_js($object->element) ?>'
4015 , action:'DOC_PREVIEW'
4016 , token: '<?php echo currentToken(); ?>'
4017 }
4018 );
4019 });
4020 $('a.documentdownload').click(function() {
4021 console.log("Call /blockedlog/ajax/block-add a.documentdownload");
4022 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
4023 , {
4024 id:<?php echo $object->id; ?>
4025 , element:'<?php echo dol_escape_js($object->element) ?>'
4026 , action:'DOC_DOWNLOAD'
4027 , token: '<?php echo currentToken(); ?>'
4028 }
4029 );
4030 });
4031 });
4032 </script>
4033 <?php
4034 }
4035 }
4036
4037 // A div for the #dialogforpopup popup
4038 print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
4039 print '<div id="dialogforpopup" style="display: none;"></div>'."\n";
4040
4041 // Add code for the asynchronous anonymous first ping (for telemetry)
4042 // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
4043 $forceping = GETPOST('forceping', 'alpha');
4044 if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceping) {
4045 //print '<!-- instance_unique_id='.$conf->file->instance_unique_id.' MAIN_FIRST_PING_OK_ID='.$conf->global->MAIN_FIRST_PING_OK_ID.' -->';
4046 $hash_unique_id = dol_hash('dolibarr'.$conf->file->instance_unique_id, 'sha256'); // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
4047
4048 if (!getDolGlobalString('MAIN_FIRST_PING_OK_DATE')
4049 || (!empty($conf->file->instance_unique_id) && ($hash_unique_id != $conf->global->MAIN_FIRST_PING_OK_ID) && (getDolGlobalString('MAIN_FIRST_PING_OK_ID') != 'disabled'))
4050 || $forceping) {
4051 // No ping done if we are into an alpha version
4052 if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
4053 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
4054 } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
4055 // MAIN_LAST_PING_KO_DATE
4056 // Disable ping if MAIN_LAST_PING_KO_DATE is set and is recent (this month)
4057 if (getDolGlobalString('MAIN_LAST_PING_KO_DATE') && substr($conf->global->MAIN_LAST_PING_KO_DATE, 0, 6) == dol_print_date(dol_now(), '%Y%m') && !$forceping) {
4058 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. An error already occurred this month, we will try later. -->\n";
4059 } else {
4060 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4061
4062 print "\n".'<!-- Includes JS for Ping of Dolibarr forceping='.$forceping.' MAIN_FIRST_PING_OK_DATE='.getDolGlobalString("MAIN_FIRST_PING_OK_DATE").' MAIN_FIRST_PING_OK_ID='.getDolGlobalString("MAIN_FIRST_PING_OK_ID").' MAIN_LAST_PING_KO_DATE='.getDolGlobalString("MAIN_LAST_PING_KO_DATE").' -->'."\n";
4063 print "\n<!-- JS CODE TO ENABLE the anonymous Ping -->\n";
4064 $url_for_ping = getDolGlobalString('MAIN_URL_FOR_PING', "https://ping.dolibarr.org/");
4065 // Try to guess the distrib used
4066 $distrib = 'standard';
4067 if ($_SERVER["SERVER_ADMIN"] == 'doliwamp@localhost') {
4068 $distrib = 'doliwamp';
4069 }
4070 if (!empty($dolibarr_distrib)) {
4071 $distrib = $dolibarr_distrib;
4072 }
4073 ?>
4074 <script>
4075 jQuery(document).ready(function (tmp) {
4076 console.log("Try Ping with hash_unique_id is dol_hash('dolibarr'+instance_unique_id, 'sha256')");
4077 $.ajax({
4078 method: "POST",
4079 url: "<?php echo $url_for_ping ?>",
4080 timeout: 500, // timeout milliseconds
4081 cache: false,
4082 data: {
4083 hash_algo: 'dol_hash-sha256',
4084 hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
4085 action: 'dolibarrping',
4086 version: '<?php echo (float) DOL_VERSION; ?>',
4087 entity: '<?php echo (int) $conf->entity; ?>',
4088 dbtype: '<?php echo dol_escape_js($db->type); ?>',
4089 country_code: '<?php echo $mysoc->country_code ? dol_escape_js($mysoc->country_code) : 'unknown'; ?>',
4090 php_version: '<?php echo dol_escape_js(phpversion()); ?>',
4091 os_version: '<?php echo dol_escape_js(version_os('smr')); ?>',
4092 db_version: '<?php echo dol_escape_js(version_db()); ?>',
4093 distrib: '<?php echo $distrib ? dol_escape_js($distrib) : 'unknown'; ?>',
4094 token: 'notrequired'
4095 },
4096 success: function (data, status, xhr) { // success callback function (data contains body of response)
4097 console.log("Ping ok");
4098 $.ajax({
4099 method: 'GET',
4100 url: '<?php echo DOL_URL_ROOT.'/core/ajax/pingresult.php'; ?>',
4101 timeout: 500, // timeout milliseconds
4102 cache: false,
4103 data: { hash_algo: 'dol_hash-sha256', hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>', action: 'firstpingok', token: '<?php echo currentToken(); ?>' }, // for update
4104 });
4105 },
4106 error: function (data,status,xhr) { // error callback function
4107 console.log("Ping ko: " + data);
4108 $.ajax({
4109 method: 'GET',
4110 url: '<?php echo DOL_URL_ROOT.'/core/ajax/pingresult.php'; ?>',
4111 timeout: 500, // timeout milliseconds
4112 cache: false,
4113 data: { hash_algo: 'dol_hash-sha256', hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>', action: 'firstpingko', token: '<?php echo currentToken(); ?>' },
4114 });
4115 }
4116 });
4117 });
4118 </script>
4119 <?php
4120 }
4121 } else {
4122 $now = dol_now();
4123 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
4124 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
4125 dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_DATE', dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
4126 dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_ID', 'disabled', 'chaine', 0, '', $conf->entity);
4127 }
4128 }
4129 }
4130
4131 $parameters = array();
4132 $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters); // Note that $action and $object may have been modified by some hooks
4133 if ($reshook > 0) {
4134 print $hookmanager->resPrint;
4135 }
4136
4137 print "</body>\n";
4138 print "</html>\n";
4139 }
4140}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
versioncompare($versionarray1, $versionarray2)
Compare 2 versions (stored into 2 arrays), to know if a version (a,b,c) is lower than (x,...
Definition admin.lib.php:71
ajax_dialog($title, $message, $w=350, $h=150)
Show an ajax dialog.
Definition ajax.lib.php:420
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
printDropdownBookmarksList()
Add area with bookmarks in top menu.
DolibarrDebugBar class.
Definition DebugBar.php:47
Class to manage generation of HTML components Only common components must be here.
static showphoto($modulepart, $object, $width=100, $height=0, $caneditfield=0, $cssclass='photowithmargin', $imagesize='', $addlinktofullsize=1, $cache=0, $forcecapture='', $noexternsourceoverwrite=0)
Return HTML code to output a photo.
Class to manage hooks.
Class to manage left menus.
Class to manage menu Auguria.
loadMenu($forcemainmenu='', $forceleftmenu='')
Load this->tabMenu.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition date.lib.php:431
if(!defined( 'DOL_APPLICATION_TITLE')) if(!defined('DOL_VERSION')) if(!defined( 'EURO')) if(!defined('LOG_DEBUG')) if(defined( 'DOL_INC_FOR_VERSION_ERROR')) dol_session_start()
Replace session_start()
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
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.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='classlink button bordertransp', $jsonopen='', $backtopagejsfields='', $accesskey='')
Return HTML code to output a button to open a dialog popup box.
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
getBrowserInfo($user_agent)
Return information about user browser.
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
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...
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
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition inc.php:420
if(!empty( $_SERVER[ 'MAIN_SHOW_TUNING_INFO'])) getArrayOfEmoji()
Return array of Emojis.
Definition main.inc.php:62
top_menu_importfile()
Build the tooltip on top menu quick add.
top_menu_quickadd()
Build the tooltip on top menu quick add.
top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $disableforlogin=0, $disablenofollow=0, $disablenoindex=0)
Output html header of a page.
top_menu_user($hideloginname=0, $urllogout='')
Build the tooltip on user login.
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET,...
Definition main.inc.php:123
left_menu($menu_array_before, $helppagename='', $notused='', $menu_array_after=array(), $leftmenuwithoutmainarea=0, $title='', $acceptdelayedhtml=0)
Show left menu bar.
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
main_area($title='')
Begin main area.
realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition main.inc.php:86
getHelpParamFor($helppagename, $langs)
Return helpbaseurl, helppage and mode.
printDropdownQuickadd($mode=0)
Generate list of quickadd items.
printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey='', $prefhtmlinputname='', $img='', $showtitlebefore=0, $autofocus=0)
Show a search area.
top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $morequerystring='', $helppagename='')
Show an HTML header + a BODY + The top menu bar.
top_menu_search()
Build the tooltip on top menu search.
analyseVarsForSqlAndScriptsInjection(&$var, $type, $stopcode=1)
Return true if security check on parameters are OK, false otherwise.
Definition main.inc.php:263
top_menu_bookmark()
Build the tooltip on top menu bookmark.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:154
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
checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode, $context='')
Return a login if login/pass was successful.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.