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