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