dolibarr 21.0.0-beta
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 Charlene Benke <charlie@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|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|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|beforeinput)[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|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|beforeinput)[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 $conf->$module->enabled = false;
742 foreach ($modulepartkeys as $modulepartkey) {
743 unset($conf->modules_parts[$modulepartkey][$module]);
744 }
745 if ($module == 'fournisseur') { // Special case
746 $conf->supplier_order->enabled = 0;
747 $conf->supplier_invoice->enabled = 0;
748 }
749 }
750 }
751}
752
753// Set current modulepart
754$modulepart = explode("/", $_SERVER["PHP_SELF"]);
755if (is_array($modulepart) && count($modulepart) > 0) {
756 foreach ($conf->modules as $module) {
757 if (in_array($module, $modulepart)) {
758 $modulepart = $module;
759 break;
760 }
761 }
762}
763if (is_array($modulepart)) {
764 $modulepart = '';
765}
766
767
768/*
769 * Phase authentication / login
770 */
771
772$login = '';
773$error = 0;
774if (!defined('NOLOGIN')) {
775 // $authmode lists the different method of identification to be tested in order of preference.
776 // Example: 'http', 'dolibarr', 'ldap', 'http,forceuser', '...'
777
778 if (defined('MAIN_AUTHENTICATION_MODE')) {
779 $dolibarr_main_authentication = constant('MAIN_AUTHENTICATION_MODE');
780 } else {
781 // Authentication mode
782 if (empty($dolibarr_main_authentication)) {
783 $dolibarr_main_authentication = 'dolibarr';
784 }
785 // Authentication mode: forceuser
786 if ($dolibarr_main_authentication == 'forceuser' && empty($dolibarr_auto_user)) {
787 $dolibarr_auto_user = 'auto';
788 }
789 }
790 // Set authmode
791 $authmode = explode(',', $dolibarr_main_authentication);
792
793 // No authentication mode
794 if (!count($authmode)) {
795 $langs->load('main');
796 dol_print_error(null, $langs->trans("ErrorConfigParameterNotDefined", 'dolibarr_main_authentication'));
797 exit;
798 }
799
800 // If login request was already post, we retrieve login from the session
801 // Call module if not realized that his request.
802 // At the end of this phase, the variable $login is defined.
803 $resultFetchUser = '';
804 $test = true;
805 $dol_authmode = null;
806
807 if (!isset($_SESSION["dol_login"])) {
808 // It is not already authenticated and it requests the login / password
809 include_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
810
811 $dol_dst_observed = GETPOSTINT("dst_observed", 3);
812 $dol_dst_first = GETPOSTINT("dst_first", 3);
813 $dol_dst_second = GETPOSTINT("dst_second", 3);
814 $dol_screenwidth = GETPOSTINT("screenwidth", 3);
815 $dol_screenheight = GETPOSTINT("screenheight", 3);
816 $dol_hide_topmenu = GETPOSTINT('dol_hide_topmenu', 3);
817 $dol_hide_leftmenu = GETPOSTINT('dol_hide_leftmenu', 3);
818 $dol_optimize_smallscreen = GETPOSTINT('dol_optimize_smallscreen', 3);
819 $dol_no_mouse_hover = GETPOSTINT('dol_no_mouse_hover', 3);
820 $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
821
822 // If in demo mode, we check we go to home page through the public/demo/index.php page
823 if (!empty($dolibarr_main_demo) && $_SERVER['PHP_SELF'] == DOL_URL_ROOT.'/index.php') { // We ask index page
824 if (empty($_SERVER['HTTP_REFERER']) || !preg_match('/public/', $_SERVER['HTTP_REFERER'])) {
825 dol_syslog("Call index page from another url than demo page (call is done from page ".(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFER']).")");
826 $url = '';
827 $url .= ($url ? '&' : '').($dol_hide_topmenu ? 'dol_hide_topmenu='.$dol_hide_topmenu : '');
828 $url .= ($url ? '&' : '').($dol_hide_leftmenu ? 'dol_hide_leftmenu='.$dol_hide_leftmenu : '');
829 $url .= ($url ? '&' : '').($dol_optimize_smallscreen ? 'dol_optimize_smallscreen='.$dol_optimize_smallscreen : '');
830 $url .= ($url ? '&' : '').($dol_no_mouse_hover ? 'dol_no_mouse_hover='.$dol_no_mouse_hover : '');
831 $url .= ($url ? '&' : '').($dol_use_jmobile ? 'dol_use_jmobile='.$dol_use_jmobile : '');
832 $url = DOL_URL_ROOT.'/public/demo/index.php'.($url ? '?'.$url : '');
833 header("Location: ".$url);
834 exit;
835 }
836 }
837
838 // Hooks for security access
839 $action = '';
840 $hookmanager->initHooks(array('login'));
841 $parameters = array();
842 $reshook = $hookmanager->executeHooks('beforeLoginAuthentication', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
843 if ($reshook < 0) {
844 $test = false;
845 $error++;
846 }
847
848 // Verification security graphic code
849 if ($test && GETPOST('actionlogin', 'aZ09') == 'login' && GETPOST("username", "alpha", 2) && getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA') && !isset($_SESSION['dol_bypass_antispam'])) {
850 $ok = false;
851
852 // Use the captcha handler to validate
853 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
854 $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard');
855
856 // List of directories where we can find captcha handlers
857 $dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array());
858 $fullpathclassfile = '';
859 foreach ($dirModCaptcha as $dir) {
860 $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2);
861 if ($fullpathclassfile) {
862 break;
863 }
864 }
865
866 // The file for captcha check has been found
867 if ($fullpathclassfile) {
868 include_once $fullpathclassfile;
869 $captchaobj = null;
870
871 // Charging the numbering class
872 $classname = "modCaptcha".ucfirst($captcha);
873 if (class_exists($classname)) {
875 $captchaobj = new $classname($db, $conf, $langs, $user);
876 '@phan-var-force ModeleCaptcha $captchaobj';
877
878 if (is_object($captchaobj) && method_exists($captchaobj, 'validateCodeAfterLoginSubmit')) {
879 $ok = $captchaobj->validateCodeAfterLoginSubmit(); // @phan-suppress-current-line PhanUndeclaredMethod
880 } else {
881 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler '.get_class($captchaobj).' does not have any method validateCodeAfterLoginSubmit()';
882 $test = false;
883 $error++;
884 }
885 } else {
886 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler class '.$classname.' was not found after the include';
887 $test = false;
888 $error++;
889 }
890 } else {
891 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha);
892 $test = false;
893 $error++;
894 }
895
896 // Process error of captcha validation
897 if (!$ok) {
898 dol_syslog('Bad value for code, connection refused', LOG_NOTICE);
899 // Load translation files required by page
900 $langs->loadLangs(array('main', 'errors'));
901
902 $_SESSION["dol_loginmesg"] = (empty($_SESSION["dol_loginmesg"]) ? "" : $_SESSION["dol_loginmesg"]."<br>\n").$langs->transnoentitiesnoconv("ErrorBadValueForCode");
903 $test = false;
904
905 // Call trigger for the "security events" log
906 $user->context['audit'] = 'ErrorBadValueForCode - login='.GETPOST("username", "alpha", 2);
907
908 // Call trigger
909 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
910 if ($result < 0) {
911 $error++;
912 }
913 // End call triggers
914
915 // Hooks on failed login
916 $action = '';
917 $hookmanager->initHooks(array('login'));
918 $parameters = array('dol_authmode' => $authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
919 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
920 if ($reshook < 0) {
921 $error++;
922 }
923
924 // Note: exit is done later ($test is false)
925 }
926 }
927
928 $allowedmethodtopostusername = 3;
929 if (defined('MAIN_AUTHENTICATION_POST_METHOD')) {
930 $allowedmethodtopostusername = constant('MAIN_AUTHENTICATION_POST_METHOD'); // Note a value of 2 is not compatible with some authentication methods that put username as GET parameter
931 }
932 // TODO Remove use of $_COOKIE['login_dolibarr'] by replacing line with $usertotest = GETPOST("username", "alpha", $allowedmethodtopostusername); ?
933 $usertotest = (!empty($_COOKIE['login_dolibarr']) ? preg_replace('/[^a-zA-Z0-9_@\-\.]/', '', $_COOKIE['login_dolibarr']) : GETPOST("username", "alpha", $allowedmethodtopostusername));
934 $passwordtotest = GETPOST('password', 'password', $allowedmethodtopostusername);
935 $entitytotest = (GETPOSTINT('entity') ? GETPOSTINT('entity') : (!empty($conf->entity) ? $conf->entity : 1));
936
937 // Define if we received the correct data to go into the test of the login with the checkLoginPassEntity().
938 $goontestloop = false;
939 if (isset($_SERVER["REMOTE_USER"]) && in_array('http', $authmode)) { // For http basic login test
940 $goontestloop = true;
941 }
942 if ($dolibarr_main_authentication == 'forceuser' && !empty($dolibarr_auto_user)) { // For automatic login with a forced user
943 $goontestloop = true;
944 }
945 if (GETPOST("username", "alpha", $allowedmethodtopostusername)) { // For posting the login form
946 $goontestloop = true;
947 }
948 if (GETPOST('openid_mode', 'alpha')) { // For openid_connect ?
949 $goontestloop = true;
950 }
951 if (GETPOST('beforeoauthloginredirect') || GETPOST('afteroauthloginreturn')) { // For oauth login
952 $goontestloop = true;
953 }
954 if (!empty($_COOKIE['login_dolibarr'])) { // TODO For ? Remove this ?
955 $goontestloop = true;
956 }
957
958 if (!is_object($langs)) { // This can occurs when calling page with NOREQUIRETRAN defined, however we need langs for error messages.
959 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
960 $langs = new Translate("", $conf);
961 $langcode = (GETPOST('lang', 'aZ09', 1) ? GETPOST('lang', 'aZ09', 1) : getDolGlobalString('MAIN_LANG_DEFAULT', 'auto'));
962 if (defined('MAIN_LANG_DEFAULT')) {
963 $langcode = constant('MAIN_LANG_DEFAULT');
964 }
965 $langs->setDefaultLang($langcode);
966 }
967
968 // Validation of login/pass/entity
969 // If ok, the variable login will be returned
970 // If error, we will put error message in session under the name dol_loginmesg
971 if ($test && $goontestloop && GETPOST('actionlogin', 'aZ09') != 'disabled' && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {
972 // Loop on each test mode defined into $authmode
973 // $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');
974 $oauthmodetotestarray = array('google');
975 foreach ($oauthmodetotestarray as $oauthmodetotest) {
976 if (in_array($oauthmodetotest.'oauth', $authmode)) { // This is an authmode that is currently qualified. Do we have to remove it ?
977 // If we click on the link to use OAuth authentication or if we goes after callback return, we do nothing
978 // TODO Use: if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {
979 if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn')) {
980 continue;
981 }
982 dol_syslog("User did not click on link for OAuth, or is not on the OAuth return, so we disable check using ".$oauthmodetotest);
983 foreach ($authmode as $tmpkey => $tmpval) {
984 if ($tmpval == $oauthmodetotest.'oauth') {
985 unset($authmode[$tmpkey]);
986 break;
987 }
988 }
989 }
990 }
991
992 // Check login for all qualified modes in array $authmode.
993 $login = checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode);
994 if ($login === '--bad-login-validity--') {
995 $login = '';
996 }
997
998 if ($login) {
999 $dol_authmode = $conf->authmode; // This property is defined only when logged, to say what mode was successfully used
1000 $dol_tz = empty($_POST["tz"]) ? (empty($_SESSION["tz"]) ? '' : $_SESSION["tz"]) : $_POST["tz"];
1001 $dol_tz_string = empty($_POST["tz_string"]) ? (empty($_SESSION["tz_string"]) ? '' : $_SESSION["tz_string"]) : $_POST["tz_string"];
1002 $dol_tz_string = preg_replace('/\s*\‍(.+\‍)$/', '', $dol_tz_string);
1003 $dol_tz_string = preg_replace('/,/', '/', $dol_tz_string);
1004 $dol_tz_string = preg_replace('/\s/', '_', $dol_tz_string);
1005 $dol_dst = 0;
1006 // Keep $_POST here. Do not use GETPOSTISSET
1007 $dol_dst_first = empty($_POST["dst_first"]) ? (empty($_SESSION["dst_first"]) ? '' : $_SESSION["dst_first"]) : $_POST["dst_first"];
1008 $dol_dst_second = empty($_POST["dst_second"]) ? (empty($_SESSION["dst_second"]) ? '' : $_SESSION["dst_second"]) : $_POST["dst_second"];
1009 if ($dol_dst_first && $dol_dst_second) {
1010 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1011 $datenow = dol_now();
1012 $datefirst = dol_stringtotime($dol_dst_first);
1013 $datesecond = dol_stringtotime($dol_dst_second);
1014 if ($datenow >= $datefirst && $datenow < $datesecond) {
1015 $dol_dst = 1;
1016 }
1017 }
1018 $dol_screenheight = empty($_POST["screenheight"]) ? (empty($_SESSION["dol_screenheight"]) ? '' : $_SESSION["dol_screenheight"]) : $_POST["screenheight"];
1019 $dol_screenwidth = empty($_POST["screenwidth"]) ? (empty($_SESSION["dol_screenwidth"]) ? '' : $_SESSION["dol_screenwidth"]) : $_POST["screenwidth"];
1020 //print $datefirst.'-'.$datesecond.'-'.$datenow.'-'.$dol_tz.'-'.$dol_tzstring.'-'.$dol_dst.'-'.sdol_screenheight.'-'.sdol_screenwidth; exit;
1021 }
1022
1023 if (!$login) {
1024 dol_syslog('Bad password, connection refused (see a previous notice message for more info)', LOG_NOTICE);
1025 // Load translation files required by page
1026 $langs->loadLangs(array('main', 'errors'));
1027
1028 // Bad password. No authmode has found a good password.
1029 // We set a generic message if not defined inside function checkLoginPassEntity or subfunctions
1030 if (empty($_SESSION["dol_loginmesg"])) {
1031 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadLoginPassword");
1032 }
1033
1034 // Call trigger for the "security events" log
1035 $user->context['audit'] = $langs->trans("ErrorBadLoginPassword").' - login='.GETPOST("username", "alpha", 2);
1036
1037 // Call trigger
1038 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1039 if ($result < 0) {
1040 $error++;
1041 }
1042 // End call triggers
1043
1044 // Hooks on failed login
1045 $action = '';
1046 $hookmanager->initHooks(array('login'));
1047 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1048 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1049 if ($reshook < 0) {
1050 $error++;
1051 }
1052
1053 // Note: exit is done in next chapter
1054 }
1055 }
1056
1057 // End test login / passwords
1058 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.
1059 // No data to test login, so we show the login page.
1060 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);
1061 if (defined('NOREDIRECTBYMAINTOLOGIN')) {
1062 // When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
1063 // See example with selectsearchbox.php. This case is reserved for the selectesearchbox.php so we can
1064 // report a message to ask to login when search ajax component is used after a timeout.
1065 //top_httphead();
1066 return 'ERROR_NOT_LOGGED';
1067 } else {
1068 if (!empty($_SERVER["HTTP_USER_AGENT"]) && $_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
1069 http_response_code(401); // It makes easier to understand if session was broken during security tests
1070 }
1071
1072 // Show login form
1073 dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : '')); // This include http headers
1074 }
1075 exit;
1076 }
1077
1078 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.
1079 if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
1080 dol_syslog('User not found or not valid, connection refused');
1081 session_destroy();
1082 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
1083 session_name($sessionname);
1085
1086 if ($resultFetchUser == 0) {
1087 // Load translation files required by page
1088 $langs->loadLangs(array('main', 'errors'));
1089
1090 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
1091
1092 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
1093 } elseif ($resultFetchUser < 0) {
1094 $_SESSION["dol_loginmesg"] = $user->error;
1095
1096 $user->context['audit'] = $user->error;
1097 } else {
1098 // Load translation files required by the page
1099 $langs->loadLangs(array('main', 'errors'));
1100
1101 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginDateValidity");
1102
1103 $user->context['audit'] = $langs->trans("ErrorLoginDateValidity").' - login='.$login;
1104 }
1105
1106 // Call trigger
1107 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1108 if ($result < 0) {
1109 $error++;
1110 }
1111 // End call triggers
1112
1113
1114 // Hooks on failed login
1115 $action = '';
1116 $hookmanager->initHooks(array('login'));
1117 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1118 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1119 if ($reshook < 0) {
1120 $error++;
1121 }
1122
1123 $paramsurl = array();
1124 if (GETPOSTINT('textbrowser')) {
1125 $paramsurl[] = 'textbrowser='.GETPOSTINT('textbrowser');
1126 }
1127 if (GETPOSTINT('nojs')) {
1128 $paramsurl[] = 'nojs='.GETPOSTINT('nojs');
1129 }
1130 if (GETPOST('lang', 'aZ09')) {
1131 $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
1132 }
1133 header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
1134 exit;
1135 } else {
1136 // User is loaded, we may need to change language for him according to its choice
1137 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1138 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1139 }
1140 }
1141 } else {
1142 // We are already into an authenticated session
1143 $login = $_SESSION["dol_login"];
1144 $entity = isset($_SESSION["dol_entity"]) ? $_SESSION["dol_entity"] : 0;
1145 dol_syslog("- This is an already logged session. _SESSION['dol_login']=".$login." _SESSION['dol_entity']=".$entity, LOG_DEBUG);
1146
1147 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entity > 0 ? $entity : -1));
1148
1149 //var_dump(dol_print_date($user->flagdelsessionsbefore, 'dayhour', 'gmt')." ".dol_print_date($_SESSION["dol_logindate"], 'dayhour', 'gmt'));
1150
1151 if ($resultFetchUser <= 0
1152 || ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"])
1153 || ($user->status != $user::STATUS_ENABLED)
1154 || ($user->isNotIntoValidityDateRange())) {
1155 if ($resultFetchUser <= 0) {
1156 // Account has been removed after login
1157 dol_syslog("Can't load user even if session logged. _SESSION['dol_login']=".$login, LOG_WARNING);
1158 } elseif ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"]) {
1159 // Session is no more valid
1160 dol_syslog("The user has a date for session invalidation = ".$user->flagdelsessionsbefore." and a session date = ".$_SESSION["dol_logindate"].". We must invalidate its sessions.");
1161 } elseif ($user->status != $user::STATUS_ENABLED) {
1162 // User is not enabled
1163 dol_syslog("The user login is disabled");
1164 } else {
1165 // User validity dates are no more valid
1166 dol_syslog("The user login has a validity between [".$user->datestartvalidity." and ".$user->dateendvalidity."], current date is ".dol_now());
1167 }
1168 session_destroy();
1169 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
1170 session_name($sessionname);
1172
1173 if ($resultFetchUser == 0) {
1174 $langs->loadLangs(array('main', 'errors'));
1175
1176 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
1177
1178 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
1179 } elseif ($resultFetchUser < 0) {
1180 $_SESSION["dol_loginmesg"] = $user->error;
1181
1182 $user->context['audit'] = $user->error;
1183 } else {
1184 $langs->loadLangs(array('main', 'errors'));
1185
1186 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorSessionInvalidatedAfterPasswordChange");
1187
1188 $user->context['audit'] = 'ErrorUserSessionWasInvalidated - login='.$login;
1189 }
1190
1191 // Call trigger
1192 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1193 if ($result < 0) {
1194 $error++;
1195 }
1196 // End call triggers
1197
1198 // Hooks on failed login
1199 $action = '';
1200 $hookmanager->initHooks(array('login'));
1201 $parameters = array('dol_authmode' => (string) $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1202 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1203 if ($reshook < 0) {
1204 $error++;
1205 }
1206
1207 $paramsurl = array();
1208 if (GETPOSTINT('textbrowser')) {
1209 $paramsurl[] = 'textbrowser='.GETPOSTINT('textbrowser');
1210 }
1211 if (GETPOSTINT('nojs')) {
1212 $paramsurl[] = 'nojs='.GETPOSTINT('nojs');
1213 }
1214 if (GETPOST('lang', 'aZ09')) {
1215 $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
1216 }
1217
1218 header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
1219 exit;
1220 } else {
1221 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
1222 $hookmanager->initHooks(array('main'));
1223
1224 // Code for search criteria persistence.
1225 if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1226 $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]);
1227 $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server
1228 // Clean $relativepathstring
1229 if (constant('DOL_URL_ROOT')) {
1230 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
1231 }
1232 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
1233 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1234 //var_dump($relativepathstring);
1235
1236 // 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
1237 if (!empty($_SESSION['lastsearch_values_tmp_'.$relativepathstring])) {
1238 $_SESSION['lastsearch_values_'.$relativepathstring] = $_SESSION['lastsearch_values_tmp_'.$relativepathstring];
1239 unset($_SESSION['lastsearch_values_tmp_'.$relativepathstring]);
1240 }
1241 if (!empty($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring])) {
1242 $_SESSION['lastsearch_contextpage_'.$relativepathstring] = $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring];
1243 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
1244 }
1245 if (!empty($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] != $conf->liste_limit) {
1246 $_SESSION['lastsearch_limit_'.$relativepathstring] = $_SESSION['lastsearch_limit_tmp_'.$relativepathstring];
1247 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
1248 }
1249 if (!empty($_SESSION['lastsearch_page_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_page_tmp_'.$relativepathstring] > 0) {
1250 $_SESSION['lastsearch_page_'.$relativepathstring] = $_SESSION['lastsearch_page_tmp_'.$relativepathstring];
1251 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
1252 }
1253 if (!empty($_SESSION['lastsearch_mode_tmp_'.$relativepathstring])) {
1254 $_SESSION['lastsearch_mode_'.$relativepathstring] = $_SESSION['lastsearch_mode_tmp_'.$relativepathstring];
1255 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
1256 }
1257 }
1258 if (!empty($_GET['save_pageforbacktolist']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1259 if (empty($_SESSION['pageforbacktolist'])) {
1260 $pageforbacktolistarray = array();
1261 } else {
1262 $pageforbacktolistarray = $_SESSION['pageforbacktolist'];
1263 }
1264 $tmparray = explode(':', $_GET['save_pageforbacktolist'], 2);
1265 if (!empty($tmparray[0]) && !empty($tmparray[1])) {
1266 $pageforbacktolistarray[$tmparray[0]] = $tmparray[1];
1267 $_SESSION['pageforbacktolist'] = $pageforbacktolistarray;
1268 }
1269 }
1270
1271 $action = '';
1272 $parameters = array();
1273 $reshook = $hookmanager->executeHooks('updateSession', $parameters, $user, $action);
1274 if ($reshook < 0) {
1275 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1276 }
1277 }
1278 }
1279
1280 // Is it a new session that has started ?
1281 // If we are here, this means authentication was successful.
1282 if (!isset($_SESSION["dol_login"])) {
1283 // New session for this login has started.
1284 $error = 0;
1285
1286 // Store value into session (values always stored)
1287 $_SESSION["dol_login"] = $user->login;
1288 $_SESSION["dol_logindate"] = dol_now('gmt');
1289 $_SESSION["dol_authmode"] = isset($dol_authmode) ? $dol_authmode : '';
1290 $_SESSION["dol_tz"] = isset($dol_tz) ? $dol_tz : '';
1291 $_SESSION["dol_tz_string"] = isset($dol_tz_string) ? $dol_tz_string : '';
1292 $_SESSION["dol_dst"] = isset($dol_dst) ? $dol_dst : '';
1293 $_SESSION["dol_dst_observed"] = isset($dol_dst_observed) ? $dol_dst_observed : '';
1294 $_SESSION["dol_dst_first"] = isset($dol_dst_first) ? $dol_dst_first : '';
1295 $_SESSION["dol_dst_second"] = isset($dol_dst_second) ? $dol_dst_second : '';
1296 $_SESSION["dol_screenwidth"] = isset($dol_screenwidth) ? $dol_screenwidth : '';
1297 $_SESSION["dol_screenheight"] = isset($dol_screenheight) ? $dol_screenheight : '';
1298 $_SESSION["dol_company"] = getDolGlobalString("MAIN_INFO_SOCIETE_NOM");
1299 $_SESSION["dol_entity"] = $conf->entity;
1300 // Store value into session (values stored only if defined)
1301 if (!empty($dol_hide_topmenu)) {
1302 $_SESSION['dol_hide_topmenu'] = $dol_hide_topmenu;
1303 }
1304 if (!empty($dol_hide_leftmenu)) {
1305 $_SESSION['dol_hide_leftmenu'] = $dol_hide_leftmenu;
1306 }
1307 if (!empty($dol_optimize_smallscreen)) {
1308 $_SESSION['dol_optimize_smallscreen'] = $dol_optimize_smallscreen;
1309 }
1310 if (!empty($dol_no_mouse_hover)) {
1311 $_SESSION['dol_no_mouse_hover'] = $dol_no_mouse_hover;
1312 }
1313 if (!empty($dol_use_jmobile)) {
1314 $_SESSION['dol_use_jmobile'] = $dol_use_jmobile;
1315 }
1316
1317 dol_syslog("This is a new started user session. _SESSION['dol_login']=".$_SESSION["dol_login"]." Session id=".session_id());
1318
1319 $db->begin();
1320
1321 $user->update_last_login_date();
1322
1323 $loginfo = 'TZ='.$_SESSION["dol_tz"].';TZString='.$_SESSION["dol_tz_string"].';Screen='.$_SESSION["dol_screenwidth"].'x'.$_SESSION["dol_screenheight"];
1324 $loginfo .= ' - authmode='.$dol_authmode.' - entity='.$conf->entity;
1325
1326 // Call triggers for the "security events" log
1327 $user->context['audit'] = $loginfo;
1328 $user->context['authentication_method'] = $dol_authmode;
1329
1330 // Call trigger
1331 $result = $user->call_trigger('USER_LOGIN', $user);
1332 if ($result < 0) {
1333 $error++;
1334 }
1335 // End call triggers
1336
1337 // Hooks on successful login
1338 $action = '';
1339 $hookmanager->initHooks(array('login'));
1340 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginfo' => $loginfo);
1341 $reshook = $hookmanager->executeHooks('afterLogin', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1342 if ($reshook < 0) {
1343 $error++;
1344 }
1345
1346 if ($error) {
1347 $db->rollback();
1348 session_destroy();
1349 dol_print_error($db, 'Error in some triggers USER_LOGIN or in some hooks afterLogin');
1350 exit;
1351 } else {
1352 $db->commit();
1353 }
1354
1355 // Change landing page if defined.
1356 $landingpage = (empty($user->conf->MAIN_LANDING_PAGE) ? (!getDolGlobalString('MAIN_LANDING_PAGE') ? '' : $conf->global->MAIN_LANDING_PAGE) : $user->conf->MAIN_LANDING_PAGE);
1357 if (!empty($landingpage)) { // Example: /index.php
1358 $newpath = dol_buildpath($landingpage, 1);
1359 if ($_SERVER["PHP_SELF"] != $newpath) { // not already on landing page (avoid infinite loop)
1360 header('Location: '.$newpath);
1361 exit;
1362 }
1363 }
1364 }
1365
1366
1367 // If user admin, we force the rights-based modules
1368 if ($user->admin) {
1369 $user->rights->user->user->lire = 1;
1370 $user->rights->user->user->creer = 1;
1371 $user->rights->user->user->password = 1;
1372 $user->rights->user->user->supprimer = 1;
1373 $user->rights->user->self->creer = 1;
1374 $user->rights->user->self->password = 1;
1375
1376 //Required if advanced permissions are used with MAIN_USE_ADVANCED_PERMS
1377 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
1378 if (!$user->hasRight('user', 'user_advance')) {
1379 $user->rights->user->user_advance = new stdClass(); // To avoid warnings
1380 }
1381 if (!$user->hasRight('user', 'self_advance')) {
1382 $user->rights->user->self_advance = new stdClass(); // To avoid warnings
1383 }
1384 if (!$user->hasRight('user', 'group_advance')) {
1385 $user->rights->user->group_advance = new stdClass(); // To avoid warnings
1386 }
1387
1388 $user->rights->user->user_advance->readperms = 1;
1389 $user->rights->user->user_advance->write = 1;
1390 $user->rights->user->self_advance->readperms = 1;
1391 $user->rights->user->self_advance->writeperms = 1;
1392 $user->rights->user->group_advance->read = 1;
1393 $user->rights->user->group_advance->readperms = 1;
1394 $user->rights->user->group_advance->write = 1;
1395 $user->rights->user->group_advance->delete = 1;
1396 }
1397 }
1398
1399 /*
1400 * Overwrite some configs globals (try to avoid this and have code to use instead $user->conf->xxx)
1401 */
1402
1403 // Set liste_limit from user setup
1404 if (isset($user->conf->MAIN_SIZE_LISTE_LIMIT)) { // If a user setup exists
1405 $conf->liste_limit = getDolUserInt('MAIN_SIZE_LISTE_LIMIT'); // Can be 0
1406 }
1407 if ((int) $conf->liste_limit <= 0) {
1408 // Mode automatic. Similar code than into conf.class.php
1409 $conf->liste_limit = 15;
1410 if (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 700) {
1411 $conf->liste_limit = 8;
1412 } elseif (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 910) {
1413 $conf->liste_limit = 10;
1414 } elseif (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] > 1130) {
1415 $conf->liste_limit = 20;
1416 }
1417 }
1418 // Set main_checkbox_left_column from user setup
1419 if (isset($user->conf->MAIN_CHECKBOX_LEFT_COLUMN)) { // If a user setup exists
1420 $conf->main_checkbox_left_column = getDolUserInt('MAIN_CHECKBOX_LEFT_COLUMN'); // Can be 0
1421 }
1422
1423 // Replace conf->css by personalized value if theme not forced
1424 if (!getDolGlobalString('MAIN_FORCETHEME') && getDolUserString('MAIN_THEME')) {
1425 $conf->theme = getDolUserString('MAIN_THEME');
1426 $conf->css = "/theme/".$conf->theme."/style.css.php";
1427 }
1428} else {
1429 // We may have NOLOGIN set, but NOREQUIREUSER not
1430 if (!empty($user) && method_exists($user, 'loadDefaultValues') && !defined('NODEFAULTVALUES')) {
1431 $user->loadDefaultValues(); // Load default values for everybody (works even if $user->id = 0
1432 }
1433}
1434
1435
1436// Case forcing style from url
1437if (GETPOST('theme', 'aZ09')) {
1438 $conf->theme = GETPOST('theme', 'aZ09', 1);
1439 $conf->css = "/theme/".$conf->theme."/style.css.php";
1440}
1441
1442// Set javascript option
1443if (GETPOSTINT('nojs')) { // If javascript was not disabled on URL
1444 $conf->use_javascript_ajax = 0;
1445} else {
1446 if (getDolUserString('MAIN_DISABLE_JAVASCRIPT')) {
1447 $conf->use_javascript_ajax = !getDolUserString('MAIN_DISABLE_JAVASCRIPT') ? 1 : 0;
1448 }
1449}
1450
1451// Set MAIN_OPTIMIZEFORTEXTBROWSER for user (must be after login part)
1452if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1453 $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER');
1454 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') == 1) {
1455 $conf->global->THEME_TOPMENU_DISABLE_IMAGE = 1;
1456 }
1457}
1458//var_dump($conf->global->THEME_TOPMENU_DISABLE_IMAGE);
1459//var_dump($user->conf->THEME_TOPMENU_DISABLE_IMAGE);
1460
1461// set MAIN_OPTIMIZEFORCOLORBLIND for user
1462$conf->global->MAIN_OPTIMIZEFORCOLORBLIND = getDolUserString('MAIN_OPTIMIZEFORCOLORBLIND');
1463
1464// Set terminal output option according to conf->browser.
1465if (GETPOSTINT('dol_hide_leftmenu') || !empty($_SESSION['dol_hide_leftmenu'])) {
1466 $conf->dol_hide_leftmenu = 1;
1467}
1468if (GETPOSTINT('dol_hide_topmenu') || !empty($_SESSION['dol_hide_topmenu'])) {
1469 $conf->dol_hide_topmenu = 1;
1470}
1471if (GETPOSTINT('dol_optimize_smallscreen') || !empty($_SESSION['dol_optimize_smallscreen'])) {
1472 $conf->dol_optimize_smallscreen = 1;
1473}
1474if (GETPOSTINT('dol_no_mouse_hover') || !empty($_SESSION['dol_no_mouse_hover'])) {
1475 $conf->dol_no_mouse_hover = 1;
1476}
1477if (GETPOSTINT('dol_use_jmobile') || !empty($_SESSION['dol_use_jmobile'])) {
1478 $conf->dol_use_jmobile = 1;
1479}
1480// If not on Desktop
1481if (!empty($conf->browser->layout) && $conf->browser->layout != 'classic') {
1482 $conf->dol_no_mouse_hover = 1;
1483}
1484
1485// If on smartphone or optimized for small screen
1486if ((!empty($conf->browser->layout) && $conf->browser->layout == 'phone')
1487 || (!empty($_SESSION['dol_screenwidth']) && $_SESSION['dol_screenwidth'] < 400)
1488 || (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 400
1489 || getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER'))
1490) {
1491 $conf->dol_optimize_smallscreen = 1;
1492
1493 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM') == 1) {
1494 $conf->global->PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE = 0;
1495 }
1496}
1497// Replace themes bugged with jmobile with eldy
1498if (!empty($conf->dol_use_jmobile) && in_array($conf->theme, array('bureau2crea', 'cameleo', 'amarok'))) {
1499 $conf->theme = 'eldy';
1500 $conf->css = "/theme/".$conf->theme."/style.css.php";
1501}
1502
1503if (!defined('NOREQUIRETRAN')) {
1504 if (!GETPOST('lang', 'aZ09')) { // If language was not forced on URL
1505 // If user has chosen its own language
1506 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1507 // If different than current language
1508 //print ">>>".$langs->getDefaultLang()."-".$user->conf->MAIN_LANG_DEFAULT;
1509 if ($langs->getDefaultLang() != $user->conf->MAIN_LANG_DEFAULT) {
1510 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1511 }
1512 }
1513 }
1514}
1515
1516if (!defined('NOLOGIN')) {
1517 // If the login is not recovered, it is identified with an account that does not exist.
1518 // Hacking attempt?
1519 if (!$user->login) {
1521 }
1522
1523 // Check if user is active
1524 if ($user->statut < 1) {
1525 // If not active, we refuse the user
1526 $langs->loadLangs(array("errors", "other"));
1527 dol_syslog("Authentication KO as login is disabled", LOG_NOTICE);
1528 accessforbidden("ErrorLoginDisabled");
1529 }
1530
1531 // Load permissions
1532 $user->loadRights();
1533}
1534
1535dol_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);
1536//Another call for easy debug
1537//dol_syslog("Access to ".$_SERVER["PHP_SELF"].' '.$_SERVER["HTTP_REFERER"].' GET='.join(',',array_keys($_GET)).'->'.join(',',$_GET).' POST:'.join(',',array_keys($_POST)).'->'.join(',',$_POST));
1538
1539// Load main languages files
1540if (!defined('NOREQUIRETRAN')) {
1541 // Load translation files required by page
1542 $langs->loadLangs(array('main', 'dict'));
1543}
1544
1545// Define some constants used for style of arrays
1546$bc = array(0 => 'class="impair"', 1 => 'class="pair"');
1547$bcdd = array(0 => 'class="drag drop oddeven"', 1 => 'class="drag drop oddeven"');
1548$bcnd = array(0 => 'class="nodrag nodrop nohover"', 1 => 'class="nodrag nodrop nohoverpair"'); // Used for tr to add new lines
1549$bctag = array(0 => 'class="impair tagtr"', 1 => 'class="pair tagtr"');
1550
1551// Define messages variables
1552$mesg = '';
1553$warning = '';
1554$error = 0;
1555// deprecated, see setEventMessages() and dol_htmloutput_events()
1556$mesgs = array();
1557$warnings = array();
1558$errors = array();
1559
1560// Constants used to defined number of lines in textarea
1561if (empty($conf->browser->firefox)) {
1562 define('ROWS_1', 1);
1563 define('ROWS_2', 2);
1564 define('ROWS_3', 3);
1565 define('ROWS_4', 4);
1566 define('ROWS_5', 5);
1567 define('ROWS_6', 6);
1568 define('ROWS_7', 7);
1569 define('ROWS_8', 8);
1570 define('ROWS_9', 9);
1571} else {
1572 define('ROWS_1', 0);
1573 define('ROWS_2', 1);
1574 define('ROWS_3', 2);
1575 define('ROWS_4', 3);
1576 define('ROWS_5', 4);
1577 define('ROWS_6', 5);
1578 define('ROWS_7', 6);
1579 define('ROWS_8', 7);
1580 define('ROWS_9', 8);
1581}
1582
1583$heightforframes = 50;
1584
1585// Init menu manager
1586if (!defined('NOREQUIREMENU')) {
1587 if (empty($user->socid)) { // If internal user or not defined
1588 $conf->standard_menu = getDolGlobalString('MAIN_MENU_STANDARD_FORCED', getDolGlobalString('MAIN_MENU_STANDARD', 'eldy_menu.php'));
1589 } else {
1590 // If external user
1591 $conf->standard_menu = getDolGlobalString('MAIN_MENUFRONT_STANDARD_FORCED', getDolGlobalString('MAIN_MENUFRONT_STANDARD', 'eldy_menu.php'));
1592 }
1593
1594 // Load the menu manager (only if not already done)
1595 $file_menu = $conf->standard_menu;
1596 if (GETPOST('menu', 'alpha')) {
1597 $file_menu = GETPOST('menu', 'alpha'); // example: menu=eldy_menu.php
1598 }
1599
1600 if (!class_exists('MenuManager')) {
1601 $menufound = 0;
1602 $dirmenus = array_merge(array("/core/menus/"), (array) $conf->modules_parts['menus']);
1603 foreach ($dirmenus as $dirmenu) {
1604 $menufound = dol_include_once($dirmenu."standard/".$file_menu);
1605 if (class_exists('MenuManager')) {
1606 break;
1607 }
1608 }
1609 if (!class_exists('MenuManager')) { // If failed to include, we try with standard eldy_menu.php
1610 dol_syslog("You define a menu manager '".$file_menu."' that can not be loaded.", LOG_WARNING);
1611 $file_menu = 'eldy_menu.php';
1612 include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".$file_menu;
1613 }
1614 }
1615 // @phan-suppress-next-line PhanRedefinedClassReference
1616 $menumanager = new MenuManager($db, empty($user->socid) ? 0 : 1);
1617 // @phan-suppress-next-line PhanRedefinedClassReference
1618 $menumanager->loadMenu();
1619}
1620
1621if (!empty(GETPOST('seteventmessages', 'alpha'))) {
1622 $message = GETPOST('seteventmessages', 'alpha');
1623 $messages = explode(',', $message);
1624 foreach ($messages as $key => $msg) {
1625 $tmp = explode(':', $msg);
1626 setEventMessages($tmp[0], null, !empty($tmp[1]) ? $tmp[1] : 'mesgs');
1627 }
1628}
1629
1630// Functions
1631
1632if (!function_exists("llxHeader")) {
1654 function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
1655 {
1656 global $conf, $hookmanager;
1657
1658 $parameters = array(
1659 'head' => & $head,
1660 'title' => & $title,
1661 'help_url' => & $help_url,
1662 'target' => & $target,
1663 'disablejs' => & $disablejs,
1664 'disablehead' => & $disablehead,
1665 'arrayofjs' => & $arrayofjs,
1666 'arrayofcss' => & $arrayofcss,
1667 'morequerystring' => & $morequerystring,
1668 'morecssonbody' => & $morecssonbody,
1669 'replacemainareaby' => & $replacemainareaby,
1670 'disablenofollow' => & $disablenofollow,
1671 'disablenoindex' => & $disablenoindex
1672
1673 );
1674 $reshook = $hookmanager->executeHooks('llxHeader', $parameters);
1675 if ($reshook > 0) {
1676 print $hookmanager->resPrint;
1677 return;
1678 }
1679
1680 // html header
1681 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
1682
1683 $tmpcsstouse = 'sidebar-collapse'.($morecssonbody ? ' '.$morecssonbody : '');
1684 // If theme MD and classic layer, we open the menulayer by default.
1685 if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1686 global $mainmenu;
1687 if ($mainmenu != 'website') {
1688 $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
1689 }
1690 }
1691
1692 if (getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND')) {
1693 $tmpcsstouse .= ' colorblind-'.strip_tags(getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND'));
1694 }
1695
1696 print '<body id="mainbody" class="'.$tmpcsstouse.'">'."\n";
1697
1698 // top menu and left menu area
1699 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && !GETPOST('dol_openinpopup', 'aZ09')) {
1700 top_menu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
1701 }
1702
1703 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1704 left_menu('', $help_url, '', array(), 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
1705 }
1706
1707 // main area
1708 if ($replacemainareaby) {
1709 print $replacemainareaby;
1710 return;
1711 }
1712
1713 main_area($title);
1714 }
1715}
1716
1717
1725function top_httphead($contenttype = 'text/html', $forcenocache = 0)
1726{
1727 global $db, $conf, $hookmanager;
1728
1729 if ($contenttype == 'text/html') {
1730 header("Content-Type: text/html; charset=".$conf->file->character_set_client);
1731 } else {
1732 header("Content-Type: ".$contenttype);
1733 }
1734
1735 // Security options
1736
1737 // X-Content-Type-Options
1738 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)
1739
1740 // X-Frame-Options
1741 if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
1742 header("X-Frame-Options: SAMEORIGIN"); // By default, frames allowed only if on same domain (stop some XSS attacks)
1743 } else {
1744 header("X-Frame-Options: ALLOWALL");
1745 }
1746
1747 if (getDolGlobalString('MAIN_SECURITY_FORCE_ACCESS_CONTROL_ALLOW_ORIGIN')) {
1748 $tmpurl = constant('DOL_MAIN_URL_ROOT');
1749 $tmpurl = preg_replace('/^(https?:\/\/[^\/]+)\/.*$/', '\1', $tmpurl);
1750 header('Access-Control-Allow-Origin: '.$tmpurl);
1751 header('Vary: Origin');
1752 }
1753
1754 // X-XSS-Protection
1755 //header("X-XSS-Protection: 1"); // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
1756
1757 // Content-Security-Policy-Report-Only
1758 if (!defined('MAIN_SECURITY_FORCECSPRO')) {
1759 // If CSP not forced from the page
1760
1761 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1762 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1763 // 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: *;
1764 // For example, to restrict everything to itself except img that can be on other servers:
1765 // default-src 'self'; img-src *;
1766 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1767 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1768 //
1769 // $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;";
1770 // $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';";
1771 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
1772
1773 if (!is_object($hookmanager)) {
1774 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1775 $hookmanager = new HookManager($db);
1776 }
1777 $hookmanager->initHooks(array("main"));
1778
1779 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'reportonly');
1780 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1781 if ($result > 0) {
1782 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1783 } else {
1784 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1785 }
1786
1787 if (!empty($contentsecuritypolicy)) {
1788 header("Content-Security-Policy-Report-Only: ".$contentsecuritypolicy);
1789 }
1790 } else {
1791 header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSPRO'));
1792 }
1793
1794 // Content-Security-Policy
1795 if (!defined('MAIN_SECURITY_FORCECSP')) {
1796 // If CSP not forced from the page
1797
1798 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1799 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1800 // 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: *;
1801 // For example, to restrict everything to itself except img that can be on other servers:
1802 // default-src 'self'; img-src *;
1803 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1804 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1805 //
1806 // $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;";
1807 // $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';";
1808 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
1809
1810 if (!is_object($hookmanager)) {
1811 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1812 $hookmanager = new HookManager($db);
1813 }
1814 $hookmanager->initHooks(array("main"));
1815
1816 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'active');
1817 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1818 if ($result > 0) {
1819 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1820 } else {
1821 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1822 }
1823
1824 if (!empty($contentsecuritypolicy)) {
1825 header("Content-Security-Policy: ".$contentsecuritypolicy);
1826 }
1827 } else {
1828 header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSP'));
1829 }
1830
1831 // Referrer-Policy
1832 // Say if we must provide the referrer when we jump onto another web page.
1833 // 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.
1834 // 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.
1835 if (!defined('MAIN_SECURITY_FORCERP')) {
1836 $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
1837
1838 header("Referrer-Policy: ".$referrerpolicy);
1839 }
1840
1841 if ($forcenocache) {
1842 header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
1843 }
1844
1845 // No need to add this token in header, we use instead the one into the forms.
1846 //header("anti-csrf-token: ".newToken());
1847}
1848
1864function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
1865{
1866 global $db, $conf, $langs, $user, $mysoc, $hookmanager;
1867
1868 top_httphead();
1869
1870 if (empty($conf->css)) {
1871 $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
1872 }
1873
1874 print '<!doctype html>'."\n";
1875
1876 print '<html lang="'.substr($langs->defaultlang, 0, 2).'">'."\n";
1877
1878 //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
1879 if (empty($disablehead)) {
1880 if (!is_object($hookmanager)) {
1881 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1882 $hookmanager = new HookManager($db);
1883 }
1884 $hookmanager->initHooks(array("main"));
1885
1886 $ext = 'layout='.(empty($conf->browser->layout) ? '' : $conf->browser->layout).'&amp;version='.urlencode(DOL_VERSION);
1887
1888 print "<head>\n";
1889
1890 if (GETPOST('dol_basehref', 'alpha')) {
1891 print '<base href="'.dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')).'">'."\n";
1892 }
1893
1894 // Displays meta
1895 print '<meta charset="utf-8">'."\n";
1896 print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
1897 print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
1898 print '<meta name="author" content="Dolibarr Development Team">'."\n";
1899 print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
1900 print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
1901 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
1902 print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
1903 }
1904 // Favicon
1905 $favicon = DOL_URL_ROOT.'/theme/dolibarr_256x256_color.png';
1906 $appletouchicon = DOL_URL_ROOT.'/theme/apple-touch-icon.png';
1907 if (!empty($mysoc->logo_squarred_mini)) {
1908 $favicon = DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_mini);
1909 }
1910 if (getDolGlobalString('MAIN_FAVICON_URL')) {
1911 $favicon = getDolGlobalString('MAIN_FAVICON_URL');
1912 }
1913 if (empty($conf->dol_use_jmobile)) {
1914 print '<link rel="shortcut icon" type="image/x-icon" href="'.$favicon.'"/>'."\n"; // Not required into an Android webview
1915 print '<link rel="apple-touch-icon" href="'.$appletouchicon.'"/>'."\n";
1916 }
1917
1918 // Mobile appli like icon
1919 $manifest = DOL_URL_ROOT.'/theme/'.$conf->theme.'/manifest.json.php';
1920 $parameters = array('manifest' => $manifest);
1921 $resHook = $hookmanager->executeHooks('hookSetManifest', $parameters); // Note that $action and $object may have been modified by some hooks
1922 if ($resHook > 0) {
1923 $manifest = $hookmanager->resPrint; // Replace manifest.json
1924 } else {
1925 $manifest .= $hookmanager->resPrint; // Concat to actual manifest declaration
1926 }
1927 if (!empty($manifest)) {
1928 print '<link rel="manifest" href="'.$manifest.'" />'."\n";
1929 }
1930
1931 if (getDolGlobalString('THEME_ELDY_TOPMENU_BACK1')) {
1932 // TODO: use auto theme color switch
1933 print '<meta name="theme-color" content="rgb(' . getDolGlobalString('THEME_ELDY_TOPMENU_BACK1').')">'."\n";
1934 }
1935
1936 // Auto refresh page
1937 if (GETPOSTINT('autorefresh') > 0) {
1938 print '<meta http-equiv="refresh" content="'.GETPOSTINT('autorefresh').'">';
1939 }
1940
1941 // Displays title
1942 $appli = constant('DOL_APPLICATION_TITLE');
1943 if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
1944 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
1945 }
1946
1947 print '<title>';
1948 $titletoshow = '';
1949 if ($title && preg_match('/showapp/', getDolGlobalString('MAIN_HTML_TITLE'))) {
1950 $titletoshow = dol_htmlentities($appli.' - '.$title);
1951 } elseif ($title) {
1952 $titletoshow = dol_htmlentities($title);
1953 } else {
1954 $titletoshow = dol_htmlentities($appli);
1955 }
1956
1957 $parameters = array('title' => $titletoshow);
1958 $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
1959 if ($result > 0) {
1960 $titletoshow = $hookmanager->resPrint; // Replace Title to show
1961 } else {
1962 $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
1963 }
1964
1965 print $titletoshow;
1966 print '</title>';
1967
1968 print "\n";
1969
1970 if (GETPOSTINT('version')) {
1971 $ext = 'version='.GETPOSTINT('version'); // useful to force no cache on css/js
1972 }
1973 // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
1974 if (GETPOST('dol_resetcache')) {
1975 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1976 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
1977 }
1978
1979 $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;
1980
1981 $themeparam .= ($ext ? '&amp;'.$ext : '').'&amp;revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV");
1982 if (GETPOSTISSET('dol_hide_topmenu')) {
1983 $themeparam .= '&amp;dol_hide_topmenu='.GETPOSTINT('dol_hide_topmenu');
1984 }
1985 if (GETPOSTISSET('dol_hide_leftmenu')) {
1986 $themeparam .= '&amp;dol_hide_leftmenu='.GETPOSTINT('dol_hide_leftmenu');
1987 }
1988 if (GETPOSTISSET('dol_openinpopup')) {
1989 $themeparam .= '&amp;dol_openinpopup='.GETPOST('dol_openinpopup', 'aZ09');
1990 }
1991 if (GETPOSTISSET('dol_optimize_smallscreen')) {
1992 $themeparam .= '&amp;dol_optimize_smallscreen='.GETPOSTINT('dol_optimize_smallscreen');
1993 }
1994 if (GETPOSTISSET('dol_no_mouse_hover')) {
1995 $themeparam .= '&amp;dol_no_mouse_hover='.GETPOSTINT('dol_no_mouse_hover');
1996 }
1997 if (GETPOSTISSET('dol_use_jmobile')) {
1998 $themeparam .= '&amp;dol_use_jmobile='.GETPOSTINT('dol_use_jmobile');
1999 $conf->dol_use_jmobile = GETPOSTINT('dol_use_jmobile');
2000 }
2001 if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
2002 $themeparam .= '&amp;THEME_DARKMODEENABLED='.GETPOSTINT('THEME_DARKMODEENABLED');
2003 }
2004 if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
2005 $themeparam .= '&amp;THEME_SATURATE_RATIO='.GETPOSTINT('THEME_SATURATE_RATIO');
2006 }
2007
2008 if (getDolGlobalString('MAIN_ENABLE_FONT_ROBOTO')) {
2009 print '<link rel="preconnect" href="https://fonts.gstatic.com">'."\n";
2010 print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">'."\n";
2011 }
2012
2013 if (!defined('DISABLE_JQUERY') && !$disablejs && $conf->use_javascript_ajax) {
2014 print '<!-- Includes CSS for JQuery (Ajax library) -->'."\n";
2015 $jquerytheme = 'base';
2016 if (getDolGlobalString('MAIN_USE_JQUERY_THEME')) {
2017 $jquerytheme = getDolGlobalString('MAIN_USE_JQUERY_THEME');
2018 }
2019 if (constant('JS_JQUERY_UI')) {
2020 print '<link rel="stylesheet" type="text/css" href="'.JS_JQUERY_UI.'css/'.$jquerytheme.'/jquery-ui.min.css'.($ext ? '?'.$ext : '').'">'."\n"; // Forced JQuery
2021 } else {
2022 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/css/'.$jquerytheme.'/jquery-ui.css'.($ext ? '?'.$ext : '').'">'."\n"; // JQuery
2023 }
2024 if (!defined('DISABLE_JQUERY_JNOTIFY')) {
2025 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css'.($ext ? '?'.$ext : '').'">'."\n"; // JNotify
2026 }
2027 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) { // jQuery plugin "mutiselect", "multiple-select", "select2"...
2028 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
2029 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/plugins/'.$tmpplugin.'/dist/css/'.$tmpplugin.'.css'.($ext ? '?'.$ext : '').'">'."\n";
2030 }
2031 }
2032
2033 if (!defined('DISABLE_FONT_AWSOME')) {
2034 print '<!-- Includes CSS for font awesome -->'."\n";
2035 $fontawesome_directory = getDolGlobalString('MAIN_FONTAWESOME_DIRECTORY', '/theme/common/fontawesome-5');
2036 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.$fontawesome_directory.'/css/all.min.css'.($ext ? '?'.$ext : '').'">'."\n";
2037 }
2038
2039 print '<!-- Includes CSS for Dolibarr theme -->'."\n";
2040 // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
2041 $themepath = dol_buildpath($conf->css, 1);
2042 $themesubdir = '';
2043 if (!empty($conf->modules_parts['theme'])) { // This slow down
2044 foreach ($conf->modules_parts['theme'] as $reldir) {
2045 if (file_exists(dol_buildpath($reldir.$conf->css, 0))) {
2046 $themepath = dol_buildpath($reldir.$conf->css, 1);
2047 $themesubdir = $reldir;
2048 break;
2049 }
2050 }
2051 }
2052
2053 //print 'themepath='.$themepath.' themeparam='.$themeparam;exit;
2054 print '<link rel="stylesheet" type="text/css" href="'.$themepath.$themeparam.'">'."\n";
2055 if (getDolGlobalString('MAIN_FIX_FLASH_ON_CHROME')) {
2056 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";
2057 }
2058
2059 // LEAFLET AND GEOMAN
2060 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
2061 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.css'.($ext ? '?'.$ext : '')."\">\n";
2062 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.css'.($ext ? '?'.$ext : '')."\">\n";
2063 }
2064
2065 // CSS forced by modules (relative url starting with /)
2066 if (!empty($conf->modules_parts['css'])) {
2067 $arraycss = (array) $conf->modules_parts['css'];
2068 foreach ($arraycss as $modcss => $filescss) {
2069 $filescss = (array) $filescss; // To be sure filecss is an array
2070 foreach ($filescss as $cssfile) {
2071 if (empty($cssfile)) {
2072 dol_syslog("Warning: module ".$modcss." declared a css path file into its descriptor that is empty.", LOG_WARNING);
2073 }
2074 // cssfile is a relative path
2075 $urlforcss = dol_buildpath($cssfile, 1);
2076 if ($urlforcss && $urlforcss != '/') {
2077 print '<!-- Includes CSS added by module '.$modcss.' -->'."\n".'<link rel="stylesheet" type="text/css" href="'.$urlforcss;
2078 // 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.
2079 if (!preg_match('/\.css$/i', $cssfile)) {
2080 print $themeparam;
2081 }
2082 print '">'."\n";
2083 } else {
2084 dol_syslog("Warning: module ".$modcss." declared a css path file for a file we can't find.", LOG_WARNING);
2085 }
2086 }
2087 }
2088 }
2089 // CSS forced by page in top_htmlhead call (relative url starting with /)
2090 if (is_array($arrayofcss)) {
2091 foreach ($arrayofcss as $cssfile) {
2092 if (preg_match('/^(http|\/\/)/i', $cssfile)) {
2093 $urltofile = $cssfile;
2094 } else {
2095 $urltofile = dol_buildpath($cssfile, 1);
2096 }
2097 print '<!-- Includes CSS added by page -->'."\n".'<link rel="stylesheet" type="text/css" title="default" href="'.$urltofile;
2098 // 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.
2099 if (!preg_match('/\.css$/i', $cssfile)) {
2100 print $themeparam;
2101 }
2102 print '">'."\n";
2103 }
2104 }
2105
2106 // Custom CSS
2107 if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
2108 // If a custom CSS was set, we add link to the custom css php file
2109 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";
2110 }
2111
2112 // Output standard javascript links
2113 if (!defined('DISABLE_JQUERY') && !$disablejs && !empty($conf->use_javascript_ajax)) {
2114 // JQuery. Must be before other includes
2115 print '<!-- Includes JS for JQuery -->'."\n";
2116 if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
2117 print '<script nonce="'.getNonce().'" src="'.JS_JQUERY.'jquery.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2118 } else {
2119 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/js/jquery.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2120 }
2121 if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
2122 print '<script nonce="'.getNonce().'" src="'.JS_JQUERY_UI.'jquery-ui.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2123 } else {
2124 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/js/jquery-ui.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2125 }
2126 // jQuery jnotify
2127 if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
2128 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2129 }
2130 // Table drag and drop lines
2131 if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
2132 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/tablednd/jquery.tablednd.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2133 }
2134 // Chart
2135 if (empty($disableforlogin) && (!getDolGlobalString('MAIN_JS_GRAPH') || getDolGlobalString('MAIN_JS_GRAPH') == 'chart') && !defined('DISABLE_JS_GRAPH')) {
2136 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/nnnick/chartjs/dist/chart.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2137 }
2138
2139 // jQuery jeditable for Edit In Place features
2140 if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !defined('DISABLE_JQUERY_JEDITABLE')) {
2141 print '<!-- JS to manage editInPlace feature -->'."\n";
2142 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2143 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2144 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2145 print '<script>'."\n";
2146 print 'var urlSaveInPlace = \''.DOL_URL_ROOT.'/core/ajax/saveinplace.php\';'."\n";
2147 print 'var urlLoadInPlace = \''.DOL_URL_ROOT.'/core/ajax/loadinplace.php\';'."\n";
2148 print 'var tooltipInPlace = \''.$langs->transnoentities('ClickToEdit').'\';'."\n"; // Added in title attribute of span
2149 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 ?
2150 print 'var cancelInPlace = \''.$langs->trans("Cancel").'\';'."\n";
2151 print 'var submitInPlace = \''.$langs->trans('Ok').'\';'."\n";
2152 print 'var indicatorInPlace = \'<img src="'.DOL_URL_ROOT."/theme/".$conf->theme."/img/working.gif".'">\';'."\n";
2153 print 'var withInPlace = 300;'; // width in pixel for default string edit
2154 print '</script>'."\n";
2155 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/editinplace.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2156 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2157 }
2158 // jQuery Timepicker
2159 if (getDolGlobalString('MAIN_USE_JQUERY_TIMEPICKER') || defined('REQUIRE_JQUERY_TIMEPICKER')) {
2160 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/timepicker/jquery-ui-timepicker-addon.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2161 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/timepicker.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
2162 }
2163 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
2164 // jQuery plugin "mutiselect", "multiple-select", "select2", ...
2165 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
2166 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
2167 }
2168 if (!defined('DISABLE_MULTISELECT')) { // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
2169 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/multiselect/jquery.multi-select.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2170 }
2171 }
2172
2173 if (!$disablejs && !empty($conf->use_javascript_ajax)) {
2174 // CKEditor
2175 if (empty($disableforlogin) && (isModEnabled('fckeditor') && (!getDolGlobalString('FCKEDITOR_EDITORNAME') || getDolGlobalString('FCKEDITOR_EDITORNAME') == 'ckeditor') && !defined('DISABLE_CKEDITOR')) || defined('FORCE_CKEDITOR')) {
2176 print '<!-- Includes JS for CKEditor -->'."\n";
2177 $pathckeditor = DOL_URL_ROOT.'/includes/ckeditor/ckeditor/';
2178 $jsckeditor = 'ckeditor.js';
2179 if (constant('JS_CKEDITOR')) {
2180 // To use external ckeditor 4 js lib
2181 $pathckeditor = constant('JS_CKEDITOR');
2182 }
2183 print '<script nonce="'.getNonce().'">';
2184 print '/* enable ckeditor by main.inc.php */';
2185 print 'var CKEDITOR_BASEPATH = \''.dol_escape_js($pathckeditor).'\';'."\n";
2186 print 'var ckeditorConfig = \''.dol_escape_js(dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js'.($ext ? '?'.$ext : ''), 1)).'\';'."\n"; // $themesubdir='' in standard usage
2187 print 'var ckeditorFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2188 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";
2189 print '</script>'."\n";
2190 print '<script src="'.$pathckeditor.$jsckeditor.($ext ? '?'.$ext : '').'"></script>'."\n";
2191 print '<script>';
2192 if (GETPOST('mode', 'aZ09') == 'Full_inline') {
2193 print 'CKEDITOR.disableAutoInline = false;'."\n";
2194 } else {
2195 print 'CKEDITOR.disableAutoInline = true;'."\n";
2196 }
2197 print '</script>'."\n";
2198 }
2199
2200 // 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).
2201 if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
2202 $enablebrowsernotif = false;
2203 if (isModEnabled('agenda') && getDolGlobalString('AGENDA_REMINDER_BROWSER')) {
2204 $enablebrowsernotif = true;
2205 }
2206 if ($conf->browser->layout == 'phone') {
2207 $enablebrowsernotif = false;
2208 }
2209 if ($enablebrowsernotif) {
2210 print '<!-- Includes JS of Dolibarr (browser layout = '.$conf->browser->layout.')-->'."\n";
2211 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_notification.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
2212 }
2213 }
2214
2215 // Global js function
2216 print '<!-- Includes JS of Dolibarr -->'."\n";
2217 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_head.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
2218
2219 // Leaflet TODO use dolibarr files
2220 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
2221 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2222 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2223 }
2224
2225 // JS forced by modules (relative url starting with /)
2226 if (!empty($conf->modules_parts['js'])) { // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
2227 $arrayjs = (array) $conf->modules_parts['js'];
2228 foreach ($arrayjs as $modjs => $filesjs) {
2229 $filesjs = (array) $filesjs; // To be sure filejs is an array
2230 foreach ($filesjs as $jsfile) {
2231 // jsfile is a relative path
2232 $urlforjs = dol_buildpath($jsfile, 1);
2233 if ($urlforjs && $urlforjs != '/') {
2234 print '<!-- Include JS added by module '.$modjs.'-->'."\n";
2235 print '<script nonce="'.getNonce().'" src="'.$urlforjs.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2236 } else {
2237 dol_syslog("Warning: module ".$modjs." declared a js path file for a file we can't find.", LOG_WARNING);
2238 }
2239 }
2240 }
2241 }
2242 // JS forced by page in top_htmlhead (relative url starting with /)
2243 if (is_array($arrayofjs)) {
2244 print '<!-- Includes JS added by page -->'."\n";
2245 foreach ($arrayofjs as $jsfile) {
2246 if (preg_match('/^(http|\/\/)/i', $jsfile)) {
2247 print '<script nonce="'.getNonce().'" src="'.$jsfile.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2248 } else {
2249 print '<script nonce="'.getNonce().'" src="'.dol_buildpath($jsfile, 1).((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2250 }
2251 }
2252 }
2253 }
2254
2255 //If you want to load custom javascript file from your selected theme directory
2256 if (getDolGlobalString('ALLOW_THEME_JS')) {
2257 $theme_js = dol_buildpath('/theme/'.$conf->theme.'/'.$conf->theme.'.js', 0);
2258 if (file_exists($theme_js)) {
2259 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/'.$conf->theme.'.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2260 }
2261 }
2262
2263 if (!empty($head)) {
2264 print $head."\n";
2265 }
2266 if (getDolGlobalString('MAIN_HTML_HEADER')) {
2267 print getDolGlobalString('MAIN_HTML_HEADER') . "\n";
2268 }
2269
2270 $parameters = array();
2271 $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
2272 print $hookmanager->resPrint; // Replace Title to show
2273
2274 print "</head>\n\n";
2275 }
2276
2277 $conf->headerdone = 1; // To tell header was output
2278}
2279
2280
2297function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $morequerystring = '', $helppagename = '')
2298{
2299 global $user, $conf, $langs, $db, $form;
2300 global $dolibarr_main_authentication, $dolibarr_main_demo;
2301 global $hookmanager, $menumanager;
2302
2303 $searchform = '';
2304
2305 // Instantiate hooks for external modules
2306 $hookmanager->initHooks(array('toprightmenu'));
2307
2308 $toprightmenu = '';
2309
2310 // For backward compatibility with old modules
2311 if (empty($conf->headerdone)) {
2312 $disablenofollow = 0;
2313 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
2314 print '<body id="mainbody">';
2315 }
2316
2317 /*
2318 * Top menu
2319 */
2320 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
2321 if (!isset($form) || !is_object($form)) {
2322 include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
2323 $form = new Form($db);
2324 }
2325
2326 print "\n".'<!-- Start top horizontal -->'."\n";
2327
2328 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.
2329
2330 // Show menu entries
2331 print '<div id="tmenu_tooltip'.(!getDolGlobalString('MAIN_MENU_INVERT') ? '' : 'invert').'" class="tmenu">'."\n";
2332 // @phan-suppress-next-line PhanRedefinedClassReference
2333 $menumanager->atarget = $target;
2334 // @phan-suppress-next-line PhanRedefinedClassReference
2335 $menumanager->showmenu('top', array('searchform' => $searchform)); // This contains a \n
2336 print "</div>\n";
2337
2338 // Define link to login card
2339 $appli = constant('DOL_APPLICATION_TITLE');
2340 if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
2341 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
2342 if (preg_match('/\d\.\d/', $appli)) {
2343 if (!preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) {
2344 $appli .= " (".DOL_VERSION.")"; // If new title contains a version that is different than core
2345 }
2346 } else {
2347 $appli .= " ".DOL_VERSION;
2348 }
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 if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
2738 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
2739 if (preg_match('/\d\.\d/', $appli)) {
2740 if (!preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) {
2741 $appli .= " (".DOL_VERSION.")"; // If new title contains a version that is different than core
2742 }
2743 } else {
2744 $appli .= " ".DOL_VERSION;
2745 }
2746 } else {
2747 $appli .= " ".DOL_VERSION;
2748 }
2749
2750 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2751 $btnUser = '<!-- div for user link -->
2752 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2753 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="dropdown-toggle login-dropdown-a valignmiddle" data-toggle="dropdown">
2754 '.$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>' : '').'
2755 </a>
2756 <div class="dropdown-menu">
2757 <!-- User image -->
2758 <div class="user-header">
2759 '.$userDropDownImage.'
2760 <p>
2761 '.$profilName.'<br>';
2762 $title = '';
2763 if ($user->datelastlogin) {
2764 $title = $langs->trans("ConnectedSince").' : '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2765 if ($user->datepreviouslogin) {
2766 $title .= '<br>'.$langs->trans("PreviousConnexion").' : '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2767 }
2768 }
2769 $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>';
2770 if ($user->datepreviouslogin) {
2771 $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>';
2772 }
2773
2774 //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
2775 $btnUser .= '
2776 </p>
2777 </div>
2778
2779 <!-- Menu Body user-->
2780 <div class="user-body">'.$dropdownBody.'</div>
2781
2782 <!-- Menu Footer-->
2783 <div class="user-footer">
2784 <div class="pull-left">
2785 '.$profilLink.'
2786 </div>
2787 <div class="pull-left">
2788 '.$virtuelcardLink.'
2789 </div>
2790 <div class="pull-right">
2791 '.$logoutLink.'
2792 </div>
2793 <div class="clearboth"></div>
2794 </div>
2795
2796 </div>
2797 </div>';
2798 } else {
2799 $btnUser = '<!-- div for user link text browser -->
2800 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2801 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="valignmiddle" alt="'.$langs->trans("MyUserCard").'">
2802 '.$userImage.(empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft small valignmiddle">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2803 </a>
2804 </div>';
2805 }
2806
2807 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
2808 $btnUser .= '
2809 <!-- Code to show/hide the user drop-down -->
2810 <script>
2811 function closeTopMenuLoginDropdown() {
2812 console.log("close login dropdown"); // This is called at each click on page, so we disable the log
2813 // Hide the menus.
2814 jQuery("#topmenu-login-dropdown").removeClass("open");
2815 }
2816 jQuery(document).ready(function() {
2817 jQuery(document).on("click", function(event) {
2818 if (!$(event.target).closest("#topmenu-login-dropdown").length) {
2819 /* console.log("click close login - we click outside"); */
2820 closeTopMenuLoginDropdown();
2821 }
2822 });
2823 ';
2824
2825
2826 //if ($conf->theme != 'md') {
2827 $btnUser .= '
2828 jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
2829 console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
2830 event.preventDefault();
2831 jQuery("#topmenu-login-dropdown").toggleClass("open");
2832 });
2833
2834 jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
2835 console.log("Click on #topmenulogincompanyinfo-btn");
2836 jQuery("#topmenulogincompanyinfo").slideToggle();
2837 });
2838
2839 jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
2840 console.log("Click on #topmenuloginmoreinfo-btn");
2841 jQuery("#topmenuloginmoreinfo").slideToggle();
2842 });';
2843 //}
2844
2845 $btnUser .= '
2846 });
2847 </script>
2848 ';
2849 }
2850
2851 return $btnUser;
2852}
2853
2861{
2862 global $conf, $langs;
2863
2864 // Button disabled on text browser
2865 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2866 return '';
2867 }
2868
2869 $html = '';
2870
2871 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2872 // accesskey is for Mac: CTRL + key for all browsers
2873 $stringforfirstkey = $langs->trans("KeyboardShortcut");
2874 if ($conf->browser->os === 'macintosh') {
2875 $stringforfirstkey .= ' CTL +';
2876 } else {
2877 if ($conf->browser->name == 'chrome') {
2878 $stringforfirstkey .= ' ALT +';
2879 } elseif ($conf->browser->name == 'firefox') {
2880 $stringforfirstkey .= ' ALT + SHIFT +';
2881 } else {
2882 $stringforfirstkey .= ' CTL +';
2883 }
2884 }
2885
2886 if (!empty($conf->use_javascript_ajax)) {
2887 $html .= '<!-- div for quick add link -->
2888 <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
2889 <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>
2890 <div class="dropdown-menu">'.printDropdownQuickadd().'</div>
2891 </div>';
2892 if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) { // This may be set by some pages that use different jquery version to avoid errors
2893 $html .= '
2894 <!-- Code to show/hide the user drop-down for the quick add -->
2895 <script>
2896 jQuery(document).ready(function() {
2897 jQuery(document).on("click", function(event) {
2898 if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
2899 /* console.log("click close quick add - we click outside"); */
2900 // Hide the menus.
2901 $("#topmenu-quickadd-dropdown").removeClass("open");
2902 }
2903 });
2904 $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
2905 console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
2906 openQuickAddDropDown(event);
2907 });
2908
2909 // Key map shortcut
2910 $(document).keydown(function(event){
2911 var ostype = \''.dol_escape_js($conf->browser->os).'\';
2912 if (ostype === "macintosh") {
2913 if ( event.which === 65 && event.ctrlKey ) {
2914 console.log(\'control + a : trigger open quick add dropdown\');
2915 openQuickAddDropDown(event);
2916 }
2917 } else {
2918 if ( event.which === 65 && event.ctrlKey && event.shiftKey ) {
2919 console.log(\'control + shift + a : trigger open quick add dropdown\');
2920 openQuickAddDropDown(event);
2921 }
2922 }
2923 });
2924
2925 var openQuickAddDropDown = function(event) {
2926 event.preventDefault();
2927 $("#topmenu-quickadd-dropdown").toggleClass("open");
2928 //$("#top-quickadd-search-input").focus();
2929 }
2930 });
2931 </script>
2932 ';
2933 }
2934 }
2935
2936 return $html;
2937}
2938
2939
2947{
2948 global $conf, $langs;
2949
2950 // Button disabled on text browser
2951 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2952 return '';
2953 }
2954
2955 $html = '';
2956
2957 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2958 // accesskey is for Mac: CTRL + key for all browsers
2959 $stringforfirstkey = $langs->trans("KeyboardShortcut");
2960 if ($conf->browser->os === 'macintosh') {
2961 $stringforfirstkey .= ' CTL +';
2962 } else {
2963 if ($conf->browser->name == 'chrome') {
2964 $stringforfirstkey .= ' ALT +';
2965 } elseif ($conf->browser->name == 'firefox') {
2966 $stringforfirstkey .= ' ALT + SHIFT +';
2967 } else {
2968 $stringforfirstkey .= ' CTL +';
2969 }
2970 }
2971
2972
2973 if (!empty($conf->use_javascript_ajax)) {
2974 $urlforuploadpage = DOL_URL_ROOT.'/core/upload_page.php';
2975 if (!is_numeric(getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE'))) {
2976 $urlforuploadpage = getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE');
2977 }
2978
2979 $html .= '<!-- div for link to upload file -->
2980 <div id="topmenu-uploadfile-dropdown" class="atoplogin dropdown inline-block">
2981 <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>
2982 </div>';
2983 }
2984
2985 return $html;
2986}
2987
2988
2995function printDropdownQuickadd($mode = 0)
2996{
2997 global $user, $langs, $hookmanager;
2998
2999 $items = array(
3000 'items' => array(
3001 array(
3002 "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
3003 "title" => "MenuNewMember@members",
3004 "name" => "Adherent@members",
3005 "picto" => "object_member",
3006 "activation" => isModEnabled('member') && $user->hasRight("adherent", "write"), // vs hooking
3007 "position" => 5,
3008 ),
3009 array(
3010 "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
3011 "title" => "MenuNewThirdParty@companies",
3012 "name" => "ThirdParty@companies",
3013 "picto" => "object_company",
3014 "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
3015 "position" => 10,
3016 ),
3017 array(
3018 "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
3019 "title" => "NewContactAddress@companies",
3020 "name" => "Contact@companies",
3021 "picto" => "object_contact",
3022 "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
3023 "position" => 20,
3024 ),
3025 array(
3026 "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
3027 "title" => "NewPropal@propal",
3028 "name" => "Proposal@propal",
3029 "picto" => "object_propal",
3030 "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
3031 "position" => 30,
3032 ),
3033
3034 array(
3035 "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
3036 "title" => "NewOrder@orders",
3037 "name" => "Order@orders",
3038 "picto" => "object_order",
3039 "activation" => isModEnabled('order') && $user->hasRight("commande", "write"), // vs hooking
3040 "position" => 40,
3041 ),
3042 array(
3043 "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
3044 "title" => "NewBill@bills",
3045 "name" => "Bill@bills",
3046 "picto" => "object_bill",
3047 "activation" => isModEnabled('invoice') && $user->hasRight("facture", "write"), // vs hooking
3048 "position" => 50,
3049 ),
3050 array(
3051 "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
3052 "title" => "NewContractSubscription@contracts",
3053 "name" => "Contract@contracts",
3054 "picto" => "object_contract",
3055 "activation" => isModEnabled('contract') && $user->hasRight("contrat", "write"), // vs hooking
3056 "position" => 60,
3057 ),
3058 array(
3059 "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
3060 "title" => "SupplierProposalNew@supplier_proposal",
3061 "name" => "SupplierProposal@supplier_proposal",
3062 "picto" => "supplier_proposal",
3063 "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
3064 "position" => 70,
3065 ),
3066 array(
3067 "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
3068 "title" => "NewSupplierOrderShort@orders",
3069 "name" => "SupplierOrder@orders",
3070 "picto" => "supplier_order",
3071 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "commande", "write")) || (isModEnabled("supplier_order") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3072 "position" => 80,
3073 ),
3074 array(
3075 "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
3076 "title" => "NewBill@bills",
3077 "name" => "SupplierBill@bills",
3078 "picto" => "supplier_invoice",
3079 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "write")) || (isModEnabled("supplier_invoice") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3080 "position" => 90,
3081 ),
3082 array(
3083 "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
3084 "title" => "NewTicket@ticket",
3085 "name" => "Ticket@ticket",
3086 "picto" => "ticket",
3087 "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
3088 "position" => 100,
3089 ),
3090 array(
3091 "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
3092 "title" => "NewIntervention@interventions",
3093 "name" => "Intervention@interventions",
3094 "picto" => "intervention",
3095 "activation" => isModEnabled('intervention') && $user->hasRight("ficheinter", "creer"), // vs hooking
3096 "position" => 110,
3097 ),
3098 array(
3099 "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
3100 "title" => "NewProduct@products",
3101 "name" => "Product@products",
3102 "picto" => "object_product",
3103 "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
3104 "position" => 400,
3105 ),
3106 array(
3107 "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
3108 "title" => "NewService@products",
3109 "name" => "Service@products",
3110 "picto" => "object_service",
3111 "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
3112 "position" => 410,
3113 ),
3114 array(
3115 "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
3116 "title" => "AddUser@users",
3117 "name" => "User@users",
3118 "picto" => "user",
3119 "activation" => $user->hasRight("user", "user", "write"), // vs hooking
3120 "position" => 500,
3121 ),
3122 ),
3123 );
3124
3125 $dropDownQuickAddHtml = '';
3126
3127 // Define $dropDownQuickAddHtml
3128 if (empty($mode)) {
3129 $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
3130 }
3131 $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
3132
3133 // Allow the $items of the menu to be manipulated by modules
3134 $parameters = array();
3135 $hook_items = $items;
3136 $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks
3137 if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
3138 if ($reshook == 0) {
3139 $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
3140 } else {
3141 $items = $hookmanager->resArray; // replace
3142 }
3143
3144 // Sort menu items by 'position' value
3145 $position = array();
3146 foreach ($items['items'] as $key => $row) {
3147 $position[$key] = $row['position'];
3148 }
3149 $array1_sort_order = SORT_ASC;
3150 array_multisort($position, $array1_sort_order, $items['items']);
3151 }
3152
3153 foreach ($items['items'] as $item) {
3154 if (!$item['activation']) {
3155 continue;
3156 }
3157 $langs->load(explode('@', $item['title'])[1]);
3158 $langs->load(explode('@', $item['name'])[1]);
3159 $dropDownQuickAddHtml .= '
3160 <a class="dropdown-item quickadd-item" href="'.DOL_URL_ROOT.$item['url'].'" title="'.$langs->trans(explode('@', $item['title'])[0]).'">
3161 '. img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
3162 ';
3163 }
3164
3165 if (empty($mode)) {
3166 $dropDownQuickAddHtml .= '</div>';
3167 }
3168 $dropDownQuickAddHtml .= '</div>';
3169
3170 return $dropDownQuickAddHtml;
3171}
3172
3179{
3180 global $langs, $conf, $user;
3181
3182 $html = '';
3183
3184 // Return empty in some case
3185 if (!isModEnabled('bookmark') || !$user->hasRight('bookmark', 'lire')) {
3186 return '';
3187 }
3188 /*
3189 if ($conf->browser->name == 'textbrowser') {
3190 return $html;
3191 }
3192 */
3193
3194 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3195 // accesskey is for Mac: CTRL + key for all browsers
3196 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3197 if ($conf->browser->os === 'macintosh') {
3198 $stringforfirstkey .= ' CTL +';
3199 } else {
3200 if ($conf->browser->name == 'chrome') {
3201 $stringforfirstkey .= ' ALT +';
3202 } elseif ($conf->browser->name == 'firefox') {
3203 $stringforfirstkey .= ' ALT + SHIFT +';
3204 } else {
3205 $stringforfirstkey .= ' CTL +';
3206 }
3207 }
3208
3209 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
3210 include_once DOL_DOCUMENT_ROOT.'/bookmarks/bookmarks.lib.php';
3211 $langs->load("bookmarks");
3212
3213 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3214 $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
3215 $html .= printDropdownBookmarksList();
3216 $html .= '</div>';
3217 } else {
3218 $html .= '<!-- div for bookmark link -->
3219 <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
3220 <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>
3221 <div class="dropdown-menu">
3223 </div>
3224 </div>';
3225
3226 $html .= '
3227 <!-- Code to show/hide the bookmark drop-down -->
3228 <script>
3229 jQuery(document).ready(function() {
3230 jQuery(document).on("click", function(event) {
3231 if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
3232 /* console.log("close bookmark dropdown - we click outside"); */
3233 // Hide the menus.
3234 $("#topmenu-bookmark-dropdown").removeClass("open");
3235 }
3236 });
3237
3238 jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
3239 console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
3240 openBookMarkDropDown(event);
3241 });
3242
3243 // Key map shortcut
3244 jQuery(document).keydown(function(event) {
3245 var ostype = \''.dol_escape_js($conf->browser->os).'\';
3246 if (ostype === "macintosh") {
3247 if ( event.which === 66 && event.ctrlKey ) {
3248 console.log("Click on control + b : trigger open bookmark dropdown");
3249 openBookMarkDropDown(event);
3250 }
3251 } else {
3252 if ( event.which === 66 && event.ctrlKey && event.shiftKey ) {
3253 console.log("Click on control + shift + b : trigger open bookmark dropdown");
3254 openBookMarkDropDown(event);
3255 }
3256 }
3257 });
3258
3259 var openBookMarkDropDown = function(event) {
3260 event.preventDefault();
3261 jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
3262 jQuery("#top-bookmark-search-input").focus();
3263 }
3264
3265 });
3266 </script>
3267 ';
3268 }
3269 }
3270 return $html;
3271}
3272
3278function top_menu_search()
3279{
3280 global $langs, $conf, $db, $user, $hookmanager; // used by htdocs/core/ajax/selectsearchbox.php
3281
3282 $html = '';
3283
3284 $usedbyinclude = 1;
3285 $arrayresult = array();
3286 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This sets $arrayresult
3287
3288 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3289 // accesskey is for Mac: CTRL + key for all browsers
3290 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3291 if ($conf->browser->name == 'chrome') {
3292 $stringforfirstkey .= ' ALT +';
3293 } elseif ($conf->browser->name == 'firefox') {
3294 $stringforfirstkey .= ' ALT + SHIFT +';
3295 } else {
3296 $stringforfirstkey .= ' CTL +';
3297 }
3298
3299 $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">';
3300
3301 $defaultAction = '';
3302 $buttonList = '<div class="dropdown-global-search-button-list" >';
3303 // Menu with all searchable items
3304 // @phan-suppress-next-line PhanEmptyForeach // array is really empty
3305 foreach ($arrayresult as $keyItem => $item) {
3306 if (empty($defaultAction)) {
3307 $defaultAction = $item['url'];
3308 }
3309 $buttonList .= '<button class="dropdown-item global-search-item tdoverflowmax300" data-target="'.dol_escape_htmltag($item['url']).'" >';
3310 $buttonList .= $item['text'];
3311 $buttonList .= '</button>';
3312 }
3313 $buttonList .= '</div>';
3314
3315 $dropDownHtml = '<form role="search" id="top-menu-action-search" name="actionsearch" method="GET" action="'.$defaultAction.'">';
3316
3317 $dropDownHtml .= '
3318 <!-- search input -->
3319 <div class="dropdown-header search-dropdown-header">
3320 ' . $searchInput.'
3321 </div>
3322 ';
3323
3324 $dropDownHtml .= '
3325 <!-- Menu Body search -->
3326 <div class="dropdown-body search-dropdown-body">
3327 '.$buttonList.'
3328 </div>
3329 ';
3330
3331 $dropDownHtml .= '</form>';
3332
3333 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3334 // accesskey is for Mac: CTRL + key for all browsers
3335 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3336 if ($conf->browser->name == 'chrome') {
3337 $stringforfirstkey .= ' ALT +';
3338 } elseif ($conf->browser->name == 'firefox') {
3339 $stringforfirstkey .= ' ALT + SHIFT +';
3340 } else {
3341 $stringforfirstkey .= ' CTL +';
3342 }
3343
3344 $html .= '<!-- div for Global Search -->
3345 <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
3346 <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Search').' ('.$stringforfirstkey.' s)">
3347 <i class="fa fa-search" aria-hidden="true" ></i>
3348 </a>
3349 <div class="dropdown-menu dropdown-search">
3350 '.$dropDownHtml.'
3351 </div>
3352 </div>';
3353
3354 $html .= '
3355 <!-- Code to show/hide the user drop-down -->
3356 <script>
3357 jQuery(document).ready(function() {
3358
3359 // prevent submitting form on press ENTER
3360 jQuery("#top-global-search-input").keydown(function (e) {
3361 if (e.keyCode == 13 || e.keyCode == 40) {
3362 var inputs = $(this).parents("form").eq(0).find(":button");
3363 if (inputs[inputs.index(this) + 1] != null) {
3364 inputs[inputs.index(this) + 1].focus();
3365 if (e.keyCode == 13){
3366 inputs[inputs.index(this) + 1].trigger("click");
3367 }
3368
3369 }
3370 e.preventDefault();
3371 return false;
3372 }
3373 });
3374
3375 // arrow key nav
3376 jQuery(document).keydown(function(e) {
3377 // Get the focused element:
3378 var $focused = $(":focus");
3379 if($focused.length && $focused.hasClass("global-search-item")){
3380
3381 // UP - move to the previous line
3382 if (e.keyCode == 38) {
3383 e.preventDefault();
3384 $focused.prev().focus();
3385 }
3386
3387 // DOWN - move to the next line
3388 if (e.keyCode == 40) {
3389 e.preventDefault();
3390 $focused.next().focus();
3391 }
3392 }
3393 });
3394
3395
3396 // submit form action
3397 jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
3398 jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
3399 jQuery("#top-menu-action-search").submit();
3400 });
3401
3402 // close drop down
3403 jQuery(document).on("click", function(event) {
3404 if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
3405 console.log("click close search - we click outside");
3406 // Hide the menus.
3407 jQuery("#topmenu-global-search-dropdown").removeClass("open");
3408 }
3409 });
3410
3411 // Open drop down
3412 jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
3413 console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
3414 openGlobalSearchDropDown();
3415 });
3416
3417 // Key map shortcut
3418 jQuery(document).keydown(function(e){
3419 if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
3420 console.log(\'control + shift + f : trigger open global-search dropdown\');
3421 openGlobalSearchDropDown();
3422 }
3423 if ( e.which === 70 && e.alKey ) {
3424 console.log(\'alt + f : trigger open global-search dropdown\');
3425 openGlobalSearchDropDown();
3426 }
3427 });
3428
3429 var openGlobalSearchDropDown = function() {
3430 jQuery("#topmenu-global-search-dropdown").toggleClass("open");
3431 jQuery("#top-global-search-input").focus();
3432 }
3433
3434 });
3435 </script>
3436 ';
3437
3438 return $html;
3439}
3440
3455function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = array(), $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
3456{
3457 global $user, $conf, $langs, $db, $form;
3458 global $hookmanager, $menumanager;
3459
3460 $searchform = '';
3461
3462 if (!empty($menu_array_before)) {
3463 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);
3464 }
3465
3466 if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
3467 // Instantiate hooks for external modules
3468 $hookmanager->initHooks(array('leftblock'));
3469
3470 print "\n".'<!-- Begin side-nav id-left -->'."\n".'<div class="side-nav"><div id="id-left">'."\n";
3471 print "\n";
3472
3473 if (!is_object($form)) {
3474 $form = new Form($db);
3475 }
3476 $selected = -1;
3477 if (!getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
3478 // Select with select2 is awful on smartphone. TODO Is this still true with select2 v4 ?
3479 if ($conf->browser->layout == 'phone') {
3480 $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
3481 }
3482
3483 $usedbyinclude = 1;
3484 $arrayresult = array();
3485 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
3486
3487 if ($conf->use_javascript_ajax && !getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3488 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3489 // accesskey is for Mac: CTRL + key for all browsers
3490 $stringforfirstkey = $langs->trans("KeyboardShortcut");
3491 if ($conf->browser->name == 'chrome') {
3492 $stringforfirstkey .= ' ALT +';
3493 } elseif ($conf->browser->name == 'firefox') {
3494 $stringforfirstkey .= ' ALT + SHIFT +';
3495 } else {
3496 $stringforfirstkey .= ' CTL +';
3497 }
3498
3499 //$textsearch = $langs->trans("Search");
3500 $textsearch = '<span class="fa fa-search paddingright pictofixedwidth"></span>'.$langs->trans("Search");
3501 $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');
3502 } else {
3503 if (is_array($arrayresult)) {
3504 // @phan-suppress-next-line PhanEmptyForeach // array is really empty in else case.
3505 foreach ($arrayresult as $key => $val) {
3506 $searchform .= printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft'.$key, $val['img']);
3507 }
3508 }
3509 }
3510
3511 // Execute hook printSearchForm
3512 $parameters = array('searchform' => $searchform);
3513 $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
3514 if (empty($reshook)) {
3515 $searchform .= $hookmanager->resPrint;
3516 } else {
3517 $searchform = $hookmanager->resPrint;
3518 }
3519
3520 // Force special value for $searchform for text browsers or very old search form
3521 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') || empty($conf->use_javascript_ajax)) {
3522 $urltosearch = DOL_URL_ROOT.'/core/search_page.php?showtitlebefore=1';
3523 $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>';
3524 } elseif ($conf->use_javascript_ajax && getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3525 $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>';
3526 $searchform .= '<script>
3527 jQuery(document).ready(function () {
3528 jQuery("#divsearchforms1").click(function(){
3529 jQuery("#divsearchforms2").toggle();
3530 });
3531 });
3532 </script>' . "\n";
3533 $searchform .= '</div>';
3534 }
3535
3536 // Key map shortcut
3537 $searchform .= '<script>
3538 jQuery(document).keydown(function(e){
3539 if( e.which === 70 && e.ctrlKey && e.shiftKey ){
3540 console.log(\'control + shift + f : trigger open global-search dropdown\');
3541 openGlobalSearchDropDown();
3542 }
3543 if( (e.which === 83 || e.which === 115) && e.altKey ){
3544 console.log(\'alt + s : trigger open global-search dropdown\');
3545 openGlobalSearchDropDown();
3546 }
3547 });
3548
3549 var openGlobalSearchDropDown = function() {
3550 jQuery("#searchselectcombo").select2(\'open\');
3551 }
3552 </script>';
3553 }
3554
3555 // Left column
3556 print '<!-- Begin left menu -->'."\n";
3557
3558 print '<div class="vmenu"'.(getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' alt="Left menu"' : '').'>'."\n\n";
3559
3560 // Show left menu with other forms
3561 // @phan-suppress-next-line PhanRedefinedClassReference
3562 $menumanager->menu_array = $menu_array_before;
3563 // @phan-suppress-next-line PhanRedefinedClassReference
3564 $menumanager->menu_array_after = $menu_array_after;
3565 if (getDolGlobalInt('MAIN_MENU_LEFT_DROPDOWN')) {
3566 // @phan-suppress-next-line PhanRedefinedClassReference
3567 $menumanager->showmenu('leftdropdown', array('searchform' => $searchform)); // output menu_array and menu found in database
3568 } else {
3569 // @phan-suppress-next-line PhanRedefinedClassReference
3570 $menumanager->showmenu('left', array('searchform' => $searchform)); // output menu_array and menu found in database
3571 }
3572
3573 // Dolibarr version + help + bug report link
3574 print "\n";
3575 print "<!-- Begin Help Block-->\n";
3576 print '<div id="blockvmenuhelp" class="blockvmenuhelp">'."\n";
3577
3578 // Version
3579 if (getDolGlobalString('MAIN_SHOW_VERSION')) { // Version is already on help picto and on login page.
3580 $doliurl = 'https://www.dolibarr.org';
3581 //local communities
3582 if (preg_match('/fr/i', $langs->defaultlang)) {
3583 $doliurl = 'https://www.dolibarr.fr';
3584 }
3585 if (preg_match('/es/i', $langs->defaultlang)) {
3586 $doliurl = 'https://www.dolibarr.es';
3587 }
3588 if (preg_match('/de/i', $langs->defaultlang)) {
3589 $doliurl = 'https://www.dolibarr.de';
3590 }
3591 if (preg_match('/it/i', $langs->defaultlang)) {
3592 $doliurl = 'https://www.dolibarr.it';
3593 }
3594 if (preg_match('/gr/i', $langs->defaultlang)) {
3595 $doliurl = 'https://www.dolibarr.gr';
3596 }
3597
3598 $appli = constant('DOL_APPLICATION_TITLE');
3599 if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
3600 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
3601 $doliurl = '';
3602 if (preg_match('/\d\.\d/', $appli)) {
3603 if (!preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) {
3604 $appli .= " (".DOL_VERSION.")"; // If new title contains a version that is different than core
3605 }
3606 } else {
3607 $appli .= " ".DOL_VERSION;
3608 }
3609 } else {
3610 $appli .= " ".DOL_VERSION;
3611 }
3612 print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
3613 if ($doliurl) {
3614 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$doliurl.'">';
3615 } else {
3616 print '<span class="help">';
3617 }
3618 print $appli;
3619 if ($doliurl) {
3620 print '</a>';
3621 } else {
3622 print '</span>';
3623 }
3624 print '</div>'."\n";
3625 }
3626
3627 // Link to bugtrack
3628 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3629 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3630
3631 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK') == 'github') {
3632 $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
3633 $bugbaseurl .= '&title=';
3634 $bugbaseurl .= urlencode("Bug: ");
3635 $bugbaseurl .= '&body=';
3636 $bugbaseurl .= urlencode("# Instructions\n");
3637 $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");
3638 $bugbaseurl .= urlencode("*Please:*\n");
3639 $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
3640 $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
3641 $bugbaseurl .= urlencode("\n");
3642 $bugbaseurl .= urlencode("\n");
3643 $bugbaseurl .= urlencode("# Bug\n");
3644 $bugbaseurl .= urlencode("[*Short description*]\n");
3645 $bugbaseurl .= urlencode("\n");
3646 $bugbaseurl .= urlencode("## Environment\n");
3647 $bugbaseurl .= urlencode("- **Version**: ".DOL_VERSION."\n");
3648 $bugbaseurl .= urlencode("- **OS**: ".php_uname('s')."\n");
3649 $bugbaseurl .= urlencode("- **Web server**: ".$_SERVER["SERVER_SOFTWARE"]."\n");
3650 $bugbaseurl .= urlencode("- **PHP**: ".php_sapi_name().' '.phpversion()."\n");
3651 $bugbaseurl .= urlencode("- **Database**: ".$db::LABEL.' '.$db->getVersion()."\n");
3652 $bugbaseurl .= urlencode("- **URL(s)**: ".$_SERVER["REQUEST_URI"]."\n");
3653 $bugbaseurl .= urlencode("\n");
3654 $bugbaseurl .= urlencode("## Expected and actual behavior\n");
3655 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3656 $bugbaseurl .= urlencode("\n");
3657 $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
3658 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3659 $bugbaseurl .= urlencode("\n");
3660 $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, dolibarr.log, debugging information…)\n");
3661 $bugbaseurl .= urlencode("[*Files*]\n");
3662 $bugbaseurl .= urlencode("\n");
3663
3664 $bugbaseurl .= urlencode("\n");
3665 $bugbaseurl .= urlencode("## Report\n");
3666 } elseif (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3667 $bugbaseurl = getDolGlobalString('MAIN_BUGTRACK_ENABLELINK');
3668 } else {
3669 $bugbaseurl = "";
3670 }
3671
3672 // Execute hook printBugtrackInfo
3673 $parameters = array('bugbaseurl' => $bugbaseurl);
3674 $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
3675 if (empty($reshook)) {
3676 $bugbaseurl .= $hookmanager->resPrint;
3677 } else {
3678 $bugbaseurl = $hookmanager->resPrint;
3679 }
3680
3681 print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
3682 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$bugbaseurl.'"><i class="fas fa-bug"></i> '.$langs->trans("FindBug").'</a>';
3683 print '</div>';
3684 }
3685
3686 print "</div>\n";
3687 print "<!-- End Help Block-->\n";
3688 print "\n";
3689
3690 print "</div>\n";
3691 print "<!-- End left menu -->\n";
3692 print "\n";
3693
3694 // Execute hook printLeftBlock
3695 $parameters = array();
3696 $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
3697 print $hookmanager->resPrint;
3698
3699 print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
3700 }
3701
3702 print "\n";
3703 print '<!-- Begin right area -->'."\n";
3704
3705 if (empty($leftmenuwithoutmainarea)) {
3706 main_area($title);
3707 }
3708}
3709
3710
3717function main_area($title = '')
3718{
3719 global $conf, $langs, $hookmanager;
3720
3721 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3722 print '<div id="id-right">';
3723 }
3724
3725 print "\n";
3726
3727 print '<!-- Begin div class="fiche" -->'."\n".'<div class="fiche">'."\n";
3728
3729 $hookmanager->initHooks(array('main'));
3730 $parameters = array();
3731 $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
3732 print $hookmanager->resPrint;
3733
3734 if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
3735 print info_admin($langs->trans("WarningYouAreInMaintenanceMode", getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')), 0, 0, '1', 'warning maintenancemode');
3736 }
3737
3738 // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
3739 if (getDolGlobalString('SHOW_SOCINFO_ON_PRINT') && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'aZ09'))) {
3740 $parameters = array();
3741 $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
3742 if (empty($reshook)) {
3743 print '<!-- Begin show mysoc info header -->'."\n";
3744 print '<div id="mysoc-info-header">'."\n";
3745 print '<table class="centpercent div-table-responsive">'."\n";
3746 print '<tbody>';
3747 print '<tr><td rowspan="0" class="width20p">';
3748 if (getDolGlobalString('MAIN_SHOW_LOGO') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalString('MAIN_INFO_SOCIETE_LOGO')) {
3749 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'))).'">';
3750 }
3751 print '</td><td rowspan="0" class="width50p"></td></tr>'."\n";
3752 print '<tr><td class="titre bold">'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_NOM')).'</td></tr>'."\n";
3753 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";
3754 if (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) {
3755 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Phone").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TEL')).'</td></tr>';
3756 }
3757 if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
3758 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Email").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')).'</td></tr>';
3759 }
3760 if (getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) {
3761 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Web").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_WEB')).'</td></tr>';
3762 }
3763 print '</tbody>';
3764 print '</table>'."\n";
3765 print '</div>'."\n";
3766 print '<!-- End show mysoc info header -->'."\n";
3767 }
3768 }
3769}
3770
3771
3779function getHelpParamFor($helppagename, $langs)
3780{
3781 $helpbaseurl = '';
3782 $helppage = '';
3783 $mode = '';
3784
3785 if (preg_match('/^http/i', $helppagename)) {
3786 // If complete URL
3787 $helpbaseurl = '%s';
3788 $helppage = $helppagename;
3789 $mode = 'local';
3790 } else {
3791 // If WIKI URL
3792 $reg = array();
3793 if (preg_match('/^es/i', $langs->defaultlang)) {
3794 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3795 if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
3796 $helppage = $reg[1];
3797 }
3798 }
3799 if (preg_match('/^fr/i', $langs->defaultlang)) {
3800 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3801 if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
3802 $helppage = $reg[1];
3803 }
3804 }
3805 if (preg_match('/^de/i', $langs->defaultlang)) {
3806 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3807 if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
3808 $helppage = $reg[1];
3809 }
3810 }
3811 if (empty($helppage)) { // If help page not already found
3812 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3813 if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
3814 $helppage = $reg[1];
3815 }
3816 }
3817 $mode = 'wiki';
3818 }
3819 return array('helpbaseurl' => $helpbaseurl, 'helppage' => $helppage, 'mode' => $mode);
3820}
3821
3822
3839function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
3840{
3841 global $langs, $user;
3842
3843 $ret = '';
3844 $ret .= '<form action="'.$urlaction.'" method="post" class="searchform nowraponall tagtr">';
3845 $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
3846 $ret .= '<input type="hidden" name="savelogin" value="'.dol_escape_htmltag($user->login).'">';
3847 if ($showtitlebefore) {
3848 $ret .= '<div class="tagtd left">'.$title.'</div> ';
3849 }
3850 $ret .= '<div class="tagtd">';
3851 $ret .= img_picto('', $img, '', 0, 0, 0, '', 'paddingright width20');
3852 $ret .= '<input type="text" class="flat '.$htmlmorecss.'"';
3853 $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
3854 $ret .= ($accesskey ? ' accesskey="'.$accesskey.'"' : '');
3855 $ret .= ' placeholder="'.strip_tags($title).'"';
3856 $ret .= ($autofocus ? ' autofocus' : '');
3857 $ret .= ' name="'.$htmlinputname.'" id="'.$prefhtmlinputname.$htmlinputname.'" />';
3858 $ret .= '<button type="submit" class="button bordertransp" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
3859 $ret .= '<span class="fa fa-search"></span>';
3860 $ret .= '</button>';
3861 $ret .= '</div>';
3862 $ret .= "</form>\n";
3863 return $ret;
3864}
3865
3866
3867if (!function_exists("llxFooter")) {
3879 function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
3880 {
3881 global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager, $action;
3882 global $delayedhtmlcontent;
3883 global $contextpage, $page, $limit, $mode;
3884 global $dolibarr_distrib;
3885
3886 $ext = 'layout='.urlencode($conf->browser->layout).'&version='.urlencode(DOL_VERSION);
3887
3888 // Hook to add more things on all pages within fiche DIV
3889 $llxfooter = '';
3890 $parameters = array();
3891 $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3892 if (empty($reshook)) {
3893 $llxfooter .= $hookmanager->resPrint;
3894 } elseif ($reshook > 0) {
3895 $llxfooter = $hookmanager->resPrint;
3896 }
3897 if ($llxfooter) {
3898 print $llxfooter;
3899 }
3900
3901 // Global html output events ($mesgs, $errors, $warnings)
3902 dol_htmloutput_events($disabledoutputofmessages);
3903
3904 // Code for search criteria persistence.
3905 // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
3906 if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
3907 // Clean and save data
3908 foreach ($user->lastsearch_values_tmp as $key => $val) {
3909 unset($_SESSION['lastsearch_values_tmp_'.$key]); // Clean array to rebuild it just after
3910 if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
3911 if (empty($val['sortfield'])) {
3912 unset($val['sortfield']);
3913 }
3914 if (empty($val['sortorder'])) {
3915 unset($val['sortorder']);
3916 }
3917 dol_syslog('Save lastsearch_values_tmp_'.$key.'='.json_encode($val, 0)." (systematic recording of last search criteria)");
3918 $_SESSION['lastsearch_values_tmp_'.$key] = json_encode($val);
3919 unset($_SESSION['lastsearch_values_'.$key]);
3920 }
3921 }
3922 }
3923
3924
3925 $relativepathstring = $_SERVER["PHP_SELF"];
3926 // Clean $relativepathstring
3927 if (constant('DOL_URL_ROOT')) {
3928 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
3929 }
3930 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3931 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3932 if (preg_match('/list\.php$/', $relativepathstring)) {
3933 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
3934 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
3935 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
3936 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
3937
3938 if (!empty($contextpage)) {
3939 $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring] = $contextpage;
3940 }
3941 if (!empty($page) && $page > 0) {
3942 $_SESSION['lastsearch_page_tmp_'.$relativepathstring] = $page;
3943 }
3944 if (!empty($limit) && $limit != $conf->liste_limit) {
3945 $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] = $limit;
3946 }
3947 if (!empty($mode)) {
3948 $_SESSION['lastsearch_mode_tmp_'.$relativepathstring] = $mode;
3949 }
3950
3951 unset($_SESSION['lastsearch_contextpage_'.$relativepathstring]);
3952 unset($_SESSION['lastsearch_page_'.$relativepathstring]);
3953 unset($_SESSION['lastsearch_limit_'.$relativepathstring]);
3954 unset($_SESSION['lastsearch_mode_'.$relativepathstring]);
3955 }
3956
3957 // Core error message
3958 if (getDolGlobalString('MAIN_CORE_ERROR')) {
3959 // Ajax version
3960 if ($conf->use_javascript_ajax) {
3961 $title = img_warning().' '.$langs->trans('CoreErrorTitle');
3962 print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
3963 } else {
3964 // html version
3965 $msg = img_warning().' '.$langs->trans('CoreErrorMessage');
3966 print '<div class="error">'.$msg.'</div>';
3967 }
3968
3969 //define("MAIN_CORE_ERROR",0); // Constant was defined and we can't change value of a constant
3970 }
3971
3972 print "\n\n";
3973
3974 print '</div> <!-- End div class="fiche" -->'."\n"; // End div fiche
3975
3976 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3977 print '</div> <!-- End div id-right -->'."\n"; // End div id-right
3978 }
3979
3980 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
3981 print '</div> <!-- End div id-container -->'."\n"; // End div container
3982 }
3983
3984 print "\n";
3985 if ($comment) {
3986 print '<!-- '.$comment.' -->'."\n";
3987 }
3988
3989 printCommonFooter($zone);
3990
3991 if (!empty($delayedhtmlcontent)) {
3992 print $delayedhtmlcontent;
3993 }
3994
3995 if (!empty($conf->use_javascript_ajax)) {
3996 print "\n".'<!-- Includes JS Footer of Dolibarr -->'."\n";
3997 print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang.($ext ? '&'.$ext : '').'"></script>'."\n";
3998 }
3999
4000 // JS wrapper to add log when clicking on download or preview
4001 if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
4002 if (in_array($object->element, array('facture')) && $object->statut > 0) { // Restrict for the moment to element 'facture'
4003 print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
4004 ?>
4005 <script>
4006 jQuery(document).ready(function () {
4007 $('a.documentpreview').click(function() {
4008 console.log("Call /blockedlog/ajax/block-add on a.documentpreview");
4009 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
4010 , {
4011 id:<?php echo $object->id; ?>
4012 , element:'<?php echo dol_escape_js($object->element) ?>'
4013 , action:'DOC_PREVIEW'
4014 , token: '<?php echo currentToken(); ?>'
4015 }
4016 );
4017 });
4018 $('a.documentdownload').click(function() {
4019 console.log("Call /blockedlog/ajax/block-add a.documentdownload");
4020 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
4021 , {
4022 id:<?php echo $object->id; ?>
4023 , element:'<?php echo dol_escape_js($object->element) ?>'
4024 , action:'DOC_DOWNLOAD'
4025 , token: '<?php echo currentToken(); ?>'
4026 }
4027 );
4028 });
4029 });
4030 </script>
4031 <?php
4032 }
4033 }
4034
4035 // A div for the #dialogforpopup popup
4036 print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
4037 print '<div id="dialogforpopup" style="display: none;"></div>'."\n";
4038
4039 // Add code for the asynchronous anonymous first ping (for telemetry)
4040 // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
4041 $forceping = GETPOST('forceping', 'alpha');
4042 if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceping) {
4043 //print '<!-- instance_unique_id='.$conf->file->instance_unique_id.' MAIN_FIRST_PING_OK_ID='.$conf->global->MAIN_FIRST_PING_OK_ID.' -->';
4044 $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.
4045
4046 if (!getDolGlobalString('MAIN_FIRST_PING_OK_DATE')
4047 || (!empty($conf->file->instance_unique_id) && ($hash_unique_id != $conf->global->MAIN_FIRST_PING_OK_ID) && (getDolGlobalString('MAIN_FIRST_PING_OK_ID') != 'disabled'))
4048 || $forceping) {
4049 // No ping done if we are into an alpha version
4050 if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
4051 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
4052 } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
4053 // MAIN_LAST_PING_KO_DATE
4054 // Disable ping if MAIN_LAST_PING_KO_DATE is set and is recent (this month)
4055 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) {
4056 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. An error already occurred this month, we will try later. -->\n";
4057 } else {
4058 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4059
4060 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";
4061 print "\n<!-- JS CODE TO ENABLE the anonymous Ping -->\n";
4062 $url_for_ping = getDolGlobalString('MAIN_URL_FOR_PING', "https://ping.dolibarr.org/");
4063 // Try to guess the distrib used
4064 $distrib = 'standard';
4065 if ($_SERVER["SERVER_ADMIN"] == 'doliwamp@localhost') {
4066 $distrib = 'doliwamp';
4067 }
4068 if (!empty($dolibarr_distrib)) {
4069 $distrib = $dolibarr_distrib;
4070 }
4071 ?>
4072 <script>
4073 jQuery(document).ready(function (tmp) {
4074 console.log("Try Ping with hash_unique_id is dol_hash('dolibarr'+instance_unique_id, 'sha256')");
4075 $.ajax({
4076 method: "POST",
4077 url: "<?php echo $url_for_ping ?>",
4078 timeout: 500, // timeout milliseconds
4079 cache: false,
4080 data: {
4081 hash_algo: 'dol_hash-sha256',
4082 hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
4083 action: 'dolibarrping',
4084 version: '<?php echo (float) DOL_VERSION; ?>',
4085 entity: '<?php echo (int) $conf->entity; ?>',
4086 dbtype: '<?php echo dol_escape_js($db->type); ?>',
4087 country_code: '<?php echo $mysoc->country_code ? dol_escape_js($mysoc->country_code) : 'unknown'; ?>',
4088 php_version: '<?php echo dol_escape_js(phpversion()); ?>',
4089 os_version: '<?php echo dol_escape_js(version_os('smr')); ?>',
4090 db_version: '<?php echo dol_escape_js(version_db()); ?>',
4091 distrib: '<?php echo $distrib ? dol_escape_js($distrib) : 'unknown'; ?>',
4092 token: 'notrequired'
4093 },
4094 success: function (data, status, xhr) { // success callback function (data contains body of response)
4095 console.log("Ping ok");
4096 $.ajax({
4097 method: 'GET',
4098 url: '<?php echo DOL_URL_ROOT.'/core/ajax/pingresult.php'; ?>',
4099 timeout: 500, // timeout milliseconds
4100 cache: false,
4101 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
4102 });
4103 },
4104 error: function (data,status,xhr) { // error callback function
4105 console.log("Ping ko: " + data);
4106 $.ajax({
4107 method: 'GET',
4108 url: '<?php echo DOL_URL_ROOT.'/core/ajax/pingresult.php'; ?>',
4109 timeout: 500, // timeout milliseconds
4110 cache: false,
4111 data: { hash_algo: 'dol_hash-sha256', hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>', action: 'firstpingko', token: '<?php echo currentToken(); ?>' },
4112 });
4113 }
4114 });
4115 });
4116 </script>
4117 <?php
4118 }
4119 } else {
4120 $now = dol_now();
4121 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
4122 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
4123 dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_DATE', dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
4124 dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_ID', 'disabled', 'chaine', 0, '', $conf->entity);
4125 }
4126 }
4127 }
4128
4129 $parameters = array();
4130 $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters); // Note that $action and $object may have been modified by some hooks
4131 if ($reshook > 0) {
4132 print $hookmanager->resPrint;
4133 }
4134
4135 print "</body>\n";
4136 print "</html>\n";
4137 }
4138}
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
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
llxFooter()
Footer empty.
Definition document.php:107
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_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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:153
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:149
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:152
checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode, $context='')
Return a login if login/pass was successful.
dol_hash($chain, $type='0', $nosalt=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.