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