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