dolibarr 24.0.0-beta
main.inc.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Xavier Dutoit <doli@sydesy.com>
4 * Copyright (C) 2004-2021 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7 * Copyright (C) 2005-2021 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2011-2014 Philippe Grand <philippe.grand@atoo-net.com>
9 * Copyright (C) 2008 Matteli
10 * Copyright (C) 2011-2016 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2012 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
13 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
14 * Copyright (C) 2020 Demarest Maxime <maxime@indelog.fr>
15 * Copyright (C) 2020-2024 Charlene Benke <charlene@patas-monkey.com>
16 * Copyright (C) 2021-2026 Frédéric France <frederic.france@free.fr>
17 * Copyright (C) 2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
18 * Copyright (C) 2023 Joachim Küter <git-jk@bloxera.com>
19 * Copyright (C) 2023 Eric Seigne <eric.seigne@cap-rel.fr>
20 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
21 * Copyright (C) 2026 William Mead <william@m34d.com>
22 *
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 3 of the License, or
26 * (at your option) any later version.
27 *
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <https://www.gnu.org/licenses/>.
35 */
36
43//@ini_set('memory_limit', '128M'); // This may be useless if memory is hard limited by your PHP
44
45// For optional tuning. Enabled if environment variable MAIN_SHOW_TUNING_INFO is defined.
46$micro_start_time = 0; // Used as global var into printCommonFooter()
47if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) {
48 [$usec, $sec] = explode(" ", microtime());
49 $micro_start_time = ((float) $usec + (float) $sec);
50 // Add Xdebug code coverage
51 //define('XDEBUGCOVERAGE',1);
52 if (defined('XDEBUGCOVERAGE')) {
53 xdebug_start_code_coverage();
54 }
55}
56
57require __DIR__.'/waf.inc.php';
58
59// Check consistency of NOREQUIREXXX DEFINES
60if ((defined('NOREQUIREDB') || defined('NOREQUIRETRAN')) && !defined('NOREQUIREMENU')) {
61 print 'If define NOREQUIREDB or NOREQUIRETRAN are set, you must also set NOREQUIREMENU or not set them.';
62 exit;
63}
64if (defined('NOREQUIREUSER') && !defined('NOREQUIREMENU')) {
65 print 'If define NOREQUIREUSER is set, you must also set NOREQUIREMENU or not set it.';
66 exit;
67}
68
69// This is to make Dolibarr working with Plesk
70if (!empty($_SERVER['DOCUMENT_ROOT']) && substr($_SERVER['DOCUMENT_ROOT'], -6) !== 'htdocs') {
71 set_include_path($_SERVER['DOCUMENT_ROOT'].'/htdocs');
72}
73
74// 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...
75require_once 'filefunc.inc.php';
90include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/securitycore.lib.php';
91
92// If there is a POST parameter to tell to save automatically some POST parameters into cookies, we do it.
93// This is used for example by form of boxes to save personalization of some options.
94// DOL_AUTOSET_COOKIE=cookiename:val1,val2 and cookiename_val1=aaa cookiename_val2=bbb will set cookie_name with value json_encode(array('val1'=> , ))
95if (GETPOST("DOL_AUTOSET_COOKIE")) {
96 $tmpautoset = explode(':', GETPOST("DOL_AUTOSET_COOKIE"), 2);
97 $tmplist = explode(',', $tmpautoset[1]);
98 $cookiearrayvalue = array();
99 foreach ($tmplist as $tmpkey) {
100 $postkey = $tmpautoset[0].'_'.$tmpkey;
101 //var_dump('tmpkey='.$tmpkey.' postkey='.$postkey.' value='.GETPOST($postkey);
102 if (GETPOST($postkey)) {
103 $cookiearrayvalue[$tmpkey] = GETPOST($postkey);
104 }
105 }
106 $cookiename = $tmpautoset[0];
107 $cookievalue = json_encode($cookiearrayvalue);
108
109 dolSetCookie($cookiename, $cookievalue);
110}
111
112// Set the handler of session
113// if (ini_get('session.save_handler') == 'user')
114if (!empty($php_session_save_handler) && $php_session_save_handler == 'db') {
115 require_once 'core/lib/phpsessionin'.$php_session_save_handler.'.lib.php';
116}
117
118// Init session. Name of session is specific to Dolibarr instance.
119// 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).
120// 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.
121$prefix = dol_getprefix('');
122$sessionname = 'DOLSESSID_'.$prefix;
123$sessiontimeout = 'DOLSESSTIMEOUT_'.$prefix;
124if (!empty($_COOKIE[$sessiontimeout])) {
125 ini_set('session.gc_maxlifetime', max(120, min(3600 * 24, (int) $_COOKIE[$sessiontimeout]))); // Between 120 and 86400
126}
127
128// This create lock, released by session_write_close() or end of page.
129// We need this lock as long as we read/write $_SESSION ['vars']. We can remove lock when finished.
130if (!defined('NOSESSION')) {
131 if (PHP_VERSION_ID < 70300) {
132 session_set_cookie_params(0, '/', null, !(empty($dolibarr_main_force_https) && isHTTPS() === false), true); // Add tag secure and httponly on session cookie (same as setting session.cookie_httponly into php.ini). Must be called before the session_start.
133 } else {
134 // Only available for php >= 7.3
135 $sessioncookieparams = array(
136 'lifetime' => 0,
137 'path' => '/',
138 //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
139 'secure' => !(empty($dolibarr_main_force_https) && isHTTPS() === false),
140 'httponly' => true,
141 'samesite' => 'Lax' // None || Lax || Strict
142 );
143 session_set_cookie_params($sessioncookieparams);
144 }
145 session_name($sessionname);
146 dol_session_start(); // This call the open and read of session handler
147 //exit; // this exist generates a call to write and close
148}
149
150
151// Init the 7 global objects, this include will make the 'new Xxx()' and set properties for: $conf, $db, $langs, $user, $mysoc, $hookmanager, $extrafields
152require_once 'master.inc.php';
164'
165@phan-var-force Conf $conf
166@phan-var-force ?DoliDB $db
167@phan-var-force ?HookManager $hookmanager
168@phan-var-force ?Translate $langs
169@phan-var-force ?User $user
170';
171
172// Uncomment this and set session.save_handler = user to use local session storing
173// include DOL_DOCUMENT_ROOT.'/core/lib/phpsessionindb.inc.php
174
175// If software has been locked. Only login getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED') is allowed.
176if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
177 $ok = 0;
178 if ((!session_id() || !isset($_SESSION["dol_login"])) && !isset($_POST["username"]) && !empty($_SERVER["GATEWAY_INTERFACE"])) {
179 $ok = 1; // We let working pages if not logged and inside a web browser (login form, to allow login by admin)
180 } elseif (isset($_POST["username"]) && in_array($_POST["username"], explode(';', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')))) {
181 $ok = 1; // We let working pages that is a login submission (login submit, to allow login by admin)
182 } elseif (defined('NOREQUIREDB')) {
183 $ok = 1; // We let working pages that don't need database access (xxx.css.php)
184 } elseif (defined('EVEN_IF_ONLY_LOGIN_ALLOWED')) {
185 $ok = 1; // We let working pages that ask to work even if only login enabled (logout.php)
186 } elseif (session_id() && isset($_SESSION["dol_login"]) && in_array($_SESSION["dol_login"], explode(';', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')))) {
187 $ok = 1; // We let working if user is allowed admin
188 }
189 if (!$ok) {
190 if (session_id() && isset($_SESSION["dol_login"]) && !in_array($_SESSION["dol_login"], explode(';', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')))) {
191 print 'Sorry, your application is offline.'."\n";
192 print 'You are logged with user "'.$_SESSION["dol_login"].'" and only administrator users (' . str_replace(';', ', ', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')).') is allowed to connect for the moment.'."\n";
193 $nexturl = dolBuildUrl(DOL_URL_ROOT . '/user/logout.php', [], true);
194 print 'Please try later or <a href="'.$nexturl.'">click here to disconnect and change login user</a>...'."\n";
195 } else {
196 print 'Sorry, your application is offline. Only administrator users (' . str_replace(';', ', ', getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')).') is allowed to connect for the moment.'."\n";
197 $nexturl = dolBuildUrl(DOL_URL_ROOT . '/');
198 print 'Please try later or <a href="'.$nexturl.'">click here to change login user</a>...'."\n";
199 }
200 exit;
201 }
202}
203
204
205// Activate end of page function
206register_shutdown_function('dol_shutdown');
207
208// Load debugbar
209if (isModEnabled('debugbar') && !GETPOST('dol_use_jmobile') && empty($_SESSION['dol_use_jmobile'])) {
210 global $debugbar;
211 include_once DOL_DOCUMENT_ROOT.'/debugbar/class/DebugBar.php';
212 $debugbar = new DolibarrDebugBar();
213 $renderer = $debugbar->getJavascriptRenderer();
214 if (!getDolGlobalString('MAIN_HTML_HEADER')) {
215 $conf->global->MAIN_HTML_HEADER = '';
216 }
217 $conf->global->MAIN_HTML_HEADER .= $renderer->renderHead();
218
219 '@phan-var-force array{time:DebugBar\DataCollector\TimeDataCollector} $debugbar';
220 $debugbar['time']->startMeasure('pageaftermaster', 'Page generation (after environment init)');
221}
222
223// Detection browser
224if (isset($_SERVER["HTTP_USER_AGENT"])) {
225 $tmp = getBrowserInfo($_SERVER["HTTP_USER_AGENT"]);
226 $conf->browser->name = $tmp['browsername'];
227 $conf->browser->os = $tmp['browseros'];
228 $conf->browser->version = $tmp['browserversion'];
229 $conf->browser->ua = $tmp['browserua'];
230 $conf->browser->layout = $tmp['layout']; // 'classic', 'phone', 'tablet'
231 //var_dump($conf->browser);
232
233 if ($conf->browser->layout == 'phone') {
234 $conf->dol_no_mouse_hover = 1;
235 }
236}
237
238// If theme is forced
239if (GETPOST('theme', 'aZ09')) {
240 $conf->theme = GETPOST('theme', 'aZ09');
241 $conf->css = "/theme/".$conf->theme."/style.css.php";
242}
243
244// Set global MAIN_OPTIMIZEFORTEXTBROWSER (must be before login part)
245if (GETPOSTINT('textbrowser') || (!empty($conf->browser->name) && $conf->browser->name == 'textbrowser')) { // If we must enable text browser
246 $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = 2;
247}
248
249// Force HTTPS if required ($conf->file->main_force_https is 0/1 or 'https dolibarr root url')
250// $_SERVER["HTTPS"] is 'on' when link is https, otherwise $_SERVER["HTTPS"] is empty or 'off'
251if (!empty($conf->file->main_force_https) && !isHTTPS() && !defined('NOHTTPSREDIRECT')) {
252 $newurl = '';
253 if (is_numeric($conf->file->main_force_https)) {
254 if ($conf->file->main_force_https == '1' && !empty($_SERVER["SCRIPT_URI"])) { // If SCRIPT_URI supported by server
255 if (preg_match('/^http:/i', $_SERVER["SCRIPT_URI"]) && !preg_match('/^https:/i', $_SERVER["SCRIPT_URI"])) { // If link is http
256 $newurl = preg_replace('/^http:/i', 'https:', $_SERVER["SCRIPT_URI"]);
257 }
258 } else {
259 // If HTTPS is not defined in DOL_MAIN_URL_ROOT,
260 // Check HTTPS environment variable (Apache/mod_ssl only)
261 $newurl = preg_replace('/^http:/i', 'https:', DOL_MAIN_URL_ROOT).$_SERVER["REQUEST_URI"];
262 }
263 } else {
264 // Check HTTPS environment variable (Apache/mod_ssl only)
265 $newurl = $conf->file->main_force_https.$_SERVER["REQUEST_URI"];
266 }
267 // Start redirect
268 if ($newurl) {
269 header_remove(); // Clean header already set to be sure to remove any header like "Set-Cookie: DOLSESSID_..." from non HTTPS answers
270 dol_syslog("main.inc: dolibarr_main_force_https is on, we make a redirect to ".$newurl);
271 header("Location: ".$newurl);
272 exit;
273 } else {
274 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);
275 }
276}
277
278if (!defined('NOLOGIN') && !defined('NOIPCHECK') && !empty($dolibarr_main_restrict_ip)) {
279 $listofip = explode(',', $dolibarr_main_restrict_ip);
280 $found = false;
281 $user_ip = $_SERVER['REMOTE_ADDR'];
282 foreach ($listofip as $ip) {
283 $authorized_ip = trim($ip);
284 if (strpos($authorized_ip, '/')) { // Check if IP with CIDR notation
285 if (checkIPInCidr($user_ip, $authorized_ip) > 0) {
286 $found = true;
287 break;
288 }
289 } elseif ($user_ip == $authorized_ip) {
290 $found = true;
291 break;
292 }
293 }
294 if (!$found) {
295 print 'Access refused by IP protection. Your detected IP is: '.dol_escape_htmltag($user_ip);
296 exit;
297 }
298}
299
300// Loading of additional presentation includes
301if (!defined('NOREQUIREHTML')) {
302 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; // Need 660ko memory (800ko in 2.2)
303}
304if (!defined('NOREQUIREAJAX')) {
305 require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; // Need 22ko memory
306}
307
308// If install or upgrade process not done or not completely finished, we call the install page.
309if (getDolGlobalString('MAIN_NOT_INSTALLED') || getDolGlobalString('MAIN_NOT_UPGRADED')) {
310 dol_syslog("main.inc: A previous install or upgrade was not complete. Redirect to install page.", LOG_WARNING);
311 header("Location: ".DOL_URL_ROOT."/install/index.php");
312 exit;
313}
314// If an upgrade process is required, we call the install page.
315$checkifupgraderequired = false;
316if (getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') && getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') != DOL_VERSION) {
317 $checkifupgraderequired = true;
318}
319if (!getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') && getDolGlobalString('MAIN_VERSION_LAST_INSTALL') && getDolGlobalString('MAIN_VERSION_LAST_INSTALL') != DOL_VERSION) {
320 $checkifupgraderequired = true;
321}
322if ($checkifupgraderequired && !defined('MAIN_VERSION_DISABLE_DB_CHECK')) {
323 $versiontocompare = getDolGlobalString('MAIN_VERSION_LAST_UPGRADE', getDolGlobalString('MAIN_VERSION_LAST_INSTALL'));
324 require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
325 $dolibarrversionlastupgrade = preg_split('/[.-]/', $versiontocompare);
326 $dolibarrversionprogram = preg_split('/[.-]/', DOL_VERSION);
327 $rescomp = versioncompare($dolibarrversionprogram, $dolibarrversionlastupgrade);
328 if ($rescomp > 0) { // Programs have a version higher than database.
329 if (!getDolGlobalString('MAIN_NO_UPGRADE_REDIRECT_ON_LEVEL_3_CHANGE') || $rescomp < 3) {
330 // We did not add "&& $rescomp < 3" because we want upgrade process for build upgrades
331 dol_syslog("main.inc: database version ".$versiontocompare." is lower than programs version ".DOL_VERSION.". Redirect to install/upgrade page.", LOG_WARNING);
332 if (php_sapi_name() === "cli") {
333 print "main.inc: database version ".$versiontocompare." is lower than programs version ".DOL_VERSION.". Try to run upgrade process.\n";
334 } else {
335 header("Location: ".DOL_URL_ROOT."/install/index.php");
336 }
337 exit;
338 }
339 }
340}
341
342// Creation of a token against CSRF vulnerabilities
343if (!defined('NOTOKENRENEWAL') && !defined('NOSESSION')) {
344 // No token renewal on .css.php, .js.php and .json.php (even if the NOTOKENRENEWAL was not provided)
345 if (!preg_match('/\.(css|js|json)\.php$/', $_SERVER["PHP_SELF"])) {
346 // Rolling token at each call ($_SESSION['token'] contains token of previous page)
347 if (isset($_SESSION['newtoken'])) {
348 $_SESSION['token'] = $_SESSION['newtoken'];
349 }
350
351 if (!isset($_SESSION['newtoken']) || getDolGlobalInt('MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL')) {
352 // Note: Using MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL is not recommended: if a user succeed in entering a data from
353 // a public page with a link that make a token regeneration, it can make use of the backoffice no more possible !
354 // Save in $_SESSION['newtoken'] what will be next token. Into forms, we will add param token = $_SESSION['newtoken']
355 $token = bin2hex(random_bytes(32));
356 $_SESSION['newtoken'] = $token;
357 dol_syslog("NEW TOKEN generated by : ".$_SERVER['PHP_SELF'], LOG_DEBUG);
358 }
359 }
360}
361
362//dol_syslog("CSRF info: ".defined('NOCSRFCHECK')." - ".$dolibarr_nocsrfcheck." - ".getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN')." - ".$_SERVER['REQUEST_METHOD']." - ".GETPOST('token', 'alpha'));
363
364// Check validity of token, only if option MAIN_SECURITY_CSRF_WITH_TOKEN enabled or if constant CSRFCHECK_WITH_TOKEN is set into page
365if ((!defined('NOCSRFCHECK') && empty($dolibarr_nocsrfcheck) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) || defined('CSRFCHECK_WITH_TOKEN')) {
366 $tmpaction = GETPOST('action', 'aZ09');
367 // Array of action code where CSRFCHECK with token will be forced (so token must be provided on url request)
368 $sensitiveget = false;
369 if ((GETPOSTISSET('massaction') || $tmpaction) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 3) {
370 // All GET actions (except the listed exceptions that are usually post for pre-actions and not real action) and mass actions are processed as sensitive.
371 // We exclude some action that are not sensitive so legitimate
372 if (GETPOSTISSET('massaction') || (strpos($tmpaction, 'display') !== 0 && !in_array($tmpaction, array('check', 'create', 'create2', 'createsite', 'createcard', 'edit', 'editcontract', 'editfile', 'editvalidator', 'file_manager', 'history', 'presend', 'presend_addmessage', 'preview', 'reconcile', 'specimen', 'testsetup', 'undeployconfirmed', 'validatenewpassword', 'view')))) {
373 // Note: 'create' is for form to ask creattion, realcreation is action 'add'
374 // Note: 'check' if for the feature to control an archive.
375 $sensitiveget = true;
376 }
377 } elseif (getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 2) {
378 // Few GET actions coded with a &token into url are also processed as sensitive.
379 $arrayofactiontoforcetokencheck = array(
380 'activate',
381 'doprev', 'donext', 'dvprev', 'dvnext',
382 'freezone', 'install',
383 'reopen'
384 );
385 if (in_array($tmpaction, $arrayofactiontoforcetokencheck)) {
386 $sensitiveget = true;
387 }
388 // We also need a valid token for actions matching one of these values
389 if (preg_match('/^(confirm_)?(add|classify|close|confirm|copy|del|disable|enable|remove|set|unset|update|save)/', $tmpaction)) {
390 $sensitiveget = true;
391 }
392 }
393
394 // Check a token is provided for all cases that need a mandatory token
395 // (all POST actions + all sensitive GET actions + all mass actions + all login/actions/logout on pages with CSRFCHECK_WITH_TOKEN set)
396 if (
397 (!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') ||
398 $sensitiveget ||
399 GETPOSTISSET('massaction') ||
400 ((GETPOSTISSET('actionlogin') || GETPOSTISSET('action')) && defined('CSRFCHECK_WITH_TOKEN'))
401 ) {
402 // If token is not provided or empty, error (we are in case it is mandatory)
403 if (!GETPOST('token', 'alpha') || GETPOST('token', 'alpha') == 'notrequired') {
404 top_httphead();
405 if (GETPOSTINT('uploadform')) {
406 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." refused. File size too large or not provided.");
407 $langs->loadLangs(array("errors", "install"));
408 print $langs->trans("ErrorFileSizeTooLarge").' ';
409 print $langs->trans("ErrorGoBackAndCorrectParameters");
410 } else {
411 http_response_code(403);
412 if (defined('CSRFCHECK_WITH_TOKEN')) {
413 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);
414 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";
415 } else {
416 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);
417 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";
418 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";
419 if (getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN')) {
420 print " instead of " . getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN');
421 }
422 print " into setup).\n";
423 }
424 }
425 die;
426 }
427 }
428
429 $sessiontokenforthisurl = (empty($_SESSION['token']) ? '' : $_SESSION['token']);
430 // 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)
431 if (GETPOSTISSET('token') && GETPOST('token') != 'notrequired' && GETPOST('token', 'alpha') != $sessiontokenforthisurl) {
432 dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." refused by CSRF protection (invalid token), so we disable POST and some GET parameters - referrer=".(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']).", action=".GETPOST('action', 'aZ09').", _GET|POST['token']=".GETPOST('token', 'alpha'), LOG_WARNING);
433 //dol_syslog("_SESSION['token']=".$sessiontokenforthisurl, LOG_DEBUG);
434 // 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.
435 if (!defined('NOTOKENRENEWAL')) {
436 // If the page is not a page that disable the token renewal, we report a warning message to explain token has expired.
437 setEventMessages('SecurityTokenHasExpiredSoActionHasBeenCanceledPleaseRetry', null, 'warnings', '', 1);
438 }
439 $savid = null;
440 if (isset($_POST['id'])) {
441 $savid = ((int) $_POST['id']);
442 }
443 unset($_POST);
444 unset($_GET['confirm']);
445 unset($_GET['action']);
446 unset($_GET['confirmmassaction']);
447 unset($_GET['massaction']);
448 unset($_GET['token']); // TODO Make a redirect if we have a token in url to remove it ?
449 if (isset($savid)) {
450 $_POST['id'] = ((int) $savid);
451 }
452 // So rest of code can know something was wrong here
453 $_GET['errorcode'] = 'InvalidToken';
454 }
455
456 // Note: There is another CSRF protection into the filefunc.inc.php
457}
458
459if (!empty($dolibarr_main_demo)) {
460 // Disable modules (this must be after session_start and after conf has been loaded)
461 if (GETPOSTISSET('disablemodules')) {
462 $_SESSION["disablemodules"] = GETPOST('disablemodules', 'alpha');
463 }
464 if (!empty($_SESSION["disablemodules"])) {
465 $modulepartkeys = array('css', 'js', 'tabs', 'triggers', 'login', 'substitutions', 'menus', 'theme', 'sms', 'tpl', 'barcode', 'models', 'societe', 'hooks', 'dir', 'syslog', 'tpllinkable', 'contactelement', 'moduleforexternal', 'websitetemplates');
466
467 $disabled_modules = explode(',', $_SESSION["disablemodules"]);
468 foreach ($disabled_modules as $module) {
469 if ($module) {
470 if (empty($conf->$module)) {
471 $conf->$module = new stdClass(); // To avoid warnings
472 }
473
474 $conf->$module->enabled = false; // Old usage
475 unset($conf->modules[$module]);
476
477 foreach ($modulepartkeys as $modulepartkey) {
478 unset($conf->modules_parts[$modulepartkey][$module]);
479 }
480 if ($module == 'fournisseur') { // Special case
481 $conf->supplier_order->enabled = 0; // Old usage
482 $conf->supplier_invoice->enabled = 0; // Old usage
483 unset($conf->modules['supplier_order']);
484 unset($conf->modules['supplier_invoice']);
485 }
486 }
487 }
488 }
489}
490
491// Set current modulepart
492$modulepart = explode("/", $_SERVER["PHP_SELF"]);
493if (is_array($modulepart) && count($modulepart) > 0) {
494 foreach ($conf->modules as $module) {
495 if (in_array($module, $modulepart)) {
496 $modulepart = $module;
497 break;
498 }
499 }
500}
501if (is_array($modulepart)) {
502 $modulepart = '';
503}
504
505
506/*
507 * Phase authentication / login
508 */
509
510$login = '';
511$error = 0;
512if (!defined('NOLOGIN')) {
513 // $authmode lists the different method of identification to be tested in order of preference.
514 // Example: 'http', 'dolibarr', 'ldap', 'http,forceuser', '...'
515
516 if (defined('MAIN_AUTHENTICATION_MODE')) {
517 $dolibarr_main_authentication = constant('MAIN_AUTHENTICATION_MODE');
518 } else {
519 // Authentication mode
520 if (empty($dolibarr_main_authentication)) {
521 $dolibarr_main_authentication = 'dolibarr';
522 }
523 // Authentication mode: forceuser
524 if ($dolibarr_main_authentication == 'forceuser' && empty($dolibarr_auto_user)) {
525 $dolibarr_auto_user = 'auto';
526 }
527 }
528 // Set authmode
529 $authmode = explode(',', $dolibarr_main_authentication);
530
531 // No authentication mode
532 if (!count($authmode)) {
533 $langs->load('main');
534 dol_print_error(null, $langs->trans("ErrorConfigParameterNotDefined", 'dolibarr_main_authentication'));
535 exit;
536 }
537
538 // If login request was already post, we retrieve login from the session
539 // Call module if not realized that his request.
540 // At the end of this phase, the variable $login is defined.
541 $resultFetchUser = '';
542 $test = true;
543 $dol_authmode = null;
544
545 if (!isset($_SESSION["dol_login"])) {
546 // It is not already authenticated and it requests the login / password
547 include_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
548
549 $dol_dst_observed = GETPOSTINT("dst_observed", 3);
550 $dol_dst_first = GETPOSTINT("dst_first", 3);
551 $dol_dst_second = GETPOSTINT("dst_second", 3);
552 $dol_screenwidth = GETPOSTINT("screenwidth", 3);
553 $dol_screenheight = GETPOSTINT("screenheight", 3);
554 $dol_hide_topmenu = GETPOSTINT('dol_hide_topmenu', 3);
555 $dol_hide_leftmenu = GETPOSTINT('dol_hide_leftmenu', 3);
556 $dol_optimize_smallscreen = GETPOSTINT('dol_optimize_smallscreen', 3);
557 $dol_no_mouse_hover = GETPOSTINT('dol_no_mouse_hover', 3);
558 $dol_use_jmobile = GETPOSTINT('dol_use_jmobile', 3); // 0=default, 1=to say we use app from a webview app, 2=to say we use app from a webview app and keep ajax
559
560 // If in demo mode, we check we go to home page through the public/demo/index.php page
561 if (!empty($dolibarr_main_demo) && $_SERVER['PHP_SELF'] == DOL_URL_ROOT.'/index.php') { // We ask index page
562 if (empty($_SERVER['HTTP_REFERER']) || !preg_match('/public/', $_SERVER['HTTP_REFERER'])) {
563 dol_syslog("Call index page from another url than demo page (call is done from page ".(empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']).")");
564 $query = [];
565 if ($dol_hide_topmenu) {
566 $query += ['dol_hide_topmenu' => $dol_hide_topmenu];
567 }
568 if ($dol_hide_leftmenu) {
569 $query += ['dol_hide_leftmenu' => $dol_hide_leftmenu];
570 }
571 if ($dol_optimize_smallscreen) {
572 $query += ['dol_optimize_smallscreen' => $dol_optimize_smallscreen];
573 }
574 if ($dol_no_mouse_hover) {
575 $query += ['dol_no_mouse_hover='.$dol_no_mouse_hover];
576 }
577 if ($dol_use_jmobile) {
578 $query += ['dol_use_jmobile='.$dol_use_jmobile];
579 }
580 header("Location: " . dolBuildUrl(DOL_URL_ROOT . '/public/demo/index.php', $query));
581 exit;
582 }
583 }
584
585 // Hooks for security access
586 $action = '';
587 $hookmanager->initHooks(array('login'));
588 $parameters = array();
589 $reshook = $hookmanager->executeHooks('beforeLoginAuthentication', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
590 if ($reshook < 0) {
591 $test = false;
592 $error++;
593 }
594
595 // Verification security graphic code
596 if ($test && GETPOST('actionlogin', 'aZ09') == 'login' && GETPOST("username", "alpha", 2) && getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA') && !isset($_SESSION['dol_bypass_antispam'])) {
597 $ok = false;
598
599 // Use the captcha handler to validate
600 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
601 $captcha = getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA_HANDLER', 'standard');
602
603 // List of directories where we can find captcha handlers
604 $dirModCaptcha = array_merge(array('main' => '/core/modules/security/captcha/'), isset($conf->modules_parts['captcha']) && is_array($conf->modules_parts['captcha']) ? $conf->modules_parts['captcha'] : array());
605 $fullpathclassfile = '';
606 foreach ($dirModCaptcha as $dir) {
607 $fullpathclassfile = dol_buildpath($dir."modCaptcha".ucfirst($captcha).'.class.php', 0, 2);
608 if ($fullpathclassfile) {
609 break;
610 }
611 }
612
613 // The file for captcha check has been found
614 if ($fullpathclassfile) {
615 include_once $fullpathclassfile;
616 $captchaobj = null;
617
618 // Charging the numbering class
619 $classname = "modCaptcha".ucfirst($captcha);
620 if (class_exists($classname)) {
622 $captchaobj = new $classname($db, $conf, $langs, $user);
623 '@phan-var-force ModeleCaptcha $captchaobj';
624
625 if (is_object($captchaobj) && method_exists($captchaobj, 'validateCodeAfterLoginSubmit')) {
626 $ok = $captchaobj->validateCodeAfterLoginSubmit(); // @phan-suppress-current-line PhanUndeclaredMethod
627 } else {
628 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler '.get_class($captchaobj).' does not have any method validateCodeAfterLoginSubmit()';
629 $test = false;
630 $error++;
631 }
632 } else {
633 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler class '.$classname.' was not found after the include';
634 $test = false;
635 $error++;
636 }
637 } else {
638 $_SESSION["dol_loginmesg"] = 'Error, the captcha handler '.$captcha.' has no class file found modCaptcha'.ucfirst($captcha);
639 $test = false;
640 $error++;
641 }
642
643 // Process error of captcha validation
644 if (!$ok) {
645 dol_syslog('--- Security warning: Bad value for code, connection refused', LOG_NOTICE);
646 // Load translation files required by page
647 $langs->loadLangs(array('main', 'errors'));
648
649 $_SESSION["dol_loginmesg"] = (empty($_SESSION["dol_loginmesg"]) ? "" : $_SESSION["dol_loginmesg"]."<br>\n").$langs->transnoentitiesnoconv("ErrorBadValueForCode");
650 $test = false;
651
652 // Call trigger for the "security events" log
653 $user->context['audit'] = 'ErrorBadValueForCode - login='.GETPOST("username", "alpha", 2);
654
655 // Call trigger
656 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
657 if ($result < 0) {
658 $error++;
659 }
660 // End call triggers
661
662 // Hooks on failed login
663 $action = '';
664 $hookmanager->initHooks(array('login'));
665 $parameters = array('dol_authmode' => $authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
666 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
667 if ($reshook < 0) {
668 $error++;
669 }
670
671 // Note: exit is done later ($test is false)
672 }
673 }
674
675 $allowedmethodtopostusername = 3;
676 if (defined('MAIN_AUTHENTICATION_POST_METHOD')) {
677 $allowedmethodtopostusername = constant('MAIN_AUTHENTICATION_POST_METHOD'); // Note a value of 2 is not compatible with some authentication methods that put username as GET parameter
678 }
679 // TODO Remove use of $_COOKIE['login_dolibarr'] by replacing line with $usertotest = GETPOST("username", "alpha", $allowedmethodtopostusername); ?
680 $usertotest = (!empty($_COOKIE['login_dolibarr']) ? preg_replace('/[^a-zA-Z0-9_@\-\.]/', '', $_COOKIE['login_dolibarr']) : GETPOST("username", "alpha", $allowedmethodtopostusername));
681 $passwordtotest = GETPOST('password', 'password', $allowedmethodtopostusername);
682 $entitytotest = (GETPOSTINT('entity') ? GETPOSTINT('entity') : (!empty($conf->entity) ? $conf->entity : 1));
683
684 // Define if we received the correct data to go into the test of the login with the checkLoginPassEntity().
685 $goontestloop = false;
686 if (isset($_SERVER["REMOTE_USER"]) && in_array('http', $authmode)) { // For http basic login test
687 $goontestloop = true;
688 }
689 if ($dolibarr_main_authentication == 'forceuser' && !empty($dolibarr_auto_user)) { // For automatic login with a forced user
690 $goontestloop = true;
691 }
692 if (GETPOST("username", "alpha", $allowedmethodtopostusername)) { // For posting the login form
693 $goontestloop = true;
694 }
695 if (GETPOST('openid_mode', 'alpha')) { // For openid_connect ?
696 $goontestloop = true;
697 }
698 if (GETPOST('beforeoauthloginredirect') || GETPOST('afteroauthloginreturn')) { // For oauth login
699 $goontestloop = true;
700 }
701 if (!empty($_COOKIE['login_dolibarr'])) { // TODO For ? Remove this ?
702 $goontestloop = true;
703 }
704
705 if (!is_object($langs)) { // This can occurs when calling page with NOREQUIRETRAN defined, however we need langs for error messages.
706 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
707 $langs = new Translate("", $conf);
708 $langcode = (GETPOST('lang', 'aZ09', 1) ? GETPOST('lang', 'aZ09', 1) : getDolGlobalString('MAIN_LANG_DEFAULT', 'auto'));
709 if (defined('MAIN_LANG_DEFAULT')) {
710 $langcode = constant('MAIN_LANG_DEFAULT');
711 }
712 $langs->setDefaultLang($langcode);
713 }
714
715 // Test HTTP header
716 if (!empty($_SERVER['HTTP_EXPOSED_CREDENTIAL_CHECK'])) {
717 // TODO Read option $dolibarr_main_no_leaked_credentials with value 1, 2, ... and return
718 //dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"].' refused by option $dolibarr_main_no_leaked_credentials='.$dolibarr_main_no_leaked_credentials, LOG_NOTICE);
719 dol_syslog('--- Security warning: credentials reported as leaked were used to try to login. HTTP_EXPOSED_CREDENTIAL_CHECK='.((int) $_SERVER['HTTP_EXPOSED_CREDENTIAL_CHECK']), LOG_NOTICE);
720 }
721
722 // Validation of login/pass/entity
723 // If ok, the variable login will be returned
724 // If error, we will put error message in session under the name dol_loginmesg
725 if ($test && $goontestloop && GETPOST('actionlogin', 'aZ09') != 'disabled' && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {
726 // Loop on each test mode defined into $authmode
727 // $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');
728 $oauthmodetotestarray = array('google');
729 foreach ($oauthmodetotestarray as $oauthmodetotest) {
730 if (in_array($oauthmodetotest.'oauth', $authmode)) { // This is an authmode that is currently qualified. Do we have to remove it ?
731 // If we click on the link to use OAuth authentication or if we go here after a callback return, we do nothing
732 if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {
733 continue;
734 }
735 dol_syslog("User did not click on link for OAuth mode ".$oauthmodetotest.", param beforeoauthloginredirect is ".GETPOST('beforeoauthloginredirect')." and param afteroauthloginreturn is ".GETPOST('afteroauthloginreturn')." so we disable check of login for mode ".$oauthmodetotest);
736 foreach ($authmode as $tmpkey => $tmpval) {
737 if ($tmpval == $oauthmodetotest.'oauth') {
738 unset($authmode[$tmpkey]);
739 break;
740 }
741 }
742 }
743 }
744
745 // Check login for all qualified modes in array $authmode.
746 $login = checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode);
747 if ($login === '--bad-login-validity--') {
748 $login = '';
749 }
750
751 if ($login) {
752 $dol_authmode = $conf->authmode; // This property is defined only when logged, to say what mode was successfully used
753 // Check POST first, then GET (for OIDC callback redirect), then SESSION
754 $dol_tz = empty($_POST["tz"]) ? (empty($_GET["tz"]) ? (empty($_SESSION["tz"]) ? '' : $_SESSION["tz"]) : (int) $_GET["tz"]) : $_POST["tz"];
755 $dol_tz_string = empty($_POST["tz_string"]) ? (empty($_GET["tz_string"]) ? (empty($_SESSION["tz_string"]) ? '' : $_SESSION["tz_string"]) : $_GET["tz_string"]) : $_POST["tz_string"];
756 $dol_tz_string = preg_replace('/\s*\‍(.+\‍)$/', '', $dol_tz_string);
757 $dol_tz_string = preg_replace('/,/', '/', $dol_tz_string);
758 $dol_tz_string = preg_replace('/\s/', '_', $dol_tz_string);
759 $dol_dst = 0;
760 // Check POST first, then GET (for OIDC callback redirect), then SESSION
761 $dol_dst_first = empty($_POST["dst_first"]) ? (empty($_GET["dst_first"]) ? (empty($_SESSION["dst_first"]) ? '' : $_SESSION["dst_first"]) : (int) $_GET["dst_first"]) : $_POST["dst_first"];
762 $dol_dst_second = empty($_POST["dst_second"]) ? (empty($_GET["dst_second"]) ? (empty($_SESSION["dst_second"]) ? '' : $_SESSION["dst_second"]) : (int) $_GET["dst_second"]) : $_POST["dst_second"];
763 if ($dol_dst_first && $dol_dst_second) {
764 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
765 $datenow = dol_now();
766 $datefirst = dol_stringtotime($dol_dst_first);
767 $datesecond = dol_stringtotime($dol_dst_second);
768 if ($datenow >= $datefirst && $datenow < $datesecond) {
769 $dol_dst = 1;
770 }
771 }
772 $dol_screenheight = empty($_POST["screenheight"]) ? (empty($_GET["screenheight"]) ? (empty($_SESSION["dol_screenheight"]) ? '' : $_SESSION["dol_screenheight"]) : (int) $_GET["screenheight"]) : $_POST["screenheight"];
773 $dol_screenwidth = empty($_POST["screenwidth"]) ? (empty($_GET["screenwidth"]) ? (empty($_SESSION["dol_screenwidth"]) ? '' : $_SESSION["dol_screenwidth"]) : (int) $_GET["screenwidth"]) : $_POST["screenwidth"];
774 //print $datefirst.'-'.$datesecond.'-'.$datenow.'-'.$dol_tz.'-'.$dol_tzstring.'-'.$dol_dst.'-'.sdol_screenheight.'-'.sdol_screenwidth; exit;
775 }
776
777 if (!$login) {
778 dol_syslog('Bad password, connection refused (see a previous notice message for more info)', LOG_NOTICE);
779 // Load translation files required by page
780 $langs->loadLangs(array('main', 'errors'));
781
782 // Bad password. No authmode has found a good password.
783 // We set a generic message if not defined inside function checkLoginPassEntity or subfunctions
784 if (empty($_SESSION["dol_loginmesg"])) {
785 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadLoginPassword");
786 }
787
788 // Call trigger for the "security events" log
789 $user->context['audit'] = $langs->trans("ErrorBadLoginPassword").' - login='.GETPOST("username", "alpha", 2);
790
791 // Call trigger
792 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
793 if ($result < 0) {
794 $error++;
795 }
796 // End call triggers
797
798 // Hooks on failed login
799 $action = '';
800 $hookmanager->initHooks(array('login'));
801 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
802 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
803 if ($reshook < 0) {
804 $error++;
805 }
806
807 // Note: exit is done in next chapter
808 }
809 }
810
811 // End test login / passwords
812 if (!$login || (in_array('ldap', $authmode) && !in_array('openid_connect', $authmode) && empty($passwordtotest))) { // With LDAP we refused empty password because some LDAP are "opened" for anonymous access so connection is a success.
813 // No data to test login, so we show the login page.
814 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);
815 if (defined('NOREDIRECTBYMAINTOLOGIN')) {
816 // When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
817 // See example with selectsearchbox.php. This case is reserved for the selectesearchbox.php so we can
818 // report a message to ask to login when search ajax component is used after a timeout.
819 //top_httphead();
820 return 'ERROR_NOT_LOGGED';
821 } else {
822 if (!empty($_SERVER["HTTP_USER_AGENT"]) && $_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
823 http_response_code(401); // It makes easier to understand if session was broken during security tests
824 }
825
826 // Show login form
827 dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : '')); // This include http headers
828 }
829 exit;
830 }
831
832 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.
833
834 if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
835 dol_syslog('User not found or not valid, connection refused');
836 session_destroy();
837 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
838 session_name($sessionname);
840
841 if ($resultFetchUser == 0) {
842 // Load translation files required by page
843 $langs->loadLangs(array('main', 'errors'));
844
845 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
846
847 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
848 } elseif ($resultFetchUser < 0) {
849 $_SESSION["dol_loginmesg"] = $user->error;
850
851 $user->context['audit'] = $user->error;
852 } else {
853 // Load translation files required by the page
854 $langs->loadLangs(array('main', 'errors'));
855
856 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginDateValidity");
857
858 $user->context['audit'] = $langs->trans("ErrorLoginDateValidity").' - login='.$login;
859 }
860
861 // Call trigger
862 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
863 if ($result < 0) {
864 $error++;
865 }
866 // End call triggers
867
868
869 // Hooks on failed login
870 $action = '';
871 $hookmanager->initHooks(array('login'));
872 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
873 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
874 if ($reshook < 0) {
875 $error++;
876 }
877
878 $paramsurl = [];
879 if (GETPOSTINT('textbrowser')) {
880 $paramsurl += ['textbrowser' => GETPOSTINT('textbrowser')];
881 }
882 if (GETPOSTINT('nojs')) {
883 $paramsurl += ['nojs' => GETPOSTINT('nojs')];
884 }
885 if (GETPOST('lang', 'aZ09')) {
886 $paramsurl += ['lang' => (string) GETPOST('lang', 'aZ09')];
887 }
888 header('Location: '.dolBuildUrl(DOL_URL_ROOT . '/index.php', $paramsurl));
889 exit;
890 } else {
891 // User is loaded, we may need to change language for him according to its choice
892 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
893 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
894 }
895 }
896 } else {
897 // We are already into an authenticated session
898 $login = $_SESSION["dol_login"];
899 $entity = isset($_SESSION["dol_entity"]) ? $_SESSION["dol_entity"] : 0;
900 dol_syslog("- This is an already logged session. _SESSION['dol_login']=".$login." _SESSION['dol_entity']=".$entity, LOG_DEBUG);
901
902 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entity > 0 ? $entity : -1));
903
904 //var_dump(dol_print_date($user->flagdelsessionsbefore, 'dayhour', 'gmt')." ".dol_print_date($_SESSION["dol_logindate"], 'dayhour', 'gmt'));
905
906 if ($resultFetchUser <= 0
907 || ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"])
908 || ($user->status != $user::STATUS_ENABLED)
909 || ($user->isNotIntoValidityDateRange())) {
910 if ($resultFetchUser <= 0) {
911 // Account has been removed after login
912 dol_syslog("Can't load user even if session logged. _SESSION['dol_login']=".$login, LOG_WARNING);
913 } elseif ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"]) {
914 // Session is no more valid
915 dol_syslog("The user has a date for session invalidation = ".$user->flagdelsessionsbefore." and a session date = ".$_SESSION["dol_logindate"].". We must invalidate its sessions.");
916 } elseif ($user->status != $user::STATUS_ENABLED) {
917 // User is not enabled
918 dol_syslog("The user login is disabled");
919 } else {
920 // User validity dates are no more valid
921 dol_syslog("The user login has a validity between [".$user->datestartvalidity." and ".$user->dateendvalidity."], current date is ".dol_now());
922 }
923 session_destroy();
924 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
925 session_name($sessionname);
927
928 if ($resultFetchUser == 0) {
929 $langs->loadLangs(array('main', 'errors'));
930
931 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
932
933 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
934 } elseif ($resultFetchUser < 0) {
935 $_SESSION["dol_loginmesg"] = $user->error;
936
937 $user->context['audit'] = $user->error;
938 } else {
939 $langs->loadLangs(array('main', 'errors'));
940
941 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorSessionInvalidatedAfterPasswordChange");
942
943 $user->context['audit'] = 'ErrorUserSessionWasInvalidated - login='.$login;
944 }
945
946 // Call trigger
947 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
948 if ($result < 0) {
949 $error++;
950 }
951 // End call triggers
952
953 // Hooks on failed login
954 $action = '';
955 $hookmanager->initHooks(array('login'));
956 $parameters = array('dol_authmode' => (string) $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
957 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
958 if ($reshook < 0) {
959 $error++;
960 }
961
962 $paramsurl = array();
963 if (GETPOSTINT('textbrowser')) {
964 $paramsurl[] = 'textbrowser='.GETPOSTINT('textbrowser');
965 }
966 if (GETPOSTINT('nojs')) {
967 $paramsurl[] = 'nojs='.GETPOSTINT('nojs');
968 }
969 if (GETPOST('lang', 'aZ09')) {
970 $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
971 }
972
973 header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
974 exit;
975 } else {
976 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
977 $hookmanager->initHooks(array('main'));
978
979 // Code for search criteria persistence.
980 if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
981 $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]);
982 $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server
983 // Clean $relativepathstring
984 if (constant('DOL_URL_ROOT')) {
985 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
986 }
987 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
988 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
989 //var_dump($relativepathstring);
990
991 // 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
992 if (!empty($_SESSION['lastsearch_values_tmp_'.$relativepathstring])) {
993 $_SESSION['lastsearch_values_'.$relativepathstring] = $_SESSION['lastsearch_values_tmp_'.$relativepathstring];
994 unset($_SESSION['lastsearch_values_tmp_'.$relativepathstring]);
995 }
996 if (!empty($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring])) {
997 $_SESSION['lastsearch_contextpage_'.$relativepathstring] = $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring];
998 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
999 }
1000 if (!empty($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] != $conf->liste_limit) {
1001 $_SESSION['lastsearch_limit_'.$relativepathstring] = $_SESSION['lastsearch_limit_tmp_'.$relativepathstring];
1002 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
1003 }
1004 if (!empty($_SESSION['lastsearch_page_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_page_tmp_'.$relativepathstring] > 0) {
1005 $_SESSION['lastsearch_page_'.$relativepathstring] = $_SESSION['lastsearch_page_tmp_'.$relativepathstring];
1006 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
1007 }
1008 if (!empty($_SESSION['lastsearch_mode_tmp_'.$relativepathstring])) {
1009 $_SESSION['lastsearch_mode_'.$relativepathstring] = $_SESSION['lastsearch_mode_tmp_'.$relativepathstring];
1010 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
1011 }
1012 }
1013 if (!empty($_GET['save_pageforbacktolist']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1014 if (empty($_SESSION['pageforbacktolist'])) {
1015 $pageforbacktolistarray = array();
1016 } else {
1017 $pageforbacktolistarray = $_SESSION['pageforbacktolist'];
1018 }
1019 $tmparray = explode(':', $_GET['save_pageforbacktolist'], 2);
1020 if (!empty($tmparray[0]) && !empty($tmparray[1])) {
1021 $pageforbacktolistarray[$tmparray[0]] = $tmparray[1];
1022 $_SESSION['pageforbacktolist'] = $pageforbacktolistarray;
1023 }
1024 }
1025
1026 $action = '';
1027 $parameters = array();
1028 $reshook = $hookmanager->executeHooks('updateSession', $parameters, $user, $action);
1029 if ($reshook < 0) {
1030 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1031 }
1032 }
1033 }
1034
1035 // Is it a new session that has started ?
1036 // If we are here, this means authentication was successful.
1037 if (!isset($_SESSION["dol_login"])) {
1038 // New session for this login has started.
1039 $error = 0;
1040
1041 // Store value into session (values always stored)
1042 $_SESSION["dol_login"] = $user->login;
1043 $_SESSION["dol_logindate"] = dol_now('gmt');
1044 $_SESSION["dol_authmode"] = isset($dol_authmode) ? $dol_authmode : '';
1045 $_SESSION["dol_tz"] = isset($dol_tz) ? $dol_tz : '';
1046 $_SESSION["dol_tz_string"] = isset($dol_tz_string) ? $dol_tz_string : '';
1047 $_SESSION["dol_dst"] = isset($dol_dst) ? $dol_dst : '';
1048 $_SESSION["dol_dst_observed"] = isset($dol_dst_observed) ? $dol_dst_observed : '';
1049 $_SESSION["dol_dst_first"] = isset($dol_dst_first) ? $dol_dst_first : '';
1050 $_SESSION["dol_dst_second"] = isset($dol_dst_second) ? $dol_dst_second : '';
1051 $_SESSION["dol_screenwidth"] = isset($dol_screenwidth) ? $dol_screenwidth : '';
1052 $_SESSION["dol_screenheight"] = isset($dol_screenheight) ? $dol_screenheight : '';
1053 $_SESSION["dol_company"] = getDolGlobalString("MAIN_INFO_SOCIETE_NOM");
1054 $_SESSION["dol_entity"] = $conf->entity;
1055 // Store value into session (values stored only if defined)
1056 if (!empty($dol_hide_topmenu)) {
1057 $_SESSION['dol_hide_topmenu'] = $dol_hide_topmenu;
1058 }
1059 if (!empty($dol_hide_leftmenu)) {
1060 $_SESSION['dol_hide_leftmenu'] = $dol_hide_leftmenu;
1061 }
1062 if (!empty($dol_optimize_smallscreen)) {
1063 $_SESSION['dol_optimize_smallscreen'] = $dol_optimize_smallscreen;
1064 }
1065 if (!empty($dol_no_mouse_hover)) {
1066 $_SESSION['dol_no_mouse_hover'] = $dol_no_mouse_hover;
1067 }
1068 if (!empty($dol_use_jmobile)) {
1069 $_SESSION['dol_use_jmobile'] = $dol_use_jmobile;
1070 }
1071
1072 dol_syslog("This is a new started user session. _SESSION['dol_login']=".$_SESSION["dol_login"]." Session id=".session_id());
1073
1074 $db->begin();
1075
1076 $user->update_last_login_date();
1077
1078 $loginfo = 'TZ='.$_SESSION["dol_tz"].';TZString='.$_SESSION["dol_tz_string"].';Screen='.$_SESSION["dol_screenwidth"].'x'.$_SESSION["dol_screenheight"];
1079 $loginfo .= ' - authmode='.$dol_authmode.' - entity='.$conf->entity;
1080
1081 // Call triggers for the "security events" log
1082 $user->context['audit'] = $loginfo;
1083 $user->context['authentication_method'] = $dol_authmode;
1084
1085 // Call trigger
1086 $result = $user->call_trigger('USER_LOGIN', $user);
1087 if ($result < 0) {
1088 $error++;
1089 }
1090 // End call triggers
1091
1092 // Hooks on successful login
1093 $action = '';
1094 $hookmanager->initHooks(array('login'));
1095 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginfo' => $loginfo);
1096 $reshook = $hookmanager->executeHooks('afterLogin', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1097 if ($reshook < 0) {
1098 $error++;
1099 }
1100
1101 if ($error) {
1102 $db->rollback();
1103 session_destroy();
1104 dol_print_error($db, 'Error in some triggers USER_LOGIN or in some hooks afterLogin');
1105 exit;
1106 } else {
1107 $db->commit();
1108 }
1109
1110 // Change landing page if defined.
1111 $landingpage = getDolUserString('MAIN_LANDING_PAGE', getDolGlobalString('MAIN_LANDING_PAGE'));
1112 if (!empty($landingpage)) { // Example: /index.php
1113 $newpath = dol_buildpath($landingpage, 1);
1114 if ($_SERVER["PHP_SELF"] != $newpath) { // not already on landing page (avoid infinite loop)
1115 header('Location: '.$newpath);
1116 exit;
1117 }
1118 }
1119 }
1120
1121 // Check if user must change password at next login
1122 if (!empty($user->force_pass_change) && $dol_authmode == 'dolibarr') {
1123 // redirect to a simple page with only one action is possible : change your password
1124 $allowedpages = array('/user/changepassword.php', '/user/logout.php');
1125 $currentpage = $_SERVER['PHP_SELF'];
1126 $isallowed = false;
1127 foreach ($allowedpages as $page) {
1128 if (preg_match('/'.preg_quote($page, '/').'$/', $currentpage)) {
1129 $isallowed = true;
1130 break;
1131 }
1132 }
1133 if (!$isallowed) {
1134 header('Location: '.DOL_URL_ROOT.'/user/changepassword.php');
1135 exit;
1136 }
1137 }
1138
1139 // If user admin, we force the rights-based modules
1140 if ($user->admin) {
1141 $user->rights->user->user->lire = 1;
1142 $user->rights->user->user->creer = 1;
1143 $user->rights->user->user->password = 1;
1144 $user->rights->user->user->supprimer = 1;
1145 $user->rights->user->self->creer = 1;
1146 $user->rights->user->self->password = 1;
1147
1148 //Required if advanced permissions are used with MAIN_USE_ADVANCED_PERMS
1149 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
1150 if (!$user->hasRight('user', 'user_advance')) {
1151 $user->rights->user->user_advance = new stdClass(); // To avoid warnings
1152 }
1153 if (!$user->hasRight('user', 'self_advance')) {
1154 $user->rights->user->self_advance = new stdClass(); // To avoid warnings
1155 }
1156 if (!$user->hasRight('user', 'group_advance')) {
1157 $user->rights->user->group_advance = new stdClass(); // To avoid warnings
1158 }
1159
1160 $user->rights->user->user_advance->readperms = 1;
1161 $user->rights->user->user_advance->write = 1;
1162 $user->rights->user->self_advance->readperms = 1;
1163 $user->rights->user->self_advance->writeperms = 1;
1164 $user->rights->user->group_advance->read = 1;
1165 $user->rights->user->group_advance->readperms = 1;
1166 $user->rights->user->group_advance->write = 1;
1167 $user->rights->user->group_advance->delete = 1;
1168 }
1169 }
1170
1171 /*
1172 * Overwrite some configs globals (try to avoid this and have code to use instead $user->conf->xxx)
1173 */
1174
1175 // Set liste_limit from user setup
1176 if (isset($user->conf->MAIN_SIZE_LISTE_LIMIT)) { // If a user setup exists
1177 $conf->liste_limit = getDolUserInt('MAIN_SIZE_LISTE_LIMIT'); // Can be 0
1178 }
1179 if ((int) $conf->liste_limit <= 0) {
1180 // Mode automatic. Similar code than into conf.class.php
1181 $conf->liste_limit = getListLimitFromScreenHeight();
1182 }
1183 // Overwrite main_checkbox_left_column from user setup
1184 if (isset($user->conf->MAIN_CHECKBOX_LEFT_COLUMN)) { // If a user setup exists
1185 $conf->main_checkbox_left_column = getDolUserInt('MAIN_CHECKBOX_LEFT_COLUMN'); // Can be 0
1186 }
1187
1188 // Replace conf->css by personalized value if theme not forced
1189 if (!getDolGlobalString('MAIN_FORCETHEME') && getDolUserString('MAIN_THEME')) {
1190 $conf->theme = getDolUserString('MAIN_THEME');
1191 $conf->css = "/theme/".$conf->theme."/style.css.php";
1192 }
1193} else {
1194 // We may have NOLOGIN set, but NOREQUIREUSER not
1195 if (!empty($user) && method_exists($user, 'loadDefaultValues') && !defined('NODEFAULTVALUES')) {
1196 $user->loadDefaultValues(); // Load default values for everybody (works even if $user->id = 0
1197 }
1198}
1199
1200
1201// Case forcing style from url
1202if (GETPOST('theme', 'aZ09')) {
1203 $conf->theme = GETPOST('theme', 'aZ09', 1);
1204 $conf->css = "/theme/".$conf->theme."/style.css.php";
1205}
1206
1207// Set javascript option
1208if (GETPOSTINT('nojs')) { // If javascript was not disabled on URL
1209 $conf->use_javascript_ajax = 0;
1210} else {
1211 if (getDolUserString('MAIN_DISABLE_JAVASCRIPT')) {
1212 $conf->use_javascript_ajax = !getDolUserString('MAIN_DISABLE_JAVASCRIPT') ? 1 : 0;
1213 }
1214}
1215
1216// Set MAIN_OPTIMIZEFORTEXTBROWSER for user (must be after login part)
1217if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1218 $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER');
1219 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') == 1) {
1220 $conf->global->THEME_TOPMENU_DISABLE_IMAGE = 1;
1221 }
1222}
1223//var_dump($conf->global->THEME_TOPMENU_DISABLE_IMAGE);
1224//var_dump($user->conf->THEME_TOPMENU_DISABLE_IMAGE);
1225
1226// set MAIN_OPTIMIZEFORCOLORBLIND for user
1227$conf->global->MAIN_OPTIMIZEFORCOLORBLIND = getDolUserString('MAIN_OPTIMIZEFORCOLORBLIND');
1228
1229// Set terminal output option according to conf->browser.
1230if (GETPOSTINT('dol_hide_leftmenu') || !empty($_SESSION['dol_hide_leftmenu'])) {
1231 $conf->dol_hide_leftmenu = 1;
1232}
1233if (GETPOSTINT('dol_hide_topmenu') || !empty($_SESSION['dol_hide_topmenu'])) {
1234 $conf->dol_hide_topmenu = 1;
1235}
1236if (GETPOSTINT('dol_optimize_smallscreen') || !empty($_SESSION['dol_optimize_smallscreen'])) {
1237 $conf->dol_optimize_smallscreen = 1;
1238}
1239if (GETPOSTINT('dol_no_mouse_hover') || !empty($_SESSION['dol_no_mouse_hover'])) {
1240 $conf->dol_no_mouse_hover = 1;
1241}
1242if (GETPOSTINT('dol_use_jmobile') || !empty($_SESSION['dol_use_jmobile'])) {
1243 $conf->dol_use_jmobile = 1;
1244}
1245// If not on Desktop
1246if (!empty($conf->browser->layout) && $conf->browser->layout != 'classic') {
1247 $conf->dol_no_mouse_hover = 1;
1248}
1249
1250// If on smartphone or optimized for small screen
1251if ((!empty($conf->browser->layout) && $conf->browser->layout == 'phone')
1252 || (!empty($_SESSION['dol_screenwidth']) && $_SESSION['dol_screenwidth'] < 400)
1253 || (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 400
1254 || getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER'))
1255) {
1256 $conf->dol_optimize_smallscreen = 1;
1257
1258 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM') == 1) {
1259 $conf->global->PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE = 0; // This was set to PRODUIT_DESC_IN_FORM and is forced to 0 if smartphone in this case
1260 }
1261}
1262// Replace themes bugged with jmobile with eldy
1263if (!empty($conf->dol_use_jmobile) && in_array($conf->theme, array('bureau2crea', 'cameleo', 'amarok'))) {
1264 $conf->theme = 'eldy';
1265 $conf->css = "/theme/".$conf->theme."/style.css.php";
1266}
1267
1268if (!defined('NOREQUIRETRAN')) {
1269 if (!GETPOST('lang', 'aZ09')) { // If language was not forced on URL
1270 // If user has chosen its own language
1271 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1272 // If different than current language
1273 //print ">>>".$langs->getDefaultLang()."-".$user->conf->MAIN_LANG_DEFAULT;
1274 if ($langs->getDefaultLang() != $user->conf->MAIN_LANG_DEFAULT) {
1275 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1276 }
1277 }
1278 }
1279}
1280
1281if (!defined('NOLOGIN')) {
1282 // If the login is not recovered, it is identified with an account that does not exist.
1283 // Hacking attempt?
1284 if (!$user->login) {
1286 }
1287
1288 // Check if user is active
1289 if ($user->status < 1) {
1290 // If not active, we refuse the user
1291 $langs->loadLangs(array("errors", "other"));
1292 dol_syslog("Authentication KO as login is disabled", LOG_NOTICE);
1293 accessforbidden("ErrorLoginDisabled");
1294 }
1295
1296 // Load permissions
1297 $user->loadRights();
1298}
1299
1300dol_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);
1301//Another call for easy debug
1302//dol_syslog("Access to ".$_SERVER["PHP_SELF"].' '.$_SERVER["HTTP_REFERER"].' GET='.join(',',array_keys($_GET)).'->'.join(',',$_GET).' POST:'.join(',',array_keys($_POST)).'->'.join(',',$_POST));
1303
1304// Load main languages files
1305if (!defined('NOREQUIRETRAN')) {
1306 // Load translation files required by page
1307 $langs->loadLangs(array('main', 'dict'));
1308
1309 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
1310 // accesskey is for Mac: CTRL + Option + key for all browsers
1311
1312 // Note: $con->browser->os and $conf->browser->name may not be defined if we are in CLI mode.
1313 $conf->browser->stringforfirstkey = $langs->transnoentities("KeyboardShortcut");
1314 if (!empty($conf->browser->os) && $conf->browser->os == 'macintosh') {
1315 $conf->browser->stringforfirstkey .= ' CTRL + Option +';
1316 } else {
1317 if (!empty($conf->browser->name) && $conf->browser->name == 'chrome') {
1318 $conf->browser->stringforfirstkey .= ' ALT +';
1319 } elseif (!empty($conf->browser->name) && $conf->browser->name == 'firefox') {
1320 $conf->browser->stringforfirstkey .= ' ALT + SHIFT +';
1321 } else {
1322 $conf->browser->stringforfirstkey .= ' CTL +';
1323 }
1324 }
1325}
1326
1327// Define some constants used for style of arrays
1328$bc = array(0 => 'class="impair"', 1 => 'class="pair"');
1329$bcdd = array(0 => 'class="drag drop oddeven"', 1 => 'class="drag drop oddeven"');
1330$bcnd = array(0 => 'class="nodrag nodrop nohover"', 1 => 'class="nodrag nodrop nohoverpair"'); // Used for tr to add new lines
1331
1332// Define messages variables
1333$mesg = '';
1334$warning = '';
1335$error = 0;
1336// deprecated, see setEventMessages() and dol_htmloutput_events()
1337$mesgs = array();
1338$warnings = array();
1339$errors = array();
1340
1341// Constants used to defined number of lines in textarea
1342if (empty($conf->browser->firefox)) {
1343 define('ROWS_1', 1);
1344 define('ROWS_2', 2);
1345 define('ROWS_3', 3);
1346 define('ROWS_4', 4);
1347 define('ROWS_5', 5);
1348 define('ROWS_6', 6);
1349 define('ROWS_7', 7);
1350 define('ROWS_8', 8);
1351 define('ROWS_9', 9);
1352} else {
1353 define('ROWS_1', 0);
1354 define('ROWS_2', 1);
1355 define('ROWS_3', 2);
1356 define('ROWS_4', 3);
1357 define('ROWS_5', 4);
1358 define('ROWS_6', 5);
1359 define('ROWS_7', 6);
1360 define('ROWS_8', 7);
1361 define('ROWS_9', 8);
1362}
1363
1364$heightforframes = 52; // Used by frames.php page
1365
1366// Init menu manager
1367if (!defined('NOREQUIREMENU')) {
1368 if (empty($user->socid)) { // If internal user or not defined
1369 $conf->standard_menu = getDolGlobalString('MAIN_MENU_STANDARD_FORCED', getDolGlobalString('MAIN_MENU_STANDARD', 'eldy_menu.php'));
1370 } else {
1371 // If external user
1372 $conf->standard_menu = getDolGlobalString('MAIN_MENUFRONT_STANDARD_FORCED', getDolGlobalString('MAIN_MENUFRONT_STANDARD', 'eldy_menu.php'));
1373 }
1374
1375 // Load the menu manager (only if not already done)
1376 $file_menu = $conf->standard_menu;
1377 if (GETPOST('menu', 'alpha')) {
1378 $file_menu = GETPOST('menu', 'alpha'); // example: menu=eldy_menu.php
1379 }
1380
1381 if (!class_exists('MenuManager')) {
1382 $menufound = 0;
1383 $dirmenus = array_merge(array("/core/menus/"), (array) $conf->modules_parts['menus']);
1384 foreach ($dirmenus as $dirmenu) {
1385 $menufound = dol_include_once($dirmenu."standard/".$file_menu);
1386 if (class_exists('MenuManager')) {
1387 break;
1388 }
1389 }
1390 if (!class_exists('MenuManager')) { // If failed to include, we try with standard eldy_menu.php
1391 dol_syslog("You define a menu manager '".$file_menu."' that can not be loaded.", LOG_WARNING);
1392 $file_menu = 'eldy_menu.php';
1393 include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".$file_menu;
1394 }
1395 }
1396 // @phan-suppress-next-line PhanRedefinedClassReference
1397 $menumanager = new MenuManager($db, empty($user->socid) ? 0 : 1);
1398 // @phan-suppress-next-line PhanRedefinedClassReference
1399 $menumanager->loadMenu();
1400}
1401
1402if (!empty(GETPOST('seteventmessages', 'alpha'))) {
1403 $message = GETPOST('seteventmessages', 'alpha');
1404 $messages = explode(',', $message);
1405 foreach ($messages as $key => $msg) {
1406 $tmp = explode(':', $msg);
1407 setEventMessages($tmp[0], null, !empty($tmp[1]) ? $tmp[1] : 'mesgs');
1408 }
1409}
1410
1411// Functions
1412
1413if (!function_exists("llxHeader")) {
1437 function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
1438 {
1439 global $conf, $hookmanager;
1440
1441 $parameters = array(
1442 'head' => & $head,
1443 'title' => & $title,
1444 'help_url' => & $help_url,
1445 'target' => & $target,
1446 'disablejs' => & $disablejs,
1447 'disablehead' => & $disablehead,
1448 'arrayofjs' => & $arrayofjs,
1449 'arrayofcss' => & $arrayofcss,
1450 'morequerystring' => & $morequerystring,
1451 'morecssonbody' => & $morecssonbody,
1452 'replacemainareaby' => & $replacemainareaby,
1453 'disablenofollow' => & $disablenofollow,
1454 'disablenoindex' => & $disablenoindex
1455
1456 );
1457 $reshook = $hookmanager->executeHooks('llxHeader', $parameters);
1458 if ($reshook > 0) {
1459 print $hookmanager->resPrint;
1460 return;
1461 }
1462
1463 // html header
1464 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
1465
1466 $tmpcsstouse = 'sidebar-collapse'.($morecssonbody ? ' '.$morecssonbody : '');
1467 // If theme MD and classic layer, we open the menulayer by default.
1468 if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1469 global $mainmenu;
1470 if ($mainmenu != 'website') {
1471 $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
1472 }
1473 }
1474
1475 if (getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND')) {
1476 $tmpcsstouse .= ' colorblind-'.strip_tags(getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND'));
1477 }
1478
1479 if (GETPOST('dol_openinpopup', 'aZ09')) {
1480 $tmpcsstouse .= ' dol_openinpopup';
1481 }
1482
1483 print '<body id="mainbody" class="'.$tmpcsstouse.'">'."\n";
1484
1485 // top menu and left menu area
1486 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && !GETPOST('dol_openinpopup', 'aZ09')) {
1487 top_menu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
1488 }
1489
1490 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1491 left_menu('', $help_url, '', array(), 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
1492 }
1493
1494 // main area
1495 if ($replacemainareaby) {
1496 print $replacemainareaby;
1497 return;
1498 }
1499
1500 main_area($title);
1501 }
1502}
1503
1504
1512function top_httphead($contenttype = 'text/html', $forcenocache = 0)
1513{
1514 global $db, $conf, $hookmanager;
1515
1516 if ($contenttype != 'none') {
1517 if ($contenttype == 'text/html') {
1518 header("Content-Type: text/html; charset=".$conf->file->character_set_client);
1519 } else {
1520 header("Content-Type: ".$contenttype);
1521 }
1522 }
1523
1524 // Security options
1525
1526 // X-Content-Type-Options
1527 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)
1528
1529 // X-Frame-Options
1530 if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
1531 header("X-Frame-Options: SAMEORIGIN"); // By default, frames allowed only if on same domain (stop some XSS attacks)
1532 } else {
1533 header("X-Frame-Options: ALLOWALL");
1534 }
1535
1536 if (getDolGlobalString('MAIN_SECURITY_FORCE_ACCESS_CONTROL_ALLOW_ORIGIN')) {
1537 $tmpurl = constant('DOL_MAIN_URL_ROOT');
1538 $tmpurl = preg_replace('/^(https?:\/\/[^\/]+)\/.*$/', '\1', $tmpurl);
1539 header('Access-Control-Allow-Origin: '.$tmpurl);
1540 header('Vary: Origin');
1541 }
1542
1543 // X-XSS-Protection
1544 //header("X-XSS-Protection: 1"); // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
1545
1546 // Content-Security-Policy-Report-Only
1547 if (!defined('MAIN_SECURITY_FORCECSPRO')) {
1548 // If CSP not forced from the page
1549
1550 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1551 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1552 // default-src 'self'; style-src: https://cdnjs.cloudflare.com https://fonts.googleapis.com; script-src: https://cdn.transifex.com https://www.googletagmanager.com; object-src https://youtube.com; frame-src https://youtube.com; img-src: *;
1553 // For example, to restrict everything to itself except img that can be on other servers:
1554 // default-src 'self'; img-src *;
1555 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1556 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1557 //
1558 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com;";
1559 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src *; script-src 'self' 'unsafe-inline' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com; style-src 'self' 'unsafe-inline'; connect-src 'self';";
1560 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
1561
1562 if (!is_object($hookmanager)) {
1563 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1564 $hookmanager = new HookManager($db);
1565 }
1566 $hookmanager->initHooks(array("main"));
1567
1568 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'reportonly');
1569 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1570 if ($result > 0) {
1571 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1572 } else {
1573 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1574 }
1575
1576 // Add Dolibarr to Content-Security-Policy
1577 $contentsecuritypolicy = preg_replace('/default-src \'self\'/', 'default-src \'self\' *.dolibarr.org', $contentsecuritypolicy);
1578
1579 if (!empty($contentsecuritypolicy)) {
1580 header("Content-Security-Policy-Report-Only: ".$contentsecuritypolicy);
1581 }
1582 } else {
1583 header("Content-Security-Policy-Report-Only: ".constant('MAIN_SECURITY_FORCECSPRO'));
1584 }
1585
1586 // Content-Security-Policy
1587 if (!defined('MAIN_SECURITY_FORCECSP')) {
1588 // If CSP not forced from the page
1589
1590 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1591 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1592 // default-src 'self'; style-src: https://cdnjs.cloudflare.com https://fonts.googleapis.com; script-src: https://cdn.transifex.com https://www.googletagmanager.com; object-src https://youtube.com; frame-src https://youtube.com; img-src: *;
1593 // For example, to restrict everything to itself except img that can be on other servers:
1594 // default-src 'self'; img-src *;
1595 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1596 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1597 //
1598 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src 'self' 'unsafe-inline' 'unsafe-eval' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com;";
1599 // $contentsecuritypolicy = "frame-ancestors 'self'; img-src * data:; font-src *; default-src *; script-src 'self' 'unsafe-inline' *.paypal.com *.stripe.com *.google.com *.googleapis.com *.google-analytics.com *.googletagmanager.com; style-src 'self' 'unsafe-inline'; connect-src 'self';";
1600 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
1601
1602 if (!is_object($hookmanager)) {
1603 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1604 $hookmanager = new HookManager($db);
1605 }
1606 $hookmanager->initHooks(array("main"));
1607
1608 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'active');
1609 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1610 if ($result > 0) {
1611 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1612 } else {
1613 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1614 }
1615
1616 // Add Dolibarr to Content-Security-Policy
1617 $contentsecuritypolicy = preg_replace('/default-src \'self\'/', 'default-src \'self\' ping.dolibarr.org', $contentsecuritypolicy);
1618
1619 if (!empty($contentsecuritypolicy)) {
1620 header("Content-Security-Policy: ".$contentsecuritypolicy);
1621 }
1622 } else {
1623 header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSP'));
1624 }
1625
1626 // Referrer-Policy
1627 // Say if we must provide the referrer when we jump onto another web page.
1628 // Default browser are 'strict-origin-when-cross-origin' (only domain is sent on other domain switching), we want more so we use 'same-origin' so browser doesn't send any referrer at all when going into another web site domain.
1629 // Note that we do not use 'strict-origin' as this breaks feature to restore filters when clicking on "back to page" link on some cases.
1630 if (!defined('MAIN_SECURITY_FORCERP')) {
1631 $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
1632 if (!empty($referrerpolicy)) {
1633 header("Referrer-Policy: ".$referrerpolicy);
1634 }
1635 } else {
1636 header("Referrer-Policy: ".constant('MAIN_SECURITY_FORCERP'));
1637 }
1638
1639 // Strict-Transport-Security
1640 if (!defined('MAIN_SECURITY_FORCESTS')) {
1641 $sts = getDolGlobalString('MAIN_SECURITY_FORCESTS', "");
1642 if (!empty($sts)) {
1643 header("Strict-Transport-Security: ".$sts);
1644 }
1645 } else {
1646 header("Strict-Transport-Security: ".constant('MAIN_SECURITY_FORCESTS'));
1647 }
1648
1649 // Permissions-Policy (old name was Feature-Policy)
1650 if (!defined('MAIN_SECURITY_FORCEPP')) {
1651 $pp = getDolGlobalString('MAIN_SECURITY_FORCEPP', "");
1652 if (!empty($pp)) {
1653 header("Permissions-Policy: ".$pp);
1654 }
1655 } else {
1656 header("Permissions-Policy: ".constant('MAIN_SECURITY_FORCEPP'));
1657 }
1658
1659 // Cache
1660 if ($forcenocache) {
1661 header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
1662 }
1663
1664 // No need to add this token in header, we use instead the one into the forms.
1665 //header("anti-csrf-token: ".newToken());
1666}
1667
1683function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
1684{
1685 global $db, $conf, $langs, $user, $mysoc, $hookmanager;
1686
1687 top_httphead();
1688
1689 if (empty($conf->css)) {
1690 $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
1691 }
1692
1693 print '<!doctype html>'."\n";
1694
1695 print '<html lang="'.substr($langs->defaultlang, 0, 2).'">'."\n";
1696
1697 //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
1698 if (empty($disablehead)) {
1699 if (!is_object($hookmanager)) {
1700 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1701 $hookmanager = new HookManager($db);
1702 }
1703 $hookmanager->initHooks(array("main"));
1704
1705 $ext = 'layout='.(empty($conf->browser->layout) ? '' : $conf->browser->layout).'&version='.urlencode(DOL_VERSION);
1706
1707 print "<head>\n";
1708
1709 if (GETPOST('dol_basehref', 'alpha')) {
1710 print '<base href="'.dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')).'">'."\n";
1711 }
1712
1713 // Displays meta
1714 print '<meta charset="utf-8">'."\n";
1715 print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
1716 print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
1717 print '<meta name="author" content="Dolibarr Development Team">'."\n";
1718 print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
1719 print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
1720 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
1721 print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
1722 }
1723 // Favicon
1724 $favicon = DOL_URL_ROOT.'/theme/dolibarr_256x256_color.png';
1725 $appletouchicon = DOL_URL_ROOT.'/theme/apple-touch-icon.png';
1726 if (!empty($mysoc->logo_squarred_mini)) {
1727 $favicon = DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_mini);
1728 }
1729 if (getDolGlobalString('MAIN_FAVICON_URL')) {
1730 $favicon = getDolGlobalString('MAIN_FAVICON_URL');
1731 }
1732 if (empty($conf->dol_use_jmobile)) {
1733 print '<link rel="shortcut icon" type="image/x-icon" href="'.$favicon.'"/>'."\n"; // Not required into an Android webview
1734 print '<link rel="apple-touch-icon" href="'.$appletouchicon.'"/>'."\n";
1735 }
1736
1737 // Mobile appli like icon
1738 $manifest = DOL_URL_ROOT.'/theme/manifest.json.php';
1739 $parameters = array('manifest' => $manifest);
1740 $resHook = $hookmanager->executeHooks('hookSetManifest', $parameters); // Note that $action and $object may have been modified by some hooks
1741 if ($resHook > 0) {
1742 $manifest = $hookmanager->resPrint; // Replace manifest.json
1743 } else {
1744 $manifest .= $hookmanager->resPrint; // Concat to actual manifest declaration
1745 }
1746 if (!empty($manifest)) {
1747 print '<link rel="manifest" href="'.$manifest.'" />'."\n";
1748 }
1749
1750 if (getDolGlobalString('THEME_ELDY_TOPMENU_BACK1')) {
1751 print '<meta name="theme-color" content="rgb(' . getDolGlobalString('THEME_ELDY_TOPMENU_BACK1').')">'."\n";
1752 }
1753
1754 // Auto refresh page
1755 if (GETPOSTINT('autorefresh') > 0) {
1756 print '<meta http-equiv="refresh" content="'.GETPOSTINT('autorefresh').'">';
1757 }
1758
1759 // Displays title
1760 $appli = constant('DOL_APPLICATION_TITLE');
1761 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
1762 if ($applicustom) {
1763 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
1764 }
1765
1766 print '<title>';
1767 $titletoshow = '';
1768 if ($title && preg_match('/showapp/', getDolGlobalString('MAIN_HTML_TITLE'))) {
1769 $titletoshow = dol_htmlentities($appli.' - '.$title);
1770 } elseif ($title) {
1771 $titletoshow = dol_htmlentities($title);
1772 } else {
1773 $titletoshow = dol_htmlentities($appli);
1774 }
1775
1776 $parameters = array('title' => $titletoshow);
1777 $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
1778 if ($result > 0) {
1779 $titletoshow = $hookmanager->resPrint; // Replace Title to show
1780 } else {
1781 $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
1782 }
1783
1784 print $titletoshow;
1785 print '</title>';
1786
1787 print "\n";
1788
1789 if (GETPOSTINT('version')) {
1790 $ext = 'version='.GETPOSTINT('version'); // useful to force no cache on css/js
1791 }
1792 // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
1793 if (GETPOST('dol_resetcache')) {
1794 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1795 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
1796 }
1797
1798 $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;
1799
1800 $themeparam .= '&' .$ext . '&revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV");
1801 if (GETPOSTISSET('dol_hide_topmenu')) {
1802 $themeparam .= '&amp;dol_hide_topmenu='.GETPOSTINT('dol_hide_topmenu');
1803 }
1804 if (GETPOSTISSET('dol_hide_leftmenu')) {
1805 $themeparam .= '&amp;dol_hide_leftmenu='.GETPOSTINT('dol_hide_leftmenu');
1806 }
1807 if (GETPOSTISSET('dol_openinpopup')) {
1808 $themeparam .= '&amp;dol_openinpopup='.GETPOST('dol_openinpopup', 'aZ09');
1809 }
1810 if (GETPOSTISSET('dol_optimize_smallscreen')) {
1811 $themeparam .= '&amp;dol_optimize_smallscreen='.GETPOSTINT('dol_optimize_smallscreen');
1812 }
1813 if (GETPOSTISSET('dol_no_mouse_hover')) {
1814 $themeparam .= '&amp;dol_no_mouse_hover='.GETPOSTINT('dol_no_mouse_hover');
1815 }
1816 if (GETPOSTISSET('dol_use_jmobile')) {
1817 $themeparam .= '&amp;dol_use_jmobile='.GETPOSTINT('dol_use_jmobile');
1818 $conf->dol_use_jmobile = GETPOSTINT('dol_use_jmobile');
1819 }
1820 if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
1821 $themeparam .= '&amp;THEME_DARKMODEENABLED='.GETPOSTINT('THEME_DARKMODEENABLED');
1822 }
1823 if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
1824 $themeparam .= '&amp;THEME_SATURATE_RATIO='.GETPOSTINT('THEME_SATURATE_RATIO');
1825 }
1826
1827
1834 $jsContextVars = [
1835 'DOL_VERSION' => DOL_VERSION,
1836 'DOL_URL_ROOT' => DOL_URL_ROOT,
1837 ];
1838
1839 $jsContextPathUrl = DOL_URL_ROOT . '/public/includes/dolibarr-js-context';
1840 $jsContextFiles = [
1841 'dolibarr-context.umd.js', // The js Dolibarr context definition
1842 'dolibarr-tool.seteventmessage.js' // The first tools to help dev for easy event in js
1843 ];
1844
1845 if (! defined('NOREQUIRETRAN')) {
1846 // Langs tool see Documentation at admin/tools/ui/dolibarr-context/index.php
1847 $jsContextFiles[] = 'dolibarr-tool.langs.js';
1848 $jsContextVars['MAIN_LANG_DEFAULT'] = $langs->getDefaultLang();// For langs tool
1849 $jsContextVars['DOL_URL_ROOT'] = DOL_URL_ROOT;
1850 $jsContextVars['DOL_LANG_INTERFACE_URL'] = dol_buildpath('public/langs/langs-tool-interface.php', 1);// For langs tool
1851 }
1852
1853 // Load context and all js tools
1854 foreach ($jsContextFiles as $jsContextFile) {
1855 print '<script nonce="'.getNonce().'" src="'.$jsContextPathUrl.'/'.$jsContextFile.'?' . $ext . '" ></script>'."\n";
1856 }
1857
1858 // DEFINE FIRST NEEDED JS CONTEXT VARS
1859 print '<script nonce="'.getNonce().'">Dolibarr.setContextVars('.json_encode($jsContextVars).');</script>'."\n";
1860
1861 // -- END OF DEFINITION OF DOLIBARR JS CONTEXT AND TOOLS
1862
1863
1864 if (getDolGlobalString('MAIN_ENABLE_FONT_ROBOTO')) {
1865 print '<link rel="preconnect" href="https://fonts.gstatic.com">'."\n";
1866 print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">'."\n";
1867 }
1868
1869 if (!defined('DISABLE_JQUERY') && (!$disablejs || $disablejs == 2) && $conf->use_javascript_ajax) {
1870 print '<!-- Includes CSS for JQuery (Ajax library) -->'."\n";
1871 if (!defined('DISABLE_JQUERY_UI')) {
1872 $jquerytheme = 'base';
1873 if (getDolGlobalString('MAIN_USE_JQUERY_THEME')) {
1874 $jquerytheme = getDolGlobalString('MAIN_USE_JQUERY_THEME');
1875 }
1876 if (constant('JS_JQUERY_UI')) {
1877 print '<link rel="stylesheet" type="text/css" href="' . JS_JQUERY_UI . 'css/' . $jquerytheme . '/jquery-ui.min.css?' . $ext . '">' . "\n"; // Forced JQuery
1878 } else {
1879 print '<link rel="stylesheet" type="text/css" href="' . DOL_URL_ROOT . '/public/includes/jquery/css/' . $jquerytheme . '/jquery-ui.css?' . $ext . '">' . "\n"; // JQuery
1880 }
1881 }
1882 if (!defined('DISABLE_JQUERY_JNOTIFY')) {
1883 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css?' . $ext . '">'."\n"; // JNotify
1884 }
1885 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) { // jQuery plugin "mutiselect", "multiple-select", "select2"...
1886 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT');
1887 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/'.$tmpplugin.'/dist/css/'.$tmpplugin.'.css?' . $ext . '">'."\n";
1888 }
1889 }
1890
1891 if (!defined('DISABLE_FONT_AWSOME')) {
1892 print '<!-- Includes CSS for font awesome -->'."\n";
1893 $fontawesome_directory = getDolGlobalString('MAIN_FONTAWESOME_DIRECTORY', '/theme/common/fontawesome-5');
1894 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.$fontawesome_directory.'/css/all.min.css?' . $ext . '">'."\n";
1895 }
1896
1897 // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
1898 $themepath = dol_buildpath($conf->css, 1);
1899 $themesubdir = '';
1900 if (!empty($conf->modules_parts['theme'])) { // This slow down
1901 foreach ($conf->modules_parts['theme'] as $reldir) {
1902 if (file_exists(dol_buildpath($reldir.$conf->css, 0))) {
1903 $themepath = dol_buildpath($reldir.$conf->css, 1);
1904 $themesubdir = $reldir;
1905 break;
1906 }
1907 }
1908 }
1909
1910 if (!defined('DISABLE_CSS_DEFAULT_THEME')) {
1911 print '<!-- Includes CSS for Dolibarr theme -->'."\n";
1912 print '<link rel="stylesheet" type="text/css" href="' . $themepath . $themeparam . '">' . "\n";
1913 }
1914
1915 // To fix old chrome bug
1916 /*
1917 if (getDolGlobalString('MAIN_FIX_FLASH_ON_CHROME')) {
1918 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";
1919 }
1920 */
1921
1922 // LEAFLET AND GEOMAN
1923 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
1924 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.css?' . $ext . "\">\n";
1925 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.css?' . $ext . "\">\n";
1926 }
1927
1928 // CSS forced by modules (relative url starting with /)
1929 if (!empty($conf->modules_parts['css'])) {
1930 $arraycss = (array) $conf->modules_parts['css'];
1931 foreach ($arraycss as $modcss => $filescss) {
1932 $filescss = (array) $filescss; // To be sure filecss is an array
1933 foreach ($filescss as $cssfile) {
1934 if (empty($cssfile)) {
1935 dol_syslog("Warning: module ".$modcss." declared a css path file into its descriptor that is empty.", LOG_WARNING);
1936 }
1937 // cssfile is a relative path
1938 $urlforcss = dol_buildpath($cssfile, 1);
1939 if ($urlforcss && $urlforcss != '/') {
1940 print '<!-- Includes CSS added by module '.$modcss.' -->'."\n".'<link rel="stylesheet" type="text/css" href="'.$urlforcss;
1941 // 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.
1942 if (!preg_match('/\.css$/i', $cssfile)) {
1943 print $themeparam;
1944 }
1945 print '">'."\n";
1946 } else {
1947 dol_syslog("Warning: module ".$modcss." declared a css path file for a file we can't find.", LOG_WARNING);
1948 }
1949 }
1950 }
1951 }
1952 // CSS forced by page in top_htmlhead call (relative url starting with /)
1953 if (is_array($arrayofcss)) {
1954 foreach ($arrayofcss as $cssfile) {
1955 if (preg_match('/^(http|\/\/)/i', $cssfile)) {
1956 $urltofile = $cssfile;
1957 } else {
1958 $urltofile = dol_buildpath($cssfile, 1);
1959 }
1960 print '<!-- Includes CSS added by page -->'."\n".'<link rel="stylesheet" type="text/css" title="default" href="'.$urltofile;
1961 // 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.
1962 if (!preg_match('/\.css$/i', $cssfile)) {
1963 print $themeparam;
1964 }
1965 print '">'."\n";
1966 }
1967 }
1968
1969 // Custom CSS
1970 if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
1971 // If a custom CSS was set, we add link to the custom css php file
1972 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/theme/custom.css.php?' . $ext . '&amp;revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV").'">'."\n";
1973 }
1974
1975 // Output standard javascript links
1976 if (!defined('DISABLE_JQUERY') && (!$disablejs || $disablejs == 2) && !empty($conf->use_javascript_ajax)) {
1977 // JQuery. Must be before other includes
1978 print '<!-- Includes JS for JQuery -->'."\n";
1979 if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
1980 print '<script nonce="'.getNonce().'" src="'.JS_JQUERY.'jquery.min.js?' . $ext . '"></script>'."\n";
1981 } else {
1982 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/js/jquery.min.js?' . $ext . '"></script>'."\n";
1983 }
1984 if (!defined('DISABLE_JQUERY_UI')) {
1985 if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
1986 print '<script nonce="' . getNonce() . '" src="' . JS_JQUERY_UI . 'jquery-ui.min.js?' . $ext . '"></script>' . "\n";
1987 } else {
1988 print '<script nonce="' . getNonce() . '" src="' . DOL_URL_ROOT . '/public/includes/jquery/js/jquery-ui.min.js?' . $ext . '"></script>' . "\n";
1989 }
1990 }
1991 // jQuery jnotify
1992 if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
1993 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jnotify/jquery.jnotify.min.js?' . $ext . '"></script>'."\n";
1994 }
1995 // Table drag and drop lines
1996 if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
1997 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/tablednd/jquery.tablednd.min.js?' . $ext . '"></script>'."\n";
1998 }
1999 // Chart
2000 if (empty($disableforlogin) && (!getDolGlobalString('MAIN_JS_GRAPH') || getDolGlobalString('MAIN_JS_GRAPH') == 'chart') && !defined('DISABLE_JS_GRAPH')) {
2001 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/nnnick/chartjs/dist/chart.min.js?' . $ext . '"></script>'."\n";
2002 }
2003
2004 // jQuery jeditable for Edit In Place features
2005 /*if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !defined('DISABLE_JQUERY_JEDITABLE')) {
2006 print '<!-- JS to manage editInPlace feature -->'."\n";
2007 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.js?' . $ext . '"></script>'."\n";
2008 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js?' . $ext . '"></script>'."\n";
2009 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js?' . $ext . '"></script>'."\n";
2010 print '<script nonce="'.getNonce().'" >'."\n";
2011 print 'var urlSaveInPlace = \''.DOL_URL_ROOT.'/core/ajax/saveinplace.php\';'."\n";
2012 print 'var urlLoadInPlace = \''.DOL_URL_ROOT.'/core/ajax/loadinplace.php\';'."\n";
2013 print 'var tooltipInPlace = \''.$langs->transnoentities('ClickToEdit').'\';'."\n"; // Added in title attribute of span
2014 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 ?
2015 print 'var cancelInPlace = \''.$langs->trans("Cancel").'\';'."\n";
2016 print 'var submitInPlace = \''.$langs->trans('Ok').'\';'."\n";
2017 print 'var indicatorInPlace = \'<img src="'.DOL_URL_ROOT."/theme/".$conf->theme."/img/working.gif".'">\';'."\n";
2018 print 'var withInPlace = 300;'; // width in pixel for default string edit
2019 print '</script>'."\n";
2020 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/editinplace.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2021 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2022 }*/
2023 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
2024 // jQuery plugin "mutiselect", "multiple-select", "select2", ...
2025 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT');
2026 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/'.$tmpplugin.'/dist/js/'.$tmpplugin.'.full.min.js?' . $ext . '"></script>'."\n"; // We include full because we need the support of containerCssClass
2027 }
2028 if (!defined('DISABLE_MULTISELECT')) { // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
2029 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/multiselect/jquery.multi-select.js?' . $ext . '"></script>'."\n";
2030 }
2031 }
2032
2033 if (!$disablejs && !empty($conf->use_javascript_ajax)) {
2034 // CKEditor
2035 if (empty($disableforlogin) && (isModEnabled('fckeditor') && (!getDolGlobalString('FCKEDITOR_EDITORNAME') || getDolGlobalString('FCKEDITOR_EDITORNAME') == 'ckeditor') && !defined('DISABLE_CKEDITOR')) || defined('FORCE_CKEDITOR')) {
2036 print '<!-- Includes JS for CKEditor -->'."\n";
2037 $pathckeditor = DOL_URL_ROOT.'/public/includes/ckeditor/ckeditor/';
2038 $jsckeditor = 'ckeditor.js';
2039 if (constant('JS_CKEDITOR')) {
2040 // To use external ckeditor 4 js lib
2041 $pathckeditor = constant('JS_CKEDITOR');
2042 }
2043 print '<script nonce="'.getNonce().'">';
2044 print '/* enable ckeditor by main.inc.php */';
2045 print 'var CKEDITOR_BASEPATH = \''.dol_escape_js($pathckeditor).'\';'."\n";
2046 print 'var ckeditorConfig = \''.dol_escape_js(dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js?' . $ext, 1)).'\';'."\n"; // $themesubdir='' in standard usage
2047 print 'var ckeditorFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2048 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";
2049 print '</script>'."\n";
2050 print '<script src="'.$pathckeditor.$jsckeditor. '?' . $ext . '"></script>'."\n";
2051 print '<script>';
2052 if (GETPOST('mode', 'aZ09') == 'Full_inline') {
2053 print 'CKEDITOR.disableAutoInline = false;'."\n";
2054 } else {
2055 print 'CKEDITOR.disableAutoInline = true;'."\n";
2056 }
2057 print '</script>'."\n";
2058 }
2059
2060 // TinyMCE (alternative WYSIWYG backend, selected by FCKEDITOR_EDITORNAME='tinymce')
2061 if (empty($disableforlogin) && (isModEnabled('fckeditor') && getDolGlobalString('FCKEDITOR_EDITORNAME') == 'tinymce' && !defined('DISABLE_TINYMCE')) || defined('FORCE_TINYMCE')) {
2062 print '<!-- Includes JS for TinyMCE -->'."\n";
2063 $pathtinymce = DOL_URL_ROOT.'/public/includes/tinymce/tinymce/';
2064 $jstinymce = 'tinymce.min.js';
2065 if (defined('JS_TINYMCE') && constant('JS_TINYMCE')) {
2066 $pathtinymce = constant('JS_TINYMCE');
2067 }
2068 print '<script src="'.$pathtinymce.$jstinymce.'?'.$ext.'"></script>'."\n";
2069 print '<script nonce="'.getNonce().'">';
2070 print '/* enable tinymce by main.inc.php */';
2071 print 'var tinymceBasePath = \''.dol_escape_js($pathtinymce).'\';'."\n";
2072 print 'var tinymceFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2073 print 'var tinymceFilebrowserImageBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Type=Image&Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2074 print '</script>'."\n";
2075 print '<script nonce="'.getNonce().'" src="'.dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/tinymce/config.js?'.$ext, 1).'"></script>'."\n";
2076 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/tinymce-ckeditor-compat.js?'.$ext.'"></script>'."\n";
2077 }
2078
2079 // 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).
2080 if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
2081 $enablebrowsernotif = false;
2082 if (isModEnabled('agenda') && getDolGlobalString('AGENDA_REMINDER_BROWSER')) {
2083 $enablebrowsernotif = true;
2084 }
2085 if ($conf->browser->layout == 'phone') {
2086 $enablebrowsernotif = false;
2087 }
2088 if ($enablebrowsernotif) {
2089 print '<!-- Includes JS of Dolibarr (browser layout = '.$conf->browser->layout.')-->'."\n";
2090 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_notification.js.php?lang='.$langs->defaultlang. '&' . $ext . '"></script>'."\n";
2091 }
2092 }
2093
2094 // Global js function
2095 print '<!-- Includes JS of Dolibarr -->'."\n";
2096 if (!defined('DISABLE_LIB_HEAD_JS')) {
2097 print '<script nonce="' . getNonce() . '" src="' . DOL_URL_ROOT . '/core/js/lib_head.js.php?lang=' . $langs->defaultlang . '&' . $ext . '"></script>' . "\n";
2098 }
2099
2100 // Leaflet
2101 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
2102 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.js?' . $ext . '"></script>'."\n";
2103 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.min.js?' . $ext . '"></script>'."\n";
2104 }
2105
2106 // JS forced by modules (relative url starting with /)
2107 if (!empty($conf->modules_parts['js'])) { // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
2108 $arrayjs = (array) $conf->modules_parts['js'];
2109 foreach ($arrayjs as $modjs => $filesjs) {
2110 $filesjs = (array) $filesjs; // To be sure filejs is an array
2111 foreach ($filesjs as $jsfile) {
2112 // jsfile is a relative path
2113 $urlforjs = dol_buildpath($jsfile, 1);
2114 if ($urlforjs && $urlforjs != '/') {
2115 print '<!-- Include JS added by module '.$modjs.'-->'."\n";
2116 print '<script nonce="'.getNonce().'" src="'.$urlforjs.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2117 } else {
2118 dol_syslog("Warning: module ".$modjs." declared a js path file for a file we can't find.", LOG_WARNING);
2119 }
2120 }
2121 }
2122 }
2123 // JS forced by page in top_htmlhead (relative url starting with /)
2124 if (is_array($arrayofjs)) {
2125 print '<!-- Includes JS added by page -->'."\n";
2126 foreach ($arrayofjs as $jsfile) {
2127 if (preg_match('/^(http|\/\/)/i', $jsfile)) {
2128 print '<script nonce="'.getNonce().'" src="'.$jsfile.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2129 } else {
2130 print '<script nonce="'.getNonce().'" src="'.dol_buildpath($jsfile, 1).((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2131 }
2132 }
2133 }
2134 }
2135
2136 //If you want to load custom javascript file from your selected theme directory
2137 if (getDolGlobalString('ALLOW_THEME_JS')) {
2138 $theme_js = dol_buildpath('/theme/'.$conf->theme.'/'.$conf->theme.'.js', 0);
2139 if (file_exists($theme_js)) {
2140 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/'.$conf->theme.'.js?' . $ext . '"></script>'."\n";
2141 }
2142 }
2143
2144 if (!empty($head)) {
2145 print $head."\n";
2146 }
2147 if (getDolGlobalString('MAIN_HTML_HEADER')) {
2148 print getDolGlobalString('MAIN_HTML_HEADER') . "\n";
2149 }
2150
2151 $parameters = array();
2152 $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
2153 print $hookmanager->resPrint; // Replace Title to show
2154
2155 print "</head>\n\n";
2156 }
2157
2158 $conf->headerdone = 1; // To tell header was output
2159}
2160
2161
2178function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $morequerystring = '', $helppagename = '')
2179{
2180 global $user, $conf, $langs, $db, $form;
2181 global $dolibarr_main_authentication, $dolibarr_main_demo;
2182 global $hookmanager, $menumanager;
2183
2184 $searchform = '';
2185
2186 // Instantiate hooks for external modules
2187 $hookmanager->initHooks(array('toprightmenu'));
2188
2189 $toprightmenu = '';
2190
2191 // For backward compatibility with old modules
2192 if (empty($conf->headerdone)) {
2193 $disablenofollow = 0;
2194 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
2195 print '<body id="mainbody">';
2196 }
2197
2198 /*
2199 * Top menu
2200 */
2201 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
2202 if (!isset($form) || !is_object($form)) {
2203 include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
2204 $form = new Form($db);
2205 }
2206
2207 print "\n".'<!-- Start top horizontal -->'."\n";
2208
2209 print '<header id="id-top" class="side-nav-vert'.(GETPOSTINT('dol_invisible_topmenu') ? ' hidden' : '').'">'; // dol_invisible_topmenu differs from dol_hide_topmenu: dol_invisible_topmenu means we output menu but we make it invisible.
2210
2211 // Show menu entries
2212 print '<div id="tmenu_tooltip'.(!getDolGlobalString('MAIN_MENU_INVERT') ? '' : 'invert').'" class="tmenu">'."\n";
2213 // @phan-suppress-next-line PhanRedefinedClassReference
2214 $menumanager->atarget = $target;
2215 // @phan-suppress-next-line PhanRedefinedClassReference
2216 $menumanager->showmenu('top', array('searchform' => $searchform)); // This contains a \n
2217 print "</div>\n";
2218
2219 // Define link to login card
2220 $appli = constant('DOL_APPLICATION_TITLE');
2221 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
2222 if ($applicustom) {
2223 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
2224 } else {
2225 $appli .= " ".DOL_VERSION;
2226 }
2227
2228 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
2229 $appli .= "<br>".$langs->trans("LevelOfFeature").': '.getDolGlobalInt('MAIN_FEATURES_LEVEL');
2230 }
2231
2232 $logouttext = '';
2233 $logouthtmltext = '';
2234 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2235 if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') {
2236 $logouthtmltext .= $langs->trans("Logout").'<br>';
2237 $logouttext .= '<a accesskey="l" href="'.DOL_URL_ROOT.'/user/logout.php?token='.newToken().'">';
2238 $logouttext .= img_picto($langs->trans('Logout').' ('.$conf->browser->stringforfirstkey.' l)', 'sign-out', '', 0, 0, 0, '', 'atoplogin valignmiddle');
2239 $logouttext .= '</a>';
2240 } else {
2241 $logouthtmltext .= $langs->trans("NoLogoutProcessWithAuthMode", $_SESSION["dol_authmode"]);
2242 $logouttext .= img_picto($langs->trans('Logout').' ('.$conf->browser->stringforfirstkey.' l)', 'sign-out', '', 0, 0, 0, '', 'atoplogin valignmiddle opacitymedium');
2243 }
2244 }
2245
2246
2247 print '<div class="login_block usedropdown">'."\n";
2248
2249
2250 // Add block for tools
2251 $toprightmenu .= '<div class="login_block_tools valignmiddle">';
2252
2253 $mode = -1;
2254 $toprightmenu .= '<div class="inline-block nowrap" style="padding: 0px;">';
2255
2256 if (getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
2257 // Add search dropdown
2258 $toprightmenu .= top_menu_search();
2259 }
2260
2261 // Add AI picto
2262 $toprightmenu .= top_menu_ai();
2263
2264 // Add bookmark dropdown
2265 $toprightmenu .= top_menu_bookmark();
2266
2267 if (getDolGlobalString('MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN')) {
2268 // Add the quick add object dropdown
2269 $toprightmenu .= top_menu_quickadd();
2270 }
2271
2272 if (getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE')) {
2273 // Add the import file link
2274 $toprightmenu .= top_menu_importfile();
2275 }
2276
2277 $toprightmenu .= '</div>';
2278
2279 $toprightmenu .= '</div>'."\n"; // end div class="login_block_tools"
2280
2281
2282 // Add block for other tools
2283 $toprightmenu .= '<div class="login_block_other valignmiddle">';
2284
2285 // Execute hook printTopRightMenu (hooks should output string like '<div class="login"><a href="">mylink</a></div>')
2286 $parameters = array();
2287 $result = $hookmanager->executeHooks('printTopRightMenu', $parameters); // Note that $action and $object may have been modified by some hooks
2288 if (is_numeric($result)) {
2289 if ($result == 0) {
2290 $toprightmenu .= $hookmanager->resPrint; // add
2291 } else {
2292 $toprightmenu = $hookmanager->resPrint; // replace
2293 }
2294 } else {
2295 $toprightmenu .= $result; // For backward compatibility
2296 }
2297
2298 // Link to module builder
2299 if (isModEnabled('modulebuilder')) {
2300 $text = '<a href="' . dolBuildUrl(DOL_URL_ROOT . '/modulebuilder/index.php', ['mainmenu' => 'home', 'leftmenu' => 'admintools']) .'" target="modulebuilder">';
2301 //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
2302 $text .= '<span class="fa fa-bug atoplogin valignmiddle"></span>';
2303 $text .= '</a>';
2304 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2305 $toprightmenu .= $form->textwithtooltip('', $langs->trans("ModuleBuilder"), 2, 1, $text, 'login_block_elem', 2);
2306 }
2307
2308 // Link to print main content area (optioncss=print)
2309 if (!getDolGlobalString('MAIN_PRINT_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2310 $qs = dol_escape_htmltag($_SERVER["QUERY_STRING"]);
2311
2312 if (isset($_POST) && is_array($_POST)) {
2313 foreach ($_POST as $key => $value) {
2314 $key = preg_replace('/[^a-z0-9_\.\-\[\]]/i', '', $key);
2315 if (in_array($key, array('action', 'massaction', 'password'))) {
2316 continue;
2317 }
2318 if (!is_array($value)) {
2319 if ($value !== '') {
2320 $qs .= '&'.urlencode($key).'='.urlencode($value);
2321 }
2322 } else {
2323 foreach ($value as $value2) {
2324 if (($value2 !== '') && (!is_array($value2))) {
2325 $qs .= '&'.urlencode($key).'[]='.urlencode($value2);
2326 }
2327 }
2328 }
2329 }
2330 }
2331 $qs .= (($qs && $morequerystring) ? '&' : '').$morequerystring;
2332 $text = '<a href="'.dol_escape_htmltag($_SERVER["PHP_SELF"]).'?'.$qs.($qs ? '&' : '').'optioncss=print" target="_blank" rel="noopener noreferrer">';
2333 //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
2334 $text .= '<span class="fa fa-print atoplogin valignmiddle"></span>';
2335 $text .= '</a>';
2336 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2337 $toprightmenu .= $form->textwithtooltip('', $langs->trans("PrintContentArea"), 2, 1, $text, 'login_block_elem', 2);
2338 }
2339
2340 // Link to Dolibarr wiki pages
2341 if (!getDolGlobalString('MAIN_HELP_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2342 $langs->load("help");
2343
2344 $helpbaseurl = '';
2345 $helppage = '';
2346 $mode = '';
2347 $helppresent = '';
2348
2349 if (empty($helppagename)) {
2350 $helppagename = 'EN:User_documentation|FR:Documentation_utilisateur|ES:Documentación_usuarios|DE:Benutzerdokumentation';
2351 } else {
2352 $helppresent = 'helppresent';
2353 }
2354
2355 // Get helpbaseurl, helppage and mode from helppagename and langs
2356 $arrayres = getHelpParamFor($helppagename, $langs);
2357 $helpbaseurl = $arrayres['helpbaseurl'];
2358 $helppage = $arrayres['helppage'];
2359 $mode = $arrayres['mode'];
2360
2361 // Link to help pages
2362 if ($helpbaseurl && $helppage) {
2363 $text = '';
2364 $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage').', ';
2365 if ($mode == 'wiki') {
2366 $title .= '<br>'.img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans("PageWiki").' '.dol_escape_htmltag('"'.strtr($helppage, '_', ' ').'"');
2367 if ($helppresent) {
2368 $title .= ' <span class="opacitymedium">('.$langs->trans("DedicatedPageAvailable").')</span>';
2369 } else {
2370 $title .= ' <span class="opacitymedium">('.$langs->trans("HomePage").')</span>';
2371 }
2372 }
2373 $text .= '<a class="help" target="_blank" rel="noopener noreferrer" href="';
2374 if ($mode == 'wiki') {
2375 // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2376 $text .= sprintf($helpbaseurl, urlencode(html_entity_decode($helppage)));
2377 } else {
2378 // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2379 $text .= sprintf($helpbaseurl, $helppage);
2380 }
2381 $text .= '">';
2382 $text .= '<span class="fa fa-question-circle atoplogin valignmiddle'.($helppresent ? ' '.$helppresent : '').'"></span>';
2383 $text .= '<span class="fa fa-long-arrow-alt-up helppresentcircle'.($helppresent ? '' : ' unvisible').'"></span>';
2384 $text .= '</a>';
2385 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2386 $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2);
2387 }
2388
2389 // Version
2390 if (getDolGlobalString('MAIN_SHOWDATABASENAMEINHELPPAGESLINK')) {
2391 $langs->load('admin');
2392 $appli .= '<br>'.$langs->trans("Database").': '.$db->database_name;
2393 }
2394 }
2395
2396 // Version
2397 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalInt('MAIN_HIDE_VERSION') == 0) {
2398 $text = '<span class="aversion"><span class="hideonsmartphone small">'.DOL_VERSION.'</span></span>';
2399 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2400 $toprightmenu .= $form->textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
2401 }
2402
2403 // Logout link
2404 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2405 $toprightmenu .= $form->textwithtooltip('', $logouthtmltext, 2, 1, $logouttext, 'login_block_elem logout-btn', 2);
2406 }
2407
2408 $toprightmenu .= '</div>'; // end div class="login_block_other"
2409
2410
2411 // Add block for user photo and name
2412 $toprightmenu .= '<div class="login_block_user">';
2413
2414 $mode = -1;
2415 $toprightmenu .= '<div class="inline-block login_block_elem login_block_elem_name nowrap centpercent" style="padding: 0px;">';
2416
2417 // Add user dropdown
2418 $toprightmenu .= top_menu_user();
2419
2420 $toprightmenu .= '</div>';
2421
2422 $toprightmenu .= '</div>'."\n";
2423
2424
2425 print $toprightmenu;
2426
2427 print "</div>\n"; // end div class="login_block"
2428
2429 print '</header>';
2430 //print '<header class="header2">&nbsp;</header>';
2431
2432 print '<div style="clear: both;"></div>';
2433 print "<!-- End top horizontal menu -->\n\n";
2434 }
2435
2436 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
2437 print '<!-- Begin div id-container --><div id="id-container" class="id-container">';
2438 }
2439}
2440
2441
2449function top_menu_user($hideloginname = 0, $urllogout = '')
2450{
2451 global $langs, $conf, $db, $hookmanager, $user, $mysoc;
2452 global $dolibarr_main_authentication, $dolibarr_main_demo;
2453 global $menumanager, $form;
2454
2455 // Return empty in some case
2456 if ($conf->browser->name == 'textbrowser') {
2457 return '';
2458 }
2459
2460 $langs->load('companies');
2461
2462 $userImage = $userDropDownImage = '';
2463 if (!empty($user->photo) || isModEnabled('gravatar')) {
2464 $userImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'photouserphoto userphoto', 'small', 0, 1);
2465 $userDropDownImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'dropdown-user-image', 'small', 0, 1);
2466 } else {
2467 $nophoto = '/public/theme/common/user_anonymous.png';
2468 if ($user->gender == 'man') {
2469 $nophoto = '/public/theme/common/user_man.png';
2470 }
2471 if ($user->gender == 'woman') {
2472 $nophoto = '/public/theme/common/user_woman.png';
2473 }
2474
2475 $userImage = img_picto('', 'user', 'class="photo photouserphoto userphoto"');
2476 //$userImage = '<img class="photo photouserphoto userphoto" alt="" src="'.DOL_URL_ROOT.$nophoto.'" aria-hidden="true">';
2477 $userDropDownImage = '<img class="photo dropdown-user-image" alt="" src="'.DOL_URL_ROOT.$nophoto.'" aria-hidden="true">';
2478 }
2479
2480 $dropdownBody = '';
2481 $dropdownBody .= '<span id="topmenulogincompanyinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowCompanyInfos").'</span>';
2482 $dropdownBody .= '<div id="topmenulogincompanyinfo" >';
2483
2484 $dropdownBody .= '<br><b>'.$langs->trans("Company").'</b>: <span>'.dol_escape_htmltag($mysoc->name).'</span>';
2485 $idprofcursor = 0;
2486 while ($idprofcursor < 10) {
2487 $idprofcursor++;
2488 $constkeyforprofid = 'MAIN_INFO_PROFID'.$idprofcursor;
2489 if ($idprofcursor == 1) {
2490 $constkeyforprofid = 'MAIN_INFO_SIREN';
2491 }
2492 if ($idprofcursor == 2) {
2493 $constkeyforprofid = 'MAIN_INFO_SIRET';
2494 }
2495 if ($idprofcursor == 3) {
2496 $constkeyforprofid = 'MAIN_INFO_APE';
2497 }
2498 if ($idprofcursor == 4) {
2499 $constkeyforprofid = 'MAIN_INFO_RCS';
2500 }
2501 $showprofid = (($idprofcursor <= 6) && $langs->transcountry("ProfId".$idprofcursor, $mysoc->country_code) != '-');
2502 if ($idprofcursor > 6 && getDolGlobalString($constkeyforprofid)) {
2503 $showprofid = true;
2504 }
2505 if ($showprofid) {
2506 $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId".$idprofcursor, $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString($constkeyforprofid), '1').'</span>';
2507 }
2508 }
2509 $dropdownBody .= '<br><b>'.$langs->trans("VATIntraShort").'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT').'</span>';
2510 $langFlag = picto_from_langcode($langs->getDefaultLang(), 'class="none"');
2511 $dropdownBody .= '<br><b>'.$langs->trans("Country").'</b>: <span>'.($mysoc->country_code ? $langs->trans("Country".$mysoc->country_code).' '.$langFlag : '').'</span>';
2512 if (isModEnabled('multicurrency')) {
2513 $dropdownBody .= '<br><b>'.$langs->trans("Currency").'</b>: <span>'.getDolCurrency().'</span>';
2514 }
2515 $dropdownBody .= '</div>';
2516
2517 $dropdownBody .= '<br>';
2518 $dropdownBody .= '<span id="topmenuloginmoreinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowMoreInfos").'</span>';
2519 $dropdownBody .= '<div id="topmenuloginmoreinfo" >';
2520
2521 // login infos
2522 if (!empty($user->admin)) {
2523 $dropdownBody .= '<br><b>'.$langs->trans("Administrator").'</b>: '.yn($user->admin).' '.img_picto('', 'admin');
2524 }
2525 $company = '';
2526 if (!empty($user->socid)) { // Add third party for external users
2527 $thirdpartystatic = new Societe($db);
2528 $thirdpartystatic->fetch($user->socid);
2529 $companylink = ' '.$thirdpartystatic->getNomUrl(2); // picto only of company
2530 $company = ' ('.$langs->trans("Company").': '.$thirdpartystatic->name.')';
2531 }
2532 $type = ($user->socid ? $langs->trans("External").$company : $langs->trans("Internal"));
2533 $dropdownBody .= '<br><b>'.$langs->trans("Type").':</b> '.$type;
2534 $dropdownBody .= '<br><b>'.$langs->trans("Status").'</b>: '.$user->getLibStatut(0);
2535 $dropdownBody .= '<br>';
2536
2537 $dropdownBody .= '<br><u>'.$langs->trans("Session").'</u>';
2538 $dropdownBody .= '<br><b>'.$langs->trans("IPAddress").'</b>: '.dol_escape_htmltag($_SERVER["REMOTE_ADDR"]);
2539 if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
2540 $dropdownBody .= '<br><b>'.$langs->trans("ConnectedOnMultiCompany").':</b> '.$conf->entity.' (user entity '.$user->entity.')';
2541 }
2542 $dropdownBody .= '<br><b>'.$langs->trans("AuthenticationMode").':</b> '.$_SESSION["dol_authmode"].(empty($dolibarr_main_demo) ? '' : ' (demo)');
2543 $dropdownBody .= '<br><b>'.$langs->trans("ConnectedSince").':</b> '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2544 $dropdownBody .= '<br><b>'.$langs->trans("PreviousConnexion").':</b> '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2545 $dropdownBody .= '<br><b>'.$langs->trans("CurrentTheme").':</b> '.$conf->theme;
2546 // @phan-suppress-next-line PhanRedefinedClassReference
2547 $dropdownBody .= '<br><b>'.$langs->trans("CurrentMenuManager").':</b> '.(isset($menumanager) ? $menumanager->name : 'unknown');
2548 $langFlag = picto_from_langcode($langs->getDefaultLang(), 'class="none"');
2549 $dropdownBody .= '<br><b>'.$langs->trans("CurrentUserLanguage").':</b> '.$langs->getDefaultLang().($langFlag ? ' '.$langFlag : '');;
2550
2551 $tz = (int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst'];
2552 $dropdownBody .= '<br><b>'.$langs->trans("ClientTZ").':</b> '.($tz ? ($tz >= 0 ? '+' : '').$tz : '');
2553 $dropdownBody .= ' <span class="opacitymedium">('.$_SESSION['dol_tz_string'].')</span>';
2554 //$dropdownBody .= ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
2555 //if ($_SESSION['dol_dst'] > 0) $dropdownBody .= yn(1);
2556 //else $dropdownBody .= yn(0);
2557
2558 $dropdownBody .= '<br><b>'.$langs->trans("Browser").':</b> '.ucfirst($conf->browser->name).($conf->browser->version ? ' '.$conf->browser->version : '');
2559 $dropdownBody .= $form->textwithpicto('', dol_escape_htmltag($_SERVER['HTTP_USER_AGENT']), 1, 'help', 'valignmiddle', 0, 3, 'useragent');
2560 $dropdownBody .= '<br><b>'.$langs->trans("Screen").':</b> '.$_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight'];
2561 $dropdownBody .= ' <span class="opacitymedium">('.$conf->browser->layout.')</span>';
2562 if (!empty($_SESSION["disablemodules"])) {
2563 $dropdownBody .= '<br><b>'.$langs->trans("DisabledModules").':</b> <br>'.implode(', ', explode(',', $_SESSION["disablemodules"]));
2564 }
2565 $dropdownBody .= '</div>';
2566
2567 // Execute hook
2568 $parameters = array('user' => $user, 'langs' => $langs);
2569 $result = $hookmanager->executeHooks('printTopRightMenuLoginDropdownBody', $parameters); // Note that $action and $object may have been modified by some hooks
2570 if (is_numeric($result)) {
2571 if ($result == 0) {
2572 $dropdownBody .= $hookmanager->resPrint; // add
2573 } else {
2574 $dropdownBody = $hookmanager->resPrint; // replace
2575 }
2576 }
2577
2578 if (empty($urllogout)) {
2579 $urllogout = dolBuildUrl(DOL_URL_ROOT . '/user/logout.php', [], true);
2580 }
2581
2582 // Defined the links for bottom of card
2583 $profilLink = '<a accesskey="u" href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="button-top-menu-dropdown" title="'.dol_escape_htmltag($langs->trans("YourUserFile").' ('.$conf->browser->stringforfirstkey.' u)').'"><i class="fa fa-user"></i> '.$langs->trans("Card").'</a>';
2584 $urltovirtualcard = '/user/virtualcard.php?id='.((int) $user->id);
2585 $jsonopen = "closeTopMenuLoginDropdown()";
2586 $virtuelcardLink = dolButtonToOpenUrlInDialogPopup('publicvirtualcardmenu', $langs->transnoentitiesnoconv("PublicVirtualCardUrl").(is_object($user) ? ' - '.$user->getFullName($langs) : '').' ('.$conf->browser->stringforfirstkey.' v)', img_picto($langs->trans("PublicVirtualCardUrl").' ('.$conf->browser->stringforfirstkey.' v)', 'card', ''), $urltovirtualcard, '', 'button-top-menu-dropdown marginleftonly nohover', $jsonopen, '', 'v');
2587 $logoutLink = '<a accesskey="l" href="'.$urllogout.'" class="button-top-menu-dropdown" title="'.dol_escape_htmltag($langs->trans("Logout").' ('.$conf->browser->stringforfirstkey.' l)').'"><i class="fa fa-sign-out-alt pictofixedwidth"></i><span class="hideonsmartphone">'.$langs->trans("Logout").'</span></a>';
2588
2589 $profilName = $user->getFullName($langs).' ('.$user->login.')';
2590 if (!empty($user->admin)) {
2591 $profilName = img_picto($langs->trans("Administrator"), 'admin').' '.$profilName;
2592 }
2593
2594 // Define version to show
2595 $appli = constant('DOL_APPLICATION_TITLE');
2596 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
2597 if ($applicustom) {
2598 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
2599 } else {
2600 $appli .= " ".DOL_VERSION;
2601 }
2602
2603 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2604 $btnUser = '<!-- div for user link -->
2605 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2606 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="dropdown-toggle login-dropdown-a valignmiddle" data-toggle="dropdown">
2607 '.$userImage.(empty($user->photo) ? '<!-- no photo so show also the login --><span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft valignmiddle small">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2608 </a>
2609 <div class="dropdown-menu">
2610 <!-- User image -->
2611 <div class="user-header">
2612 '.$userDropDownImage.'
2613 <p>
2614 '.$profilName.'<br>';
2615 $title = '';
2616 if ($user->datelastlogin) {
2617 $title = $langs->trans("ConnectedSince").' : '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2618 if ($user->datepreviouslogin) {
2619 $title .= '<br>'.$langs->trans("PreviousConnexion").' : '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2620 }
2621 }
2622 $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>';
2623 if ($user->datepreviouslogin) {
2624 $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>';
2625 }
2626
2627 //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
2628 $btnUser .= '
2629 </p>
2630 </div>
2631
2632 <!-- Menu Body user-->
2633 <div class="user-body">'.$dropdownBody.'</div>
2634
2635 <!-- Menu Footer-->
2636 <div class="user-footer">
2637 <div class="pull-left">
2638 '.$profilLink.'
2639 </div>
2640 <div class="pull-left">
2641 '.$virtuelcardLink.'
2642 </div>
2643 <div class="pull-right">
2644 '.$logoutLink.'
2645 </div>
2646 <div class="clearboth"></div>
2647 </div>
2648
2649 </div>
2650 </div>';
2651 } else {
2652 $btnUser = '<!-- div for user link text browser -->
2653 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2654 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="valignmiddle" alt="'.$langs->trans("MyUserCard").'">
2655 '.$userImage.(empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft small valignmiddle">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2656 </a>
2657 </div>';
2658 }
2659
2660 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
2661 $btnUser .= '
2662 <!-- Code to show/hide the user drop-down -->
2663 <script>
2664 function closeTopMenuLoginDropdown() {
2665 console.log("close login dropdown"); // This is called at each click on page, so we disable the log
2666 // Hide the menus.
2667 jQuery("#topmenu-login-dropdown").removeClass("open");
2668 }
2669 jQuery(document).ready(function() {
2670 jQuery(document).on("click", function(event) {
2671 if (jQuery("#topmenu-login-dropdown").hasClass("open")) {
2672 if (!$(event.target).closest("#topmenu-login-dropdown").length) {
2673 /* console.log("click close login - we click outside"); */
2674 closeTopMenuLoginDropdown();
2675 }
2676 }
2677 });
2678 ';
2679
2680
2681 $btnUser .= '
2682 jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
2683 console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
2684 event.preventDefault();
2685 jQuery("#topmenu-login-dropdown").toggleClass("open");
2686 });
2687
2688 jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
2689 console.log("Click on #topmenulogincompanyinfo-btn");
2690 if (!jQuery("#topmenuloginmoreinfo").is(\':hidden\')) {
2691 jQuery("#topmenuloginmoreinfo").slideToggle();
2692 }
2693 jQuery("#topmenulogincompanyinfo").slideToggle();
2694 });
2695
2696 jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
2697 console.log("Click on #topmenuloginmoreinfo-btn");
2698 if (!jQuery("#topmenulogincompanyinfo").is(\':hidden\')) {
2699 jQuery("#topmenulogincompanyinfo").slideToggle();
2700 }
2701 jQuery("#topmenuloginmoreinfo").slideToggle();
2702 });';
2703
2704 $btnUser .= '
2705 });
2706 </script>
2707 ';
2708 }
2709
2710 return $btnUser;
2711}
2712
2721function top_menu_ai()
2722{
2723 global $conf, $langs, $user;
2724
2725 $html = '';
2726
2727 if (!isModEnabled('ai') || !getDolGlobalString('AI_ASSISTANT_ENABLED') || empty($conf->use_javascript_ajax)) {
2728 return $html;
2729 }
2730 // Per-user gate: same right as the assistant page and its endpoints
2731 if (!$user->hasRight('ai', 'assistant', 'use')) {
2732 return $html;
2733 }
2734
2735 $ailabel = $langs->trans('AIAssistant').' ('.$conf->browser->stringforfirstkey.' a)';
2736
2737 // Chat CSS is needed on every page showing the icon (link-in-body is valid HTML5,
2738 // the standalone page ai/assistant/index.php uses the same pattern).
2739 $html .= '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/ai/css/ai_assistant.css">';
2740
2741 // Toggle icon. The accesskey "a" keeps the Alt+A shortcut: its browser
2742 // activation fires the click handler below, so it toggles the popover.
2743 $html .= '<!-- div for AI Assistant link (opens the AI chat popover) -->
2744 <div id="topmenu-ai-dropdown" class="atoplogin dropdown inline-block">
2745 <a accesskey="a" href="#" id="topmenu-ai-toggle" class="login-dropdown-a nofocusvisible" title="'.dol_escape_htmltag($ailabel).'"><i class="fa fa-magic"></i></a>
2746 </div>';
2747
2748 // Popover shell (hidden by CSS until .open). The chat fragment is fetched on
2749 // first open; afterwards open/close only toggles visibility so the
2750 // conversation survives. Moved to <body> on first use by the script below.
2751 $html .= '<div id="topmenu-ai-popover" class="ai-popover" role="dialog" aria-modal="false" aria-label="'.dol_escape_htmltag($langs->trans('AIAssistant')).'">
2752 <div class="ai-popover-body"><div class="ai-popover-loading"><i class="fa fa-circle-notch fa-spin"></i></div></div>
2753 </div>';
2754
2755 // Cache-busting version for the JS module: filemtime invalidates the browser
2756 // cache whenever the file actually changes (e.g. after a branch switch),
2757 // avoiding a stale module without the initAiAssistant() export.
2758 $aijsfile = DOL_DOCUMENT_ROOT.'/ai/js/ai_assistant.js';
2759 $aijsver = @filemtime($aijsfile);
2760 $aijsurl = DOL_URL_ROOT.'/ai/js/ai_assistant.js?v='.urlencode((string) ($aijsver ? $aijsver : DOL_VERSION));
2761
2762 $html .= '<script nonce="'.getNonce().'">
2763 (function () {
2764 var toggle = document.getElementById("topmenu-ai-toggle");
2765 var popover = document.getElementById("topmenu-ai-popover");
2766 if (!toggle || !popover) { return; }
2767 var body = popover.querySelector(".ai-popover-body");
2768 var loaded = false;
2769 var loading = false;
2770
2771 function positionPopover() {
2772 var top = document.getElementById("id-top");
2773 var anchor = (top ? top.getBoundingClientRect().bottom : 44) + 4;
2774 popover.style.setProperty("--ai-popover-top", anchor + "px");
2775 }
2776
2777 function loadChat() {
2778 if (loaded || loading) { return; }
2779 loading = true;
2780 fetch("'.DOL_URL_ROOT.'/ai/assistant/popover.php", { credentials: "same-origin" })
2781 .then(function (resp) {
2782 if (!resp.ok) { throw new Error("HTTP " + resp.status); }
2783 return resp.text();
2784 })
2785 .then(function (htmlcontent) {
2786 body.innerHTML = htmlcontent;
2787 return import("'.dol_escape_js($aijsurl).'").then(function (mod) {
2788 mod.initAiAssistant(body.querySelector(".ai-chat-container"));
2789 });
2790 })
2791 .then(function () {
2792 loaded = true;
2793 focusInput();
2794 })
2795 .catch(function (e) {
2796 console.error("AI Assistant popover load failed", e);
2797 body.innerHTML = "<div class=\"ai-popover-loading\">'.dol_escape_js($langs->trans('Error')).'</div>";
2798 })
2799 .finally(function () { loading = false; });
2800 }
2801
2802 function focusInput() {
2803 var input = body.querySelector("#user-input");
2804 if (input) { input.focus(); }
2805 }
2806
2807 toggle.addEventListener("click", function (event) {
2808 event.preventDefault();
2809 // position:fixed can be hijacked by a transformed ancestor: hosting the
2810 // panel directly under <body> guarantees viewport coordinates.
2811 if (popover.parentNode !== document.body) { document.body.appendChild(popover); }
2812 positionPopover();
2813 var isOpen = popover.classList.toggle("open");
2814 if (isOpen) {
2815 loadChat();
2816 if (loaded) { focusInput(); }
2817 }
2818 });
2819
2820 popover.addEventListener("click", function (event) {
2821 var closeBtn = event.target.closest("#ai-close-btn");
2822 var expandBtn = event.target.closest("#ai-expand-btn");
2823 if (closeBtn) {
2824 popover.classList.remove("open");
2825 } else if (expandBtn) {
2826 var expanded = popover.classList.toggle("expanded");
2827 var icon = expandBtn.querySelector("i");
2828 if (icon) { icon.className = expanded ? "fa fa-compress-alt" : "fa fa-expand-alt"; }
2829 expandBtn.title = expanded ? (expandBtn.dataset.titleReduce || "") : (expandBtn.dataset.titleExpand || "");
2830 }
2831 });
2832
2833 document.addEventListener("keydown", function (event) {
2834 if (event.key === "Escape" && popover.classList.contains("open")) {
2835 popover.classList.remove("open");
2836 }
2837 });
2838 })();
2839 </script>';
2840
2841 return $html;
2842}
2843
2851{
2852 global $conf, $langs;
2853
2854 // Button disabled on text browser
2855 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2856 return '';
2857 }
2858
2859 $html = '';
2860
2861 if (!empty($conf->use_javascript_ajax)) {
2862 $html .= '<!-- div for quick add link -->
2863 <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
2864 <a accesskey="c" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('QuickAdd').' ('.$conf->browser->stringforfirstkey.' c)"><i class="fa fa-plus-circle"></i></a>
2865 <div class="dropdown-menu">'.printDropdownQuickadd().'</div>
2866 </div>';
2867 if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) { // This may be set by some pages that use different jquery version to avoid errors
2868 $html .= '
2869 <!-- Code to show/hide the user drop-down for the quick add -->
2870 <script>
2871 jQuery(document).ready(function() {
2872 jQuery(document).on("click", function(event) {
2873 if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
2874 /* console.log("click close quick add - we click outside"); */
2875 // Hide the menus.
2876 $("#topmenu-quickadd-dropdown").removeClass("open");
2877 }
2878 });
2879 $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
2880 console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
2881 openQuickAddDropDown(event);
2882 });
2883
2884 // Key map shortcut
2885 $(document).keydown(function(event){
2886 var ostype = \''.dol_escape_js($conf->browser->os).'\';
2887 if (ostype === "macintosh") {
2888 if ( event.which === 65 && event.ctrlKey ) {
2889 console.log(\'control + a : trigger open quick add dropdown\');
2890 openQuickAddDropDown(event);
2891 }
2892 } else {
2893 if ( event.which === 65 && event.ctrlKey && event.shiftKey ) {
2894 console.log(\'control + shift + a : trigger open quick add dropdown\');
2895 openQuickAddDropDown(event);
2896 }
2897 }
2898 });
2899
2900 var openQuickAddDropDown = function(event) {
2901 event.preventDefault();
2902 $("#topmenu-quickadd-dropdown").toggleClass("open");
2903 }
2904 });
2905 </script>
2906 ';
2907 }
2908 }
2909
2910 return $html;
2911}
2912
2913
2921{
2922 global $conf, $langs;
2923
2924 // Button disabled on text browser
2925 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2926 return '';
2927 }
2928
2929 $html = '';
2930
2931 if (!empty($conf->use_javascript_ajax)) {
2932 $urlforuploadpage = DOL_URL_ROOT.'/core/upload_page.php';
2933 if (!is_numeric(getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE'))) {
2934 $urlforuploadpage = getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE');
2935 }
2936
2937 $html .= '<!-- div for link to upload file -->
2938 <div id="topmenu-uploadfile-dropdown" class="atoplogin dropdown inline-block">
2939 <a accesskey="i" class="dropdown-togglex login-dropdown-a nofocusvisible" data-toggle="dropdown" href="'.$urlforuploadpage.'" title="'.$langs->trans('UploadFile').' ('.$conf->browser->stringforfirstkey.' i)"><i class="fa fa-upload"></i></a>
2940 </div>';
2941 }
2942
2943 return $html;
2944}
2945
2946
2953function printDropdownQuickadd($mode = 0)
2954{
2955 global $user, $langs, $hookmanager;
2956
2957 $items = array(
2958 'items' => array(
2959 array(
2960 "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
2961 "title" => "MenuNewMember@members",
2962 "name" => "Adherent@members",
2963 "picto" => "object_member",
2964 "activation" => isModEnabled('member') && $user->hasRight("adherent", "write"), // vs hooking
2965 "position" => 5,
2966 ),
2967 array(
2968 "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
2969 "title" => "MenuNewThirdParty@companies",
2970 "name" => "ThirdParty@companies",
2971 "picto" => "object_company",
2972 "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
2973 "position" => 10,
2974 ),
2975 array(
2976 "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
2977 "title" => "NewContactAddress@companies",
2978 "name" => "Contact@companies",
2979 "picto" => "object_contact",
2980 "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
2981 "position" => 20,
2982 ),
2983 array(
2984 "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
2985 "title" => "NewPropal@propal",
2986 "name" => "Proposal@propal",
2987 "picto" => "object_propal",
2988 "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
2989 "position" => 30,
2990 ),
2991
2992 array(
2993 "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
2994 "title" => "NewOrder@orders",
2995 "name" => "Order@orders",
2996 "picto" => "object_order",
2997 "activation" => isModEnabled('order') && $user->hasRight("commande", "write"), // vs hooking
2998 "position" => 40,
2999 ),
3000 array(
3001 "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
3002 "title" => "NewBill@bills",
3003 "name" => "Bill@bills",
3004 "picto" => "object_bill",
3005 "activation" => isModEnabled('invoice') && $user->hasRight("facture", "write"), // vs hooking
3006 "position" => 50,
3007 ),
3008 array(
3009 "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
3010 "title" => "NewContractSubscription@contracts",
3011 "name" => "Contract@contracts",
3012 "picto" => "object_contract",
3013 "activation" => isModEnabled('contract') && $user->hasRight("contrat", "write"), // vs hooking
3014 "position" => 60,
3015 ),
3016 array(
3017 "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
3018 "title" => "SupplierProposalNew@supplier_proposal",
3019 "name" => "SupplierProposal@supplier_proposal",
3020 "picto" => "supplier_proposal",
3021 "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
3022 "position" => 70,
3023 ),
3024 array(
3025 "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
3026 "title" => "NewSupplierOrderShort@orders",
3027 "name" => "SupplierOrder@orders",
3028 "picto" => "supplier_order",
3029 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "commande", "write")) || (isModEnabled("supplier_order") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3030 "position" => 80,
3031 ),
3032 array(
3033 "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
3034 "title" => "NewBill@bills",
3035 "name" => "SupplierBill@bills",
3036 "picto" => "supplier_invoice",
3037 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "write")) || (isModEnabled("supplier_invoice") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3038 "position" => 90,
3039 ),
3040 array(
3041 "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
3042 "title" => "NewTicket@ticket",
3043 "name" => "Ticket@ticket",
3044 "picto" => "ticket",
3045 "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
3046 "position" => 100,
3047 ),
3048 array(
3049 "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
3050 "title" => "NewIntervention@interventions",
3051 "name" => "Intervention@interventions",
3052 "picto" => "intervention",
3053 "activation" => isModEnabled('intervention') && $user->hasRight("ficheinter", "creer"), // vs hooking
3054 "position" => 110,
3055 ),
3056 array(
3057 "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
3058 "title" => "NewProduct@products",
3059 "name" => "Product@products",
3060 "picto" => "object_product",
3061 "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
3062 "position" => 400,
3063 ),
3064 array(
3065 "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
3066 "title" => "NewService@products",
3067 "name" => "Service@products",
3068 "picto" => "object_service",
3069 "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
3070 "position" => 410,
3071 ),
3072 array(
3073 "url" => "/product/stock/stocktransfer/stocktransfer_card.php?action=create&amp;mainmenu=products",
3074 "title" => "StockTransferNew@stocks",
3075 "name" => "StockTransfer@stocks",
3076 "picto" => "stock",
3077 "activation" => isModEnabled("stocktransfer") && $user->hasRight("stocktransfer", "stocktransfer", "write"), // vs hooking
3078 "position" => 415,
3079 ),
3080 array(
3081 "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
3082 "title" => "AddUser@users",
3083 "name" => "User@users",
3084 "picto" => "user",
3085 "activation" => $user->hasRight("user", "user", "write"), // vs hooking
3086 "position" => 500,
3087 ),
3088 ),
3089 );
3090
3091 $dropDownQuickAddHtml = '';
3092
3093 // Define $dropDownQuickAddHtml
3094 if (empty($mode)) {
3095 $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
3096 }
3097 $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
3098
3099 // Allow the $items of the menu to be manipulated by modules
3100 $parameters = array();
3101 $hook_items = $items;
3102 $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks @phan-suppress-current-line PhanTypeMismatchArgument
3103 if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
3104 if ($reshook == 0) {
3105 $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
3106 } else {
3107 $items = $hookmanager->resArray; // replace
3108 }
3109
3110 // Sort menu items by 'position' value
3111 $position = array();
3112 foreach ($items['items'] as $key => $row) {
3113 $position[$key] = $row['position'];
3114 }
3115 $array1_sort_order = SORT_ASC;
3116 array_multisort($position, $array1_sort_order, $items['items']);
3117 }
3118
3119 foreach ($items['items'] as $item) {
3120 if (!$item['activation']) {
3121 continue;
3122 }
3123 $langs->load(explode('@', $item['title'])[1]);
3124 $langs->load(explode('@', $item['name'])[1]);
3125 $dropDownQuickAddHtml .= '
3126 <a class="dropdown-item quickadd-item" href="'.DOL_URL_ROOT.$item['url'].'" title="'.$langs->trans(explode('@', $item['title'])[0]).'">
3127 '. img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
3128 ';
3129 }
3130
3131 if (empty($mode)) {
3132 $dropDownQuickAddHtml .= '</div>';
3133 }
3134 $dropDownQuickAddHtml .= '</div>';
3135
3136 return $dropDownQuickAddHtml;
3137}
3138
3145{
3146 global $langs, $conf, $user;
3147
3148 $html = '';
3149
3150 // Return empty in some case
3151 if (!isModEnabled('bookmark') || !$user->hasRight('bookmark', 'lire')) {
3152 return '';
3153 }
3154 /*
3155 if ($conf->browser->name == 'textbrowser') {
3156 return $html;
3157 }
3158 */
3159
3160 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
3161 include_once DOL_DOCUMENT_ROOT.'/bookmarks/bookmarks.lib.php';
3162 $langs->load("bookmarks");
3163
3164 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3165 $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
3166 $html .= printDropdownBookmarksList();
3167 $html .= '</div>';
3168 } else {
3169 $html .= '<!-- div for bookmark link -->
3170 <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
3171 <a accesskey="b" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Bookmarks').' ('.$conf->browser->stringforfirstkey.' b)"><i class="fa fa-star"></i></a>
3172 <div class="dropdown-menu">
3174 </div>
3175 </div>';
3176
3177 $html .= '
3178 <!-- Code to show/hide the bookmark drop-down -->
3179 <script>
3180 jQuery(document).ready(function() {
3181 jQuery(document).on("click", function(event) {
3182 if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
3183 /* console.log("close bookmark dropdown - we click outside"); */
3184 // Hide the menus.
3185 $("#topmenu-bookmark-dropdown").removeClass("open");
3186 }
3187 });
3188
3189 jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
3190 console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
3191 openBookMarkDropDown(event);
3192 });
3193
3194 // Key map shortcut
3195 jQuery(document).keydown(function(event) {
3196 var ostype = \''.dol_escape_js($conf->browser->os).'\';
3197 if (ostype === "macintosh") {
3198 if ( event.which === 66 && event.ctrlKey ) {
3199 console.log("Click on control + b : trigger open bookmark dropdown");
3200 openBookMarkDropDown(event);
3201 }
3202 } else {
3203 if ( event.which === 66 && event.ctrlKey && event.shiftKey ) {
3204 console.log("Click on control + shift + b : trigger open bookmark dropdown");
3205 openBookMarkDropDown(event);
3206 }
3207 }
3208 });
3209
3210 var openBookMarkDropDown = function(event) {
3211 console.log("toggle #topmenu-bookmark-dropdown and force focus");
3212 event.preventDefault();
3213 jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
3214 jQuery("#top-bookmark-search-input").focus();
3215 }
3216
3217 });
3218 </script>
3219 ';
3220 }
3221 }
3222 return $html;
3223}
3224
3230function top_menu_search()
3231{
3232 global $langs, $conf, $db, $user, $hookmanager; // used by htdocs/core/ajax/selectsearchbox.php
3233
3234 $html = '';
3235
3236 $usedbyinclude = 1;
3237 $arrayresult = array();
3238 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This sets $arrayresult
3239
3240 $searchInput = '<input type="search" name="search_all" title="'.dol_escape_htmltag($conf->browser->stringforfirstkey.' s').'" id="top-global-search-input" class="dropdown-search-input search_component_input" placeholder="'.$langs->trans('Search').'" autocomplete="off">';
3241
3242 $defaultAction = '';
3243 $buttonList = '<div class="dropdown-global-search-button-list" >';
3244 // Menu with all searchable items
3245 // @phan-suppress-next-line PhanEmptyForeach // array is really empty
3246 foreach ($arrayresult as $keyItem => $item) {
3247 if (empty($defaultAction)) {
3248 $defaultAction = $item['url'];
3249 }
3250 $buttonList .= '<button class="dropdown-item global-search-item tdoverflowmax300" data-target="'.dol_escape_htmltag($item['url']).'" >';
3251 $buttonList .= $item['text'];
3252 $buttonList .= '</button>';
3253 }
3254 $buttonList .= '</div>';
3255
3256 $dropDownHtml = '<form role="search" id="top-menu-action-search" name="actionsearch" method="GET" action="'.$defaultAction.'">';
3257
3258 $dropDownHtml .= '
3259 <!-- search input -->
3260 <div class="dropdown-header search-dropdown-header">
3261 ' . $searchInput.'
3262 </div>
3263 ';
3264
3265 $dropDownHtml .= '
3266 <!-- Menu Body search -->
3267 <div class="dropdown-body search-dropdown-body">
3268 '.$buttonList.'
3269 </div>
3270 ';
3271
3272 $dropDownHtml .= '</form>';
3273
3274 $html .= '<!-- div for Global Search -->
3275 <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
3276 <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Search').' ('.$conf->browser->stringforfirstkey.' s)">
3277 <i class="fa fa-search" aria-hidden="true" ></i>
3278 </a>
3279 <div class="dropdown-menu dropdown-search">
3280 '.$dropDownHtml.'
3281 </div>
3282 </div>';
3283
3284 $html .= '
3285 <!-- Code to show/hide the user drop-down -->
3286 <script>
3287 jQuery(document).ready(function() {
3288
3289 // prevent submitting form on press ENTER
3290 jQuery("#top-global-search-input").keydown(function (e) {
3291 if (e.keyCode == 13 || e.keyCode == 40) {
3292 var inputs = $(this).parents("form").eq(0).find(":button");
3293 if (inputs[inputs.index(this) + 1] != null) {
3294 console.log("Force focus after keydow on #top-global-search-input");
3295 inputs[inputs.index(this) + 1].focus();
3296 if (e.keyCode == 13){
3297 inputs[inputs.index(this) + 1].trigger("click");
3298 }
3299
3300 }
3301 e.preventDefault();
3302 return false;
3303 }
3304 });
3305
3306 // arrow key nav
3307 jQuery(document).keydown(function(e) {
3308 // Get the focused element:
3309 var $focused = $(":focus");
3310 if($focused.length && $focused.hasClass("global-search-item")){
3311
3312 // UP - move to the previous line
3313 if (e.keyCode == 38) {
3314 e.preventDefault();
3315 console.log("Force focus after keycode 38");
3316 $focused.prev().focus();
3317 }
3318
3319 // DOWN - move to the next line
3320 if (e.keyCode == 40) {
3321 e.preventDefault();
3322 console.log("Force focus after keycode 40");
3323 $focused.next().focus();
3324 }
3325 }
3326 });
3327
3328
3329 // submit form action
3330 jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
3331 jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
3332 jQuery("#top-menu-action-search").submit();
3333 });
3334
3335 // close drop down
3336 jQuery(document).on("click", function(event) {
3337 if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
3338 console.log("click close search - we click outside");
3339 // Hide the menus.
3340 jQuery("#topmenu-global-search-dropdown").removeClass("open");
3341 }
3342 });
3343
3344 // Open drop down
3345 jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
3346 console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
3347 openGlobalSearchDropDown();
3348 });
3349
3350 // Key map shortcut
3351 jQuery(document).keydown(function(e){
3352 if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
3353 console.log(\'control + shift + f : trigger open global-search dropdown\');
3354 openGlobalSearchDropDown();
3355 }
3356 if ( e.which === 70 && e.alKey ) {
3357 console.log(\'alt + f : trigger open global-search dropdown\');
3358 openGlobalSearchDropDown();
3359 }
3360 });
3361
3362 var openGlobalSearchDropDown = function() {
3363 jQuery("#topmenu-global-search-dropdown").toggleClass("open");
3364 jQuery("#top-global-search-input").focus();
3365 }
3366
3367 });
3368 </script>
3369 ';
3370
3371 return $html;
3372}
3373
3388function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = array(), $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
3389{
3390 global $user, $conf, $langs, $db, $form;
3391 global $hookmanager, $menumanager;
3392
3393 $searchform = '';
3394
3395 if (!empty($menu_array_before)) {
3396 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);
3397 }
3398
3399 if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
3400 // Instantiate hooks for external modules
3401 $hookmanager->initHooks(array('leftblock'));
3402
3403 print "\n".'<!-- Begin side-nav id-left -->'."\n".'<div class="side-nav"><div id="id-left">'."\n";
3404 print "\n";
3405
3406 if (!is_object($form)) {
3407 $form = new Form($db);
3408 }
3409 $selected = -1;
3410 if (!getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
3411 // Select with select2 is awful on smartphone. TODO Is this still true with select2 v4 ?
3412 if ($conf->browser->layout == 'phone') {
3413 $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
3414 }
3415
3416 $usedbyinclude = 1;
3417 $arrayresult = array();
3418 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
3419
3420 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3421 //$textsearch = $langs->trans("Search");
3422 $textsearch = '<span class="fa fa-search paddingright pictofixedwidth"></span>'.$langs->trans("Search");
3423 $searchform .= $form->selectArrayFilter('searchselectcombo', $arrayresult, (string) $selected, 'accesskey="s"', 1, 0, (getDolGlobalString('MAIN_SEARCHBOX_CONTENT_LOADED_BEFORE_KEY') ? 0 : 1), 'vmenusearchselectcombo', 1, $textsearch, 1, $conf->browser->stringforfirstkey.' s');
3424 } else {
3425 if (is_array($arrayresult)) {
3426 // @phan-suppress-next-line PhanEmptyForeach // array is really empty in else case.
3427 foreach ($arrayresult as $key => $val) {
3428 $searchform .= printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft'.$key, $val['img']);
3429 }
3430 }
3431 }
3432
3433 // Execute hook printSearchForm
3434 $parameters = array('searchform' => $searchform);
3435 $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
3436 if (empty($reshook)) {
3437 $searchform .= $hookmanager->resPrint;
3438 } else {
3439 $searchform = $hookmanager->resPrint;
3440 }
3441
3442 // Force special value for $searchform for text browsers or very old search form
3443 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') || empty($conf->use_javascript_ajax)) {
3444 $urltosearch = DOL_URL_ROOT.'/core/search_page.php?showtitlebefore=1';
3445 $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>';
3446 } elseif ($conf->use_javascript_ajax && getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3447 $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>';
3448 $searchform .= '<script>
3449 jQuery(document).ready(function () {
3450 jQuery("#divsearchforms1").click(function(){
3451 jQuery("#divsearchforms2").toggle();
3452 });
3453 });
3454 </script>' . "\n";
3455 $searchform .= '</div>';
3456 }
3457
3458 // Key map shortcut
3459 $searchform .= '<script>
3460 jQuery(document).keydown(function(e){
3461 if( e.which === 70 && e.ctrlKey && e.shiftKey ){
3462 console.log(\'control + shift + f : trigger open global-search dropdown\');
3463 openGlobalSearchDropDown();
3464 }
3465 if( (e.which === 83 || e.which === 115) && e.altKey ){
3466 console.log(\'alt + s : trigger open global-search dropdown\');
3467 openGlobalSearchDropDown();
3468 }
3469 });
3470
3471 var openGlobalSearchDropDown = function() {
3472 jQuery("#searchselectcombo").select2(\'open\');
3473 }
3474 </script>';
3475 }
3476
3477 // Left column
3478 print '<!-- Begin left menu -->'."\n";
3479
3480 print '<div class="vmenu"'.(getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' alt="Left menu"' : '').'>'."\n\n";
3481
3482 // Show left menu with other forms
3483 // @phan-suppress-next-line PhanRedefinedClassReference
3484 $menumanager->menu_array = $menu_array_before;
3485 // @phan-suppress-next-line PhanRedefinedClassReference
3486 $menumanager->menu_array_after = $menu_array_after;
3487 if (getDolGlobalInt('MAIN_MENU_LEFT_DROPDOWN')) {
3488 // @phan-suppress-next-line PhanRedefinedClassReference
3489 $menumanager->showmenu('leftdropdown', array('searchform' => $searchform)); // output menu_array and menu found in database
3490 } else {
3491 // @phan-suppress-next-line PhanRedefinedClassReference
3492 $menumanager->showmenu('left', array('searchform' => $searchform)); // output menu_array and menu found in database
3493 }
3494
3495 // Dolibarr version + help + bug report link
3496 if (getDolGlobalString('MAIN_SHOW_VERSION') || getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3497 print "\n";
3498 print "<!-- Begin Help Block-->\n";
3499 print '<div id="blockvmenuhelp" class="blockvmenuhelp">'."\n";
3500
3501 // Version
3502 if (getDolGlobalString('MAIN_SHOW_VERSION')) { // Version is already on help picto and on login page.
3503 $doliurl = 'https://www.dolibarr.org';
3504 //local communities
3505 if (preg_match('/fr/i', $langs->defaultlang)) {
3506 $doliurl = 'https://www.dolibarr.fr';
3507 }
3508 if (preg_match('/es/i', $langs->defaultlang)) {
3509 $doliurl = 'https://www.dolibarr.es';
3510 }
3511 if (preg_match('/de/i', $langs->defaultlang)) {
3512 $doliurl = 'https://www.dolibarr.de';
3513 }
3514 if (preg_match('/it/i', $langs->defaultlang)) {
3515 $doliurl = 'https://www.dolibarr.it';
3516 }
3517 if (preg_match('/gr/i', $langs->defaultlang)) {
3518 $doliurl = 'https://www.dolibarr.gr';
3519 }
3520
3521 $appli = constant('DOL_APPLICATION_TITLE');
3522 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
3523 if ($applicustom) {
3524 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
3525 } else {
3526 $appli .= " ".DOL_VERSION;
3527 }
3528
3529 // Clean doliurl if we use a custom application name
3530 if ($applicustom) {
3531 $doliurl = '';
3532 }
3533
3534 print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
3535 if ($doliurl) {
3536 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$doliurl.'">';
3537 } else {
3538 print '<span class="help">';
3539 }
3540 print $appli;
3541 if ($doliurl) {
3542 print '</a>';
3543 } else {
3544 print '</span>';
3545 }
3546 print '</div>'."\n";
3547 }
3548
3549 // Link to bugtrack
3550 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3551 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3552
3553 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK') == 'github') {
3554 $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
3555 $bugbaseurl .= '&title=';
3556 $bugbaseurl .= urlencode("Bug: ");
3557 $bugbaseurl .= '&body=';
3558 $bugbaseurl .= urlencode("# Instructions\n");
3559 $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");
3560 $bugbaseurl .= urlencode("*Please:*\n");
3561 $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
3562 $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
3563 $bugbaseurl .= urlencode("\n");
3564 $bugbaseurl .= urlencode("\n");
3565 $bugbaseurl .= urlencode("# Bug\n");
3566 $bugbaseurl .= urlencode("[*Short description*]\n");
3567 $bugbaseurl .= urlencode("\n");
3568 $bugbaseurl .= urlencode("## Environment\n");
3569 $bugbaseurl .= urlencode("- **Version**: ".DOL_VERSION."\n");
3570 $bugbaseurl .= urlencode("- **OS**: ".php_uname('s')."\n");
3571 $bugbaseurl .= urlencode("- **Web server**: ".$_SERVER["SERVER_SOFTWARE"]."\n");
3572 $bugbaseurl .= urlencode("- **PHP**: ".php_sapi_name().' '.phpversion()."\n");
3573 $bugbaseurl .= urlencode("- **Database**: ".$db::LABEL.' '.$db->getVersion()."\n");
3574 $bugbaseurl .= urlencode("- **URL(s)**: ".$_SERVER["REQUEST_URI"]."\n");
3575 $bugbaseurl .= urlencode("\n");
3576 $bugbaseurl .= urlencode("## Expected and actual behavior\n");
3577 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3578 $bugbaseurl .= urlencode("\n");
3579 $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
3580 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3581 $bugbaseurl .= urlencode("\n");
3582 $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, dolibarr.log, debugging information…)\n");
3583 $bugbaseurl .= urlencode("[*Files*]\n");
3584 $bugbaseurl .= urlencode("\n");
3585
3586 $bugbaseurl .= urlencode("\n");
3587 $bugbaseurl .= urlencode("## Report\n");
3588 } elseif (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3589 $bugbaseurl = getDolGlobalString('MAIN_BUGTRACK_ENABLELINK');
3590 } else {
3591 $bugbaseurl = "";
3592 }
3593
3594 // Execute hook printBugtrackInfo
3595 $parameters = array('bugbaseurl' => $bugbaseurl);
3596 $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
3597 if (empty($reshook)) {
3598 $bugbaseurl .= $hookmanager->resPrint;
3599 } else {
3600 $bugbaseurl = $hookmanager->resPrint;
3601 }
3602
3603 print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
3604 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$bugbaseurl.'"><i class="fas fa-bug"></i> '.$langs->trans("FindBug").'</a>';
3605 print '</div>';
3606 }
3607
3608 print "</div>\n";
3609 print "<!-- End Help Block-->\n";
3610 print "\n";
3611 }
3612
3613 print "</div>\n";
3614 print "<!-- End left menu -->\n";
3615 print "\n";
3616
3617 // Execute hook printLeftBlock
3618 $parameters = array();
3619 $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
3620 print $hookmanager->resPrint;
3621
3622 print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
3623 }
3624
3625 print "\n";
3626 print '<!-- Begin right area -->'."\n";
3627
3628 if (empty($leftmenuwithoutmainarea)) {
3629 main_area($title);
3630 }
3631}
3632
3633
3640function main_area($title = '')
3641{
3642 global $conf, $langs, $hookmanager;
3643
3644 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3645 print '<div id="id-right">';
3646 }
3647
3648 print "\n";
3649
3650 print '<!-- Begin div class="fiche" -->'."\n".'<div class="fiche">'."\n";
3651
3652 $hookmanager->initHooks(array('main'));
3653 $parameters = array();
3654 $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
3655 print $hookmanager->resPrint;
3656
3657 if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
3658 print info_admin($langs->trans("WarningYouAreInMaintenanceMode", getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')), 0, 0, '1', 'warning maintenancemode');
3659 }
3660
3661 // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
3662 if (getDolGlobalString('SHOW_SOCINFO_ON_PRINT') && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'aZ09'))) {
3663 $parameters = array();
3664 $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
3665 if (empty($reshook)) {
3666 print '<!-- Begin show mysoc info header -->'."\n";
3667 print '<div id="mysoc-info-header">'."\n";
3668 print '<table class="centpercent div-table-responsive">'."\n";
3669 print '<tbody>';
3670 print '<tr><td rowspan="0" class="width20p">';
3671 if (getDolGlobalString('MAIN_SHOW_LOGO') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalString('MAIN_INFO_SOCIETE_LOGO')) {
3672 print '<img id="mysoc-info-header-logo" style="max-width:100%" alt="" src="'.DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_LOGO'))).'">';
3673 }
3674 print '</td><td rowspan="0" class="width50p"></td></tr>'."\n";
3675 print '<tr><td class="titre bold">'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_NOM')).'</td></tr>'."\n";
3676 print '<tr><td>'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_ADDRESS')).'<br>'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_ZIP')).' '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TOWN')).'</td></tr>'."\n";
3677 if (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) {
3678 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Phone").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TEL')).'</td></tr>';
3679 }
3680 if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
3681 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Email").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')).'</td></tr>';
3682 }
3683 if (getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) {
3684 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Web").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_WEB')).'</td></tr>';
3685 }
3686 print '</tbody>';
3687 print '</table>'."\n";
3688 print '</div>'."\n";
3689 print '<!-- End show mysoc info header -->'."\n";
3690 }
3691 }
3692}
3693
3694
3702function getHelpParamFor($helppagename, $langs)
3703{
3704 $helpbaseurl = '';
3705 $helppage = '';
3706 $mode = '';
3707
3708 if (preg_match('/^http/i', $helppagename)) {
3709 // If complete URL
3710 $helpbaseurl = '%s';
3711 $helppage = $helppagename;
3712 $mode = 'local';
3713 } else {
3714 // If WIKI URL
3715 $reg = array();
3716 if (preg_match('/^es/i', $langs->defaultlang)) {
3717 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3718 if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
3719 $helppage = $reg[1];
3720 }
3721 }
3722 if (preg_match('/^fr/i', $langs->defaultlang)) {
3723 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3724 if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
3725 $helppage = $reg[1];
3726 }
3727 }
3728 if (preg_match('/^de/i', $langs->defaultlang)) {
3729 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3730 if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
3731 $helppage = $reg[1];
3732 }
3733 }
3734 if (empty($helppage)) { // If help page not already found
3735 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3736 if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
3737 $helppage = $reg[1];
3738 }
3739 }
3740 $mode = 'wiki';
3741 }
3742 return array('helpbaseurl' => $helpbaseurl, 'helppage' => $helppage, 'mode' => $mode);
3743}
3744
3745
3762function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
3763{
3764 global $langs, $user;
3765
3766 $ret = '';
3767 $ret .= '<form action="'.$urlaction.'" method="post" class="searchform nowraponall tagtr">';
3768 $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
3769 $ret .= '<input type="hidden" name="savelogin" value="'.dol_escape_htmltag($user->login).'">';
3770 if ($showtitlebefore) {
3771 $ret .= '<div class="tagtd left">'.$title.'</div> ';
3772 }
3773 $ret .= '<div class="tagtd">';
3774 $ret .= img_picto('', $img, '', 0, 0, 0, '', 'paddingright width20');
3775 $ret .= '<input type="text" class="flat '.$htmlmorecss.'"';
3776 $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
3777 $ret .= ($accesskey ? ' accesskey="'.$accesskey.'"' : '');
3778 $ret .= ' placeholder="'.strip_tags($title).'"';
3779 $ret .= ($autofocus ? ' autofocus' : '');
3780 $ret .= ' name="'.$htmlinputname.'" id="'.$prefhtmlinputname.$htmlinputname.'" />';
3781 $ret .= '<button type="submit" class="button bordertransp nohover" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
3782 $ret .= '<span class="fa fa-search"></span>';
3783 $ret .= '</button>';
3784 $ret .= '</div>';
3785 $ret .= "</form>\n";
3786 return $ret;
3787}
3788
3789
3790if (!function_exists("llxFooter")) {
3804 function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
3805 {
3806 global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager, $action;
3807 global $delayedhtmlcontent;
3808 global $contextpage, $page, $limit, $mode;
3809 global $dolibarr_distrib;
3810
3811 $ext = 'layout='.urlencode($conf->browser->layout).'&version='.urlencode(DOL_VERSION);
3812
3813 // Hook to add more things on all pages within fiche DIV
3814 $llxfooter = '';
3815 $parameters = array();
3816 $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3817 if (empty($reshook)) {
3818 $llxfooter .= $hookmanager->resPrint;
3819 } elseif ($reshook > 0) {
3820 $llxfooter = $hookmanager->resPrint;
3821 }
3822 if ($llxfooter) {
3823 print $llxfooter;
3824 }
3825
3826 // Global html output events ($mesgs, $errors, $warnings)
3827 dol_htmloutput_events($disabledoutputofmessages);
3828
3829 // Code for search criteria persistence.
3830 // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
3831 if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
3832 // Clean and save data
3833 foreach ($user->lastsearch_values_tmp as $key => $val) {
3834 unset($_SESSION['lastsearch_values_tmp_'.$key]); // Clean array to rebuild it just after
3835 if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
3836 if (empty($val['sortfield'])) {
3837 unset($val['sortfield']);
3838 }
3839 if (empty($val['sortorder'])) {
3840 unset($val['sortorder']);
3841 }
3842 dol_syslog('Save lastsearch_values_tmp_'.$key.'='.json_encode($val, 0)." (systematic recording of last search criteria)");
3843 $_SESSION['lastsearch_values_tmp_'.$key] = json_encode($val);
3844 unset($_SESSION['lastsearch_values_'.$key]);
3845 }
3846 }
3847 }
3848
3849
3850 $relativepathstring = $_SERVER["PHP_SELF"];
3851 // Clean $relativepathstring
3852 if (constant('DOL_URL_ROOT')) {
3853 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
3854 }
3855 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3856 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3857 if (preg_match('/list\.php$/', $relativepathstring)) {
3858 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
3859 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
3860 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
3861 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
3862
3863 if (!empty($contextpage)) {
3864 $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring] = $contextpage;
3865 }
3866 if (!empty($page) && $page > 0) {
3867 $_SESSION['lastsearch_page_tmp_'.$relativepathstring] = $page;
3868 }
3869 if (!empty($limit) && $limit != $conf->liste_limit) {
3870 $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] = $limit;
3871 }
3872 if (!empty($mode)) {
3873 $_SESSION['lastsearch_mode_tmp_'.$relativepathstring] = $mode;
3874 }
3875
3876 unset($_SESSION['lastsearch_contextpage_'.$relativepathstring]);
3877 unset($_SESSION['lastsearch_page_'.$relativepathstring]);
3878 unset($_SESSION['lastsearch_limit_'.$relativepathstring]);
3879 unset($_SESSION['lastsearch_mode_'.$relativepathstring]);
3880 }
3881
3882 // Core error message
3883 if (getDolGlobalString('MAIN_CORE_ERROR')) {
3884 // Ajax version
3885 if ($conf->use_javascript_ajax) {
3886 $title = img_warning().' '.$langs->trans('CoreErrorTitle');
3887 print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
3888 } else {
3889 // html version
3890 $msg = img_warning().' '.$langs->trans('CoreErrorMessage');
3891 print '<div class="error">'.$msg.'</div>';
3892 }
3893
3894 //define("MAIN_CORE_ERROR",0); // Constant was defined and we can't change value of a constant
3895 }
3896
3897 print "\n\n";
3898
3899 print '</div> <!-- End div class="fiche" -->'."\n"; // End div fiche
3900
3901 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3902 print '</div> <!-- End div id-right -->'."\n"; // End div id-right
3903 }
3904
3905 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
3906 print '</div> <!-- End div id-container -->'."\n"; // End div container
3907 }
3908
3909 print "\n";
3910 if ($comment) {
3911 print '<!-- '.$comment.' -->'."\n";
3912 }
3913
3914 printCommonFooter($zone);
3915
3916 if (!empty($delayedhtmlcontent)) {
3917 print $delayedhtmlcontent;
3918 }
3919
3920 if (!empty($conf->use_javascript_ajax)) {
3921 print "\n".'<!-- Includes JS Footer of Dolibarr -->'."\n";
3922 print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang . '&' . $ext .'"></script>'."\n";
3923 }
3924
3925 // JS wrapper to add an unalterable log when clicking on Download or Preview
3926 // This is done on customer invoices only.
3927 // This add a log and increase the pos_print_counter too (done by block-add.php).
3928 /* NOTE: No more required, the trigger is now included into the call of the wrapper documents.php
3929 if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
3930 if (in_array($object->element, array('facture')) && $object->statut > 0) { // Restrict for the moment to element 'facture'
3931 print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
3932 ?>
3933 <script>
3934 jQuery(document).ready(function () {
3935 $('a.documentpreview').click(function() {
3936 console.log("Call /blockedlog/ajax/block-add on a.documentpreview (DOC_PREVIEW)");
3937 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
3938 , {
3939 id: <?php echo $object->id; ?>
3940 , element: '<?php echo dol_escape_js($object->element) ?>'
3941 , action: 'DOC_PREVIEW'
3942 , lang: '<?php echo dol_escape_js($langs->defaultlang); ?>'
3943 , token: '<?php echo currentToken(); ?>'
3944 }
3945 );
3946 });
3947 $('a.documentdownload').click(function() {
3948 console.log("Call /blockedlog/ajax/block-add on a.documentdownload (DOC_DOWNLOAD)");
3949 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
3950 , {
3951 id: <?php echo $object->id; ?>
3952 , element: '<?php echo dol_escape_js($object->element) ?>'
3953 , action: 'DOC_DOWNLOAD'
3954 , lang: '<?php echo dol_escape_js($langs->defaultlang); ?>'
3955 , token: '<?php echo currentToken(); ?>'
3956 }
3957 );
3958 });
3959 });
3960 </script>
3961 <?php
3962 }
3963 }
3964 */
3965
3966 // A div for the #dialogforpopup popup
3967 print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
3968 print '<div id="dialogforpopup" style="display: none;"></div>'."\n";
3969
3970 // A div for the #uiblock
3971 print "\n<!-- A div to allow uiblock by dolBlockUI(message) -->\n";
3972 print '<div id="dol-block-ui" style="display: none;"><div class="message">Loading...</div></div>'."\n";
3973
3974
3975 // Add code for the asynchronous anonymous first ping (for telemetry)
3976 // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
3977 $forceping = GETPOSTINT('forceping');
3978
3979 if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceping) {
3980 require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
3981
3982 $hash_unique_id_ping = getHashUniqueIdOfRegistration('sha256');
3983 $constanttosavelastko = 'MAIN_LAST_PING_KO_DATE';
3984 $constanttosavefirstok = 'MAIN_FIRST_PING_OK_DATE';
3985 $constanttosavefirstokid = 'MAIN_FIRST_PING_OK_ID';
3986
3987 if (!getDolGlobalString($constanttosavefirstok)
3988 || (!empty($conf->file->instance_unique_id) && (($hash_unique_id_ping.' - '.DOL_VERSION) != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
3989 || $forceping) {
3990 // No ping done if we are into an alpha version
3991 if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
3992 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
3993 } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id_ping]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
3994 // Output code for ping
3995 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3996
3997 $arrayofmoredata = array(
3998 'action' => 'dolibarrping',
3999 'datesys' => dol_print_date(dol_now(), 'standard', 'gmt'),
4000
4001 'country_code' => ($mysoc->country_code ? $mysoc->country_code : 'unknown')
4002 );
4003 printCodeForPing($constanttosavelastko, $constanttosavefirstok, $arrayofmoredata, $forceping);
4004 } else {
4005 $now = dol_now();
4006 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
4007 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
4008 dolibarr_set_const($db, $constanttosavefirstok, dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
4009 dolibarr_set_const($db, $constanttosavefirstokid, 'disabled', 'chaine', 0, '', $conf->entity);
4010 }
4011 } else {
4012 print "\n<!-- NO JS CODE TO call the ping. It was already done for this couple uniqueid and version -->\n";
4013 }
4014 }
4015
4016 // Add code for the asynchronous registration of the use of the BlockedLog module if not yet done but ready (in case past submission failed)
4017 // You can use &forceregistration=1 in parameters to force also the recall if the call was already sent.
4018 $forceregistration = GETPOSTINT('forceregistration');
4019
4020 if (isModEnabled('blockedlog') && (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceregistration)) {
4021 require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
4022
4023 if (!isALNEQualifiedVersion()) {
4024 print "\n<!-- NO JS CODE TO ENABLE the registration. Not a LNE qualified version -->\n";
4025 } elseif (!isRegistrationDataSaved()) {
4026 print "\n<!-- NO JS CODE TO ENABLE the registration. Registration data not saved -->\n";
4027 } else {
4028 $hash_unique_id_registration = getHashUniqueIdOfRegistration();
4029 $constanttosavelastko = 'MAIN_LAST_REGISTRATION_KO_DATE';
4030 $constanttosavefirstok = 'MAIN_FIRST_REGISTRATION_OK_DATE';
4031 $constanttosavefirstokid = 'MAIN_FIRST_REGISTRATION_OK_ID';
4032
4033 if (!getDolGlobalString($constanttosavefirstok)
4034 || (!empty($conf->file->instance_unique_id) && ($hash_unique_id_registration.' - '.DOL_VERSION != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
4035 || $forceregistration) {
4036 // No registration done if we are into an alpha or beta version
4037 if ((strpos('alpha', DOL_VERSION) > 0 || strpos('beta', DOL_VERSION) > 0) && !$forceregistration) {
4038 print "\n<!-- NO JS CODE TO ENABLE the registration. It is an alpha or beta version -->\n";
4039 } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id_registration]) || $forceregistration) { // Cookie is set when we uncheck the checkbox in the installation wizard.
4040 // Output code for ping
4041 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4042
4043 $arrayofdata = array(
4044 'action' => 'dolibarrregistration',
4045 'datesys' => dol_print_date(dol_now(), 'standard', 'gmt'),
4046
4047 'company_name' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_NAME', $mysoc->name),
4048 'company_email' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_EMAIL', $mysoc->email),
4049 'company_idprof1' => getDolGlobalString('MAIN_INFO_SIREN', $mysoc->idprof1),
4050 'company_idprof2' => getDolGlobalString('MAIN_INFO_SIRET', $mysoc->idprof2),
4051 'company_address' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_ADDRESS', $mysoc->address),
4052 'company_state' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_STATE', $mysoc->state),
4053 'company_zip' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_ZIP', $mysoc->zip),
4054 'company_town' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_TOWN', $mysoc->town),
4055 'country_code' => $mysoc->country_code,
4056
4057 'provider_name' => getDolGlobalString('MAIN_INFO_ITPROVIDER_NAME'),
4058 'provider_email' => getDolGlobalString('MAIN_INFO_ITPROVIDER_MAIL'),
4059 'provider_phone' => getDolGlobalString('MAIN_INFO_ITPROVIDER_PHONE'),
4060 'provider_address' => getDolGlobalString('MAIN_INFO_ITPROVIDER_ADDRESS'),
4061 'provider_state' => getDolGlobalString('MAIN_INFO_ITPROVIDER_STATE'),
4062 'provider_zip' => getDolGlobalString('MAIN_INFO_ITPROVIDER_ZIP'),
4063 'provider_town' => getDolGlobalString('MAIN_INFO_ITPROVIDER_TOWN'),
4064 'provider_country' => getDolGlobalString('MAIN_INFO_ITPROVIDER_COUNTRY'),
4065 'provider_idprof1' => getDolGlobalString('MAIN_INFO_ITPROVIDER_IDPROF1'),
4066 'provider_idprof2' => getDolGlobalString('MAIN_INFO_ITPROVIDER_IDPROF2')
4067 );
4068 printCodeForPing($constanttosavelastko, $constanttosavefirstok, $arrayofdata, $forceregistration);
4069 } else {
4070 $now = dol_now();
4071 print "\n<!-- NO JS CODE TO ENABLE the registration. It was disabled -->\n";
4072 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
4073 dolibarr_set_const($db, $constanttosavefirstok, dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
4074 dolibarr_set_const($db, $constanttosavefirstokid, 'disabled', 'chaine', 0, '', $conf->entity);
4075 }
4076 } else {
4077 print "\n<!-- NO JS CODE TO call the registration. It was already done for this couple uniqueid and version -->\n";
4078 }
4079 }
4080 }
4081
4082 // Add code for the asynchronous emulation of pushing a tracking counter of the use of the BlockedLog module trigger(for test purposes)
4083 // You can use &forceregistration=1 in parameters to force also the recall if the call was already sent.
4084 /*
4085 $forcepushcounter = GETPOSTINT('forcepushcounter');
4086
4087 if (isModEnabled('blockedlog') && ($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') && $forcepushcounter) {
4088 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
4089 $islne = isALNEQualifiedVersion(1, 1);
4090 if (!$islne) {
4091 print "\n<!-- NO CALL TO API TO PUSH COUNTER. Not a LNE qualified version -->\n";
4092 } elseif (!isRegistrationDataSaved()) {
4093 print "\n<!-- NO CALL TO API TO PUSH COUNTER. Registration data not saved -->\n";
4094 } else {
4095 // Get last ID and hash into $tmpresult
4096 include_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
4097 $tmpblockedlog = new BlockedLog($db);
4098 $tmpresult = $tmpblockedlog->getPreviousHash(0, 0);
4099
4100 if ((int) $tmpresult['previousid']) {
4101 $tmpresult2 = $tmpblockedlog->getPreviousHash(0, (int) $tmpresult['previousid']); // Get previous record
4102
4103 if ((int) $tmpresult2['previousid']) {
4104 // Call remote API service to record the last counter
4105 $resultcall = callApiToPushCounter((int) $tmpresult['previousid'], $tmpresult['previoushash'], $tmpresult['previousdatecreation'], 1, (int) $tmpresult2['previousid'], $tmpresult2['previoushash'], $tmpresult2['previousdatecreation']);
4106
4107 $algo = 'sha256';
4108 $hash_unique_id = getHashUniqueIdOfRegistration($algo); // The hash of the unique IDof instance
4109
4110 print "\n<!-- API TO PUSH COUNTER WAS CALLED. Result is ".$resultcall.". You may have log into dolibarr_dolibarrpushcounter.log for hash_unique_id=".dol_trunc($hash_unique_id, 10)." -->\n";
4111 }
4112 } else {
4113 print "\n<!-- NO CALL TO API TO PUSH COUNTER. Last rowid and signature not found -->\n";
4114 }
4115 }
4116 }
4117 */
4118
4119
4120
4121 $parameters = array();
4122 $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
4123 if ($reshook > 0) {
4124 print $hookmanager->resPrint;
4125 }
4126
4127 print "</body>\n";
4128 print "</html>\n";
4129 }
4130}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
versioncompare($versionarray1, $versionarray2)
Compare 2 versions (stored into 2 arrays), to know if a version (a,b,c) is lower than (x,...
Definition admin.lib.php:72
ajax_dialog($title, $message, $w=350, $h=150)
Show an ajax dialog.
Definition ajax.lib.php:433
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
isALNEQualifiedVersion($ignoredev=0, $ignoremodule=0)
Return if the version is a candidate version to get the LNE certification and if the prerequisites ar...
isRegistrationDataSaved()
Return if the KYC mandatory parameters are set Must be the same fields than the one defined as mandat...
getHashUniqueIdOfRegistration($algo='sha256')
Return a hash unique identifier of the registration (used to identify the registration of instance wi...
printDropdownBookmarksList()
Add area with bookmarks in top menu.
DolibarrDebugBar class.
Definition DebugBar.php:47
Class to manage generation of HTML components Only common components must be here.
static showphoto($modulepart, $object, $width=100, $height=0, $caneditfield=0, $cssclass='photowithmargin', $imagesize='', $addlinktofullsize=1, $cache=0, $forcecapture='', $noexternsourceoverwrite=0, $usesharelinkifavailable=0)
Return HTML code to output a photo.
Class to manage hooks.
Class to manage left menus.
Class to manage menu Auguria.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
global $mysoc
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:435
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
if(!defined('LOG_DEBUG')) if(defined( 'DOL_INC_FOR_VERSION_ERROR')) dol_session_start()
Replace session_start()
printCodeForPing($constanttosavelastko, $constanttosavefirstok, $arrayofdata=array(), $forceping=0)
Function to output HTML to make an ajax call to make registration.
dol_now($mode='gmt')
Return date for now.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='')
Show information in HTML for admin users or standard users.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
getDolUserInt($key, $default=0, $tmpuser=null)
Return Dolibarr user constant int value.
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='classlink button bordertransp', $jsonopen='', $jsonclose='', $accesskey='')
Return HTML code to output a button to open a dialog popup box.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
dolSetCookie(string $cookiename, string $cookievalue, int $expire=-1)
Set a cookie.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
getBrowserInfo($user_agent)
Return information about user browser.
getDolCurrency()
Return the main currency ('EUR', 'USD', ...)
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
dol_htmloutput_events($disabledoutputofmessages=0)
Print formatted messages to output (Used to show messages on html output).
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1)
Format professional IDs according to their country.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
top_menu_importfile()
Build the tooltip on top menu quick add.
top_menu_quickadd()
Build the tooltip on top menu quick add.
top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $disableforlogin=0, $disablenofollow=0, $disablenoindex=0)
Output html header of a page.
top_menu_ai()
Build the HTML for the AI Assistant entry of the top menu: a toggle icon and a floating popover panel...
top_menu_user($hideloginname=0, $urllogout='')
Build the tooltip on user login.
left_menu($menu_array_before, $helppagename='', $notused='', $menu_array_after=array(), $leftmenuwithoutmainarea=0, $title='', $acceptdelayedhtml=0)
Show left menu bar.
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
main_area($title='')
Begin main area.
getHelpParamFor($helppagename, $langs)
Return helpbaseurl, helppage and mode.
printDropdownQuickadd($mode=0)
Generate list of quickadd items.
printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey='', $prefhtmlinputname='', $img='', $showtitlebefore=0, $autofocus=0)
Show a search area.
top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $morequerystring='', $helppagename='')
Show an HTML header + a BODY + The top menu bar.
top_menu_search()
Build the tooltip on top menu search.
top_menu_bookmark()
Build the tooltip on top menu bookmark.
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:134
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode, $context='')
Return a login if login/pass was successful.
checkIPInCidr($ip, $cidr)
Check if IP address is in CIDR range.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...