dolibarr  19.0.0-dev
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/API/GlobalEventHandlers
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|afterprint|animation|auxclick|beforecopy|beforecut|beforeprint|beforeunload|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)\s*=/i', $val);
174  $inj += preg_match('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)\s*=/i', $val);
175  $inj += preg_match('/on(lostpointercapture|offline|online|pagehide|pageshow)\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)\s*=/i', $val);
177  $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)\s*=/i', $val);
178  // More not into the previous list
179  $inj += preg_match('/on(repeat|begin|finish|beforeinput)\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/API/GlobalEventHandlers
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|afterprint|animation|auxclick|beforecopy|beforecut|beforeprint|beforeunload|blur|cancel|canplay|canplaythrough|change|click|close|contextmenu|cuechange|copy|cut)\s*=/i', $tmpval);
186  $inj += preg_match('/on(dblclick|drop|durationchange|emptied|end|ended|error|focus|focusin|focusout|formdata|gotpointercapture|hashchange|input|invalid)\s*=/i', $tmpval);
187  $inj += preg_match('/on(lostpointercapture|offline|online|pagehide|pageshow)\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)\s*=/i', $tmpval);
189  $inj += preg_match('/on(timeupdate|toggle|unload|volumechange|waiting|wheel)\s*=/i', $tmpval);
190  // More not into the previous list
191  $inj += preg_match('/on(repeat|begin|finish|beforeinput)\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')) {
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|sepa)/', 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 ".$_SERVER['HTTP_REFERER'].")");
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->trigger_mesg = '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  //print $datefirst.'-'.$datesecond.'-'.$datenow.'-'.$dol_tz.'-'.$dol_tzstring.'-'.$dol_dst; exit;
849  }
850 
851  if (!$login) {
852  dol_syslog('Bad password, connexion refused', LOG_DEBUG);
853  // Load translation files required by page
854  $langs->loadLangs(array('main', 'errors'));
855 
856  // Bad password. No authmode has found a good password.
857  // We set a generic message if not defined inside function checkLoginPassEntity or subfunctions
858  if (empty($_SESSION["dol_loginmesg"])) {
859  $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadLoginPassword");
860  }
861 
862  // Call trigger for the "security events" log
863  $user->trigger_mesg = $langs->trans("ErrorBadLoginPassword").' - login='.GETPOST("username", "alpha", 2);
864 
865  // Call trigger
866  $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
867  if ($result < 0) {
868  $error++;
869  }
870  // End call triggers
871 
872  // Hooks on failed login
873  $action = '';
874  $hookmanager->initHooks(array('login'));
875  $parameters = array('dol_authmode'=>$dol_authmode, 'dol_loginmesg'=>$_SESSION["dol_loginmesg"]);
876  $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
877  if ($reshook < 0) {
878  $error++;
879  }
880 
881  // Note: exit is done in next chapter
882  }
883  }
884 
885  // End test login / passwords
886  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.
887  // No data to test login, so we show the login page.
888  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);
889  if (defined('NOREDIRECTBYMAINTOLOGIN')) {
890  // When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
891  // See example with selectsearchbox.php. This case is reserverd for the selectesearchbox.php so we can
892  // report a message to ask to login when search ajax component is used after a timeout.
893  //top_httphead();
894  return 'ERROR_NOT_LOGGED';
895  } else {
896  if ($_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
897  http_response_code(401); // It makes easier to understand if session was broken during security tests
898  }
899  dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : '')); // This include http headers
900  }
901  exit;
902  }
903 
904  $resultFetchUser = $user->fetch('', $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.
905  if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
906  dol_syslog('User not found or not valid, connexion refused');
907  session_destroy();
908  session_set_cookie_params(0, '/', null, (empty($dolibarr_main_force_https) ? false : true), true); // Add tag secure and httponly on session cookie
909  session_name($sessionname);
910  session_start();
911 
912  if ($resultFetchUser == 0) {
913  // Load translation files required by page
914  $langs->loadLangs(array('main', 'errors'));
915 
916  $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
917 
918  $user->trigger_mesg = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
919  } elseif ($resultFetchUser < 0) {
920  $_SESSION["dol_loginmesg"] = $user->error;
921 
922  $user->trigger_mesg = $user->error;
923  } else {
924  // Load translation files required by the page
925  $langs->loadLangs(array('main', 'errors'));
926 
927  $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginDateValidity");
928 
929  $user->trigger_mesg = $langs->trans("ErrorLoginDateValidity").' - login='.$login;
930  }
931 
932  // Call trigger
933  $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
934  if ($result < 0) {
935  $error++;
936  }
937  // End call triggers
938 
939 
940  // Hooks on failed login
941  $action = '';
942  $hookmanager->initHooks(array('login'));
943  $parameters = array('dol_authmode'=>$dol_authmode, 'dol_loginmesg'=>$_SESSION["dol_loginmesg"]);
944  $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
945  if ($reshook < 0) {
946  $error++;
947  }
948 
949  $paramsurl = array();
950  if (GETPOST('textbrowser', 'int')) {
951  $paramsurl[] = 'textbrowser='.GETPOST('textbrowser', 'int');
952  }
953  if (GETPOST('nojs', 'int')) {
954  $paramsurl[] = 'nojs='.GETPOST('nojs', 'int');
955  }
956  if (GETPOST('lang', 'aZ09')) {
957  $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
958  }
959  header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
960  exit;
961  } else {
962  // User is loaded, we may need to change language for him according to its choice
963  if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
964  $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
965  }
966  }
967  } else {
968  // We are already into an authenticated session
969  $login = $_SESSION["dol_login"];
970  $entity = isset($_SESSION["dol_entity"]) ? $_SESSION["dol_entity"] : 0;
971  dol_syslog("- This is an already logged session. _SESSION['dol_login']=".$login." _SESSION['dol_entity']=".$entity, LOG_DEBUG);
972 
973  $resultFetchUser = $user->fetch('', $login, '', 1, ($entity > 0 ? $entity : -1));
974 
975  //var_dump(dol_print_date($user->flagdelsessionsbefore, 'dayhour', 'gmt')." ".dol_print_date($_SESSION["dol_logindate"], 'dayhour', 'gmt'));
976 
977  if ($resultFetchUser <= 0
978  || ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"])
979  || ($user->status != $user::STATUS_ENABLED)
980  || ($user->isNotIntoValidityDateRange())) {
981  if ($resultFetchUser <= 0) {
982  // Account has been removed after login
983  dol_syslog("Can't load user even if session logged. _SESSION['dol_login']=".$login, LOG_WARNING);
984  } elseif ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"]) {
985  // Session is no more valid
986  dol_syslog("The user has a date for session invalidation = ".$user->flagdelsessionsbefore." and a session date = ".$_SESSION["dol_logindate"].". We must invalidate its sessions.");
987  } elseif ($user->status != $user::STATUS_ENABLED) {
988  // User is not enabled
989  dol_syslog("The user login is disabled");
990  } else {
991  // User validity dates are no more valid
992  dol_syslog("The user login has a validity between [".$user->datestartvalidity." and ".$user->dateendvalidity."], curren date is ".dol_now());
993  }
994  session_destroy();
995  session_set_cookie_params(0, '/', null, (empty($dolibarr_main_force_https) ? false : true), true); // Add tag secure and httponly on session cookie
996  session_name($sessionname);
997  session_start();
998 
999  if ($resultFetchUser == 0) {
1000  $langs->loadLangs(array('main', 'errors'));
1001 
1002  $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
1003 
1004  $user->trigger_mesg = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
1005  } elseif ($resultFetchUser < 0) {
1006  $_SESSION["dol_loginmesg"] = $user->error;
1007 
1008  $user->trigger_mesg = $user->error;
1009  } else {
1010  $langs->loadLangs(array('main', 'errors'));
1011 
1012  $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorSessionInvalidatedAfterPasswordChange");
1013 
1014  $user->trigger_mesg = 'ErrorUserSessionWasInvalidated - login='.$login;
1015  }
1016 
1017  // Call trigger
1018  $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
1019  if ($result < 0) {
1020  $error++;
1021  }
1022  // End call triggers
1023 
1024  // Hooks on failed login
1025  $action = '';
1026  $hookmanager->initHooks(array('login'));
1027  $parameters = array('dol_authmode' => (isset($dol_authmode) ? $dol_authmode : ''), 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
1028  $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1029  if ($reshook < 0) {
1030  $error++;
1031  }
1032 
1033  $paramsurl = array();
1034  if (GETPOST('textbrowser', 'int')) {
1035  $paramsurl[] = 'textbrowser='.GETPOST('textbrowser', 'int');
1036  }
1037  if (GETPOST('nojs', 'int')) {
1038  $paramsurl[] = 'nojs='.GETPOST('nojs', 'int');
1039  }
1040  if (GETPOST('lang', 'aZ09')) {
1041  $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
1042  }
1043  header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
1044  exit;
1045  } else {
1046  // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
1047  $hookmanager->initHooks(array('main'));
1048 
1049  // Code for search criteria persistence.
1050  if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1051  $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]);
1052  $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server
1053  // Clean $relativepathstring
1054  if (constant('DOL_URL_ROOT')) {
1055  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
1056  }
1057  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
1058  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1059  //var_dump($relativepathstring);
1060 
1061  // 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
1062  if (!empty($_SESSION['lastsearch_values_tmp_'.$relativepathstring])) {
1063  $_SESSION['lastsearch_values_'.$relativepathstring] = $_SESSION['lastsearch_values_tmp_'.$relativepathstring];
1064  unset($_SESSION['lastsearch_values_tmp_'.$relativepathstring]);
1065  }
1066  if (!empty($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring])) {
1067  $_SESSION['lastsearch_contextpage_'.$relativepathstring] = $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring];
1068  unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
1069  }
1070  if (!empty($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] != $conf->liste_limit) {
1071  $_SESSION['lastsearch_limit_'.$relativepathstring] = $_SESSION['lastsearch_limit_tmp_'.$relativepathstring];
1072  unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
1073  }
1074  if (!empty($_SESSION['lastsearch_page_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_page_tmp_'.$relativepathstring] > 0) {
1075  $_SESSION['lastsearch_page_'.$relativepathstring] = $_SESSION['lastsearch_page_tmp_'.$relativepathstring];
1076  unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
1077  }
1078  if (!empty($_SESSION['lastsearch_mode_tmp_'.$relativepathstring])) {
1079  $_SESSION['lastsearch_mode_'.$relativepathstring] = $_SESSION['lastsearch_mode_tmp_'.$relativepathstring];
1080  unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
1081  }
1082  }
1083 
1084  $action = '';
1085  $parameters = array();
1086  $reshook = $hookmanager->executeHooks('updateSession', $parameters, $user, $action);
1087  if ($reshook < 0) {
1088  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1089  }
1090  }
1091  }
1092 
1093  // Is it a new session that has started ?
1094  // If we are here, this means authentication was successfull.
1095  if (!isset($_SESSION["dol_login"])) {
1096  // New session for this login has started.
1097  $error = 0;
1098 
1099  // Store value into session (values always stored)
1100  $_SESSION["dol_login"] = $user->login;
1101  $_SESSION["dol_logindate"] = dol_now('gmt');
1102  $_SESSION["dol_authmode"] = isset($dol_authmode) ? $dol_authmode : '';
1103  $_SESSION["dol_tz"] = isset($dol_tz) ? $dol_tz : '';
1104  $_SESSION["dol_tz_string"] = isset($dol_tz_string) ? $dol_tz_string : '';
1105  $_SESSION["dol_dst"] = isset($dol_dst) ? $dol_dst : '';
1106  $_SESSION["dol_dst_observed"] = isset($dol_dst_observed) ? $dol_dst_observed : '';
1107  $_SESSION["dol_dst_first"] = isset($dol_dst_first) ? $dol_dst_first : '';
1108  $_SESSION["dol_dst_second"] = isset($dol_dst_second) ? $dol_dst_second : '';
1109  $_SESSION["dol_screenwidth"] = isset($dol_screenwidth) ? $dol_screenwidth : '';
1110  $_SESSION["dol_screenheight"] = isset($dol_screenheight) ? $dol_screenheight : '';
1111  $_SESSION["dol_company"] = getDolGlobalString("MAIN_INFO_SOCIETE_NOM");
1112  $_SESSION["dol_entity"] = $conf->entity;
1113  // Store value into session (values stored only if defined)
1114  if (!empty($dol_hide_topmenu)) {
1115  $_SESSION['dol_hide_topmenu'] = $dol_hide_topmenu;
1116  }
1117  if (!empty($dol_hide_leftmenu)) {
1118  $_SESSION['dol_hide_leftmenu'] = $dol_hide_leftmenu;
1119  }
1120  if (!empty($dol_optimize_smallscreen)) {
1121  $_SESSION['dol_optimize_smallscreen'] = $dol_optimize_smallscreen;
1122  }
1123  if (!empty($dol_no_mouse_hover)) {
1124  $_SESSION['dol_no_mouse_hover'] = $dol_no_mouse_hover;
1125  }
1126  if (!empty($dol_use_jmobile)) {
1127  $_SESSION['dol_use_jmobile'] = $dol_use_jmobile;
1128  }
1129 
1130  dol_syslog("This is a new started user session. _SESSION['dol_login']=".$_SESSION["dol_login"]." Session id=".session_id());
1131 
1132  $db->begin();
1133 
1134  $user->update_last_login_date();
1135 
1136  $loginfo = 'TZ='.$_SESSION["dol_tz"].';TZString='.$_SESSION["dol_tz_string"].';Screen='.$_SESSION["dol_screenwidth"].'x'.$_SESSION["dol_screenheight"];
1137 
1138  // Call triggers for the "security events" log
1139  $user->trigger_mesg = $loginfo;
1140 
1141  // Call trigger
1142  $result = $user->call_trigger('USER_LOGIN', $user);
1143  if ($result < 0) {
1144  $error++;
1145  }
1146  // End call triggers
1147 
1148  // Hooks on successfull login
1149  $action = '';
1150  $hookmanager->initHooks(array('login'));
1151  $parameters = array('dol_authmode'=>$dol_authmode, 'dol_loginfo'=>$loginfo);
1152  $reshook = $hookmanager->executeHooks('afterLogin', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1153  if ($reshook < 0) {
1154  $error++;
1155  }
1156 
1157  if ($error) {
1158  $db->rollback();
1159  session_destroy();
1160  dol_print_error($db, 'Error in some triggers USER_LOGIN or in some hooks afterLogin');
1161  exit;
1162  } else {
1163  $db->commit();
1164  }
1165 
1166  // Change landing page if defined.
1167  $landingpage = (empty($user->conf->MAIN_LANDING_PAGE) ? (empty($conf->global->MAIN_LANDING_PAGE) ? '' : $conf->global->MAIN_LANDING_PAGE) : $user->conf->MAIN_LANDING_PAGE);
1168  if (!empty($landingpage)) { // Example: /index.php
1169  $newpath = dol_buildpath($landingpage, 1);
1170  if ($_SERVER["PHP_SELF"] != $newpath) { // not already on landing page (avoid infinite loop)
1171  header('Location: '.$newpath);
1172  exit;
1173  }
1174  }
1175  }
1176 
1177 
1178  // If user admin, we force the rights-based modules
1179  if ($user->admin) {
1180  $user->rights->user->user->lire = 1;
1181  $user->rights->user->user->creer = 1;
1182  $user->rights->user->user->password = 1;
1183  $user->rights->user->user->supprimer = 1;
1184  $user->rights->user->self->creer = 1;
1185  $user->rights->user->self->password = 1;
1186 
1187  //Required if advanced permissions are used with MAIN_USE_ADVANCED_PERMS
1188  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS)) {
1189  if (empty($user->rights->user->user_advance)) {
1190  $user->rights->user->user_advance = new stdClass(); // To avoid warnings
1191  }
1192  if (empty($user->rights->user->self_advance)) {
1193  $user->rights->user->self_advance = new stdClass(); // To avoid warnings
1194  }
1195  if (empty($user->rights->user->group_advance)) {
1196  $user->rights->user->group_advance = new stdClass(); // To avoid warnings
1197  }
1198 
1199  $user->rights->user->user_advance->readperms = 1;
1200  $user->rights->user->user_advance->write = 1;
1201  $user->rights->user->self_advance->readperms = 1;
1202  $user->rights->user->self_advance->writeperms = 1;
1203  $user->rights->user->group_advance->read = 1;
1204  $user->rights->user->group_advance->readperms = 1;
1205  $user->rights->user->group_advance->write = 1;
1206  $user->rights->user->group_advance->delete = 1;
1207  }
1208  }
1209 
1210  /*
1211  * Overwrite some configs globals (try to avoid this and have code to use instead $user->conf->xxx)
1212  */
1213 
1214  // Set liste_limit
1215  if (isset($user->conf->MAIN_SIZE_LISTE_LIMIT)) {
1216  $conf->liste_limit = $user->conf->MAIN_SIZE_LISTE_LIMIT; // Can be 0
1217  }
1218  if (isset($user->conf->PRODUIT_LIMIT_SIZE)) {
1219  $conf->product->limit_size = $user->conf->PRODUIT_LIMIT_SIZE; // Can be 0
1220  }
1221 
1222  // Replace conf->css by personalized value if theme not forced
1223  if (empty($conf->global->MAIN_FORCETHEME) && !empty($user->conf->MAIN_THEME)) {
1224  $conf->theme = $user->conf->MAIN_THEME;
1225  $conf->css = "/theme/".$conf->theme."/style.css.php";
1226  }
1227 } else {
1228  // We may have NOLOGIN set, but NOREQUIREUSER not
1229  if (!empty($user) && method_exists($user, 'loadDefaultValues') && !defined('NODEFAULTVALUES')) {
1230  $user->loadDefaultValues(); // Load default values for everybody (works even if $user->id = 0
1231  }
1232 }
1233 
1234 
1235 // Case forcing style from url
1236 if (GETPOST('theme', 'aZ09')) {
1237  $conf->theme = GETPOST('theme', 'aZ09', 1);
1238  $conf->css = "/theme/".$conf->theme."/style.css.php";
1239 }
1240 
1241 // Set javascript option
1242 if (GETPOST('nojs', 'int')) { // If javascript was not disabled on URL
1243  $conf->use_javascript_ajax = 0;
1244 } else {
1245  if (!empty($user->conf->MAIN_DISABLE_JAVASCRIPT)) {
1246  $conf->use_javascript_ajax = !$user->conf->MAIN_DISABLE_JAVASCRIPT;
1247  }
1248 }
1249 
1250 // Set MAIN_OPTIMIZEFORTEXTBROWSER for user (must be after login part)
1251 if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && !empty($user->conf->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1252  $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = $user->conf->MAIN_OPTIMIZEFORTEXTBROWSER;
1253 }
1254 
1255 // set MAIN_OPTIMIZEFORCOLORBLIND for user
1256 $conf->global->MAIN_OPTIMIZEFORCOLORBLIND = empty($user->conf->MAIN_OPTIMIZEFORCOLORBLIND) ? '' : $user->conf->MAIN_OPTIMIZEFORCOLORBLIND;
1257 
1258 // Set terminal output option according to conf->browser.
1259 if (GETPOST('dol_hide_leftmenu', 'int') || !empty($_SESSION['dol_hide_leftmenu'])) {
1260  $conf->dol_hide_leftmenu = 1;
1261 }
1262 if (GETPOST('dol_hide_topmenu', 'int') || !empty($_SESSION['dol_hide_topmenu'])) {
1263  $conf->dol_hide_topmenu = 1;
1264 }
1265 if (GETPOST('dol_optimize_smallscreen', 'int') || !empty($_SESSION['dol_optimize_smallscreen'])) {
1266  $conf->dol_optimize_smallscreen = 1;
1267 }
1268 if (GETPOST('dol_no_mouse_hover', 'int') || !empty($_SESSION['dol_no_mouse_hover'])) {
1269  $conf->dol_no_mouse_hover = 1;
1270 }
1271 if (GETPOST('dol_use_jmobile', 'int') || !empty($_SESSION['dol_use_jmobile'])) {
1272  $conf->dol_use_jmobile = 1;
1273 }
1274 // If not on Desktop
1275 if (!empty($conf->browser->layout) && $conf->browser->layout != 'classic') {
1276  $conf->dol_no_mouse_hover = 1;
1277 }
1278 
1279 // If on smartphone or optmized for small screen
1280 if ((!empty($conf->browser->layout) && $conf->browser->layout == 'phone')
1281  || (!empty($_SESSION['dol_screenwidth']) && $_SESSION['dol_screenwidth'] < 400)
1282  || (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 400
1283  || !empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1284 ) {
1285  $conf->dol_optimize_smallscreen = 1;
1286 
1287  if (isset($conf->global->PRODUIT_DESC_IN_FORM) && $conf->global->PRODUIT_DESC_IN_FORM == 1) {
1288  $conf->global->PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE = 0;
1289  }
1290 }
1291 // Replace themes bugged with jmobile with eldy
1292 if (!empty($conf->dol_use_jmobile) && in_array($conf->theme, array('bureau2crea', 'cameleo', 'amarok'))) {
1293  $conf->theme = 'eldy';
1294  $conf->css = "/theme/".$conf->theme."/style.css.php";
1295 }
1296 
1297 if (!defined('NOREQUIRETRAN')) {
1298  if (!GETPOST('lang', 'aZ09')) { // If language was not forced on URL
1299  // If user has chosen its own language
1300  if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1301  // If different than current language
1302  //print ">>>".$langs->getDefaultLang()."-".$user->conf->MAIN_LANG_DEFAULT;
1303  if ($langs->getDefaultLang() != $user->conf->MAIN_LANG_DEFAULT) {
1304  $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1305  }
1306  }
1307  }
1308 }
1309 
1310 if (!defined('NOLOGIN')) {
1311  // If the login is not recovered, it is identified with an account that does not exist.
1312  // Hacking attempt?
1313  if (!$user->login) {
1314  accessforbidden();
1315  }
1316 
1317  // Check if user is active
1318  if ($user->statut < 1) {
1319  // If not active, we refuse the user
1320  $langs->loadLangs(array("errors", "other"));
1321  dol_syslog("Authentication KO as login is disabled", LOG_NOTICE);
1322  accessforbidden("ErrorLoginDisabled");
1323  }
1324 
1325  // Load permissions
1326  $user->getrights();
1327 }
1328 
1329 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);
1330 //Another call for easy debugg
1331 //dol_syslog("Access to ".$_SERVER["PHP_SELF"].' '.$_SERVER["HTTP_REFERER"].' GET='.join(',',array_keys($_GET)).'->'.join(',',$_GET).' POST:'.join(',',array_keys($_POST)).'->'.join(',',$_POST));
1332 
1333 // Load main languages files
1334 if (!defined('NOREQUIRETRAN')) {
1335  // Load translation files required by page
1336  $langs->loadLangs(array('main', 'dict'));
1337 }
1338 
1339 // Define some constants used for style of arrays
1340 $bc = array(0=>'class="impair"', 1=>'class="pair"');
1341 $bcdd = array(0=>'class="drag drop oddeven"', 1=>'class="drag drop oddeven"');
1342 $bcnd = array(0=>'class="nodrag nodrop nohover"', 1=>'class="nodrag nodrop nohoverpair"'); // Used for tr to add new lines
1343 $bctag = array(0=>'class="impair tagtr"', 1=>'class="pair tagtr"');
1344 
1345 // Define messages variables
1346 $mesg = ''; $warning = ''; $error = 0;
1347 // deprecated, see setEventMessages() and dol_htmloutput_events()
1348 $mesgs = array(); $warnings = array(); $errors = array();
1349 
1350 // Constants used to defined number of lines in textarea
1351 if (empty($conf->browser->firefox)) {
1352  define('ROWS_1', 1);
1353  define('ROWS_2', 2);
1354  define('ROWS_3', 3);
1355  define('ROWS_4', 4);
1356  define('ROWS_5', 5);
1357  define('ROWS_6', 6);
1358  define('ROWS_7', 7);
1359  define('ROWS_8', 8);
1360  define('ROWS_9', 9);
1361 } else {
1362  define('ROWS_1', 0);
1363  define('ROWS_2', 1);
1364  define('ROWS_3', 2);
1365  define('ROWS_4', 3);
1366  define('ROWS_5', 4);
1367  define('ROWS_6', 5);
1368  define('ROWS_7', 6);
1369  define('ROWS_8', 7);
1370  define('ROWS_9', 8);
1371 }
1372 
1373 $heightforframes = 50;
1374 
1375 // Init menu manager
1376 if (!defined('NOREQUIREMENU')) {
1377  if (empty($user->socid)) { // If internal user or not defined
1378  $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);
1379  } else {
1380  // If external user
1381  $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);
1382  }
1383 
1384  // Load the menu manager (only if not already done)
1385  $file_menu = $conf->standard_menu;
1386  if (GETPOST('menu', 'alpha')) {
1387  $file_menu = GETPOST('menu', 'alpha'); // example: menu=eldy_menu.php
1388  }
1389  if (!class_exists('MenuManager')) {
1390  $menufound = 0;
1391  $dirmenus = array_merge(array("/core/menus/"), (array) $conf->modules_parts['menus']);
1392  foreach ($dirmenus as $dirmenu) {
1393  $menufound = dol_include_once($dirmenu."standard/".$file_menu);
1394  if (class_exists('MenuManager')) {
1395  break;
1396  }
1397  }
1398  if (!class_exists('MenuManager')) { // If failed to include, we try with standard eldy_menu.php
1399  dol_syslog("You define a menu manager '".$file_menu."' that can not be loaded.", LOG_WARNING);
1400  $file_menu = 'eldy_menu.php';
1401  include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".$file_menu;
1402  }
1403  }
1404  $menumanager = new MenuManager($db, empty($user->socid) ? 0 : 1);
1405  $menumanager->loadMenu();
1406 }
1407 
1408 if (!empty(GETPOST('seteventmessages', 'alpha'))) {
1409  $message = GETPOST('seteventmessages', 'alpha');
1410  $messages = explode(',', $message);
1411  foreach ($messages as $key => $msg) {
1412  $tmp = explode(':', $msg);
1413  setEventMessages($tmp[0], null, !empty($tmp[1]) ? $tmp[1] : 'mesgs');
1414  }
1415 }
1416 
1417 // Functions
1418 
1419 if (!function_exists("llxHeader")) {
1440  function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
1441  {
1442  global $conf, $hookmanager;
1443 
1444  // html header
1445  top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
1446 
1447  $tmpcsstouse = 'sidebar-collapse'.($morecssonbody ? ' '.$morecssonbody : '');
1448  // If theme MD and classic layer, we open the menulayer by default.
1449  if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1450  global $mainmenu;
1451  if ($mainmenu != 'website') {
1452  $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
1453  }
1454  }
1455 
1456  if (!empty($conf->global->MAIN_OPTIMIZEFORCOLORBLIND)) {
1457  $tmpcsstouse .= ' colorblind-'.strip_tags($conf->global->MAIN_OPTIMIZEFORCOLORBLIND);
1458  }
1459 
1460  print '<body id="mainbody" class="'.$tmpcsstouse.'">'."\n";
1461 
1462  $parameters = array('help_url' => $help_url);
1463  $reshook = $hookmanager->executeHooks('changeHelpURL', $parameters);
1464  if ($reshook > 0) {
1465  $help_url = $hookmanager->resPrint;
1466  }
1467 
1468  // top menu and left menu area
1469  if ((empty($conf->dol_hide_topmenu) || GETPOST('dol_invisible_topmenu', 'int')) && !GETPOST('dol_openinpopup', 'aZ09')) {
1470  top_menu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
1471  }
1472 
1473  if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1474  left_menu(array(), $help_url, '', '', 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
1475  }
1476 
1477  // main area
1478  if ($replacemainareaby) {
1479  print $replacemainareaby;
1480  return;
1481  }
1482  main_area($title);
1483  }
1484 }
1485 
1486 
1494 function top_httphead($contenttype = 'text/html', $forcenocache = 0)
1495 {
1496  global $db, $conf, $hookmanager;
1497 
1498  if ($contenttype == 'text/html') {
1499  header("Content-Type: text/html; charset=".$conf->file->character_set_client);
1500  } else {
1501  header("Content-Type: ".$contenttype);
1502  }
1503 
1504  // Security options
1505 
1506  // X-Content-Type-Options
1507  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)
1508 
1509  // X-Frame-Options
1510  if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
1511  header("X-Frame-Options: SAMEORIGIN"); // Frames allowed only if on same domain (stop some XSS attacks)
1512  } else {
1513  header("X-Frame-Options: ALLOWALL");
1514  }
1515 
1516  // X-XSS-Protection
1517  //header("X-XSS-Protection: 1"); // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
1518 
1519  // Content-Security-Policy-Report-Only
1520  if (!defined('MAIN_SECURITY_FORCECSPRO')) {
1521  // If CSP not forced from the page
1522 
1523  // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1524  // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1525  // 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: *;
1526  // For example, to restrict everything to itself except img that can be on other servers:
1527  // default-src 'self'; img-src *;
1528  // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1529  // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1530  //
1531  // $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;";
1532  // $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';";
1533  $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
1534 
1535  if (!is_object($hookmanager)) {
1536  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1537  $hookmanager = new HookManager($db);
1538  }
1539  $hookmanager->initHooks(array("main"));
1540 
1541  $parameters = array('contentsecuritypolicy'=>$contentsecuritypolicy, 'mode'=>'reportonly');
1542  $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1543  if ($result > 0) {
1544  $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1545  } else {
1546  $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1547  }
1548 
1549  if (!empty($contentsecuritypolicy)) {
1550  header("Content-Security-Policy-Report-Only: ".$contentsecuritypolicy);
1551  }
1552  } else {
1553  header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSPRO'));
1554  }
1555 
1556  // Content-Security-Policy
1557  if (!defined('MAIN_SECURITY_FORCECSP')) {
1558  // If CSP not forced from the page
1559 
1560  // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1561  // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1562  // 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: *;
1563  // For example, to restrict everything to itself except img that can be on other servers:
1564  // default-src 'self'; img-src *;
1565  // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1566  // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1567  //
1568  // $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;";
1569  // $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';";
1570  $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
1571 
1572  if (!is_object($hookmanager)) {
1573  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1574  $hookmanager = new HookManager($db);
1575  }
1576  $hookmanager->initHooks(array("main"));
1577 
1578  $parameters = array('contentsecuritypolicy'=>$contentsecuritypolicy, 'mode'=>'active');
1579  $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1580  if ($result > 0) {
1581  $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1582  } else {
1583  $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1584  }
1585 
1586  if (!empty($contentsecuritypolicy)) {
1587  header("Content-Security-Policy: ".$contentsecuritypolicy);
1588  }
1589  } else {
1590  header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSP'));
1591  }
1592 
1593  // Referrer-Policy
1594  // Say if we must provide the referrer when we jump onto another web page.
1595  // 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.
1596  if (!defined('MAIN_SECURITY_FORCERP')) {
1597  $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
1598 
1599  header("Referrer-Policy: ".$referrerpolicy);
1600  }
1601 
1602  if ($forcenocache) {
1603  header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
1604  }
1605 
1606  // No need to add this token in header, we use instead the one into the forms.
1607  //header("anti-csrf-token: ".newToken());
1608 }
1609 
1625 function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
1626 {
1627  global $db, $conf, $langs, $user, $mysoc, $hookmanager;
1628 
1629  top_httphead();
1630 
1631  if (empty($conf->css)) {
1632  $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
1633  }
1634 
1635  print '<!doctype html>'."\n";
1636 
1637  print '<html lang="'.substr($langs->defaultlang, 0, 2).'">'."\n";
1638 
1639  //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
1640  if (empty($disablehead)) {
1641  if (!is_object($hookmanager)) {
1642  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1643  $hookmanager = new HookManager($db);
1644  }
1645  $hookmanager->initHooks(array("main"));
1646 
1647  $ext = 'layout='.$conf->browser->layout.'&amp;version='.urlencode(DOL_VERSION);
1648 
1649  print "<head>\n";
1650 
1651  if (GETPOST('dol_basehref', 'alpha')) {
1652  print '<base href="'.dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')).'">'."\n";
1653  }
1654 
1655  // Displays meta
1656  print '<meta charset="utf-8">'."\n";
1657  print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
1658  print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
1659  print '<meta name="author" content="Dolibarr Development Team">'."\n";
1660  print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
1661  print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
1662  if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
1663  print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
1664  }
1665  // Favicon
1666  $favicon = DOL_URL_ROOT.'/theme/dolibarr_256x256_color.png';
1667  if (!empty($mysoc->logo_squarred_mini)) {
1668  $favicon = DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_mini);
1669  }
1670  if (getDolGlobalString('MAIN_FAVICON_URL')) {
1671  $favicon = getDolGlobalString('MAIN_FAVICON_URL');
1672  }
1673  if (empty($conf->dol_use_jmobile)) {
1674  print '<link rel="shortcut icon" type="image/x-icon" href="'.$favicon.'"/>'."\n"; // Not required into an Android webview
1675  }
1676 
1677  //if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print '<link rel="top" title="'.$langs->trans("Home").'" href="'.(DOL_URL_ROOT?DOL_URL_ROOT:'/').'">'."\n";
1678  //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";
1679  //if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) print '<link rel="author" title="Dolibarr Development Team" href="https://www.dolibarr.org">'."\n";
1680 
1681  // Mobile appli like icon
1682  $manifest = DOL_URL_ROOT.'/theme/'.$conf->theme.'/manifest.json.php';
1683  if (!empty($manifest)) {
1684  print '<link rel="manifest" href="'.$manifest.'" />'."\n";
1685  }
1686 
1687  if (!empty($conf->global->THEME_ELDY_TOPMENU_BACK1)) {
1688  // TODO: use auto theme color switch
1689  print '<meta name="theme-color" content="rgb('.$conf->global->THEME_ELDY_TOPMENU_BACK1.')">'."\n";
1690  }
1691 
1692  // Auto refresh page
1693  if (GETPOST('autorefresh', 'int') > 0) {
1694  print '<meta http-equiv="refresh" content="'.GETPOST('autorefresh', 'int').'">';
1695  }
1696 
1697  // Displays title
1698  $appli = constant('DOL_APPLICATION_TITLE');
1699  if (!empty($conf->global->MAIN_APPLICATION_TITLE)) {
1700  $appli = $conf->global->MAIN_APPLICATION_TITLE;
1701  }
1702 
1703  print '<title>';
1704  $titletoshow = '';
1705  if ($title && !empty($conf->global->MAIN_HTML_TITLE) && preg_match('/noapp/', $conf->global->MAIN_HTML_TITLE)) {
1706  $titletoshow = dol_htmlentities($title);
1707  } elseif ($title) {
1708  $titletoshow = dol_htmlentities($appli.' - '.$title);
1709  } else {
1710  $titletoshow = dol_htmlentities($appli);
1711  }
1712 
1713  $parameters = array('title'=>$titletoshow);
1714  $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
1715  if ($result > 0) {
1716  $titletoshow = $hookmanager->resPrint; // Replace Title to show
1717  } else {
1718  $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
1719  }
1720 
1721  print $titletoshow;
1722  print '</title>';
1723 
1724  print "\n";
1725 
1726  if (GETPOST('version', 'int')) {
1727  $ext = 'version='.GETPOST('version', 'int'); // usefull to force no cache on css/js
1728  }
1729  // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
1730  if (GETPOST('dol_resetcache')) {
1731  dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", ((int) $conf->global->MAIN_IHM_PARAMS_REV) + 1, 'chaine', 0, '', $conf->entity);
1732  }
1733 
1734  $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;
1735 
1736  $themeparam .= ($ext ? '&amp;'.$ext : '').'&amp;revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV");
1737  if (GETPOSTISSET('dol_hide_topmenu')) {
1738  $themeparam .= '&amp;dol_hide_topmenu='.GETPOST('dol_hide_topmenu', 'int');
1739  }
1740  if (GETPOSTISSET('dol_hide_leftmenu')) {
1741  $themeparam .= '&amp;dol_hide_leftmenu='.GETPOST('dol_hide_leftmenu', 'int');
1742  }
1743  if (GETPOSTISSET('dol_optimize_smallscreen')) {
1744  $themeparam .= '&amp;dol_optimize_smallscreen='.GETPOST('dol_optimize_smallscreen', 'int');
1745  }
1746  if (GETPOSTISSET('dol_no_mouse_hover')) {
1747  $themeparam .= '&amp;dol_no_mouse_hover='.GETPOST('dol_no_mouse_hover', 'int');
1748  }
1749  if (GETPOSTISSET('dol_use_jmobile')) {
1750  $themeparam .= '&amp;dol_use_jmobile='.GETPOST('dol_use_jmobile', 'int'); $conf->dol_use_jmobile = GETPOST('dol_use_jmobile', 'int');
1751  }
1752  if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
1753  $themeparam .= '&amp;THEME_DARKMODEENABLED='.GETPOST('THEME_DARKMODEENABLED', 'int');
1754  }
1755  if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
1756  $themeparam .= '&amp;THEME_SATURATE_RATIO='.GETPOST('THEME_SATURATE_RATIO', 'int');
1757  }
1758 
1759  if (!empty($conf->global->MAIN_ENABLE_FONT_ROBOTO)) {
1760  print '<link rel="preconnect" href="https://fonts.gstatic.com">'."\n";
1761  print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">'."\n";
1762  }
1763 
1764  if (!defined('DISABLE_JQUERY') && !$disablejs && $conf->use_javascript_ajax) {
1765  print '<!-- Includes CSS for JQuery (Ajax library) -->'."\n";
1766  $jquerytheme = 'base';
1767  if (!empty($conf->global->MAIN_USE_JQUERY_THEME)) {
1768  $jquerytheme = $conf->global->MAIN_USE_JQUERY_THEME;
1769  }
1770  if (constant('JS_JQUERY_UI')) {
1771  print '<link rel="stylesheet" type="text/css" href="'.JS_JQUERY_UI.'css/'.$jquerytheme.'/jquery-ui.min.css'.($ext ? '?'.$ext : '').'">'."\n"; // Forced JQuery
1772  } else {
1773  print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/css/'.$jquerytheme.'/jquery-ui.css'.($ext ? '?'.$ext : '').'">'."\n"; // JQuery
1774  }
1775  if (!defined('DISABLE_JQUERY_JNOTIFY')) {
1776  print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css'.($ext ? '?'.$ext : '').'">'."\n"; // JNotify
1777  }
1778  if (!defined('DISABLE_SELECT2') && (!empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT'))) { // jQuery plugin "mutiselect", "multiple-select", "select2"...
1779  $tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
1780  print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/includes/jquery/plugins/'.$tmpplugin.'/dist/css/'.$tmpplugin.'.css'.($ext ? '?'.$ext : '').'">'."\n";
1781  }
1782  }
1783 
1784  if (!defined('DISABLE_FONT_AWSOME')) {
1785  print '<!-- Includes CSS for font awesome -->'."\n";
1786  print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/theme/common/fontawesome-5/css/all.min.css'.($ext ? '?'.$ext : '').'">'."\n";
1787  print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/theme/common/fontawesome-5/css/v4-shims.min.css'.($ext ? '?'.$ext : '').'">'."\n";
1788  }
1789 
1790  print '<!-- Includes CSS for Dolibarr theme -->'."\n";
1791  // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
1792  $themepath = dol_buildpath($conf->css, 1);
1793  $themesubdir = '';
1794  if (!empty($conf->modules_parts['theme'])) { // This slow down
1795  foreach ($conf->modules_parts['theme'] as $reldir) {
1796  if (file_exists(dol_buildpath($reldir.$conf->css, 0))) {
1797  $themepath = dol_buildpath($reldir.$conf->css, 1);
1798  $themesubdir = $reldir;
1799  break;
1800  }
1801  }
1802  }
1803 
1804  //print 'themepath='.$themepath.' themeparam='.$themeparam;exit;
1805  print '<link rel="stylesheet" type="text/css" href="'.$themepath.$themeparam.'">'."\n";
1806  if (!empty($conf->global->MAIN_FIX_FLASH_ON_CHROME)) {
1807  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";
1808  }
1809 
1810  // CSS forced by modules (relative url starting with /)
1811  if (!empty($conf->modules_parts['css'])) {
1812  $arraycss = (array) $conf->modules_parts['css'];
1813  foreach ($arraycss as $modcss => $filescss) {
1814  $filescss = (array) $filescss; // To be sure filecss is an array
1815  foreach ($filescss as $cssfile) {
1816  if (empty($cssfile)) {
1817  dol_syslog("Warning: module ".$modcss." declared a css path file into its descriptor that is empty.", LOG_WARNING);
1818  }
1819  // cssfile is a relative path
1820  $urlforcss = dol_buildpath($cssfile, 1);
1821  if ($urlforcss && $urlforcss != '/') {
1822  print '<!-- Includes CSS added by module '.$modcss.' -->'."\n".'<link rel="stylesheet" type="text/css" href="'.$urlforcss;
1823  // 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.
1824  if (!preg_match('/\.css$/i', $cssfile)) {
1825  print $themeparam;
1826  }
1827  print '">'."\n";
1828  } else {
1829  dol_syslog("Warning: module ".$modcss." declared a css path file for a file we can't find.", LOG_WARNING);
1830  }
1831  }
1832  }
1833  }
1834  // CSS forced by page in top_htmlhead call (relative url starting with /)
1835  if (is_array($arrayofcss)) {
1836  foreach ($arrayofcss as $cssfile) {
1837  if (preg_match('/^(http|\/\/)/i', $cssfile)) {
1838  $urltofile = $cssfile;
1839  } else {
1840  $urltofile = dol_buildpath($cssfile, 1);
1841  }
1842  print '<!-- Includes CSS added by page -->'."\n".'<link rel="stylesheet" type="text/css" title="default" href="'.$urltofile;
1843  // 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.
1844  if (!preg_match('/\.css$/i', $cssfile)) {
1845  print $themeparam;
1846  }
1847  print '">'."\n";
1848  }
1849  }
1850 
1851  // Custom CSS
1852  if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
1853  // If a custom CSS was set, we add link to the custom css php file
1854  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";
1855  }
1856 
1857  // Output standard javascript links
1858  if (!defined('DISABLE_JQUERY') && !$disablejs && !empty($conf->use_javascript_ajax)) {
1859  // JQuery. Must be before other includes
1860  print '<!-- Includes JS for JQuery -->'."\n";
1861  if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
1862  print '<script nonce="'.getNonce().'" src="'.JS_JQUERY.'jquery.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1863  } else {
1864  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/js/jquery.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1865  }
1866  if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
1867  print '<script nonce="'.getNonce().'" src="'.JS_JQUERY_UI.'jquery-ui.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1868  } else {
1869  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/js/jquery-ui.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1870  }
1871  // jQuery jnotify
1872  if (empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && !defined('DISABLE_JQUERY_JNOTIFY')) {
1873  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jnotify/jquery.jnotify.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1874  }
1875  // Table drag and drop lines
1876  if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
1877  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/tablednd/jquery.tablednd.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1878  }
1879  // Chart
1880  if (empty($disableforlogin) && (empty($conf->global->MAIN_JS_GRAPH) || $conf->global->MAIN_JS_GRAPH == 'chart') && !defined('DISABLE_JS_GRAPH')) {
1881  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/nnnick/chartjs/dist/chart.min.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1882  }
1883 
1884  // jQuery jeditable for Edit In Place features
1885  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !defined('DISABLE_JQUERY_JEDITABLE')) {
1886  print '<!-- JS to manage editInPlace feature -->'."\n";
1887  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1888  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1889  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1890  print '<script>'."\n";
1891  print 'var urlSaveInPlace = \''.DOL_URL_ROOT.'/core/ajax/saveinplace.php\';'."\n";
1892  print 'var urlLoadInPlace = \''.DOL_URL_ROOT.'/core/ajax/loadinplace.php\';'."\n";
1893  print 'var tooltipInPlace = \''.$langs->transnoentities('ClickToEdit').'\';'."\n"; // Added in title attribute of span
1894  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 ?
1895  print 'var cancelInPlace = \''.$langs->trans("Cancel").'\';'."\n";
1896  print 'var submitInPlace = \''.$langs->trans('Ok').'\';'."\n";
1897  print 'var indicatorInPlace = \'<img src="'.DOL_URL_ROOT."/theme/".$conf->theme."/img/working.gif".'">\';'."\n";
1898  print 'var withInPlace = 300;'; // width in pixel for default string edit
1899  print '</script>'."\n";
1900  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/editinplace.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1901  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1902  }
1903  // jQuery Timepicker
1904  if (!empty($conf->global->MAIN_USE_JQUERY_TIMEPICKER) || defined('REQUIRE_JQUERY_TIMEPICKER')) {
1905  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/timepicker/jquery-ui-timepicker-addon.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1906  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/timepicker.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
1907  }
1908  if (!defined('DISABLE_SELECT2') && (!empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT'))) {
1909  // jQuery plugin "mutiselect", "multiple-select", "select2", ...
1910  $tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ?constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
1911  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
1912  }
1913  if (!defined('DISABLE_MULTISELECT')) { // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
1914  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/multiselect/jquery.multi-select.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1915  }
1916  }
1917 
1918  if (!$disablejs && !empty($conf->use_javascript_ajax)) {
1919  // CKEditor
1920  if (empty($disableforlogin) && (isModEnabled('fckeditor') && (empty($conf->global->FCKEDITOR_EDITORNAME) || $conf->global->FCKEDITOR_EDITORNAME == 'ckeditor') && !defined('DISABLE_CKEDITOR')) || defined('FORCE_CKEDITOR')) {
1921  print '<!-- Includes JS for CKEditor -->'."\n";
1922  $pathckeditor = DOL_URL_ROOT.'/includes/ckeditor/ckeditor/';
1923  $jsckeditor = 'ckeditor.js';
1924  if (constant('JS_CKEDITOR')) {
1925  // To use external ckeditor 4 js lib
1926  $pathckeditor = constant('JS_CKEDITOR');
1927  }
1928  print '<script nonce="'.getNonce().'">';
1929  print '/* enable ckeditor by main.inc.php */';
1930  print 'var CKEDITOR_BASEPATH = \''.dol_escape_js($pathckeditor).'\';'."\n";
1931  print 'var ckeditorConfig = \''.dol_escape_js(dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js'.($ext ? '?'.$ext : ''), 1)).'\';'."\n"; // $themesubdir='' in standard usage
1932  print 'var ckeditorFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
1933  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";
1934  print '</script>'."\n";
1935  print '<script src="'.$pathckeditor.$jsckeditor.($ext ? '?'.$ext : '').'"></script>'."\n";
1936  print '<script>';
1937  if (GETPOST('mode', 'aZ09') == 'Full_inline') {
1938  print 'CKEDITOR.disableAutoInline = false;'."\n";
1939  } else {
1940  print 'CKEDITOR.disableAutoInline = true;'."\n";
1941  }
1942  print '</script>'."\n";
1943  }
1944 
1945  // 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).
1946  if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
1947  $enablebrowsernotif = false;
1948  if (isModEnabled('agenda') && !empty($conf->global->AGENDA_REMINDER_BROWSER)) {
1949  $enablebrowsernotif = true;
1950  }
1951  if ($conf->browser->layout == 'phone') {
1952  $enablebrowsernotif = false;
1953  }
1954  if ($enablebrowsernotif) {
1955  print '<!-- Includes JS of Dolibarr (browser layout = '.$conf->browser->layout.')-->'."\n";
1956  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_notification.js.php'.($ext ? '?'.$ext : '').'"></script>'."\n";
1957  }
1958  }
1959 
1960  // Global js function
1961  print '<!-- Includes JS of Dolibarr -->'."\n";
1962  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_head.js.php?lang='.$langs->defaultlang.($ext ? '&amp;'.$ext : '').'"></script>'."\n";
1963 
1964  // JS forced by modules (relative url starting with /)
1965  if (!empty($conf->modules_parts['js'])) { // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
1966  $arrayjs = (array) $conf->modules_parts['js'];
1967  foreach ($arrayjs as $modjs => $filesjs) {
1968  $filesjs = (array) $filesjs; // To be sure filejs is an array
1969  foreach ($filesjs as $jsfile) {
1970  // jsfile is a relative path
1971  $urlforjs = dol_buildpath($jsfile, 1);
1972  if ($urlforjs && $urlforjs != '/') {
1973  print '<!-- Include JS added by module '.$modjs.'-->'."\n";
1974  print '<script nonce="'.getNonce().'" src="'.$urlforjs.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
1975  } else {
1976  dol_syslog("Warning: module ".$modjs." declared a js path file for a file we can't find.", LOG_WARNING);
1977  }
1978  }
1979  }
1980  }
1981  // JS forced by page in top_htmlhead (relative url starting with /)
1982  if (is_array($arrayofjs)) {
1983  print '<!-- Includes JS added by page -->'."\n";
1984  foreach ($arrayofjs as $jsfile) {
1985  if (preg_match('/^(http|\/\/)/i', $jsfile)) {
1986  print '<script nonce="'.getNonce().'" src="'.$jsfile.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
1987  } else {
1988  print '<script nonce="'.getNonce().'" src="'.dol_buildpath($jsfile, 1).((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
1989  }
1990  }
1991  }
1992  }
1993 
1994  //If you want to load custom javascript file from your selected theme directory
1995  if (!empty($conf->global->ALLOW_THEME_JS)) {
1996  $theme_js = dol_buildpath('/theme/'.$conf->theme.'/'.$conf->theme.'.js', 0);
1997  if (file_exists($theme_js)) {
1998  print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/'.$conf->theme.'.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
1999  }
2000  }
2001 
2002  if (!empty($head)) {
2003  print $head."\n";
2004  }
2005  if (!empty($conf->global->MAIN_HTML_HEADER)) {
2006  print $conf->global->MAIN_HTML_HEADER."\n";
2007  }
2008 
2009  $parameters = array();
2010  $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
2011  print $hookmanager->resPrint; // Replace Title to show
2012 
2013  print "</head>\n\n";
2014  }
2015 
2016  $conf->headerdone = 1; // To tell header was output
2017 }
2018 
2019 
2036 function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $helppagename = '')
2037 {
2038  global $user, $conf, $langs, $db;
2039  global $dolibarr_main_authentication, $dolibarr_main_demo;
2040  global $hookmanager, $menumanager;
2041 
2042  $searchform = '';
2043 
2044  // Instantiate hooks for external modules
2045  $hookmanager->initHooks(array('toprightmenu'));
2046 
2047  $toprightmenu = '';
2048 
2049  // For backward compatibility with old modules
2050  if (empty($conf->headerdone)) {
2051  $disablenofollow = 0;
2052  top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
2053  print '<body id="mainbody">';
2054  }
2055 
2056  /*
2057  * Top menu
2058  */
2059  if ((empty($conf->dol_hide_topmenu) || GETPOST('dol_invisible_topmenu', 'int')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
2060  if (!isset($form) || !is_object($form)) {
2061  include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
2062  $form = new Form($db);
2063  }
2064 
2065  print "\n".'<!-- Start top horizontal -->'."\n";
2066 
2067  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.
2068 
2069  // Show menu entries
2070  print '<div id="tmenu_tooltip'.(empty($conf->global->MAIN_MENU_INVERT) ? '' : 'invert').'" class="tmenu">'."\n";
2071  $menumanager->atarget = $target;
2072  $menumanager->showmenu('top', array('searchform'=>$searchform)); // This contains a \n
2073  print "</div>\n";
2074 
2075  // Define link to login card
2076  $appli = constant('DOL_APPLICATION_TITLE');
2077  if (!empty($conf->global->MAIN_APPLICATION_TITLE)) {
2078  $appli = $conf->global->MAIN_APPLICATION_TITLE;
2079  if (preg_match('/\d\.\d/', $appli)) {
2080  if (!preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) {
2081  $appli .= " (".DOL_VERSION.")"; // If new title contains a version that is different than core
2082  }
2083  } else {
2084  $appli .= " ".DOL_VERSION;
2085  }
2086  } else {
2087  $appli .= " ".DOL_VERSION;
2088  }
2089 
2090  if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
2091  $appli .= "<br>".$langs->trans("LevelOfFeature").': '.getDolGlobalInt('MAIN_FEATURES_LEVEL');
2092  }
2093 
2094  $logouttext = '';
2095  $logouthtmltext = '';
2096  if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2097  //$logouthtmltext=$appli.'<br>';
2098  if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') {
2099  $logouthtmltext .= $langs->trans("Logout").'<br>';
2100 
2101  $stringforfirstkey = $langs->trans("KeyboardShortcut");
2102  if ($conf->browser->name == 'chrome') {
2103  $stringforfirstkey .= ' ALT +';
2104  } elseif ($conf->browser->name == 'firefox') {
2105  $stringforfirstkey .= ' ALT + SHIFT +';
2106  } else {
2107  $stringforfirstkey .= ' CTL +';
2108  }
2109 
2110  $logouttext .= '<a accesskey="l" href="'.DOL_URL_ROOT.'/user/logout.php?token='.newToken().'">';
2111  $logouttext .= img_picto($langs->trans('Logout').' ('.$stringforfirstkey.' l)', 'sign-out', '', false, 0, 0, '', 'atoplogin valignmiddle');
2112  $logouttext .= '</a>';
2113  } else {
2114  $logouthtmltext .= $langs->trans("NoLogoutProcessWithAuthMode", $_SESSION["dol_authmode"]);
2115  $logouttext .= img_picto($langs->trans('Logout').' ('.$stringforfirstkey.' l)', 'sign-out', '', false, 0, 0, '', 'atoplogin valignmiddle opacitymedium');
2116  }
2117  }
2118 
2119  print '<div class="login_block usedropdown">'."\n";
2120 
2121  $toprightmenu .= '<div class="login_block_other">';
2122 
2123  // Execute hook printTopRightMenu (hooks should output string like '<div class="login"><a href="">mylink</a></div>')
2124  $parameters = array();
2125  $result = $hookmanager->executeHooks('printTopRightMenu', $parameters); // Note that $action and $object may have been modified by some hooks
2126  if (is_numeric($result)) {
2127  if ($result == 0) {
2128  $toprightmenu .= $hookmanager->resPrint; // add
2129  } else {
2130  $toprightmenu = $hookmanager->resPrint; // replace
2131  }
2132  } else {
2133  $toprightmenu .= $result; // For backward compatibility
2134  }
2135 
2136  // Link to module builder
2137  if (isModEnabled('modulebuilder')) {
2138  $text = '<a href="'.DOL_URL_ROOT.'/modulebuilder/index.php?mainmenu=home&leftmenu=admintools" target="modulebuilder">';
2139  //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
2140  $text .= '<span class="fa fa-bug atoplogin valignmiddle"></span>';
2141  $text .= '</a>';
2142  $toprightmenu .= $form->textwithtooltip('', $langs->trans("ModuleBuilder"), 2, 1, $text, 'login_block_elem', 2);
2143  }
2144 
2145  // Link to print main content area
2146  if (empty($conf->global->MAIN_PRINT_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2147  $qs = dol_escape_htmltag($_SERVER["QUERY_STRING"]);
2148 
2149  if (isset($_POST) && is_array($_POST)) {
2150  foreach ($_POST as $key => $value) {
2151  if ($key !== 'action' && $key !== 'password' && !is_array($value)) {
2152  $qs .= '&'.$key.'='.urlencode($value);
2153  }
2154  }
2155  }
2156  $qs .= (($qs && $morequerystring) ? '&' : '').$morequerystring;
2157  $text = '<a href="'.dol_escape_htmltag($_SERVER["PHP_SELF"]).'?'.$qs.($qs ? '&' : '').'optioncss=print" target="_blank" rel="noopener noreferrer">';
2158  //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
2159  $text .= '<span class="fa fa-print atoplogin valignmiddle"></span>';
2160  $text .= '</a>';
2161  $toprightmenu .= $form->textwithtooltip('', $langs->trans("PrintContentArea"), 2, 1, $text, 'login_block_elem', 2);
2162  }
2163 
2164  // Link to Dolibarr wiki pages
2165  if (empty($conf->global->MAIN_HELP_DISABLELINK) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2166  $langs->load("help");
2167 
2168  $helpbaseurl = '';
2169  $helppage = '';
2170  $mode = '';
2171  $helppresent = '';
2172 
2173  if (empty($helppagename)) {
2174  $helppagename = 'EN:User_documentation|FR:Documentation_utilisateur|ES:Documentación_usuarios|DE:Benutzerdokumentation';
2175  } else {
2176  $helppresent = 'helppresent';
2177  }
2178 
2179  // Get helpbaseurl, helppage and mode from helppagename and langs
2180  $arrayres = getHelpParamFor($helppagename, $langs);
2181  $helpbaseurl = $arrayres['helpbaseurl'];
2182  $helppage = $arrayres['helppage'];
2183  $mode = $arrayres['mode'];
2184 
2185  // Link to help pages
2186  if ($helpbaseurl && $helppage) {
2187  $text = '';
2188  $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage').', ';
2189  if ($mode == 'wiki') {
2190  $title .= '<br>'.img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans("PageWiki").' '.dol_escape_htmltag('"'.strtr($helppage, '_', ' ').'"');
2191  if ($helppresent) {
2192  $title .= ' <span class="opacitymedium">('.$langs->trans("DedicatedPageAvailable").')</span>';
2193  } else {
2194  $title .= ' <span class="opacitymedium">('.$langs->trans("HomePage").')</span>';
2195  }
2196  }
2197  $text .= '<a class="help" target="_blank" rel="noopener noreferrer" href="';
2198  if ($mode == 'wiki') {
2199  $text .= sprintf($helpbaseurl, urlencode(html_entity_decode($helppage)));
2200  } else {
2201  $text .= sprintf($helpbaseurl, $helppage);
2202  }
2203  $text .= '">';
2204  $text .= '<span class="fa fa-question-circle atoplogin valignmiddle'.($helppresent ? ' '.$helppresent : '').'"></span>';
2205  $text .= '<span class="fa fa-long-arrow-alt-up helppresentcircle'.($helppresent ? '' : ' unvisible').'"></span>';
2206  $text .= '</a>';
2207  $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2);
2208  }
2209 
2210  // Version
2211  if (!empty($conf->global->MAIN_SHOWDATABASENAMEINHELPPAGESLINK)) {
2212  $langs->load('admin');
2213  $appli .= '<br>'.$langs->trans("Database").': '.$db->database_name;
2214  }
2215  }
2216 
2217  if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2218  $text = '<span class="aversion"><span class="hideonsmartphone small">'.DOL_VERSION.'</span></span>';
2219  $toprightmenu .= $form->textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
2220  }
2221 
2222  // Logout link
2223  $toprightmenu .= $form->textwithtooltip('', $logouthtmltext, 2, 1, $logouttext, 'login_block_elem logout-btn', 2);
2224 
2225  $toprightmenu .= '</div>'; // end div class="login_block_other"
2226 
2227 
2228  // Add login user link
2229  $toprightmenu .= '<div class="login_block_user">';
2230 
2231  // Login name with photo and tooltip
2232  $mode = -1;
2233  $toprightmenu .= '<div class="inline-block nowrap"><div class="inline-block login_block_elem login_block_elem_name" style="padding: 0px;">';
2234 
2235  if (!empty($conf->global->MAIN_USE_TOP_MENU_SEARCH_DROPDOWN)) {
2236  // Add search dropdown
2237  $toprightmenu .= top_menu_search();
2238  }
2239 
2240  if (!empty($conf->global->MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN)) {
2241  // Add search dropdown
2242  $toprightmenu .= top_menu_quickadd();
2243  }
2244 
2245  // Add bookmark dropdown
2246  $toprightmenu .= top_menu_bookmark();
2247 
2248  // Add user dropdown
2249  $toprightmenu .= top_menu_user();
2250 
2251  $toprightmenu .= '</div></div>';
2252 
2253  $toprightmenu .= '</div>'."\n";
2254 
2255 
2256  print $toprightmenu;
2257 
2258  print "</div>\n"; // end div class="login_block"
2259 
2260  print '</header>';
2261  //print '<header class="header2">&nbsp;</header>';
2262 
2263  print '<div style="clear: both;"></div>';
2264  print "<!-- End top horizontal menu -->\n\n";
2265  }
2266 
2267  if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
2268  print '<!-- Begin div id-container --><div id="id-container" class="id-container">';
2269  }
2270 }
2271 
2272 
2280 function top_menu_user($hideloginname = 0, $urllogout = '')
2281 {
2282  global $langs, $conf, $db, $hookmanager, $user, $mysoc;
2283  global $dolibarr_main_authentication, $dolibarr_main_demo;
2284  global $menumanager;
2285 
2286  $langs->load('companies');
2287 
2288  $userImage = $userDropDownImage = '';
2289  if (!empty($user->photo)) {
2290  $userImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'photouserphoto userphoto', 'small', 0, 1);
2291  $userDropDownImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'dropdown-user-image', 'small', 0, 1);
2292  } else {
2293  $nophoto = '/public/theme/common/user_anonymous.png';
2294  if ($user->gender == 'man') {
2295  $nophoto = '/public/theme/common/user_man.png';
2296  }
2297  if ($user->gender == 'woman') {
2298  $nophoto = '/public/theme/common/user_woman.png';
2299  }
2300 
2301  $userImage = '<img class="photo photouserphoto userphoto" alt="No photo" src="'.DOL_URL_ROOT.$nophoto.'">';
2302  $userDropDownImage = '<img class="photo dropdown-user-image" alt="No photo" src="'.DOL_URL_ROOT.$nophoto.'">';
2303  }
2304 
2305  $dropdownBody = '';
2306  $dropdownBody .= '<span id="topmenulogincompanyinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowCompanyInfos").'</span>';
2307  $dropdownBody .= '<div id="topmenulogincompanyinfo" >';
2308 
2309  $dropdownBody .= '<br><b>'.$langs->trans("Company").'</b>: <span>'.dol_escape_htmltag($mysoc->name).'</span>';
2310  if ($langs->transcountry("ProfId1", $mysoc->country_code) != '-') {
2311  $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId1", $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_SIREN"), 1).'</span>';
2312  }
2313  if ($langs->transcountry("ProfId2", $mysoc->country_code) != '-') {
2314  $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId2", $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_SIRET"), 2).'</span>';
2315  }
2316  if ($langs->transcountry("ProfId3", $mysoc->country_code) != '-') {
2317  $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId3", $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_APE"), 3).'</span>';
2318  }
2319  if ($langs->transcountry("ProfId4", $mysoc->country_code) != '-') {
2320  $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId4", $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_RCS"), 4).'</span>';
2321  }
2322  if ($langs->transcountry("ProfId5", $mysoc->country_code) != '-') {
2323  $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId5", $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_PROFID5"), 5).'</span>';
2324  }
2325  if ($langs->transcountry("ProfId6", $mysoc->country_code) != '-') {
2326  $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId6", $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_PROFID6"), 6).'</span>';
2327  }
2328  $dropdownBody .= '<br><b>'.$langs->trans("VATIntraShort").'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT').'</span>';
2329  $dropdownBody .= '<br><b>'.$langs->trans("Country").'</b>: <span>'.($mysoc->country_code ? $langs->trans("Country".$mysoc->country_code) : '').'</span>';
2330  if (isModEnabled('multicurrency')) {
2331  $dropdownBody .= '<br><b>'.$langs->trans("Currency").'</b>: <span>'.$conf->currency.'</span>';
2332  }
2333  $dropdownBody .= '</div>';
2334 
2335  $dropdownBody .= '<br>';
2336  $dropdownBody .= '<span id="topmenuloginmoreinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowMoreInfos").'</span>';
2337  $dropdownBody .= '<div id="topmenuloginmoreinfo" >';
2338 
2339  // login infos
2340  if (!empty($user->admin)) {
2341  $dropdownBody .= '<br><b>'.$langs->trans("Administrator").'</b>: '.yn($user->admin);
2342  }
2343  if (!empty($user->socid)) { // Add thirdparty for external users
2344  $thirdpartystatic = new Societe($db);
2345  $thirdpartystatic->fetch($user->socid);
2346  $companylink = ' '.$thirdpartystatic->getNomUrl(2); // picto only of company
2347  $company = ' ('.$langs->trans("Company").': '.$thirdpartystatic->name.')';
2348  }
2349  $type = ($user->socid ? $langs->trans("External").$company : $langs->trans("Internal"));
2350  $dropdownBody .= '<br><b>'.$langs->trans("Type").':</b> '.$type;
2351  $dropdownBody .= '<br><b>'.$langs->trans("Status").'</b>: '.$user->getLibStatut(0);
2352  $dropdownBody .= '<br>';
2353 
2354  $dropdownBody .= '<br><u>'.$langs->trans("Session").'</u>';
2355  $dropdownBody .= '<br><b>'.$langs->trans("IPAddress").'</b>: '.dol_escape_htmltag($_SERVER["REMOTE_ADDR"]);
2356  if (!empty($conf->global->MAIN_MODULE_MULTICOMPANY)) {
2357  $dropdownBody .= '<br><b>'.$langs->trans("ConnectedOnMultiCompany").':</b> '.$conf->entity.' (user entity '.$user->entity.')';
2358  }
2359  $dropdownBody .= '<br><b>'.$langs->trans("AuthenticationMode").':</b> '.$_SESSION["dol_authmode"].(empty($dolibarr_main_demo) ? '' : ' (demo)');
2360  $dropdownBody .= '<br><b>'.$langs->trans("ConnectedSince").':</b> '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2361  $dropdownBody .= '<br><b>'.$langs->trans("PreviousConnexion").':</b> '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2362  $dropdownBody .= '<br><b>'.$langs->trans("CurrentTheme").':</b> '.$conf->theme;
2363  $dropdownBody .= '<br><b>'.$langs->trans("CurrentMenuManager").':</b> '.(isset($menumanager) ? $menumanager->name : 'unknown');
2364  $langFlag = picto_from_langcode($langs->getDefaultLang());
2365  $dropdownBody .= '<br><b>'.$langs->trans("CurrentUserLanguage").':</b> '.($langFlag ? $langFlag.' ' : '').$langs->getDefaultLang();
2366 
2367  $tz = (int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst'];
2368  $dropdownBody .= '<br><b>'.$langs->trans("ClientTZ").':</b> '.($tz ? ($tz >= 0 ? '+' : '').$tz : '');
2369  $dropdownBody .= ' ('.$_SESSION['dol_tz_string'].')';
2370  //$dropdownBody .= ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
2371  //if ($_SESSION['dol_dst'] > 0) $dropdownBody .= yn(1);
2372  //else $dropdownBody .= yn(0);
2373 
2374  $dropdownBody .= '<br><b>'.$langs->trans("Browser").':</b> '.$conf->browser->name.($conf->browser->version ? ' '.$conf->browser->version : '').' ('.dol_escape_htmltag($_SERVER['HTTP_USER_AGENT']).')';
2375  $dropdownBody .= '<br><b>'.$langs->trans("Layout").':</b> '.$conf->browser->layout;
2376  $dropdownBody .= '<br><b>'.$langs->trans("Screen").':</b> '.$_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight'];
2377  if ($conf->browser->layout == 'phone') {
2378  $dropdownBody .= '<br><b>'.$langs->trans("Phone").':</b> '.$langs->trans("Yes");
2379  }
2380  if (!empty($_SESSION["disablemodules"])) {
2381  $dropdownBody .= '<br><b>'.$langs->trans("DisabledModules").':</b> <br>'.join(', ', explode(',', $_SESSION["disablemodules"]));
2382  }
2383  $dropdownBody .= '</div>';
2384 
2385  // Execute hook
2386  $parameters = array('user'=>$user, 'langs' => $langs);
2387  $result = $hookmanager->executeHooks('printTopRightMenuLoginDropdownBody', $parameters); // Note that $action and $object may have been modified by some hooks
2388  if (is_numeric($result)) {
2389  if ($result == 0) {
2390  $dropdownBody .= $hookmanager->resPrint; // add
2391  } else {
2392  $dropdownBody = $hookmanager->resPrint; // replace
2393  }
2394  }
2395 
2396  if (empty($urllogout)) {
2397  $urllogout = DOL_URL_ROOT.'/user/logout.php?token='.newToken();
2398  }
2399 
2400  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2401  // accesskey is for Mac: CTRL + key for all browsers
2402  $stringforfirstkey = $langs->trans("KeyboardShortcut");
2403  if ($conf->browser->name == 'chrome') {
2404  $stringforfirstkey .= ' ALT +';
2405  } elseif ($conf->browser->name == 'firefox') {
2406  $stringforfirstkey .= ' ALT + SHIFT +';
2407  } else {
2408  $stringforfirstkey .= ' CTL +';
2409  }
2410 
2411  // Defined the links for bottom of card
2412  $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>';
2413  $urltovirtualcard = '/user/virtualcard.php?id='.((int) $user->id);
2414  $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');
2415  $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>';
2416 
2417  $profilName = $user->getFullName($langs).' ('.$user->login.')';
2418  if (!empty($user->admin)) {
2419  $profilName = '<i class="far fa-star classfortooltip" title="'.$langs->trans("Administrator").'" ></i> '.$profilName;
2420  }
2421 
2422  // Define version to show
2423  $appli = constant('DOL_APPLICATION_TITLE');
2424  if (!empty($conf->global->MAIN_APPLICATION_TITLE)) {
2425  $appli = $conf->global->MAIN_APPLICATION_TITLE;
2426  if (preg_match('/\d\.\d/', $appli)) {
2427  if (!preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) {
2428  $appli .= " (".DOL_VERSION.")"; // If new title contains a version that is different than core
2429  }
2430  } else {
2431  $appli .= " ".DOL_VERSION;
2432  }
2433  } else {
2434  $appli .= " ".DOL_VERSION;
2435  }
2436 
2437  if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2438  $btnUser = '<!-- div for user link -->
2439  <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2440  <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="dropdown-toggle login-dropdown-a" data-toggle="dropdown">
2441  '.$userImage.(empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2442  </a>
2443  <div class="dropdown-menu">
2444  <!-- User image -->
2445  <div class="user-header">
2446  '.$userDropDownImage.'
2447  <p>
2448  '.$profilName.'<br>';
2449  if ($user->datelastlogin) {
2450  $title = $langs->trans("ConnectedSince").' : '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2451  if ($user->datepreviouslogin) {
2452  $title .= '<br>'.$langs->trans("PreviousConnexion").' : '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2453  }
2454  }
2455  $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>';
2456  if ($user->datepreviouslogin) {
2457  $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>';
2458  }
2459 
2460  //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
2461  $btnUser .= '
2462  </p>
2463  </div>
2464 
2465  <!-- Menu Body user-->
2466  <div class="user-body">'.$dropdownBody.'</div>
2467 
2468  <!-- Menu Footer-->
2469  <div class="user-footer">
2470  <div class="pull-left">
2471  '.$profilLink.'
2472  </div>
2473  <div class="pull-left">
2474  '.$virtuelcardLink.'
2475  </div>
2476  <div class="pull-right">
2477  '.$logoutLink.'
2478  </div>
2479  <div class="clearboth"></div>
2480  </div>
2481 
2482  </div>
2483  </div>';
2484  } else {
2485  $btnUser = '<!-- div for user link -->
2486  <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2487  <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'">
2488  '.$userImage.'
2489  <span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>
2490  </a>
2491  </div>';
2492  }
2493 
2494  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
2495  $btnUser .= '
2496  <!-- Code to show/hide the user drop-down -->
2497  <script>
2498  function closeTopMenuLoginDropdown() {
2499  //console.log("close login dropdown"); // This is call at each click on page, so we disable the log
2500  // Hide the menus.
2501  jQuery("#topmenu-login-dropdown").removeClass("open");
2502  }
2503  jQuery(document).ready(function() {
2504  jQuery(document).on("click", function(event) {
2505  // console.log("Click somewhere on screen");
2506  if (!$(event.target).closest("#topmenu-login-dropdown").length) {
2507  closeTopMenuLoginDropdown();
2508  }
2509  });
2510 
2511  jQuery(".butAction.dropdown-toggle").on("click", function(event) {
2512  console.log("Click on .butAction.dropdown-toggle");
2513  var parentholder = jQuery(".butAction.dropdown-toggle").closest(".dropdown");
2514  var offset = parentholder.offset();
2515  var widthdocument = $(document).width();
2516  var left = offset.left;
2517  var right = widthdocument - offset.left - parentholder.width();
2518  var widthpopup = parentholder.children(".dropdown-content").width();
2519  console.log("left="+left+" right="+right+" width="+widthpopup+" widthdocument="+widthdocument);
2520  if (widthpopup + right >= widthdocument) {
2521  right = 10;
2522  }
2523  parentholder.toggleClass("open");
2524  parentholder.children(".dropdown-content").css({"right": right+"px", "left": "auto"});
2525  });
2526  ';
2527 
2528 
2529  if ($conf->theme != 'md') {
2530  $btnUser .= '
2531  jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
2532  console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
2533  event.preventDefault();
2534  jQuery("#topmenu-login-dropdown").toggleClass("open");
2535  });
2536 
2537  jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
2538  console.log("Clik on #topmenulogincompanyinfo-btn");
2539  jQuery("#topmenulogincompanyinfo").slideToggle();
2540  });
2541 
2542  jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
2543  console.log("Clik on #topmenuloginmoreinfo-btn");
2544  jQuery("#topmenuloginmoreinfo").slideToggle();
2545  });';
2546  }
2547 
2548  $btnUser .= '
2549  });
2550  </script>
2551  ';
2552  }
2553 
2554  return $btnUser;
2555 }
2556 
2563 {
2564  global $conf, $langs;
2565 
2566  $html = '';
2567 
2568  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2569  // accesskey is for Mac: CTRL + key for all browsers
2570  $stringforfirstkey = $langs->trans("KeyboardShortcut");
2571  if ($conf->browser->name == 'chrome') {
2572  $stringforfirstkey .= ' ALT +';
2573  } elseif ($conf->browser->name == 'firefox') {
2574  $stringforfirstkey .= ' ALT + SHIFT +';
2575  } else {
2576  $stringforfirstkey .= ' CTL +';
2577  }
2578 
2579  $html .= '<!-- div for quick add link -->
2580  <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
2581  <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>
2582  <div class="dropdown-menu">'.printDropdownQuickadd().'</div>
2583  </div>';
2584  $html .= '
2585  <!-- Code to show/hide the user drop-down -->
2586  <script>
2587  jQuery(document).ready(function() {
2588  jQuery(document).on("click", function(event) {
2589  if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
2590  // Hide the menus.
2591  $("#topmenu-quickadd-dropdown").removeClass("open");
2592  }
2593  });
2594  $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
2595  console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
2596  openQuickAddDropDown(event);
2597  });
2598 
2599  // Key map shortcut
2600  $(document).keydown(function(event){
2601  if ( event.which === 76 && event.ctrlKey && event.shiftKey ){
2602  console.log(\'control + shift + l : trigger open quick add dropdown\');
2603  openQuickAddDropDown(event);
2604  }
2605  });
2606 
2607  var openQuickAddDropDown = function(event) {
2608  event.preventDefault();
2609  $("#topmenu-quickadd-dropdown").toggleClass("open");
2610  //$("#top-quickadd-search-input").focus();
2611  }
2612  });
2613  </script>
2614  ';
2615  return $html;
2616 }
2617 
2624 {
2625  global $conf, $user, $langs, $hookmanager;
2626 
2627  $items = array(
2628  'items' => array(
2629  array(
2630  "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
2631  "title" => "MenuNewMember@members",
2632  "name" => "Adherent@members",
2633  "picto" => "object_member",
2634  "activation" => isModEnabled('adherent') && $user->hasRight("adherent", "write"), // vs hooking
2635  "position" => 5,
2636  ),
2637  array(
2638  "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
2639  "title" => "MenuNewThirdParty@companies",
2640  "name" => "ThirdParty@companies",
2641  "picto" => "object_company",
2642  "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
2643  "position" => 10,
2644  ),
2645  array(
2646  "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
2647  "title" => "NewContactAddress@companies",
2648  "name" => "Contact@companies",
2649  "picto" => "object_contact",
2650  "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
2651  "position" => 20,
2652  ),
2653  array(
2654  "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
2655  "title" => "NewPropal@propal",
2656  "name" => "Proposal@propal",
2657  "picto" => "object_propal",
2658  "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
2659  "position" => 30,
2660  ),
2661 
2662  array(
2663  "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
2664  "title" => "NewOrder@orders",
2665  "name" => "Order@orders",
2666  "picto" => "object_order",
2667  "activation" => isModEnabled('commande') && $user->hasRight("commande", "write"), // vs hooking
2668  "position" => 40,
2669  ),
2670  array(
2671  "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
2672  "title" => "NewBill@bills",
2673  "name" => "Bill@bills",
2674  "picto" => "object_bill",
2675  "activation" => isModEnabled('facture') && $user->hasRight("facture", "write"), // vs hooking
2676  "position" => 50,
2677  ),
2678  array(
2679  "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
2680  "title" => "NewContractSubscription@contracts",
2681  "name" => "Contract@contracts",
2682  "picto" => "object_contract",
2683  "activation" => isModEnabled('contrat') && $user->hasRight("contrat", "write"), // vs hooking
2684  "position" => 60,
2685  ),
2686  array(
2687  "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
2688  "title" => "SupplierProposalNew@supplier_proposal",
2689  "name" => "SupplierProposal@supplier_proposal",
2690  "picto" => "supplier_proposal",
2691  "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
2692  "position" => 70,
2693  ),
2694  array(
2695  "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
2696  "title" => "NewSupplierOrderShort@orders",
2697  "name" => "SupplierOrder@orders",
2698  "picto" => "supplier_order",
2699  "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
2700  "position" => 80,
2701  ),
2702  array(
2703  "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
2704  "title" => "NewBill@bills",
2705  "name" => "SupplierBill@bills",
2706  "picto" => "supplier_invoice",
2707  "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
2708  "position" => 90,
2709  ),
2710  array(
2711  "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
2712  "title" => "NewTicket@ticket",
2713  "name" => "Ticket@ticket",
2714  "picto" => "ticket",
2715  "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
2716  "position" => 100,
2717  ),
2718  array(
2719  "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
2720  "title" => "NewIntervention@interventions",
2721  "name" => "Intervention@interventions",
2722  "picto" => "intervention",
2723  "activation" => isModEnabled('ficheinter') && $user->hasRight("ficheinter", "creer"), // vs hooking
2724  "position" => 110,
2725  ),
2726  array(
2727  "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
2728  "title" => "NewProduct@products",
2729  "name" => "Product@products",
2730  "picto" => "object_product",
2731  "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
2732  "position" => 400,
2733  ),
2734  array(
2735  "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
2736  "title" => "NewService@products",
2737  "name" => "Service@products",
2738  "picto" => "object_service",
2739  "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
2740  "position" => 410,
2741  ),
2742  array(
2743  "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
2744  "title" => "AddUser@users",
2745  "name" => "User@users",
2746  "picto" => "user",
2747  "activation" => $user->hasRight("user", "user", "write"), // vs hooking
2748  "position" => 500,
2749  ),
2750  ),
2751  );
2752 
2753  $dropDownQuickAddHtml = '';
2754 
2755  // Define $dropDownQuickAddHtml
2756  $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
2757  $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
2758 
2759  // Allow the $items of the menu to be manipulated by modules
2760  $parameters = array();
2761  $hook_items = $items;
2762  $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks
2763  if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
2764  if ($reshook == 0) {
2765  $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
2766  } else {
2767  $items = $hookmanager->resArray; // replace
2768  }
2769 
2770  // Sort menu items by 'position' value
2771  $position = array();
2772  foreach ($items['items'] as $key => $row) {
2773  $position[$key] = $row['position'];
2774  }
2775  $array1_sort_order = SORT_ASC;
2776  array_multisort($position, $array1_sort_order, $items['items']);
2777  }
2778 
2779  foreach ($items['items'] as $item) {
2780  if (!$item['activation']) {
2781  continue;
2782  }
2783  $langs->load(explode('@', $item['title'])[1]);
2784  $langs->load(explode('@', $item['name'])[1]);
2785  $dropDownQuickAddHtml .= '
2786  <a class="dropdown-item quickadd-item" href="'.DOL_URL_ROOT.$item['url'].'" title="'.$langs->trans(explode('@', $item['title'])[0]).'">
2787  '. img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
2788  ';
2789  }
2790 
2791  $dropDownQuickAddHtml .= '</div>';
2792  $dropDownQuickAddHtml .= '</div>';
2793 
2794  return $dropDownQuickAddHtml;
2795 }
2796 
2803 {
2804  global $langs, $conf, $db, $user;
2805 
2806  $html = '';
2807 
2808  // Define $bookmarks
2809  if (!isModEnabled('bookmark') || empty($user->rights->bookmark->lire)) {
2810  return $html;
2811  }
2812 
2813  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2814  // accesskey is for Mac: CTRL + key for all browsers
2815  $stringforfirstkey = $langs->trans("KeyboardShortcut");
2816  if ($conf->browser->name == 'chrome') {
2817  $stringforfirstkey .= ' ALT +';
2818  } elseif ($conf->browser->name == 'firefox') {
2819  $stringforfirstkey .= ' ALT + SHIFT +';
2820  } else {
2821  $stringforfirstkey .= ' CTL +';
2822  }
2823 
2824  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
2825  include_once DOL_DOCUMENT_ROOT.'/bookmarks/bookmarks.lib.php';
2826  $langs->load("bookmarks");
2827 
2828  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2829  $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
2830  $html .= printDropdownBookmarksList();
2831  $html .= '</div>';
2832  } else {
2833  $html .= '<!-- div for bookmark link -->
2834  <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
2835  <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>
2836  <div class="dropdown-menu">
2838  </div>
2839  </div>';
2840 
2841  $html .= '
2842  <!-- Code to show/hide the bookmark drop-down -->
2843  <script>
2844  jQuery(document).ready(function() {
2845  jQuery(document).on("click", function(event) {
2846  if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
2847  //console.log("close bookmark dropdown - we click outside");
2848  // Hide the menus.
2849  $("#topmenu-bookmark-dropdown").removeClass("open");
2850  }
2851  });
2852 
2853  jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
2854  console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
2855  openBookMarkDropDown(event);
2856  });
2857 
2858  // Key map shortcut
2859  jQuery(document).keydown(function(event){
2860  if( event.which === 77 && event.ctrlKey && event.shiftKey ){
2861  console.log("Click on control + shift + m : trigger open bookmark dropdown");
2862  openBookMarkDropDown(event);
2863  }
2864  });
2865 
2866  var openBookMarkDropDown = function(event) {
2867  event.preventDefault();
2868  jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
2869  jQuery("#top-bookmark-search-input").focus();
2870  }
2871 
2872  });
2873  </script>
2874  ';
2875  }
2876  }
2877  return $html;
2878 }
2879 
2886 {
2887  global $langs, $conf, $db, $user, $hookmanager;
2888 
2889  $html = '';
2890 
2891  $usedbyinclude = 1;
2892  $arrayresult = null;
2893  include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This set $arrayresult
2894 
2895  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2896  // accesskey is for Mac: CTRL + key for all browsers
2897  $stringforfirstkey = $langs->trans("KeyboardShortcut");
2898  if ($conf->browser->name == 'chrome') {
2899  $stringforfirstkey .= ' ALT +';
2900  } elseif ($conf->browser->name == 'firefox') {
2901  $stringforfirstkey .= ' ALT + SHIFT +';
2902  } else {
2903  $stringforfirstkey .= ' CTL +';
2904  }
2905 
2906  $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">';
2907 
2908  $defaultAction = '';
2909  $buttonList = '<div class="dropdown-global-search-button-list" >';
2910  // Menu with all searchable items
2911  foreach ($arrayresult as $keyItem => $item) {
2912  if (empty($defaultAction)) {
2913  $defaultAction = $item['url'];
2914  }
2915  $buttonList .= '<button class="dropdown-item global-search-item tdoverflowmax300" data-target="'.dol_escape_htmltag($item['url']).'" >';
2916  $buttonList .= $item['text'];
2917  $buttonList .= '</button>';
2918  }
2919  $buttonList .= '</div>';
2920 
2921  $dropDownHtml = '<form id="top-menu-action-search" name="actionsearch" method="GET" action="'.$defaultAction.'">';
2922 
2923  $dropDownHtml .= '
2924  <!-- search input -->
2925  <div class="dropdown-header search-dropdown-header">
2926  ' . $searchInput.'
2927  </div>
2928  ';
2929 
2930  $dropDownHtml .= '
2931  <!-- Menu Body search -->
2932  <div class="dropdown-body search-dropdown-body">
2933  '.$buttonList.'
2934  </div>
2935  ';
2936 
2937  $dropDownHtml .= '</form>';
2938 
2939  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
2940  // accesskey is for Mac: CTRL + key for all browsers
2941  $stringforfirstkey = $langs->trans("KeyboardShortcut");
2942  if ($conf->browser->name == 'chrome') {
2943  $stringforfirstkey .= ' ALT +';
2944  } elseif ($conf->browser->name == 'firefox') {
2945  $stringforfirstkey .= ' ALT + SHIFT +';
2946  } else {
2947  $stringforfirstkey .= ' CTL +';
2948  }
2949 
2950  $html .= '<!-- div for Global Search -->
2951  <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
2952  <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Search').' ('.$stringforfirstkey.' s)">
2953  <i class="fa fa-search" ></i>
2954  </a>
2955  <div class="dropdown-menu dropdown-search">
2956  '.$dropDownHtml.'
2957  </div>
2958  </div>';
2959 
2960  $html .= '
2961  <!-- Code to show/hide the user drop-down -->
2962  <script>
2963  jQuery(document).ready(function() {
2964 
2965  // prevent submiting form on press ENTER
2966  jQuery("#top-global-search-input").keydown(function (e) {
2967  if (e.keyCode == 13) {
2968  var inputs = $(this).parents("form").eq(0).find(":button");
2969  if (inputs[inputs.index(this) + 1] != null) {
2970  inputs[inputs.index(this) + 1].focus();
2971  }
2972  e.preventDefault();
2973  return false;
2974  }
2975  });
2976 
2977  // arrow key nav
2978  jQuery(document).keydown(function(e) {
2979  // Get the focused element:
2980  var $focused = $(":focus");
2981  if($focused.length && $focused.hasClass("global-search-item")){
2982 
2983  // UP - move to the previous line
2984  if (e.keyCode == 38) {
2985  e.preventDefault();
2986  $focused.prev().focus();
2987  }
2988 
2989  // DOWN - move to the next line
2990  if (e.keyCode == 40) {
2991  e.preventDefault();
2992  $focused.next().focus();
2993  }
2994  }
2995  });
2996 
2997 
2998  // submit form action
2999  jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
3000  jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
3001  jQuery("#top-menu-action-search").submit();
3002  });
3003 
3004  // close drop down
3005  jQuery(document).on("click", function(event) {
3006  if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
3007  console.log("click close search - we click outside");
3008  // Hide the menus.
3009  jQuery("#topmenu-global-search-dropdown").removeClass("open");
3010  }
3011  });
3012 
3013  // Open drop down
3014  jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
3015  console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
3016  openGlobalSearchDropDown();
3017  });
3018 
3019  // Key map shortcut
3020  jQuery(document).keydown(function(e){
3021  if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
3022  console.log(\'control + shift + f : trigger open global-search dropdown\');
3023  openGlobalSearchDropDown();
3024  }
3025  if ( e.which === 70 && e.alKey ) {
3026  console.log(\'alt + f : trigger open global-search dropdown\');
3027  openGlobalSearchDropDown();
3028  }
3029  });
3030 
3031  var openGlobalSearchDropDown = function() {
3032  jQuery("#topmenu-global-search-dropdown").toggleClass("open");
3033  jQuery("#top-global-search-input").focus();
3034  }
3035 
3036  });
3037  </script>
3038  ';
3039 
3040  return $html;
3041 }
3042 
3057 function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = '', $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
3058 {
3059  global $user, $conf, $langs, $db, $form;
3060  global $hookmanager, $menumanager;
3061 
3062  $searchform = '';
3063 
3064  if (!empty($menu_array_before)) {
3065  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);
3066  }
3067 
3068  if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
3069  // Instantiate hooks for external modules
3070  $hookmanager->initHooks(array('leftblock'));
3071 
3072  print "\n".'<!-- Begin side-nav id-left -->'."\n".'<div class="side-nav"><div id="id-left">'."\n";
3073  print "\n";
3074 
3075  if (!is_object($form)) {
3076  $form = new Form($db);
3077  }
3078  $selected = -1;
3079  if (empty($conf->global->MAIN_USE_TOP_MENU_SEARCH_DROPDOWN)) {
3080  // Select into select2 is awfull on smartphone. TODO Is this still true with select2 v4 ?
3081  if ($conf->browser->layout == 'phone') {
3082  $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
3083  }
3084 
3085  $usedbyinclude = 1;
3086  $arrayresult = array();
3087  include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
3088 
3089  if ($conf->use_javascript_ajax && empty($conf->global->MAIN_USE_OLD_SEARCH_FORM)) {
3090  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
3091  // accesskey is for Mac: CTRL + key for all browsers
3092  $stringforfirstkey = $langs->trans("KeyboardShortcut");
3093  if ($conf->browser->name == 'chrome') {
3094  $stringforfirstkey .= ' ALT +';
3095  } elseif ($conf->browser->name == 'firefox') {
3096  $stringforfirstkey .= ' ALT + SHIFT +';
3097  } else {
3098  $stringforfirstkey .= ' CTL +';
3099  }
3100 
3101  $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');
3102  } else {
3103  if (is_array($arrayresult)) {
3104  foreach ($arrayresult as $key => $val) {
3105  $searchform .= printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft'.$key, $val['img']);
3106  }
3107  }
3108  }
3109 
3110  // Execute hook printSearchForm
3111  $parameters = array('searchform' => $searchform);
3112  $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
3113  if (empty($reshook)) {
3114  $searchform .= $hookmanager->resPrint;
3115  } else {
3116  $searchform = $hookmanager->resPrint;
3117  }
3118 
3119  // Force special value for $searchform for text browsers or very old search form
3120  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) || empty($conf->use_javascript_ajax)) {
3121  $urltosearch = DOL_URL_ROOT.'/core/search_page.php?showtitlebefore=1';
3122  $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>';
3123  } elseif ($conf->use_javascript_ajax && !empty($conf->global->MAIN_USE_OLD_SEARCH_FORM)) {
3124  $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>';
3125  $searchform .= '<script>
3126  jQuery(document).ready(function () {
3127  jQuery("#divsearchforms1").click(function(){
3128  jQuery("#divsearchforms2").toggle();
3129  });
3130  });
3131  </script>' . "\n";
3132  $searchform .= '</div>';
3133  }
3134 
3135  // Key map shortcut
3136  $searchform .= '<script>
3137  jQuery(document).keydown(function(e){
3138  if( e.which === 70 && e.ctrlKey && e.shiftKey ){
3139  console.log(\'control + shift + f : trigger open global-search dropdown\');
3140  openGlobalSearchDropDown();
3141  }
3142  if( (e.which === 83 || e.which === 115) && e.altKey ){
3143  console.log(\'alt + s : trigger open global-search dropdown\');
3144  openGlobalSearchDropDown();
3145  }
3146  });
3147 
3148  var openGlobalSearchDropDown = function() {
3149  jQuery("#searchselectcombo").select2(\'open\');
3150  }
3151  </script>';
3152  }
3153 
3154  // Left column
3155  print '<!-- Begin left menu -->'."\n";
3156 
3157  print '<div class="vmenu"'.(empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) ? '' : ' title="Left menu"').'>'."\n\n";
3158 
3159  // Show left menu with other forms
3160  $menumanager->menu_array = $menu_array_before;
3161  $menumanager->menu_array_after = $menu_array_after;
3162  $menumanager->showmenu('left', array('searchform'=>$searchform)); // output menu_array and menu found in database
3163 
3164  // Dolibarr version + help + bug report link
3165  print "\n";
3166  print "<!-- Begin Help Block-->\n";
3167  print '<div id="blockvmenuhelp" class="blockvmenuhelp">'."\n";
3168 
3169  // Version
3170  if (!empty($conf->global->MAIN_SHOW_VERSION)) { // Version is already on help picto and on login page.
3171  $doliurl = 'https://www.dolibarr.org';
3172  //local communities
3173  if (preg_match('/fr/i', $langs->defaultlang)) {
3174  $doliurl = 'https://www.dolibarr.fr';
3175  }
3176  if (preg_match('/es/i', $langs->defaultlang)) {
3177  $doliurl = 'https://www.dolibarr.es';
3178  }
3179  if (preg_match('/de/i', $langs->defaultlang)) {
3180  $doliurl = 'https://www.dolibarr.de';
3181  }
3182  if (preg_match('/it/i', $langs->defaultlang)) {
3183  $doliurl = 'https://www.dolibarr.it';
3184  }
3185  if (preg_match('/gr/i', $langs->defaultlang)) {
3186  $doliurl = 'https://www.dolibarr.gr';
3187  }
3188 
3189  $appli = constant('DOL_APPLICATION_TITLE');
3190  if (!empty($conf->global->MAIN_APPLICATION_TITLE)) {
3191  $appli = $conf->global->MAIN_APPLICATION_TITLE; $doliurl = '';
3192  if (preg_match('/\d\.\d/', $appli)) {
3193  if (!preg_match('/'.preg_quote(DOL_VERSION).'/', $appli)) {
3194  $appli .= " (".DOL_VERSION.")"; // If new title contains a version that is different than core
3195  }
3196  } else {
3197  $appli .= " ".DOL_VERSION;
3198  }
3199  } else {
3200  $appli .= " ".DOL_VERSION;
3201  }
3202  print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
3203  if ($doliurl) {
3204  print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$doliurl.'">';
3205  } else {
3206  print '<span class="help">';
3207  }
3208  print $appli;
3209  if ($doliurl) {
3210  print '</a>';
3211  } else {
3212  print '</span>';
3213  }
3214  print '</div>'."\n";
3215  }
3216 
3217  // Link to bugtrack
3218  if (!empty($conf->global->MAIN_BUGTRACK_ENABLELINK)) {
3219  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3220 
3221  if ($conf->global->MAIN_BUGTRACK_ENABLELINK == 'github') {
3222  $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
3223  $bugbaseurl .= '&title=';
3224  $bugbaseurl .= urlencode("Bug: ");
3225  $bugbaseurl .= '&body=';
3226  $bugbaseurl .= urlencode("# Instructions\n");
3227  $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");
3228  $bugbaseurl .= urlencode("*Please:*\n");
3229  $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
3230  $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
3231  $bugbaseurl .= urlencode("\n");
3232  $bugbaseurl .= urlencode("\n");
3233  $bugbaseurl .= urlencode("# Bug\n");
3234  $bugbaseurl .= urlencode("[*Short description*]\n");
3235  $bugbaseurl .= urlencode("\n");
3236  $bugbaseurl .= urlencode("## Environment\n");
3237  $bugbaseurl .= urlencode("- **Version**: ".DOL_VERSION."\n");
3238  $bugbaseurl .= urlencode("- **OS**: ".php_uname('s')."\n");
3239  $bugbaseurl .= urlencode("- **Web server**: ".$_SERVER["SERVER_SOFTWARE"]."\n");
3240  $bugbaseurl .= urlencode("- **PHP**: ".php_sapi_name().' '.phpversion()."\n");
3241  $bugbaseurl .= urlencode("- **Database**: ".$db::LABEL.' '.$db->getVersion()."\n");
3242  $bugbaseurl .= urlencode("- **URL(s)**: ".$_SERVER["REQUEST_URI"]."\n");
3243  $bugbaseurl .= urlencode("\n");
3244  $bugbaseurl .= urlencode("## Expected and actual behavior\n");
3245  $bugbaseurl .= urlencode("[*Verbose description*]\n");
3246  $bugbaseurl .= urlencode("\n");
3247  $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
3248  $bugbaseurl .= urlencode("[*Verbose description*]\n");
3249  $bugbaseurl .= urlencode("\n");
3250  $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, dolibarr.log, debugging informations…)\n");
3251  $bugbaseurl .= urlencode("[*Files*]\n");
3252  $bugbaseurl .= urlencode("\n");
3253 
3254  $bugbaseurl .= urlencode("\n");
3255  $bugbaseurl .= urlencode("## Report\n");
3256  } elseif (!empty($conf->global->MAIN_BUGTRACK_ENABLELINK)) {
3257  $bugbaseurl = $conf->global->MAIN_BUGTRACK_ENABLELINK;
3258  } else {
3259  $bugbaseurl = "";
3260  }
3261 
3262  // Execute hook printBugtrackInfo
3263  $parameters = array('bugbaseurl' => $bugbaseurl);
3264  $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
3265  if (empty($reshook)) {
3266  $bugbaseurl .= $hookmanager->resPrint;
3267  } else {
3268  $bugbaseurl = $hookmanager->resPrint;
3269  }
3270 
3271  print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
3272  print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$bugbaseurl.'"><i class="fas fa-bug"></i> '.$langs->trans("FindBug").'</a>';
3273  print '</div>';
3274  }
3275 
3276  print "</div>\n";
3277  print "<!-- End Help Block-->\n";
3278  print "\n";
3279 
3280  print "</div>\n";
3281  print "<!-- End left menu -->\n";
3282  print "\n";
3283 
3284  // Execute hook printLeftBlock
3285  $parameters = array();
3286  $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
3287  print $hookmanager->resPrint;
3288 
3289  print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
3290  }
3291 
3292  print "\n";
3293  print '<!-- Begin right area -->'."\n";
3294 
3295  if (empty($leftmenuwithoutmainarea)) {
3296  main_area($title);
3297  }
3298 }
3299 
3300 
3307 function main_area($title = '')
3308 {
3309  global $conf, $langs, $hookmanager;
3310 
3311  if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup')) {
3312  print '<div id="id-right">';
3313  }
3314 
3315  print "\n";
3316 
3317  print '<!-- Begin div class="fiche" -->'."\n".'<div class="fiche">'."\n";
3318 
3319  $hookmanager->initHooks(array('main'));
3320  $parameters = array();
3321  $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
3322  print $hookmanager->resPrint;
3323 
3324  if (!empty($conf->global->MAIN_ONLY_LOGIN_ALLOWED)) {
3325  print info_admin($langs->trans("WarningYouAreInMaintenanceMode", $conf->global->MAIN_ONLY_LOGIN_ALLOWED), 0, 0, 1, 'warning maintenancemode');
3326  }
3327 
3328  // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
3329  if (!empty($conf->global->SHOW_SOCINFO_ON_PRINT) && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'az09'))) {
3330  $parameters = array();
3331  $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
3332  if (empty($reshook)) {
3333  print '<!-- Begin show mysoc info header -->'."\n";
3334  print '<div id="mysoc-info-header">'."\n";
3335  print '<table class="centpercent div-table-responsive">'."\n";
3336  print '<tbody>';
3337  print '<tr><td rowspan="0" class="width20p">';
3338  if ($conf->global->MAIN_SHOW_LOGO && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && !empty($conf->global->MAIN_INFO_SOCIETE_LOGO)) {
3339  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)).'">';
3340  }
3341  print '</td><td rowspan="0" class="width50p"></td></tr>'."\n";
3342  print '<tr><td class="titre bold">'.dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_NOM).'</td></tr>'."\n";
3343  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";
3344  if (!empty($conf->global->MAIN_INFO_SOCIETE_TEL)) {
3345  print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Phone").' : '.dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_TEL).'</td></tr>';
3346  }
3347  if (!empty($conf->global->MAIN_INFO_SOCIETE_MAIL)) {
3348  print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Email").' : '.dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_MAIL).'</td></tr>';
3349  }
3350  if (!empty($conf->global->MAIN_INFO_SOCIETE_WEB)) {
3351  print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Web").' : '.dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_WEB).'</td></tr>';
3352  }
3353  print '</tbody>';
3354  print '</table>'."\n";
3355  print '</div>'."\n";
3356  print '<!-- End show mysoc info header -->'."\n";
3357  }
3358  }
3359 }
3360 
3361 
3369 function getHelpParamFor($helppagename, $langs)
3370 {
3371  $helpbaseurl = '';
3372  $helppage = '';
3373  $mode = '';
3374 
3375  if (preg_match('/^http/i', $helppagename)) {
3376  // If complete URL
3377  $helpbaseurl = '%s';
3378  $helppage = $helppagename;
3379  $mode = 'local';
3380  } else {
3381  // If WIKI URL
3382  $reg = array();
3383  if (preg_match('/^es/i', $langs->defaultlang)) {
3384  $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3385  if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
3386  $helppage = $reg[1];
3387  }
3388  }
3389  if (preg_match('/^fr/i', $langs->defaultlang)) {
3390  $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3391  if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
3392  $helppage = $reg[1];
3393  }
3394  }
3395  if (preg_match('/^de/i', $langs->defaultlang)) {
3396  $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3397  if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
3398  $helppage = $reg[1];
3399  }
3400  }
3401  if (empty($helppage)) { // If help page not already found
3402  $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3403  if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
3404  $helppage = $reg[1];
3405  }
3406  }
3407  $mode = 'wiki';
3408  }
3409  return array('helpbaseurl'=>$helpbaseurl, 'helppage'=>$helppage, 'mode'=>$mode);
3410 }
3411 
3412 
3429 function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
3430 {
3431  global $conf, $langs, $user;
3432 
3433  $ret = '';
3434  $ret .= '<form action="'.$urlaction.'" method="post" class="searchform nowraponall tagtr">';
3435  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
3436  $ret .= '<input type="hidden" name="savelogin" value="'.dol_escape_htmltag($user->login).'">';
3437  if ($showtitlebefore) {
3438  $ret .= '<div class="tagtd left">'.$title.'</div> ';
3439  }
3440  $ret .= '<div class="tagtd">';
3441  $ret .= img_picto('', $img, '', false, 0, 0, '', 'paddingright width20');
3442  $ret .= '<input type="text" class="flat '.$htmlmorecss.'"';
3443  $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
3444  $ret .= ($accesskey ? ' accesskey="'.$accesskey.'"' : '');
3445  $ret .= ' placeholder="'.strip_tags($title).'"';
3446  $ret .= ($autofocus ? ' autofocus' : '');
3447  $ret .= ' name="'.$htmlinputname.'" id="'.$prefhtmlinputname.$htmlinputname.'" />';
3448  $ret .= '<button type="submit" class="button bordertransp" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
3449  $ret .= '<span class="fa fa-search"></span>';
3450  $ret .= '</button>';
3451  $ret .= '</div>';
3452  $ret .= "</form>\n";
3453  return $ret;
3454 }
3455 
3456 
3457 if (!function_exists("llxFooter")) {
3468  function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
3469  {
3470  global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager;
3471  global $delayedhtmlcontent;
3472  global $contextpage, $page, $limit, $mode;
3473  global $dolibarr_distrib;
3474 
3475  $ext = 'layout='.urlencode($conf->browser->layout).'&version='.urlencode(DOL_VERSION);
3476 
3477  // Hook to add more things on all pages within fiche DIV
3478  $llxfooter = '';
3479  $parameters = array();
3480  $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3481  if (empty($reshook)) {
3482  $llxfooter .= $hookmanager->resPrint;
3483  } elseif ($reshook > 0) {
3484  $llxfooter = $hookmanager->resPrint;
3485  }
3486  if ($llxfooter) {
3487  print $llxfooter;
3488  }
3489 
3490  // Global html output events ($mesgs, $errors, $warnings)
3491  dol_htmloutput_events($disabledoutputofmessages);
3492 
3493  // Code for search criteria persistence.
3494  // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
3495  if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
3496  // Clean and save data
3497  foreach ($user->lastsearch_values_tmp as $key => $val) {
3498  unset($_SESSION['lastsearch_values_tmp_'.$key]); // Clean array to rebuild it just after
3499  if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
3500  if (empty($val['sortfield'])) {
3501  unset($val['sortfield']);
3502  }
3503  if (empty($val['sortorder'])) {
3504  unset($val['sortorder']);
3505  }
3506  dol_syslog('Save lastsearch_values_tmp_'.$key.'='.json_encode($val, 0)." (systematic recording of last search criterias)");
3507  $_SESSION['lastsearch_values_tmp_'.$key] = json_encode($val);
3508  unset($_SESSION['lastsearch_values_'.$key]);
3509  }
3510  }
3511  }
3512 
3513 
3514  $relativepathstring = $_SERVER["PHP_SELF"];
3515  // Clean $relativepathstring
3516  if (constant('DOL_URL_ROOT')) {
3517  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
3518  }
3519  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3520  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3521  if (preg_match('/list\.php$/', $relativepathstring)) {
3522  unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
3523  unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
3524  unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
3525  unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
3526 
3527  if (!empty($contextpage)) {
3528  $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring] = $contextpage;
3529  }
3530  if (!empty($page) && $page > 0) {
3531  $_SESSION['lastsearch_page_tmp_'.$relativepathstring] = $page;
3532  }
3533  if (!empty($limit) && $limit != $conf->liste_limit) {
3534  $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] = $limit;
3535  }
3536  if (!empty($mode)) {
3537  $_SESSION['lastsearch_mode_tmp_'.$relativepathstring] = $mode;
3538  }
3539 
3540  unset($_SESSION['lastsearch_contextpage_'.$relativepathstring]);
3541  unset($_SESSION['lastsearch_page_'.$relativepathstring]);
3542  unset($_SESSION['lastsearch_limit_'.$relativepathstring]);
3543  unset($_SESSION['lastsearch_mode_'.$relativepathstring]);
3544  }
3545 
3546  // Core error message
3547  if (!empty($conf->global->MAIN_CORE_ERROR)) {
3548  // Ajax version
3549  if ($conf->use_javascript_ajax) {
3550  $title = img_warning().' '.$langs->trans('CoreErrorTitle');
3551  print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
3552  } else {
3553  // html version
3554  $msg = img_warning().' '.$langs->trans('CoreErrorMessage');
3555  print '<div class="error">'.$msg.'</div>';
3556  }
3557 
3558  //define("MAIN_CORE_ERROR",0); // Constant was defined and we can't change value of a constant
3559  }
3560 
3561  print "\n\n";
3562 
3563  print '</div> <!-- End div class="fiche" -->'."\n"; // End div fiche
3564 
3565  if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup')) {
3566  print '</div> <!-- End div id-right -->'."\n"; // End div id-right
3567  }
3568 
3569  if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
3570  print '</div> <!-- End div id-container -->'."\n"; // End div container
3571  }
3572 
3573  print "\n";
3574  if ($comment) {
3575  print '<!-- '.$comment.' -->'."\n";
3576  }
3577 
3578  printCommonFooter($zone);
3579 
3580  if (!empty($delayedhtmlcontent)) {
3581  print $delayedhtmlcontent;
3582  }
3583 
3584  if (!empty($conf->use_javascript_ajax)) {
3585  print "\n".'<!-- Includes JS Footer of Dolibarr -->'."\n";
3586  print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang.($ext ? '&'.$ext : '').'"></script>'."\n";
3587  }
3588 
3589  // Wrapper to add log when clicking on download or preview
3590  if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
3591  if (in_array($object->element, array('facture')) && $object->statut > 0) { // Restrict for the moment to element 'facture'
3592  print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
3593  ?>
3594  <script>
3595  jQuery(document).ready(function () {
3596  $('a.documentpreview').click(function() {
3597  $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
3598  , {
3599  id:<?php echo $object->id; ?>
3600  , element:'<?php echo $object->element ?>'
3601  , action:'DOC_PREVIEW'
3602  , token: '<?php echo currentToken(); ?>'
3603  }
3604  );
3605  });
3606  $('a.documentdownload').click(function() {
3607  $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
3608  , {
3609  id:<?php echo $object->id; ?>
3610  , element:'<?php echo $object->element ?>'
3611  , action:'DOC_DOWNLOAD'
3612  , token: '<?php echo currentToken(); ?>'
3613  }
3614  );
3615  });
3616  });
3617  </script>
3618  <?php
3619  }
3620  }
3621 
3622  // A div for the address popup
3623  print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
3624  print '<div id="dialogforpopup" style="display: none;"></div>'."\n";
3625 
3626  // Add code for the asynchronous anonymous first ping (for telemetry)
3627  // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
3628  $forceping = GETPOST('forceping', 'alpha');
3629  if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceping) {
3630  //print '<!-- instance_unique_id='.$conf->file->instance_unique_id.' MAIN_FIRST_PING_OK_ID='.$conf->global->MAIN_FIRST_PING_OK_ID.' -->';
3631  $hash_unique_id = md5('dolibarr'.$conf->file->instance_unique_id); // Do not use dol_hash(), must not change if salt changes.
3632 
3633  if (empty($conf->global->MAIN_FIRST_PING_OK_DATE)
3634  || (!empty($conf->file->instance_unique_id) && ($hash_unique_id != $conf->global->MAIN_FIRST_PING_OK_ID) && ($conf->global->MAIN_FIRST_PING_OK_ID != 'disabled'))
3635  || $forceping) {
3636  // No ping done if we are into an alpha version
3637  if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
3638  print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
3639  } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
3640  // MAIN_LAST_PING_KO_DATE
3641  // Disable ping if MAIN_LAST_PING_KO_DATE is set and is recent (this month)
3642  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) {
3643  print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. An error already occured this month, we will try later. -->\n";
3644  } else {
3645  include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3646 
3647  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";
3648  print "\n<!-- JS CODE TO ENABLE the anonymous Ping -->\n";
3649  $url_for_ping = (empty($conf->global->MAIN_URL_FOR_PING) ? "https://ping.dolibarr.org/" : $conf->global->MAIN_URL_FOR_PING);
3650  // Try to guess the distrib used
3651  $distrib = 'standard';
3652  if ($_SERVER["SERVER_ADMIN"] == 'doliwamp@localhost') {
3653  $distrib = 'doliwamp';
3654  }
3655  if (!empty($dolibarr_distrib)) {
3656  $distrib = $dolibarr_distrib;
3657  }
3658  ?>
3659  <script>
3660  jQuery(document).ready(function (tmp) {
3661  console.log("Try Ping with hash_unique_id is md5('dolibarr'+instance_unique_id)");
3662  $.ajax({
3663  method: "POST",
3664  url: "<?php echo $url_for_ping ?>",
3665  timeout: 500, // timeout milliseconds
3666  cache: false,
3667  data: {
3668  hash_algo: 'md5',
3669  hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
3670  action: 'dolibarrping',
3671  version: '<?php echo (float) DOL_VERSION; ?>',
3672  entity: '<?php echo (int) $conf->entity; ?>',
3673  dbtype: '<?php echo dol_escape_js($db->type); ?>',
3674  country_code: '<?php echo $mysoc->country_code ? dol_escape_js($mysoc->country_code) : 'unknown'; ?>',
3675  php_version: '<?php echo dol_escape_js(phpversion()); ?>',
3676  os_version: '<?php echo dol_escape_js(version_os('smr')); ?>',
3677  distrib: '<?php echo $distrib ? dol_escape_js($distrib) : 'unknown'; ?>',
3678  token: 'notrequired'
3679  },
3680  success: function (data, status, xhr) { // success callback function (data contains body of response)
3681  console.log("Ping ok");
3682  $.ajax({
3683  method: 'GET',
3684  url: '<?php echo DOL_URL_ROOT.'/core/ajax/pingresult.php'; ?>',
3685  timeout: 500, // timeout milliseconds
3686  cache: false,
3687  data: { hash_algo: 'md5', hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>', action: 'firstpingok', token: '<?php echo currentToken(); ?>' }, // for update
3688  });
3689  },
3690  error: function (data,status,xhr) { // error callback function
3691  console.log("Ping ko: " + data);
3692  $.ajax({
3693  method: 'GET',
3694  url: '<?php echo DOL_URL_ROOT.'/core/ajax/pingresult.php'; ?>',
3695  timeout: 500, // timeout milliseconds
3696  cache: false,
3697  data: { hash_algo: 'md5', hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>', action: 'firstpingko', token: '<?php echo currentToken(); ?>' },
3698  });
3699  }
3700  });
3701  });
3702  </script>
3703  <?php
3704  }
3705  } else {
3706  $now = dol_now();
3707  print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
3708  include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
3709  dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_DATE', dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
3710  dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_ID', 'disabled', 'chaine', 0, '', $conf->entity);
3711  }
3712  }
3713  }
3714 
3715  $parameters = array();
3716  $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters); // Note that $action and $object may have been modified by some hooks
3717  if ($reshook > 0) {
3718  print $hookmanager->resPrint;
3719  }
3720 
3721  print "</body>\n";
3722  print "</html>\n";
3723  }
3724 }
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:408
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:3057
top_menu_quickadd()
Build the tooltip on top menu quick add.
Definition: main.inc.php:2562
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:2280
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:3307
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:1494
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:1625
getHelpParamFor($helppagename, $langs)
Return helpbaseurl, helppage and mode.
Definition: main.inc.php:3369
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:2036
printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey='', $prefhtmlinputname='', $img='', $showtitlebefore=0, $autofocus=0)
Show a search area.
Definition: main.inc.php:3429
top_menu_search()
Build the tooltip on top menu tsearch.
Definition: main.inc.php:2885
printDropdownQuickadd()
Generate list of quickadd items.
Definition: main.inc.php:2623
top_menu_bookmark()
Build the tooltip on top menu bookmark.
Definition: main.inc.php:2802
div float
Buy price without taxes.
Definition: style.css.php:921
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.