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