dolibarr 24.0.1
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 // Validation of login/pass/entity
756 // If ok, the variable login will be returned
757 // If error, we will put error message in session under the name dol_loginmesg
758 if ($test && $goontestloop && GETPOST('actionlogin', 'aZ09') != 'disabled' && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {
759 // Loop on each test mode defined into $authmode
760 // $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');
761 $oauthmodetotestarray = array('google');
762 foreach ($oauthmodetotestarray as $oauthmodetotest) {
763 if (in_array($oauthmodetotest.'oauth', $authmode)) { // This is an authmode that is currently qualified. Do we have to remove it ?
764 // If we click on the link to use OAuth authentication or if we go here after a callback return, we do nothing
765 if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {
766 continue;
767 }
768 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);
769 foreach ($authmode as $tmpkey => $tmpval) {
770 if ($tmpval == $oauthmodetotest.'oauth') {
771 unset($authmode[$tmpkey]);
772 break;
773 }
774 }
775 }
776 }
777
778 // Check login for all qualified modes in array $authmode.
779 $login = checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode);
780 if ($login === '--bad-login-validity--') {
781 $login = '';
782 }
783
784 if ($login) {
785 $dol_authmode = $conf->authmode; // This property is defined only when logged, to say what mode was successfully used
786 // Check POST first, then GET (for OIDC callback redirect), then SESSION
787 $dol_tz = empty($_POST["tz"]) ? (empty($_GET["tz"]) ? (empty($_SESSION["tz"]) ? '' : $_SESSION["tz"]) : (int) $_GET["tz"]) : $_POST["tz"];
788 $dol_tz_string = empty($_POST["tz_string"]) ? (empty($_GET["tz_string"]) ? (empty($_SESSION["tz_string"]) ? '' : $_SESSION["tz_string"]) : $_GET["tz_string"]) : $_POST["tz_string"];
789 $dol_tz_string = preg_replace('/\s*\‍(.+\‍)$/', '', $dol_tz_string);
790 $dol_tz_string = preg_replace('/,/', '/', $dol_tz_string);
791 $dol_tz_string = preg_replace('/\s/', '_', $dol_tz_string);
792 $dol_dst = 0;
793 // Check POST first, then GET (for OIDC callback redirect), then SESSION
794 $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"];
795 $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"];
796 if ($dol_dst_first && $dol_dst_second) {
797 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
798 $datenow = dol_now();
799 $datefirst = dol_stringtotime($dol_dst_first);
800 $datesecond = dol_stringtotime($dol_dst_second);
801 if ($datenow >= $datefirst && $datenow < $datesecond) {
802 $dol_dst = 1;
803 }
804 }
805 $dol_screenheight = empty($_POST["screenheight"]) ? (empty($_GET["screenheight"]) ? (empty($_SESSION["dol_screenheight"]) ? '' : $_SESSION["dol_screenheight"]) : (int) $_GET["screenheight"]) : $_POST["screenheight"];
806 $dol_screenwidth = empty($_POST["screenwidth"]) ? (empty($_GET["screenwidth"]) ? (empty($_SESSION["dol_screenwidth"]) ? '' : $_SESSION["dol_screenwidth"]) : (int) $_GET["screenwidth"]) : $_POST["screenwidth"];
807 //print $datefirst.'-'.$datesecond.'-'.$datenow.'-'.$dol_tz.'-'.$dol_tzstring.'-'.$dol_dst.'-'.sdol_screenheight.'-'.sdol_screenwidth; exit;
808 }
809
810 if (!$login) {
811 dol_syslog('Bad password, connection refused (see a previous notice message for more info)', LOG_NOTICE);
812 // Load translation files required by page
813 $langs->loadLangs(array('main', 'errors'));
814
815 // Bad password. No authmode has found a good password.
816 // We set a generic message if not defined inside function checkLoginPassEntity or subfunctions
817 if (empty($_SESSION["dol_loginmesg"])) {
818 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadLoginPassword");
819 }
820
821 // Call trigger for the "security events" log
822 $user->context['audit'] = $langs->trans("ErrorBadLoginPassword").' - login='.GETPOST("username", "alpha", 2);
823
824 // Call trigger
825 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
826 if ($result < 0) {
827 $error++;
828 }
829 // End call triggers
830
831 // Hooks on failed login
832 $action = '';
833 $hookmanager->initHooks(array('login'));
834 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
835 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
836 if ($reshook < 0) {
837 $error++;
838 }
839
840 // Note: exit is done in next chapter
841 }
842 }
843
844 // End test login / passwords
845 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.
846 // No data to test login, so we show the login page.
847 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);
848 if (defined('NOREDIRECTBYMAINTOLOGIN')) {
849 // When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
850 // See example with selectsearchbox.php. This case is reserved for the selectesearchbox.php so we can
851 // report a message to ask to login when search ajax component is used after a timeout.
852 //top_httphead();
853 return 'ERROR_NOT_LOGGED';
854 } else {
855 if (!empty($_SERVER["HTTP_USER_AGENT"]) && $_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
856 http_response_code(401); // It makes easier to understand if session was broken during security tests
857 }
858
859 // Show login form
860 dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : '')); // This include http headers
861 }
862 exit;
863 }
864
865 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.
866
867 if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
868 dol_syslog('User not found or not valid, connection refused');
869 session_destroy();
870 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
871 session_name($sessionname);
873
874 if ($resultFetchUser == 0) {
875 // Load translation files required by page
876 $langs->loadLangs(array('main', 'errors'));
877
878 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
879
880 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
881 } elseif ($resultFetchUser < 0) {
882 $_SESSION["dol_loginmesg"] = $user->error;
883
884 $user->context['audit'] = $user->error;
885 } else {
886 // Load translation files required by the page
887 $langs->loadLangs(array('main', 'errors'));
888
889 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginDateValidity");
890
891 $user->context['audit'] = $langs->trans("ErrorLoginDateValidity").' - login='.$login;
892 }
893
894 // Call trigger
895 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
896 if ($result < 0) {
897 $error++;
898 }
899 // End call triggers
900
901
902 // Hooks on failed login
903 $action = '';
904 $hookmanager->initHooks(array('login'));
905 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
906 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
907 if ($reshook < 0) {
908 $error++;
909 }
910
911 $paramsurl = [];
912 if (GETPOSTINT('textbrowser')) {
913 $paramsurl += ['textbrowser' => GETPOSTINT('textbrowser')];
914 }
915 if (GETPOSTINT('nojs')) {
916 $paramsurl += ['nojs' => GETPOSTINT('nojs')];
917 }
918 if (GETPOST('lang', 'aZ09')) {
919 $paramsurl += ['lang' => (string) GETPOST('lang', 'aZ09')];
920 }
921 header('Location: '.dolBuildUrl(DOL_URL_ROOT . '/index.php', $paramsurl));
922 exit;
923 } else {
924 // User is loaded, we may need to change language for him according to its choice
925 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
926 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
927 }
928
929 if ($entitytotest > 0 && $conf->entity != $entitytotest) {
930 // We asked to force login to $entitytotest that differs from default $conf->entity, and we succeed, so
931 // we must force conf->entity to the new value, so the rest of the code that load $user->loadRights() and
932 // set $_SESSION['dol_entity'] will be done in correct environment.
933 $conf->entity = $entitytotest;
934 }
935 }
936 } else {
937 // We are already into an authenticated session
938 $login = $_SESSION["dol_login"];
939 $entity = isset($_SESSION["dol_entity"]) ? $_SESSION["dol_entity"] : 0;
940 dol_syslog("- This is an already logged session. _SESSION['dol_login']=".$login." _SESSION['dol_entity']=".$entity, LOG_DEBUG);
941
942 $resultFetchUser = $user->fetch(0, $login, '', 1, ($entity > 0 ? $entity : -1));
943
944 //var_dump(dol_print_date($user->flagdelsessionsbefore, 'dayhour', 'gmt')." ".dol_print_date($_SESSION["dol_logindate"], 'dayhour', 'gmt'));
945
946 if ($resultFetchUser <= 0
947 || ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"])
948 || ($user->status != $user::STATUS_ENABLED)
949 || ($user->isNotIntoValidityDateRange())) {
950 if ($resultFetchUser <= 0) {
951 // Account has been removed after login
952 dol_syslog("Can't load user even if session logged. _SESSION['dol_login']=".$login, LOG_WARNING);
953 } elseif ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"]) {
954 // Session is no more valid
955 dol_syslog("The user has a date for session invalidation = ".$user->flagdelsessionsbefore." and a session date = ".$_SESSION["dol_logindate"].". We must invalidate its sessions.");
956 } elseif ($user->status != $user::STATUS_ENABLED) {
957 // User is not enabled
958 dol_syslog("The user login is disabled");
959 } else {
960 // User validity dates are no more valid
961 dol_syslog("The user login has a validity between [".$user->datestartvalidity." and ".$user->dateendvalidity."], current date is ".dol_now());
962 }
963 session_destroy();
964 session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
965 session_name($sessionname);
967
968 if ($resultFetchUser == 0) {
969 $langs->loadLangs(array('main', 'errors'));
970
971 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
972
973 $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login='.$login;
974 } elseif ($resultFetchUser < 0) {
975 $_SESSION["dol_loginmesg"] = $user->error;
976
977 $user->context['audit'] = $user->error;
978 } else {
979 $langs->loadLangs(array('main', 'errors'));
980
981 $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorSessionInvalidatedAfterPasswordChange");
982
983 $user->context['audit'] = 'ErrorUserSessionWasInvalidated - login='.$login;
984 }
985
986 // Call trigger
987 $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
988 if ($result < 0) {
989 $error++;
990 }
991 // End call triggers
992
993 // Hooks on failed login
994 $action = '';
995 $hookmanager->initHooks(array('login'));
996 $parameters = array('dol_authmode' => (string) $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
997 $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
998 if ($reshook < 0) {
999 $error++;
1000 }
1001
1002 $paramsurl = array();
1003 if (GETPOSTINT('textbrowser')) {
1004 $paramsurl[] = 'textbrowser='.GETPOSTINT('textbrowser');
1005 }
1006 if (GETPOSTINT('nojs')) {
1007 $paramsurl[] = 'nojs='.GETPOSTINT('nojs');
1008 }
1009 if (GETPOST('lang', 'aZ09')) {
1010 $paramsurl[] = 'lang='.GETPOST('lang', 'aZ09');
1011 }
1012
1013 header('Location: '.DOL_URL_ROOT.'/index.php'.(count($paramsurl) ? '?'.implode('&', $paramsurl) : ''));
1014 exit;
1015 } else {
1016 // Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
1017 $hookmanager->initHooks(array('main'));
1018
1019 // Code for search criteria persistence.
1020 if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1021 $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]);
1022 $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server
1023 // Clean $relativepathstring
1024 if (constant('DOL_URL_ROOT')) {
1025 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
1026 }
1027 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
1028 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
1029 //var_dump($relativepathstring);
1030
1031 // 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
1032 if (!empty($_SESSION['lastsearch_values_tmp_'.$relativepathstring])) {
1033 $_SESSION['lastsearch_values_'.$relativepathstring] = $_SESSION['lastsearch_values_tmp_'.$relativepathstring];
1034 unset($_SESSION['lastsearch_values_tmp_'.$relativepathstring]);
1035 }
1036 if (!empty($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring])) {
1037 $_SESSION['lastsearch_contextpage_'.$relativepathstring] = $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring];
1038 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
1039 }
1040 if (!empty($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] != $conf->liste_limit) {
1041 $_SESSION['lastsearch_limit_'.$relativepathstring] = $_SESSION['lastsearch_limit_tmp_'.$relativepathstring];
1042 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
1043 }
1044 if (!empty($_SESSION['lastsearch_page_tmp_'.$relativepathstring]) && $_SESSION['lastsearch_page_tmp_'.$relativepathstring] > 0) {
1045 $_SESSION['lastsearch_page_'.$relativepathstring] = $_SESSION['lastsearch_page_tmp_'.$relativepathstring];
1046 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
1047 }
1048 if (!empty($_SESSION['lastsearch_mode_tmp_'.$relativepathstring])) {
1049 $_SESSION['lastsearch_mode_'.$relativepathstring] = $_SESSION['lastsearch_mode_tmp_'.$relativepathstring];
1050 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
1051 }
1052 }
1053 if (!empty($_GET['save_pageforbacktolist']) && !empty($_SERVER["HTTP_REFERER"])) { // We must use $_GET here
1054 if (empty($_SESSION['pageforbacktolist'])) {
1055 $pageforbacktolistarray = array();
1056 } else {
1057 $pageforbacktolistarray = $_SESSION['pageforbacktolist'];
1058 }
1059 $tmparray = explode(':', $_GET['save_pageforbacktolist'], 2);
1060 if (!empty($tmparray[0]) && !empty($tmparray[1])) {
1061 $pageforbacktolistarray[$tmparray[0]] = $tmparray[1];
1062 $_SESSION['pageforbacktolist'] = $pageforbacktolistarray;
1063 }
1064 }
1065
1066 $action = '';
1067 $parameters = array();
1068 $reshook = $hookmanager->executeHooks('updateSession', $parameters, $user, $action);
1069 if ($reshook < 0) {
1070 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1071 }
1072 }
1073 }
1074
1075 // Is it a new session that has started ?
1076 // If we are here, this means authentication was successful.
1077 if (!isset($_SESSION["dol_login"])) {
1078 // New session for this login has started.
1079 $error = 0;
1080
1081 // Store value into session (values always stored)
1082 $_SESSION["dol_login"] = $user->login;
1083 $_SESSION["dol_logindate"] = dol_now('gmt');
1084 $_SESSION["dol_authmode"] = isset($dol_authmode) ? $dol_authmode : '';
1085 $_SESSION["dol_tz"] = isset($dol_tz) ? $dol_tz : '';
1086 $_SESSION["dol_tz_string"] = isset($dol_tz_string) ? $dol_tz_string : '';
1087 $_SESSION["dol_dst"] = isset($dol_dst) ? $dol_dst : '';
1088 $_SESSION["dol_dst_observed"] = isset($dol_dst_observed) ? $dol_dst_observed : '';
1089 $_SESSION["dol_dst_first"] = isset($dol_dst_first) ? $dol_dst_first : '';
1090 $_SESSION["dol_dst_second"] = isset($dol_dst_second) ? $dol_dst_second : '';
1091 $_SESSION["dol_screenwidth"] = isset($dol_screenwidth) ? $dol_screenwidth : '';
1092 $_SESSION["dol_screenheight"] = isset($dol_screenheight) ? $dol_screenheight : '';
1093 $_SESSION["dol_company"] = getDolGlobalString("MAIN_INFO_SOCIETE_NOM");
1094 $_SESSION["dol_entity"] = $conf->entity;
1095 // Store value into session (values stored only if defined)
1096 // Note: do not store the hide-menu flags when the login was done from inside a dialog popup iframe
1097 // (dol_openinpopup set, for example after a session timeout inside a popup opened by
1098 // dolButtonToOpenUrlInDialogPopup()), otherwise the whole session loses its menus.
1099 if (!empty($dol_hide_topmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1100 $_SESSION['dol_hide_topmenu'] = $dol_hide_topmenu;
1101 }
1102 if (!empty($dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1103 $_SESSION['dol_hide_leftmenu'] = $dol_hide_leftmenu;
1104 }
1105 if (!empty($dol_optimize_smallscreen)) {
1106 $_SESSION['dol_optimize_smallscreen'] = $dol_optimize_smallscreen;
1107 }
1108 if (!empty($dol_no_mouse_hover)) {
1109 $_SESSION['dol_no_mouse_hover'] = $dol_no_mouse_hover;
1110 }
1111 if (!empty($dol_use_jmobile)) {
1112 $_SESSION['dol_use_jmobile'] = $dol_use_jmobile;
1113 }
1114
1115 dol_syslog("This is a new started user session. _SESSION['dol_login']=".$_SESSION["dol_login"]." Session id=".session_id());
1116
1117 $db->begin();
1118
1119 $user->update_last_login_date();
1120
1121 $loginfo = 'TZ='.$_SESSION["dol_tz"].';TZString='.$_SESSION["dol_tz_string"].';Screen='.$_SESSION["dol_screenwidth"].'x'.$_SESSION["dol_screenheight"];
1122 $loginfo .= ' - authmode='.$dol_authmode.' - entity='.$conf->entity;
1123
1124 // Call triggers for the "security events" log
1125 $user->context['audit'] = $loginfo;
1126 $user->context['authentication_method'] = $dol_authmode;
1127
1128 // Call trigger
1129 $result = $user->call_trigger('USER_LOGIN', $user);
1130 if ($result < 0) {
1131 $error++;
1132 }
1133 // End call triggers
1134
1135 // Hooks on successful login
1136 $action = '';
1137 $hookmanager->initHooks(array('login'));
1138 $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginfo' => $loginfo);
1139 $reshook = $hookmanager->executeHooks('afterLogin', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1140 if ($reshook < 0) {
1141 $error++;
1142 }
1143
1144 if ($error) {
1145 $db->rollback();
1146 session_destroy();
1147 dol_print_error($db, 'Error in some triggers USER_LOGIN or in some hooks afterLogin');
1148 exit;
1149 } else {
1150 $db->commit();
1151 }
1152
1153 // Change landing page if defined.
1154 $landingpage = getDolUserString('MAIN_LANDING_PAGE', getDolGlobalString('MAIN_LANDING_PAGE'));
1155 if (!empty($landingpage)) { // Example: /index.php
1156 $newpath = dol_buildpath($landingpage, 1);
1157 if ($_SERVER["PHP_SELF"] != $newpath) { // not already on landing page (avoid infinite loop)
1158 header('Location: '.$newpath);
1159 exit;
1160 }
1161 }
1162 }
1163
1164 // Check if user must change password at next login
1165 if (!empty($user->force_pass_change) && $dol_authmode == 'dolibarr') {
1166 // redirect to a simple page with only one action is possible : change your password
1167 $allowedpages = array('/user/changepassword.php', '/user/logout.php');
1168 $currentpage = $_SERVER['PHP_SELF'];
1169 $isallowed = false;
1170 foreach ($allowedpages as $page) {
1171 if (preg_match('/'.preg_quote($page, '/').'$/', $currentpage)) {
1172 $isallowed = true;
1173 break;
1174 }
1175 }
1176 if (!$isallowed) {
1177 header('Location: '.DOL_URL_ROOT.'/user/changepassword.php');
1178 exit;
1179 }
1180 }
1181
1182 // If user admin, we force the rights-based modules
1183 if ($user->admin) {
1184 $user->rights->user->user->lire = 1;
1185 $user->rights->user->user->creer = 1;
1186 $user->rights->user->user->password = 1;
1187 $user->rights->user->user->supprimer = 1;
1188 $user->rights->user->self->creer = 1;
1189 $user->rights->user->self->password = 1;
1190
1191 //Required if advanced permissions are used with MAIN_USE_ADVANCED_PERMS
1192 if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
1193 if (!$user->hasRight('user', 'user_advance')) {
1194 $user->rights->user->user_advance = new stdClass(); // To avoid warnings
1195 }
1196 if (!$user->hasRight('user', 'self_advance')) {
1197 $user->rights->user->self_advance = new stdClass(); // To avoid warnings
1198 }
1199 if (!$user->hasRight('user', 'group_advance')) {
1200 $user->rights->user->group_advance = new stdClass(); // To avoid warnings
1201 }
1202
1203 $user->rights->user->user_advance->readperms = 1;
1204 $user->rights->user->user_advance->write = 1;
1205 $user->rights->user->self_advance->readperms = 1;
1206 $user->rights->user->self_advance->writeperms = 1;
1207 $user->rights->user->group_advance->read = 1;
1208 $user->rights->user->group_advance->readperms = 1;
1209 $user->rights->user->group_advance->write = 1;
1210 $user->rights->user->group_advance->delete = 1;
1211 }
1212 }
1213
1214 /*
1215 * Overwrite some configs globals (try to avoid this and have code to use instead $user->conf->xxx)
1216 */
1217
1218 // Set liste_limit from user setup
1219 if (isset($user->conf->MAIN_SIZE_LISTE_LIMIT)) { // If a user setup exists
1220 $conf->liste_limit = getDolUserInt('MAIN_SIZE_LISTE_LIMIT'); // Can be 0
1221 }
1222 if ((int) $conf->liste_limit <= 0) {
1223 // Mode automatic.
1224 $conf->liste_limit = getListLimitFromScreenHeight();
1225 }
1226 // Overwrite main_checkbox_left_column from user setup
1227 if (isset($user->conf->MAIN_CHECKBOX_LEFT_COLUMN)) { // If a user setup exists
1228 $conf->main_checkbox_left_column = getDolUserInt('MAIN_CHECKBOX_LEFT_COLUMN'); // Can be 0
1229 }
1230
1231 // Replace conf->css by personalized value if theme not forced
1232 if (!getDolGlobalString('MAIN_FORCETHEME') && getDolUserString('MAIN_THEME')) {
1233 $conf->theme = getDolUserString('MAIN_THEME');
1234 $conf->css = "/theme/".$conf->theme."/style.css.php";
1235 }
1236} else {
1237 // We may have NOLOGIN set, but NOREQUIREUSER not
1238 if (!empty($user) && method_exists($user, 'loadDefaultValues') && !defined('NODEFAULTVALUES')) {
1239 $user->loadDefaultValues(); // Load default values for everybody (works even if $user->id = 0
1240 }
1241}
1242
1243
1244// Case forcing style from url
1245if (GETPOST('theme', 'aZ09')) {
1246 $conf->theme = GETPOST('theme', 'aZ09', 1);
1247 $conf->css = "/theme/".$conf->theme."/style.css.php";
1248}
1249
1250// Set javascript option
1251if (GETPOSTINT('nojs')) { // If javascript was not disabled on URL
1252 $conf->use_javascript_ajax = 0;
1253} else {
1254 if (getDolUserString('MAIN_DISABLE_JAVASCRIPT')) {
1255 $conf->use_javascript_ajax = !getDolUserString('MAIN_DISABLE_JAVASCRIPT') ? 1 : 0;
1256 }
1257}
1258
1259// Set MAIN_OPTIMIZEFORTEXTBROWSER for user (must be after login part)
1260if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1261 $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER');
1262 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') == 1) {
1263 $conf->global->THEME_TOPMENU_DISABLE_IMAGE = 1;
1264 }
1265}
1266//var_dump($conf->global->THEME_TOPMENU_DISABLE_IMAGE);
1267//var_dump($user->conf->THEME_TOPMENU_DISABLE_IMAGE);
1268
1269// set MAIN_OPTIMIZEFORCOLORBLIND for user
1270$conf->global->MAIN_OPTIMIZEFORCOLORBLIND = getDolUserString('MAIN_OPTIMIZEFORCOLORBLIND');
1271
1272// Set terminal output option according to conf->browser.
1273if (GETPOSTINT('dol_hide_leftmenu') || !empty($_SESSION['dol_hide_leftmenu'])) {
1274 $conf->dol_hide_leftmenu = 1;
1275}
1276if (GETPOSTINT('dol_hide_topmenu') || !empty($_SESSION['dol_hide_topmenu'])) {
1277 $conf->dol_hide_topmenu = 1;
1278}
1279if (GETPOSTINT('dol_optimize_smallscreen') || !empty($_SESSION['dol_optimize_smallscreen'])) {
1280 $conf->dol_optimize_smallscreen = 1;
1281}
1282if (GETPOSTINT('dol_no_mouse_hover') || !empty($_SESSION['dol_no_mouse_hover'])) {
1283 $conf->dol_no_mouse_hover = 1;
1284}
1285if (GETPOSTINT('dol_use_jmobile') || !empty($_SESSION['dol_use_jmobile'])) {
1286 $conf->dol_use_jmobile = 1;
1287}
1288// If not on Desktop
1289if (!empty($conf->browser->layout) && $conf->browser->layout != 'classic') {
1290 $conf->dol_no_mouse_hover = 1;
1291}
1292
1293// If on smartphone or optimized for small screen
1294if ((!empty($conf->browser->layout) && $conf->browser->layout == 'phone')
1295 || (!empty($_SESSION['dol_screenwidth']) && $_SESSION['dol_screenwidth'] < 400)
1296 || (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 400
1297 || getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER'))
1298) {
1299 $conf->dol_optimize_smallscreen = 1;
1300
1301 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM') == 1) {
1302 $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
1303 }
1304}
1305// Replace themes bugged with jmobile with eldy
1306if (!empty($conf->dol_use_jmobile) && in_array($conf->theme, array('bureau2crea', 'cameleo', 'amarok'))) {
1307 $conf->theme = 'eldy';
1308 $conf->css = "/theme/".$conf->theme."/style.css.php";
1309}
1310
1311if (!defined('NOREQUIRETRAN')) {
1312 if (!GETPOST('lang', 'aZ09')) { // If language was not forced on URL
1313 // If user has chosen its own language
1314 if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1315 // If different than current language
1316 //print ">>>".$langs->getDefaultLang()."-".$user->conf->MAIN_LANG_DEFAULT;
1317 if ($langs->getDefaultLang() != $user->conf->MAIN_LANG_DEFAULT) {
1318 $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1319 }
1320 }
1321 }
1322}
1323
1324if (!defined('NOLOGIN')) {
1325 // If the login is not recovered, it is identified with an account that does not exist.
1326 // Hacking attempt?
1327 if (!$user->login) {
1329 }
1330
1331 // Check if user is active
1332 if ($user->status < 1) {
1333 // If not active, we refuse the user
1334 $langs->loadLangs(array("errors", "other"));
1335 dol_syslog("Authentication KO as login is disabled", LOG_NOTICE);
1336 accessforbidden("ErrorLoginDisabled");
1337 }
1338
1339 // Load permissions for entity = $conf->entity
1340 $user->loadRights();
1341}
1342
1343dol_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);
1344//Another call for easy debug
1345//dol_syslog("Access to ".$_SERVER["PHP_SELF"].' '.$_SERVER["HTTP_REFERER"].' GET='.join(',',array_keys($_GET)).'->'.join(',',$_GET).' POST:'.join(',',array_keys($_POST)).'->'.join(',',$_POST));
1346
1347// Load main languages files
1348if (!defined('NOREQUIRETRAN')) {
1349 // Load translation files required by page
1350 $langs->loadLangs(array('main', 'dict'));
1351
1352 // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
1353 // accesskey is for Mac: CTRL + Option + key for all browsers
1354
1355 // Note: $con->browser->os and $conf->browser->name may not be defined if we are in CLI mode.
1356 $conf->browser->stringforfirstkey = $langs->transnoentities("KeyboardShortcut");
1357 if (!empty($conf->browser->os) && $conf->browser->os == 'macintosh') {
1358 $conf->browser->stringforfirstkey .= ' CTRL + Option +';
1359 } else {
1360 if (!empty($conf->browser->name) && $conf->browser->name == 'chrome') {
1361 $conf->browser->stringforfirstkey .= ' ALT +';
1362 } elseif (!empty($conf->browser->name) && $conf->browser->name == 'firefox') {
1363 $conf->browser->stringforfirstkey .= ' ALT + SHIFT +';
1364 } else {
1365 $conf->browser->stringforfirstkey .= ' CTL +';
1366 }
1367 }
1368}
1369
1370// Define some constants used for style of arrays
1371$bc = array(0 => 'class="impair"', 1 => 'class="pair"');
1372$bcdd = array(0 => 'class="drag drop oddeven"', 1 => 'class="drag drop oddeven"');
1373$bcnd = array(0 => 'class="nodrag nodrop nohover"', 1 => 'class="nodrag nodrop nohoverpair"'); // Used for tr to add new lines
1374
1375// Define messages variables
1376$mesg = '';
1377$warning = '';
1378$error = 0;
1379// deprecated, see setEventMessages() and dol_htmloutput_events()
1380$mesgs = array();
1381$warnings = array();
1382$errors = array();
1383
1384// Constants used to defined number of lines in textarea
1385if (empty($conf->browser->firefox)) {
1386 define('ROWS_1', 1);
1387 define('ROWS_2', 2);
1388 define('ROWS_3', 3);
1389 define('ROWS_4', 4);
1390 define('ROWS_5', 5);
1391 define('ROWS_6', 6);
1392 define('ROWS_7', 7);
1393 define('ROWS_8', 8);
1394 define('ROWS_9', 9);
1395} else {
1396 define('ROWS_1', 0);
1397 define('ROWS_2', 1);
1398 define('ROWS_3', 2);
1399 define('ROWS_4', 3);
1400 define('ROWS_5', 4);
1401 define('ROWS_6', 5);
1402 define('ROWS_7', 6);
1403 define('ROWS_8', 7);
1404 define('ROWS_9', 8);
1405}
1406
1407$heightforframes = 52; // Used by frames.php page
1408
1409// Init menu manager
1410if (!defined('NOREQUIREMENU')) {
1411 if (empty($user->socid)) { // If internal user or not defined
1412 $conf->standard_menu = getDolGlobalString('MAIN_MENU_STANDARD_FORCED', getDolGlobalString('MAIN_MENU_STANDARD', 'eldy_menu.php'));
1413 } else {
1414 // If external user
1415 $conf->standard_menu = getDolGlobalString('MAIN_MENUFRONT_STANDARD_FORCED', getDolGlobalString('MAIN_MENUFRONT_STANDARD', 'eldy_menu.php'));
1416 }
1417
1418 // Load the menu manager (only if not already done)
1419 $file_menu = $conf->standard_menu;
1420 if (GETPOST('menu', 'alpha')) {
1421 $file_menu = GETPOST('menu', 'alpha'); // example: menu=eldy_menu.php
1422 }
1423
1424 if (!class_exists('MenuManager')) {
1425 $menufound = 0;
1426 $dirmenus = array_merge(array("/core/menus/"), (array) $conf->modules_parts['menus']);
1427 foreach ($dirmenus as $dirmenu) {
1428 $menufound = dol_include_once($dirmenu."standard/".$file_menu);
1429 if (class_exists('MenuManager')) {
1430 break;
1431 }
1432 }
1433 if (!class_exists('MenuManager')) { // If failed to include, we try with standard eldy_menu.php
1434 dol_syslog("You define a menu manager '".$file_menu."' that can not be loaded.", LOG_WARNING);
1435 $file_menu = 'eldy_menu.php';
1436 include_once DOL_DOCUMENT_ROOT."/core/menus/standard/".$file_menu;
1437 }
1438 }
1439 // @phan-suppress-next-line PhanRedefinedClassReference
1440 $menumanager = new MenuManager($db, empty($user->socid) ? 0 : 1);
1441 // @phan-suppress-next-line PhanRedefinedClassReference
1442 $menumanager->loadMenu();
1443}
1444
1445if (!empty(GETPOST('seteventmessages', 'alpha'))) {
1446 $message = GETPOST('seteventmessages', 'alpha');
1447 $messages = explode(',', $message);
1448 foreach ($messages as $key => $msg) {
1449 $tmp = explode(':', $msg);
1450 setEventMessages($tmp[0], null, !empty($tmp[1]) ? $tmp[1] : 'mesgs');
1451 }
1452}
1453
1454// Functions
1455
1456if (!function_exists("llxHeader")) {
1480 function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
1481 {
1482 global $conf, $hookmanager;
1483
1484 $parameters = array(
1485 'head' => & $head,
1486 'title' => & $title,
1487 'help_url' => & $help_url,
1488 'target' => & $target,
1489 'disablejs' => & $disablejs,
1490 'disablehead' => & $disablehead,
1491 'arrayofjs' => & $arrayofjs,
1492 'arrayofcss' => & $arrayofcss,
1493 'morequerystring' => & $morequerystring,
1494 'morecssonbody' => & $morecssonbody,
1495 'replacemainareaby' => & $replacemainareaby,
1496 'disablenofollow' => & $disablenofollow,
1497 'disablenoindex' => & $disablenoindex
1498
1499 );
1500 $reshook = $hookmanager->executeHooks('llxHeader', $parameters);
1501 if ($reshook > 0) {
1502 print $hookmanager->resPrint;
1503 return;
1504 }
1505
1506 // html header
1507 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
1508
1509 $tmpcsstouse = 'sidebar-collapse'.($morecssonbody ? ' '.$morecssonbody : '');
1510 // If theme MD and classic layer, we open the menulayer by default.
1511 if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1512 global $mainmenu;
1513 if ($mainmenu != 'website') {
1514 $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
1515 }
1516 }
1517
1518 if (getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND')) {
1519 $tmpcsstouse .= ' colorblind-'.strip_tags(getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND'));
1520 }
1521
1522 if (GETPOST('dol_openinpopup', 'aZ09')) {
1523 $tmpcsstouse .= ' dol_openinpopup';
1524 }
1525
1526 print '<body id="mainbody" class="'.$tmpcsstouse.'">'."\n";
1527
1528 // top menu and left menu area
1529 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && !GETPOST('dol_openinpopup', 'aZ09')) {
1530 top_menu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
1531 }
1532
1533 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1534 left_menu('', $help_url, '', array(), 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
1535 }
1536
1537 // main area
1538 if ($replacemainareaby) {
1539 print $replacemainareaby;
1540 return;
1541 }
1542
1543 main_area($title);
1544 }
1545}
1546
1547
1555function top_httphead($contenttype = 'text/html', $forcenocache = 0)
1556{
1557 global $db, $conf, $hookmanager;
1558
1559 if ($contenttype != 'none') {
1560 if ($contenttype == 'text/html') {
1561 header("Content-Type: text/html; charset=".$conf->file->character_set_client);
1562 } else {
1563 header("Content-Type: ".$contenttype);
1564 }
1565 }
1566
1567 // Security options
1568
1569 // X-Content-Type-Options
1570 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)
1571
1572 // X-Frame-Options
1573 if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
1574 header("X-Frame-Options: SAMEORIGIN"); // By default, frames allowed only if on same domain (stop some XSS attacks)
1575 } else {
1576 header("X-Frame-Options: ALLOWALL");
1577 }
1578
1579 if (getDolGlobalString('MAIN_SECURITY_FORCE_ACCESS_CONTROL_ALLOW_ORIGIN')) {
1580 $tmpurl = constant('DOL_MAIN_URL_ROOT');
1581 $tmpurl = preg_replace('/^(https?:\/\/[^\/]+)\/.*$/', '\1', $tmpurl);
1582 header('Access-Control-Allow-Origin: '.$tmpurl);
1583 header('Vary: Origin');
1584 }
1585
1586 // X-XSS-Protection
1587 //header("X-XSS-Protection: 1"); // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
1588
1589 // Content-Security-Policy-Report-Only
1590 if (!defined('MAIN_SECURITY_FORCECSPRO')) {
1591 // If CSP not forced from the page
1592
1593 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1594 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1595 // 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: *;
1596 // For example, to restrict everything to itself except img that can be on other servers:
1597 // default-src 'self'; img-src *;
1598 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1599 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1600 //
1601 // $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;";
1602 // $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';";
1603 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
1604
1605 if (!is_object($hookmanager)) {
1606 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1607 $hookmanager = new HookManager($db);
1608 }
1609 $hookmanager->initHooks(array("main"));
1610
1611 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'reportonly');
1612 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1613 if ($result > 0) {
1614 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1615 } else {
1616 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1617 }
1618
1619 // Add Dolibarr to Content-Security-Policy
1620 $contentsecuritypolicy = preg_replace('/default-src \'self\'/', 'default-src \'self\' *.dolibarr.org', $contentsecuritypolicy);
1621
1622 if (!empty($contentsecuritypolicy)) {
1623 header("Content-Security-Policy-Report-Only: ".$contentsecuritypolicy);
1624 }
1625 } else {
1626 header("Content-Security-Policy-Report-Only: ".constant('MAIN_SECURITY_FORCECSPRO'));
1627 }
1628
1629 // Content-Security-Policy
1630 if (!defined('MAIN_SECURITY_FORCECSP')) {
1631 // If CSP not forced from the page
1632
1633 // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1634 // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1635 // 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: *;
1636 // For example, to restrict everything to itself except img that can be on other servers:
1637 // default-src 'self'; img-src *;
1638 // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1639 // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1640 //
1641 // $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;";
1642 // $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';";
1643 $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
1644
1645 if (!is_object($hookmanager)) {
1646 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1647 $hookmanager = new HookManager($db);
1648 }
1649 $hookmanager->initHooks(array("main"));
1650
1651 $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'active');
1652 $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1653 if ($result > 0) {
1654 $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1655 } else {
1656 $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1657 }
1658
1659 // Add Dolibarr to Content-Security-Policy
1660 $contentsecuritypolicy = preg_replace('/default-src \'self\'/', 'default-src \'self\' ping.dolibarr.org', $contentsecuritypolicy);
1661
1662 if (!empty($contentsecuritypolicy)) {
1663 header("Content-Security-Policy: ".$contentsecuritypolicy);
1664 }
1665 } else {
1666 header("Content-Security-Policy: ".constant('MAIN_SECURITY_FORCECSP'));
1667 }
1668
1669 // Referrer-Policy
1670 // Say if we must provide the referrer when we jump onto another web page.
1671 // 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.
1672 // 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.
1673 if (!defined('MAIN_SECURITY_FORCERP')) {
1674 $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
1675 if (!empty($referrerpolicy)) {
1676 header("Referrer-Policy: ".$referrerpolicy);
1677 }
1678 } else {
1679 header("Referrer-Policy: ".constant('MAIN_SECURITY_FORCERP'));
1680 }
1681
1682 // Strict-Transport-Security
1683 if (!defined('MAIN_SECURITY_FORCESTS')) {
1684 $sts = getDolGlobalString('MAIN_SECURITY_FORCESTS', "");
1685 if (!empty($sts)) {
1686 header("Strict-Transport-Security: ".$sts);
1687 }
1688 } else {
1689 header("Strict-Transport-Security: ".constant('MAIN_SECURITY_FORCESTS'));
1690 }
1691
1692 // Permissions-Policy (old name was Feature-Policy)
1693 if (!defined('MAIN_SECURITY_FORCEPP')) {
1694 $pp = getDolGlobalString('MAIN_SECURITY_FORCEPP', "");
1695 if (!empty($pp)) {
1696 header("Permissions-Policy: ".$pp);
1697 }
1698 } else {
1699 header("Permissions-Policy: ".constant('MAIN_SECURITY_FORCEPP'));
1700 }
1701
1702 // Cache
1703 if ($forcenocache) {
1704 header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
1705 }
1706
1707 // No need to add this token in header, we use instead the one into the forms.
1708 //header("anti-csrf-token: ".newToken());
1709}
1710
1726function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
1727{
1728 global $db, $conf, $langs, $user, $mysoc, $hookmanager;
1729
1730 top_httphead();
1731
1732 if (empty($conf->css)) {
1733 $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
1734 }
1735
1736 print '<!doctype html>'."\n";
1737
1738 print '<html lang="'.substr($langs->defaultlang, 0, 2).'">'."\n";
1739
1740 //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
1741 if (empty($disablehead)) {
1742 if (!is_object($hookmanager)) {
1743 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1744 $hookmanager = new HookManager($db);
1745 }
1746 $hookmanager->initHooks(array("main"));
1747
1748 $ext = 'layout='.(empty($conf->browser->layout) ? '' : $conf->browser->layout).'&version='.urlencode(DOL_VERSION);
1749
1750 print "<head>\n";
1751
1752 if (GETPOST('dol_basehref', 'alpha')) {
1753 print '<base href="'.dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')).'">'."\n";
1754 }
1755
1756 // Displays meta
1757 print '<meta charset="utf-8">'."\n";
1758 print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
1759 print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
1760 print '<meta name="author" content="Dolibarr Development Team">'."\n";
1761 print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
1762 print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
1763 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
1764 print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
1765 }
1766 // Favicon
1767 $favicon = DOL_URL_ROOT.'/theme/dolibarr_256x256_color.png';
1768 $appletouchicon = DOL_URL_ROOT.'/theme/apple-touch-icon.png';
1769 if (!empty($mysoc->logo_squarred_mini)) {
1770 $favicon = DOL_URL_ROOT.'/viewimage.php?cache=1&modulepart=mycompany&file='.urlencode('logos/thumbs/'.$mysoc->logo_squarred_mini);
1771 }
1772 if (getDolGlobalString('MAIN_FAVICON_URL')) {
1773 $favicon = getDolGlobalString('MAIN_FAVICON_URL');
1774 }
1775 if (empty($conf->dol_use_jmobile)) {
1776 print '<link rel="shortcut icon" type="image/x-icon" href="'.$favicon.'"/>'."\n"; // Not required into an Android webview
1777 print '<link rel="apple-touch-icon" href="'.$appletouchicon.'"/>'."\n";
1778 }
1779
1780 // Mobile appli like icon
1781 $manifest = DOL_URL_ROOT.'/theme/manifest.json.php';
1782 $parameters = array('manifest' => $manifest);
1783 $resHook = $hookmanager->executeHooks('hookSetManifest', $parameters); // Note that $action and $object may have been modified by some hooks
1784 if ($resHook > 0) {
1785 $manifest = $hookmanager->resPrint; // Replace manifest.json
1786 } else {
1787 $manifest .= $hookmanager->resPrint; // Concat to actual manifest declaration
1788 }
1789 if (!empty($manifest)) {
1790 print '<link rel="manifest" href="'.$manifest.'" />'."\n";
1791 }
1792
1793 if (getDolGlobalString('THEME_ELDY_TOPMENU_BACK1')) {
1794 print '<meta name="theme-color" content="rgb(' . getDolGlobalString('THEME_ELDY_TOPMENU_BACK1').')">'."\n";
1795 }
1796
1797 // Auto refresh page
1798 if (GETPOSTINT('autorefresh') > 0) {
1799 print '<meta http-equiv="refresh" content="'.GETPOSTINT('autorefresh').'">';
1800 }
1801
1802 // Displays title
1803 $appli = constant('DOL_APPLICATION_TITLE');
1804 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
1805 if ($applicustom) {
1806 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
1807 }
1808
1809 print '<title>';
1810 $titletoshow = '';
1811 if ($title && preg_match('/showapp/', getDolGlobalString('MAIN_HTML_TITLE'))) {
1812 $titletoshow = dol_htmlentities($appli.' - '.$title);
1813 } elseif ($title) {
1814 $titletoshow = dol_htmlentities($title);
1815 } else {
1816 $titletoshow = dol_htmlentities($appli);
1817 }
1818
1819 $parameters = array('title' => $titletoshow);
1820 $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
1821 if ($result > 0) {
1822 $titletoshow = $hookmanager->resPrint; // Replace Title to show
1823 } else {
1824 $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
1825 }
1826
1827 print $titletoshow;
1828 print '</title>';
1829
1830 print "\n";
1831
1832 if (GETPOSTINT('version')) {
1833 $ext = 'version='.GETPOSTINT('version'); // useful to force no cache on css/js
1834 }
1835 // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
1836 if (GETPOST('dol_resetcache')) {
1837 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1838 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
1839 }
1840
1841 $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;
1842
1843 $themeparam .= '&' .$ext . '&revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV");
1844 if (GETPOSTISSET('dol_hide_topmenu')) {
1845 $themeparam .= '&amp;dol_hide_topmenu='.GETPOSTINT('dol_hide_topmenu');
1846 }
1847 if (GETPOSTISSET('dol_hide_leftmenu')) {
1848 $themeparam .= '&amp;dol_hide_leftmenu='.GETPOSTINT('dol_hide_leftmenu');
1849 }
1850 if (GETPOSTISSET('dol_openinpopup')) {
1851 $themeparam .= '&amp;dol_openinpopup='.GETPOST('dol_openinpopup', 'aZ09');
1852 }
1853 if (GETPOSTISSET('dol_optimize_smallscreen')) {
1854 $themeparam .= '&amp;dol_optimize_smallscreen='.GETPOSTINT('dol_optimize_smallscreen');
1855 }
1856 if (GETPOSTISSET('dol_no_mouse_hover')) {
1857 $themeparam .= '&amp;dol_no_mouse_hover='.GETPOSTINT('dol_no_mouse_hover');
1858 }
1859 if (GETPOSTISSET('dol_use_jmobile')) {
1860 $themeparam .= '&amp;dol_use_jmobile='.GETPOSTINT('dol_use_jmobile');
1861 $conf->dol_use_jmobile = GETPOSTINT('dol_use_jmobile');
1862 }
1863 if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
1864 $themeparam .= '&amp;THEME_DARKMODEENABLED='.GETPOSTINT('THEME_DARKMODEENABLED');
1865 }
1866 if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
1867 $themeparam .= '&amp;THEME_SATURATE_RATIO='.GETPOSTINT('THEME_SATURATE_RATIO');
1868 }
1869
1870
1877 $jsContextVars = [
1878 'DOL_VERSION' => DOL_VERSION,
1879 'DOL_URL_ROOT' => DOL_URL_ROOT,
1880 ];
1881
1882 $jsContextPathUrl = DOL_URL_ROOT . '/public/includes/dolibarr-js-context';
1883 $jsContextFiles = [
1884 'dolibarr-context.umd.js', // The js Dolibarr context definition
1885 'dolibarr-tool.seteventmessage.js' // The first tools to help dev for easy event in js
1886 ];
1887
1888 if (! defined('NOREQUIRETRAN')) {
1889 // Langs tool see Documentation at admin/tools/ui/dolibarr-context/index.php
1890 $jsContextFiles[] = 'dolibarr-tool.langs.js';
1891 $jsContextVars['MAIN_LANG_DEFAULT'] = $langs->getDefaultLang();// For langs tool
1892 $jsContextVars['DOL_URL_ROOT'] = DOL_URL_ROOT;
1893 $jsContextVars['DOL_LANG_INTERFACE_URL'] = dol_buildpath('public/langs/langs-tool-interface.php', 1);// For langs tool
1894 }
1895
1896 // Load context and all js tools
1897 foreach ($jsContextFiles as $jsContextFile) {
1898 print '<script nonce="'.getNonce().'" src="'.$jsContextPathUrl.'/'.$jsContextFile.'?' . $ext . '" ></script>'."\n";
1899 }
1900
1901 // DEFINE FIRST NEEDED JS CONTEXT VARS
1902 print '<script nonce="'.getNonce().'">Dolibarr.setContextVars('.json_encode($jsContextVars).');</script>'."\n";
1903
1904 // -- END OF DEFINITION OF DOLIBARR JS CONTEXT AND TOOLS
1905
1906
1907 if (getDolGlobalString('MAIN_ENABLE_FONT_ROBOTO')) {
1908 print '<link rel="preconnect" href="https://fonts.gstatic.com">'."\n";
1909 print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">'."\n";
1910 }
1911
1912 if (!defined('DISABLE_JQUERY') && (!$disablejs || $disablejs == 2) && $conf->use_javascript_ajax) {
1913 print '<!-- Includes CSS for JQuery (Ajax library) -->'."\n";
1914 if (!defined('DISABLE_JQUERY_UI')) {
1915 $jquerytheme = 'base';
1916 if (getDolGlobalString('MAIN_USE_JQUERY_THEME')) {
1917 $jquerytheme = getDolGlobalString('MAIN_USE_JQUERY_THEME');
1918 }
1919 if (constant('JS_JQUERY_UI')) {
1920 print '<link rel="stylesheet" type="text/css" href="' . JS_JQUERY_UI . 'css/' . $jquerytheme . '/jquery-ui.min.css?' . $ext . '">' . "\n"; // Forced JQuery
1921 } else {
1922 print '<link rel="stylesheet" type="text/css" href="' . DOL_URL_ROOT . '/public/includes/jquery/css/' . $jquerytheme . '/jquery-ui.css?' . $ext . '">' . "\n"; // JQuery
1923 }
1924 }
1925 if (!defined('DISABLE_JQUERY_JNOTIFY')) {
1926 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css?' . $ext . '">'."\n"; // JNotify
1927 }
1928 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) { // jQuery plugin "mutiselect", "multiple-select", "select2"...
1929 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT');
1930 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/'.$tmpplugin.'/dist/css/'.$tmpplugin.'.css?' . $ext . '">'."\n";
1931 }
1932 }
1933
1934 if (!defined('DISABLE_FONT_AWSOME')) {
1935 print '<!-- Includes CSS for font awesome -->'."\n";
1936 $fontawesome_directory = getDolGlobalString('MAIN_FONTAWESOME_DIRECTORY', '/theme/common/fontawesome-5');
1937 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.$fontawesome_directory.'/css/all.min.css?' . $ext . '">'."\n";
1938 }
1939
1940 // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
1941 $themepath = dol_buildpath($conf->css, 1);
1942 $themesubdir = '';
1943 if (!empty($conf->modules_parts['theme'])) { // This slow down
1944 foreach ($conf->modules_parts['theme'] as $reldir) {
1945 if (file_exists(dol_buildpath($reldir.$conf->css, 0))) {
1946 $themepath = dol_buildpath($reldir.$conf->css, 1);
1947 $themesubdir = $reldir;
1948 break;
1949 }
1950 }
1951 }
1952
1953 if (!defined('DISABLE_CSS_DEFAULT_THEME')) {
1954 print '<!-- Includes CSS for Dolibarr theme -->'."\n";
1955 print '<link rel="stylesheet" type="text/css" href="' . $themepath . $themeparam . '">' . "\n";
1956 }
1957
1958 // To fix old chrome bug
1959 /*
1960 if (getDolGlobalString('MAIN_FIX_FLASH_ON_CHROME')) {
1961 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";
1962 }
1963 */
1964
1965 // LEAFLET AND GEOMAN
1966 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
1967 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.css?' . $ext . "\">\n";
1968 print '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.css?' . $ext . "\">\n";
1969 }
1970
1971 // CSS forced by modules (relative url starting with /)
1972 if (!empty($conf->modules_parts['css'])) {
1973 $arraycss = (array) $conf->modules_parts['css'];
1974 foreach ($arraycss as $modcss => $filescss) {
1975 $filescss = (array) $filescss; // To be sure filecss is an array
1976 foreach ($filescss as $cssfile) {
1977 if (empty($cssfile)) {
1978 dol_syslog("Warning: module ".$modcss." declared a css path file into its descriptor that is empty.", LOG_WARNING);
1979 }
1980 // cssfile is a relative path
1981 $urlforcss = dol_buildpath($cssfile, 1);
1982 if ($urlforcss && $urlforcss != '/') {
1983 print '<!-- Includes CSS added by module '.$modcss.' -->'."\n".'<link rel="stylesheet" type="text/css" href="'.$urlforcss;
1984 // 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.
1985 if (!preg_match('/\.css$/i', $cssfile)) {
1986 print $themeparam;
1987 }
1988 print '">'."\n";
1989 } else {
1990 dol_syslog("Warning: module ".$modcss." declared a css path file for a file we can't find.", LOG_WARNING);
1991 }
1992 }
1993 }
1994 }
1995 // CSS forced by page in top_htmlhead call (relative url starting with /)
1996 if (is_array($arrayofcss)) {
1997 foreach ($arrayofcss as $cssfile) {
1998 if (preg_match('/^(http|\/\/)/i', $cssfile)) {
1999 $urltofile = $cssfile;
2000 } else {
2001 $urltofile = dol_buildpath($cssfile, 1);
2002 }
2003 print '<!-- Includes CSS added by page -->'."\n".'<link rel="stylesheet" type="text/css" title="default" href="'.$urltofile;
2004 // 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.
2005 if (!preg_match('/\.css$/i', $cssfile)) {
2006 print $themeparam;
2007 }
2008 print '">'."\n";
2009 }
2010 }
2011
2012 // Custom CSS
2013 if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
2014 // If a custom CSS was set, we add link to the custom css php file
2015 print '<link rel="stylesheet" type="text/css" href="'.DOL_URL_ROOT.'/theme/custom.css.php?' . $ext . '&amp;revision='.getDolGlobalInt("MAIN_IHM_PARAMS_REV").'">'."\n";
2016 }
2017
2018 // Output standard javascript links
2019 if (!defined('DISABLE_JQUERY') && (!$disablejs || $disablejs == 2) && !empty($conf->use_javascript_ajax)) {
2020 // JQuery. Must be before other includes
2021 print '<!-- Includes JS for JQuery -->'."\n";
2022 if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
2023 print '<script nonce="'.getNonce().'" src="'.JS_JQUERY.'jquery.min.js?' . $ext . '"></script>'."\n";
2024 } else {
2025 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/js/jquery.min.js?' . $ext . '"></script>'."\n";
2026 }
2027 if (!defined('DISABLE_JQUERY_UI')) {
2028 if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
2029 print '<script nonce="' . getNonce() . '" src="' . JS_JQUERY_UI . 'jquery-ui.min.js?' . $ext . '"></script>' . "\n";
2030 } else {
2031 print '<script nonce="' . getNonce() . '" src="' . DOL_URL_ROOT . '/public/includes/jquery/js/jquery-ui.min.js?' . $ext . '"></script>' . "\n";
2032 }
2033 }
2034 // jQuery jnotify
2035 if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
2036 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jnotify/jquery.jnotify.min.js?' . $ext . '"></script>'."\n";
2037 }
2038 // Table drag and drop lines
2039 if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
2040 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/tablednd/jquery.tablednd.min.js?' . $ext . '"></script>'."\n";
2041 }
2042 // Chart
2043 if (empty($disableforlogin) && (!getDolGlobalString('MAIN_JS_GRAPH') || getDolGlobalString('MAIN_JS_GRAPH') == 'chart') && !defined('DISABLE_JS_GRAPH')) {
2044 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/nnnick/chartjs/dist/chart.min.js?' . $ext . '"></script>'."\n";
2045 }
2046
2047 // jQuery jeditable for Edit In Place features
2048 /*if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !defined('DISABLE_JQUERY_JEDITABLE')) {
2049 print '<!-- JS to manage editInPlace feature -->'."\n";
2050 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.js?' . $ext . '"></script>'."\n";
2051 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js?' . $ext . '"></script>'."\n";
2052 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js?' . $ext . '"></script>'."\n";
2053 print '<script nonce="'.getNonce().'" >'."\n";
2054 print 'var urlSaveInPlace = \''.DOL_URL_ROOT.'/core/ajax/saveinplace.php\';'."\n";
2055 print 'var urlLoadInPlace = \''.DOL_URL_ROOT.'/core/ajax/loadinplace.php\';'."\n";
2056 print 'var tooltipInPlace = \''.$langs->transnoentities('ClickToEdit').'\';'."\n"; // Added in title attribute of span
2057 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 ?
2058 print 'var cancelInPlace = \''.$langs->trans("Cancel").'\';'."\n";
2059 print 'var submitInPlace = \''.$langs->trans('Ok').'\';'."\n";
2060 print 'var indicatorInPlace = \'<img src="'.DOL_URL_ROOT."/theme/".$conf->theme."/img/working.gif".'">\';'."\n";
2061 print 'var withInPlace = 300;'; // width in pixel for default string edit
2062 print '</script>'."\n";
2063 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/editinplace.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2064 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js'.($ext ? '?'.$ext : '').'"></script>'."\n";
2065 }*/
2066 if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
2067 // jQuery plugin "mutiselect", "multiple-select", "select2", ...
2068 $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT');
2069 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
2070 }
2071 if (!defined('DISABLE_MULTISELECT')) { // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
2072 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/public/includes/jquery/plugins/multiselect/jquery.multi-select.js?' . $ext . '"></script>'."\n";
2073 }
2074 }
2075
2076 if (!$disablejs && !empty($conf->use_javascript_ajax)) {
2077 // CKEditor
2078 if (empty($disableforlogin) && (isModEnabled('fckeditor') && (!getDolGlobalString('FCKEDITOR_EDITORNAME') || getDolGlobalString('FCKEDITOR_EDITORNAME') == 'ckeditor') && !defined('DISABLE_CKEDITOR')) || defined('FORCE_CKEDITOR')) {
2079 print '<!-- Includes JS for CKEditor -->'."\n";
2080 $pathckeditor = DOL_URL_ROOT.'/public/includes/ckeditor/ckeditor/';
2081 $jsckeditor = 'ckeditor.js';
2082 if (constant('JS_CKEDITOR')) {
2083 // To use external ckeditor 4 js lib
2084 $pathckeditor = constant('JS_CKEDITOR');
2085 }
2086 print '<script nonce="'.getNonce().'">';
2087 print '/* enable ckeditor by main.inc.php */';
2088 print 'var CKEDITOR_BASEPATH = \''.dol_escape_js($pathckeditor).'\';'."\n";
2089 print 'var ckeditorConfig = \''.dol_escape_js(dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js?' . $ext, 1)).'\';'."\n"; // $themesubdir='' in standard usage
2090 print 'var ckeditorFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2091 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";
2092 print '</script>'."\n";
2093 print '<script src="'.$pathckeditor.$jsckeditor. '?' . $ext . '"></script>'."\n";
2094 print '<script>';
2095 if (GETPOST('mode', 'aZ09') == 'Full_inline') {
2096 print 'CKEDITOR.disableAutoInline = false;'."\n";
2097 } else {
2098 print 'CKEDITOR.disableAutoInline = true;'."\n";
2099 }
2100 print '</script>'."\n";
2101 }
2102
2103 // TinyMCE (alternative WYSIWYG backend, selected by FCKEDITOR_EDITORNAME='tinymce')
2104 if (empty($disableforlogin) && (isModEnabled('fckeditor') && getDolGlobalString('FCKEDITOR_EDITORNAME') == 'tinymce' && !defined('DISABLE_TINYMCE')) || defined('FORCE_TINYMCE')) {
2105 print '<!-- Includes JS for TinyMCE -->'."\n";
2106 $pathtinymce = DOL_URL_ROOT.'/public/includes/tinymce/tinymce/';
2107 $jstinymce = 'tinymce.min.js';
2108 if (defined('JS_TINYMCE') && constant('JS_TINYMCE')) {
2109 $pathtinymce = constant('JS_TINYMCE');
2110 }
2111 print '<script src="'.$pathtinymce.$jstinymce.'?'.$ext.'"></script>'."\n";
2112 print '<script nonce="'.getNonce().'">';
2113 print '/* enable tinymce by main.inc.php */';
2114 print 'var tinymceBasePath = \''.dol_escape_js($pathtinymce).'\';'."\n";
2115 print 'var tinymceFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
2116 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";
2117 print '</script>'."\n";
2118 print '<script nonce="'.getNonce().'" src="'.dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/tinymce/config.js?'.$ext, 1).'"></script>'."\n";
2119 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/tinymce-ckeditor-compat.js?'.$ext.'"></script>'."\n";
2120 }
2121
2122 // 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).
2123 if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
2124 $enablebrowsernotif = false;
2125 if (isModEnabled('agenda') && getDolGlobalString('AGENDA_REMINDER_BROWSER')) {
2126 $enablebrowsernotif = true;
2127 }
2128 if ($conf->browser->layout == 'phone') {
2129 $enablebrowsernotif = false;
2130 }
2131 if ($enablebrowsernotif) {
2132 print '<!-- Includes JS of Dolibarr (browser layout = '.$conf->browser->layout.')-->'."\n";
2133 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/core/js/lib_notification.js.php?lang='.$langs->defaultlang. '&' . $ext . '"></script>'."\n";
2134 }
2135 }
2136
2137 // Global js function
2138 print '<!-- Includes JS of Dolibarr -->'."\n";
2139 if (!defined('DISABLE_LIB_HEAD_JS')) {
2140 print '<script nonce="' . getNonce() . '" src="' . DOL_URL_ROOT . '/core/js/lib_head.js.php?lang=' . $langs->defaultlang . '&' . $ext . '"></script>' . "\n";
2141 }
2142
2143 // Leaflet
2144 if (getDolGlobalString('MAIN_USE_GEOPHP')) {
2145 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet.js?' . $ext . '"></script>'."\n";
2146 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/includes/leaflet/leaflet-geoman.min.js?' . $ext . '"></script>'."\n";
2147 }
2148
2149 // JS forced by modules (relative url starting with /)
2150 if (!empty($conf->modules_parts['js'])) { // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
2151 $arrayjs = (array) $conf->modules_parts['js'];
2152 foreach ($arrayjs as $modjs => $filesjs) {
2153 $filesjs = (array) $filesjs; // To be sure filejs is an array
2154 foreach ($filesjs as $jsfile) {
2155 // jsfile is a relative path
2156 $urlforjs = dol_buildpath($jsfile, 1);
2157 if ($urlforjs && $urlforjs != '/') {
2158 print '<!-- Include JS added by module '.$modjs.'-->'."\n";
2159 print '<script nonce="'.getNonce().'" src="'.$urlforjs.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2160 } else {
2161 dol_syslog("Warning: module ".$modjs." declared a js path file for a file we can't find.", LOG_WARNING);
2162 }
2163 }
2164 }
2165 }
2166 // JS forced by page in top_htmlhead (relative url starting with /)
2167 if (is_array($arrayofjs)) {
2168 print '<!-- Includes JS added by page -->'."\n";
2169 foreach ($arrayofjs as $jsfile) {
2170 if (preg_match('/^(http|\/\/)/i', $jsfile)) {
2171 print '<script nonce="'.getNonce().'" src="'.$jsfile.((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2172 } else {
2173 print '<script nonce="'.getNonce().'" src="'.dol_buildpath($jsfile, 1).((strpos($jsfile, '?') === false) ? '?' : '&amp;').'lang='.$langs->defaultlang.'"></script>'."\n";
2174 }
2175 }
2176 }
2177 }
2178
2179 //If you want to load custom javascript file from your selected theme directory
2180 if (getDolGlobalString('ALLOW_THEME_JS')) {
2181 $theme_js = dol_buildpath('/theme/'.$conf->theme.'/'.$conf->theme.'.js', 0);
2182 if (file_exists($theme_js)) {
2183 print '<script nonce="'.getNonce().'" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/'.$conf->theme.'.js?' . $ext . '"></script>'."\n";
2184 }
2185 }
2186
2187 if (!empty($head)) {
2188 print $head."\n";
2189 }
2190 if (getDolGlobalString('MAIN_HTML_HEADER')) {
2191 print getDolGlobalString('MAIN_HTML_HEADER') . "\n";
2192 }
2193
2194 $parameters = array();
2195 $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
2196 print $hookmanager->resPrint; // Replace Title to show
2197
2198 print "</head>\n\n";
2199 }
2200
2201 $conf->headerdone = 1; // To tell header was output
2202}
2203
2204
2221function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $morequerystring = '', $helppagename = '')
2222{
2223 global $user, $conf, $langs, $db, $form;
2224 global $dolibarr_main_authentication, $dolibarr_main_demo;
2225 global $hookmanager, $menumanager;
2226
2227 $searchform = '';
2228
2229 // Instantiate hooks for external modules
2230 $hookmanager->initHooks(array('toprightmenu'));
2231
2232 $toprightmenu = '';
2233
2234 // For backward compatibility with old modules
2235 if (empty($conf->headerdone)) {
2236 $disablenofollow = 0;
2237 top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
2238 print '<body id="mainbody">';
2239 }
2240
2241 /*
2242 * Top menu
2243 */
2244 if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
2245 if (!isset($form) || !is_object($form)) {
2246 include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
2247 $form = new Form($db);
2248 }
2249
2250 print "\n".'<!-- Start top horizontal -->'."\n";
2251
2252 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.
2253
2254 // Show menu entries
2255 print '<div id="tmenu_tooltip'.(!getDolGlobalString('MAIN_MENU_INVERT') ? '' : 'invert').'" class="tmenu">'."\n";
2256 // @phan-suppress-next-line PhanRedefinedClassReference
2257 $menumanager->atarget = $target;
2258 // @phan-suppress-next-line PhanRedefinedClassReference
2259 $menumanager->showmenu('top', array('searchform' => $searchform)); // This contains a \n
2260 print "</div>\n";
2261
2262 // Define link to login card
2263 $appli = constant('DOL_APPLICATION_TITLE');
2264 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
2265 if ($applicustom) {
2266 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
2267 } else {
2268 $appli .= " ".DOL_VERSION;
2269 }
2270
2271 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
2272 $appli .= "<br>".$langs->trans("LevelOfFeature").': '.getDolGlobalInt('MAIN_FEATURES_LEVEL');
2273 }
2274
2275 $logouttext = '';
2276 $logouthtmltext = '';
2277 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2278 if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') {
2279 $logouthtmltext .= $langs->trans("Logout").'<br>';
2280 $logouttext .= '<a accesskey="l" href="'.DOL_URL_ROOT.'/user/logout.php?token='.newToken().'">';
2281 $logouttext .= img_picto($langs->trans('Logout').' ('.$conf->browser->stringforfirstkey.' l)', 'sign-out', '', 0, 0, 0, '', 'atoplogin valignmiddle');
2282 $logouttext .= '</a>';
2283 } else {
2284 $logouthtmltext .= $langs->trans("NoLogoutProcessWithAuthMode", $_SESSION["dol_authmode"]);
2285 $logouttext .= img_picto($langs->trans('Logout').' ('.$conf->browser->stringforfirstkey.' l)', 'sign-out', '', 0, 0, 0, '', 'atoplogin valignmiddle opacitymedium');
2286 }
2287 }
2288
2289
2290 print '<div class="login_block usedropdown">'."\n";
2291
2292
2293 // Add block for tools
2294 $toprightmenu .= '<div class="login_block_tools valignmiddle">';
2295
2296 $mode = -1;
2297 $toprightmenu .= '<div class="inline-block nowrap" style="padding: 0px;">';
2298
2299 if (getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
2300 // Add search dropdown
2301 $toprightmenu .= top_menu_search();
2302 }
2303
2304 // Add AI picto
2305 $toprightmenu .= top_menu_ai();
2306
2307 // Add bookmark dropdown
2308 $toprightmenu .= top_menu_bookmark();
2309
2310 if (getDolGlobalString('MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN')) {
2311 // Add the quick add object dropdown
2312 $toprightmenu .= top_menu_quickadd();
2313 }
2314
2315 if (getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE')) {
2316 // Add the import file link
2317 $toprightmenu .= top_menu_importfile();
2318 }
2319
2320 $toprightmenu .= '</div>';
2321
2322 $toprightmenu .= '</div>'."\n"; // end div class="login_block_tools"
2323
2324
2325 // Add block for other tools
2326 $toprightmenu .= '<div class="login_block_other valignmiddle">';
2327
2328 // Execute hook printTopRightMenu (hooks should output string like '<div class="login"><a href="">mylink</a></div>')
2329 $parameters = array();
2330 $result = $hookmanager->executeHooks('printTopRightMenu', $parameters); // Note that $action and $object may have been modified by some hooks
2331 if (is_numeric($result)) {
2332 if ($result == 0) {
2333 $toprightmenu .= $hookmanager->resPrint; // add
2334 } else {
2335 $toprightmenu = $hookmanager->resPrint; // replace
2336 }
2337 } else {
2338 $toprightmenu .= $result; // For backward compatibility
2339 }
2340
2341 // Link to module builder
2342 if (isModEnabled('modulebuilder')) {
2343 $text = '<a href="' . dolBuildUrl(DOL_URL_ROOT . '/modulebuilder/index.php', ['mainmenu' => 'home', 'leftmenu' => 'admintools']) .'" target="modulebuilder">';
2344 //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
2345 $text .= '<span class="fa fa-bug atoplogin valignmiddle"></span>';
2346 $text .= '</a>';
2347 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2348 $toprightmenu .= $form->textwithtooltip('', $langs->trans("ModuleBuilder"), 2, 1, $text, 'login_block_elem', 2);
2349 }
2350
2351 // Link to print main content area (optioncss=print)
2352 if (!getDolGlobalString('MAIN_PRINT_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2353 $qs = dol_escape_htmltag($_SERVER["QUERY_STRING"]);
2354
2355 if (isset($_POST) && is_array($_POST)) {
2356 foreach ($_POST as $key => $value) {
2357 $key = preg_replace('/[^a-z0-9_\.\-\[\]]/i', '', $key);
2358 if (in_array($key, array('action', 'massaction', 'password'))) {
2359 continue;
2360 }
2361 if (!is_array($value)) {
2362 if ($value !== '') {
2363 $qs .= '&'.urlencode($key).'='.urlencode($value);
2364 }
2365 } else {
2366 foreach ($value as $value2) {
2367 if (($value2 !== '') && (!is_array($value2))) {
2368 $qs .= '&'.urlencode($key).'[]='.urlencode($value2);
2369 }
2370 }
2371 }
2372 }
2373 }
2374 $qs .= (($qs && $morequerystring) ? '&' : '').$morequerystring;
2375 $text = '<a href="'.dol_escape_htmltag($_SERVER["PHP_SELF"]).'?'.$qs.($qs ? '&' : '').'optioncss=print" target="_blank" rel="noopener noreferrer">';
2376 //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
2377 $text .= '<span class="fa fa-print atoplogin valignmiddle"></span>';
2378 $text .= '</a>';
2379 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2380 $toprightmenu .= $form->textwithtooltip('', $langs->trans("PrintContentArea"), 2, 1, $text, 'login_block_elem', 2);
2381 }
2382
2383 // Link to Dolibarr wiki pages
2384 if (!getDolGlobalString('MAIN_HELP_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2385 $langs->load("help");
2386
2387 $helpbaseurl = '';
2388 $helppage = '';
2389 $mode = '';
2390 $helppresent = '';
2391
2392 if (empty($helppagename)) {
2393 $helppagename = 'EN:User_documentation|FR:Documentation_utilisateur|ES:Documentación_usuarios|DE:Benutzerdokumentation';
2394 } else {
2395 $helppresent = 'helppresent';
2396 }
2397
2398 // Get helpbaseurl, helppage and mode from helppagename and langs
2399 $arrayres = getHelpParamFor($helppagename, $langs);
2400 $helpbaseurl = $arrayres['helpbaseurl'];
2401 $helppage = $arrayres['helppage'];
2402 $mode = $arrayres['mode'];
2403
2404 // Link to help pages
2405 if ($helpbaseurl && $helppage) {
2406 $text = '';
2407 $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage').', ';
2408 if ($mode == 'wiki') {
2409 $title .= '<br>'.img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans("PageWiki").' '.dol_escape_htmltag('"'.strtr($helppage, '_', ' ').'"');
2410 if ($helppresent) {
2411 $title .= ' <span class="opacitymedium">('.$langs->trans("DedicatedPageAvailable").')</span>';
2412 } else {
2413 $title .= ' <span class="opacitymedium">('.$langs->trans("HomePage").')</span>';
2414 }
2415 }
2416 $text .= '<a class="help" target="_blank" rel="noopener noreferrer" href="';
2417 if ($mode == 'wiki') {
2418 // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2419 $text .= sprintf($helpbaseurl, urlencode(html_entity_decode($helppage)));
2420 } else {
2421 // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2422 $text .= sprintf($helpbaseurl, $helppage);
2423 }
2424 $text .= '">';
2425 $text .= '<span class="fa fa-question-circle atoplogin valignmiddle'.($helppresent ? ' '.$helppresent : '').'"></span>';
2426 $text .= '<span class="fa fa-long-arrow-alt-up helppresentcircle'.($helppresent ? '' : ' unvisible').'"></span>';
2427 $text .= '</a>';
2428 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2429 $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2);
2430 }
2431
2432 // Version
2433 if (getDolGlobalString('MAIN_SHOWDATABASENAMEINHELPPAGESLINK')) {
2434 $langs->load('admin');
2435 $appli .= '<br>'.$langs->trans("Database").': '.$db->database_name;
2436 }
2437 }
2438
2439 // Version
2440 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalInt('MAIN_HIDE_VERSION') == 0) {
2441 $text = '<span class="aversion"><span class="hideonsmartphone small">'.DOL_VERSION.'</span></span>';
2442 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2443 $toprightmenu .= $form->textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
2444 }
2445
2446 // Logout link
2447 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2448 $toprightmenu .= $form->textwithtooltip('', $logouthtmltext, 2, 1, $logouttext, 'login_block_elem logout-btn', 2);
2449 }
2450
2451 $toprightmenu .= '</div>'; // end div class="login_block_other"
2452
2453
2454 // Add block for user photo and name
2455 $toprightmenu .= '<div class="login_block_user">';
2456
2457 $mode = -1;
2458 $toprightmenu .= '<div class="inline-block login_block_elem login_block_elem_name nowrap centpercent" style="padding: 0px;">';
2459
2460 // Add user dropdown
2461 $toprightmenu .= top_menu_user();
2462
2463 $toprightmenu .= '</div>';
2464
2465 $toprightmenu .= '</div>'."\n";
2466
2467
2468 print $toprightmenu;
2469
2470 print "</div>\n"; // end div class="login_block"
2471
2472 print '</header>';
2473 //print '<header class="header2">&nbsp;</header>';
2474
2475 print '<div style="clear: both;"></div>';
2476 print "<!-- End top horizontal menu -->\n\n";
2477 }
2478
2479 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
2480 print '<!-- Begin div id-container --><div id="id-container" class="id-container">';
2481 }
2482}
2483
2484
2492function top_menu_user($hideloginname = 0, $urllogout = '')
2493{
2494 global $langs, $conf, $db, $hookmanager, $user, $mysoc;
2495 global $dolibarr_main_authentication, $dolibarr_main_demo;
2496 global $menumanager, $form;
2497
2498 // Return empty in some case
2499 if ($conf->browser->name == 'textbrowser') {
2500 return '';
2501 }
2502
2503 $langs->load('companies');
2504
2505 $userImage = $userDropDownImage = '';
2506 if (!empty($user->photo) || isModEnabled('gravatar')) {
2507 $userImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'photouserphoto userphoto', 'small', 0, 1);
2508 $userDropDownImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'dropdown-user-image', 'small', 0, 1);
2509 } else {
2510 $nophoto = '/public/theme/common/user_anonymous.png';
2511 if ($user->gender == 'man') {
2512 $nophoto = '/public/theme/common/user_man.png';
2513 }
2514 if ($user->gender == 'woman') {
2515 $nophoto = '/public/theme/common/user_woman.png';
2516 }
2517
2518 $userImage = img_picto('', 'user', 'class="photo photouserphoto userphoto"');
2519 //$userImage = '<img class="photo photouserphoto userphoto" alt="" src="'.DOL_URL_ROOT.$nophoto.'" aria-hidden="true">';
2520 $userDropDownImage = '<img class="photo dropdown-user-image" alt="" src="'.DOL_URL_ROOT.$nophoto.'" aria-hidden="true">';
2521 }
2522
2523 $dropdownBody = '';
2524 $dropdownBody .= '<span id="topmenulogincompanyinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowCompanyInfos").'</span>';
2525 $dropdownBody .= '<div id="topmenulogincompanyinfo" >';
2526
2527 $dropdownBody .= '<br><b>'.$langs->trans("Company").'</b>: <span>'.dol_escape_htmltag($mysoc->name).'</span>';
2528 $idprofcursor = 0;
2529 while ($idprofcursor < 10) {
2530 $idprofcursor++;
2531 $constkeyforprofid = 'MAIN_INFO_PROFID'.$idprofcursor;
2532 if ($idprofcursor == 1) {
2533 $constkeyforprofid = 'MAIN_INFO_SIREN';
2534 }
2535 if ($idprofcursor == 2) {
2536 $constkeyforprofid = 'MAIN_INFO_SIRET';
2537 }
2538 if ($idprofcursor == 3) {
2539 $constkeyforprofid = 'MAIN_INFO_APE';
2540 }
2541 if ($idprofcursor == 4) {
2542 $constkeyforprofid = 'MAIN_INFO_RCS';
2543 }
2544 $showprofid = (($idprofcursor <= 6) && $langs->transcountry("ProfId".$idprofcursor, $mysoc->country_code) != '-');
2545 if ($idprofcursor > 6 && getDolGlobalString($constkeyforprofid)) {
2546 $showprofid = true;
2547 }
2548 if ($showprofid) {
2549 $dropdownBody .= '<br><b>'.$langs->transcountry("ProfId".$idprofcursor, $mysoc->country_code).'</b>: <span>'.dol_print_profids(getDolGlobalString($constkeyforprofid), '1').'</span>';
2550 }
2551 }
2552 $dropdownBody .= '<br><b>'.$langs->trans("VATIntraShort").'</b>: <span>'.dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT').'</span>';
2553 $langFlag = picto_from_langcode($langs->getDefaultLang(), 'class="none"');
2554 $dropdownBody .= '<br><b>'.$langs->trans("Country").'</b>: <span>'.($mysoc->country_code ? $langs->trans("Country".$mysoc->country_code).' '.$langFlag : '').'</span>';
2555 if (isModEnabled('multicurrency')) {
2556 $dropdownBody .= '<br><b>'.$langs->trans("Currency").'</b>: <span>'.getDolCurrency().'</span>';
2557 }
2558 $dropdownBody .= '</div>';
2559
2560 $dropdownBody .= '<br>';
2561 $dropdownBody .= '<span id="topmenuloginmoreinfo-btn"><i class="fa fa-caret-right"></i> '.$langs->trans("ShowMoreInfos").'</span>';
2562 $dropdownBody .= '<div id="topmenuloginmoreinfo" >';
2563
2564 // login infos
2565 if (!empty($user->admin)) {
2566 $dropdownBody .= '<br><b>'.$langs->trans("Administrator").'</b>: '.yn($user->admin).' '.img_picto('', 'admin');
2567 }
2568 $company = '';
2569 if (!empty($user->socid)) { // Add third party for external users
2570 $thirdpartystatic = new Societe($db);
2571 $thirdpartystatic->fetch($user->socid);
2572 $companylink = ' '.$thirdpartystatic->getNomUrl(2); // picto only of company
2573 $company = ' ('.$langs->trans("Company").': '.$thirdpartystatic->name.')';
2574 }
2575 $type = ($user->socid ? $langs->trans("External").$company : $langs->trans("Internal"));
2576 $dropdownBody .= '<br><b>'.$langs->trans("Type").':</b> '.$type;
2577 $dropdownBody .= '<br><b>'.$langs->trans("Status").'</b>: '.$user->getLibStatut(0);
2578 $dropdownBody .= '<br>';
2579
2580 $dropdownBody .= '<br><u>'.$langs->trans("Session").'</u>';
2581 $dropdownBody .= '<br><b>'.$langs->trans("IPAddress").'</b>: '.dol_escape_htmltag($_SERVER["REMOTE_ADDR"]);
2582 if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
2583 $dropdownBody .= '<br><b>'.$langs->trans("ConnectedOnMultiCompany").':</b> '.$conf->entity.' (user entity '.$user->entity.')';
2584 }
2585 $dropdownBody .= '<br><b>'.$langs->trans("AuthenticationMode").':</b> '.$_SESSION["dol_authmode"].(empty($dolibarr_main_demo) ? '' : ' (demo)');
2586 $dropdownBody .= '<br><b>'.$langs->trans("ConnectedSince").':</b> '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2587 $dropdownBody .= '<br><b>'.$langs->trans("PreviousConnexion").':</b> '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2588 $dropdownBody .= '<br><b>'.$langs->trans("CurrentTheme").':</b> '.$conf->theme;
2589 // @phan-suppress-next-line PhanRedefinedClassReference
2590 $dropdownBody .= '<br><b>'.$langs->trans("CurrentMenuManager").':</b> '.(isset($menumanager) ? $menumanager->name : 'unknown');
2591 $langFlag = picto_from_langcode($langs->getDefaultLang(), 'class="none"');
2592 $dropdownBody .= '<br><b>'.$langs->trans("CurrentUserLanguage").':</b> '.$langs->getDefaultLang().($langFlag ? ' '.$langFlag : '');;
2593
2594 $tz = (int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst'];
2595 $dropdownBody .= '<br><b>'.$langs->trans("ClientTZ").':</b> '.($tz ? ($tz >= 0 ? '+' : '').$tz : '');
2596 $dropdownBody .= ' <span class="opacitymedium">('.$_SESSION['dol_tz_string'].')</span>';
2597 //$dropdownBody .= ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
2598 //if ($_SESSION['dol_dst'] > 0) $dropdownBody .= yn(1);
2599 //else $dropdownBody .= yn(0);
2600
2601 $dropdownBody .= '<br><b>'.$langs->trans("Browser").':</b> '.ucfirst($conf->browser->name).($conf->browser->version ? ' '.$conf->browser->version : '');
2602 $dropdownBody .= $form->textwithpicto('', dol_escape_htmltag($_SERVER['HTTP_USER_AGENT']), 1, 'help', 'valignmiddle', 0, 3, 'useragent');
2603 $dropdownBody .= '<br><b>'.$langs->trans("Screen").':</b> '.$_SESSION['dol_screenwidth'].' x '.$_SESSION['dol_screenheight'];
2604 $dropdownBody .= ' <span class="opacitymedium">('.$conf->browser->layout.')</span>';
2605 if (!empty($_SESSION["disablemodules"])) {
2606 $dropdownBody .= '<br><b>'.$langs->trans("DisabledModules").':</b> <br>'.implode(', ', explode(',', $_SESSION["disablemodules"]));
2607 }
2608 $dropdownBody .= '</div>';
2609
2610 // Execute hook
2611 $parameters = array('user' => $user, 'langs' => $langs);
2612 $result = $hookmanager->executeHooks('printTopRightMenuLoginDropdownBody', $parameters); // Note that $action and $object may have been modified by some hooks
2613 if (is_numeric($result)) {
2614 if ($result == 0) {
2615 $dropdownBody .= $hookmanager->resPrint; // add
2616 } else {
2617 $dropdownBody = $hookmanager->resPrint; // replace
2618 }
2619 }
2620
2621 if (empty($urllogout)) {
2622 $urllogout = dolBuildUrl(DOL_URL_ROOT . '/user/logout.php', [], true);
2623 }
2624
2625 // Defined the links for bottom of card
2626 $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>';
2627 $urltovirtualcard = '/user/virtualcard.php?id='.((int) $user->id);
2628 $jsonopen = "closeTopMenuLoginDropdown()";
2629 $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');
2630 $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>';
2631
2632 $profilName = $user->getFullName($langs).' ('.$user->login.')';
2633 if (!empty($user->admin)) {
2634 $profilName = img_picto($langs->trans("Administrator"), 'admin').' '.$profilName;
2635 }
2636
2637 // Define version to show
2638 $appli = constant('DOL_APPLICATION_TITLE');
2639 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
2640 if ($applicustom) {
2641 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
2642 } else {
2643 $appli .= " ".DOL_VERSION;
2644 }
2645
2646 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2647 $btnUser = '<!-- div for user link -->
2648 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2649 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="dropdown-toggle login-dropdown-a valignmiddle" data-toggle="dropdown">
2650 '.$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>' : '').'
2651 </a>
2652 <div class="dropdown-menu">
2653 <!-- User image -->
2654 <div class="user-header">
2655 '.$userDropDownImage.'
2656 <p>
2657 '.$profilName.'<br>';
2658 $title = '';
2659 if ($user->datelastlogin) {
2660 $title = $langs->trans("ConnectedSince").' : '.dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2661 if ($user->datepreviouslogin) {
2662 $title .= '<br>'.$langs->trans("PreviousConnexion").' : '.dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2663 }
2664 }
2665 $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>';
2666 if ($user->datepreviouslogin) {
2667 $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>';
2668 }
2669
2670 //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
2671 $btnUser .= '
2672 </p>
2673 </div>
2674
2675 <!-- Menu Body user-->
2676 <div class="user-body">'.$dropdownBody.'</div>
2677
2678 <!-- Menu Footer-->
2679 <div class="user-footer">
2680 <div class="pull-left">
2681 '.$profilLink.'
2682 </div>
2683 <div class="pull-left">
2684 '.$virtuelcardLink.'
2685 </div>
2686 <div class="pull-right">
2687 '.$logoutLink.'
2688 </div>
2689 <div class="clearboth"></div>
2690 </div>
2691
2692 </div>
2693 </div>';
2694 } else {
2695 $btnUser = '<!-- div for user link text browser -->
2696 <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2697 <a href="'.DOL_URL_ROOT.'/user/card.php?id='.$user->id.'" class="valignmiddle" alt="'.$langs->trans("MyUserCard").'">
2698 '.$userImage.(empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft small valignmiddle">'.dol_trunc($user->firstname ? $user->firstname : $user->login, 10).'</span>' : '').'
2699 </a>
2700 </div>';
2701 }
2702
2703 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
2704 $btnUser .= '
2705 <!-- Code to show/hide the user drop-down -->
2706 <script>
2707 function closeTopMenuLoginDropdown() {
2708 console.log("close login dropdown"); // This is called at each click on page, so we disable the log
2709 // Hide the menus.
2710 jQuery("#topmenu-login-dropdown").removeClass("open");
2711 }
2712 jQuery(document).ready(function() {
2713 jQuery(document).on("click", function(event) {
2714 if (jQuery("#topmenu-login-dropdown").hasClass("open")) {
2715 if (!$(event.target).closest("#topmenu-login-dropdown").length) {
2716 console.log("click close login - we click outside");
2717 // Hide the dropdown.
2718 closeTopMenuLoginDropdown();
2719 }
2720 }
2721 });
2722 ';
2723
2724
2725 $btnUser .= '
2726 jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
2727 console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
2728 event.preventDefault();
2729 jQuery("#topmenu-login-dropdown").toggleClass("open");
2730 });
2731
2732 jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
2733 console.log("Click on #topmenulogincompanyinfo-btn");
2734 if (!jQuery("#topmenuloginmoreinfo").is(\':hidden\')) {
2735 jQuery("#topmenuloginmoreinfo").slideToggle();
2736 }
2737 jQuery("#topmenulogincompanyinfo").slideToggle();
2738 });
2739
2740 jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
2741 console.log("Click on #topmenuloginmoreinfo-btn");
2742 if (!jQuery("#topmenulogincompanyinfo").is(\':hidden\')) {
2743 jQuery("#topmenulogincompanyinfo").slideToggle();
2744 }
2745 jQuery("#topmenuloginmoreinfo").slideToggle();
2746 });';
2747
2748 $btnUser .= '
2749 });
2750 </script>
2751 ';
2752 }
2753
2754 return $btnUser;
2755}
2756
2765function top_menu_ai()
2766{
2767 global $conf, $langs, $user;
2768
2769 $html = '';
2770
2771 if (!isModEnabled('ai') || !getDolGlobalString('AI_ASSISTANT_ENABLED') || empty($conf->use_javascript_ajax)) {
2772 return $html;
2773 }
2774 // Per-user gate: same right as the assistant page and its endpoints
2775 if (!$user->hasRight('ai', 'assistant', 'use')) {
2776 return $html;
2777 }
2778
2779 $ailabel = $langs->trans('AIAssistant').' ('.$conf->browser->stringforfirstkey.' a)';
2780
2781 // Chat CSS is needed on every page showing the icon (link-in-body is valid HTML5,
2782 // the standalone page ai/assistant/index.php uses the same pattern).
2783 $html .= '<link rel="stylesheet" href="'.DOL_URL_ROOT.'/ai/css/ai_assistant.css">';
2784
2785 // Toggle icon. The accesskey "a" keeps the Alt+A shortcut: its browser
2786 // activation fires the click handler below, so it toggles the popover.
2787 $html .= '<!-- div for AI Assistant link (opens the AI chat popover) -->
2788 <div id="topmenu-ai-dropdown" class="atoplogin dropdown inline-block">
2789 <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>
2790 </div>';
2791
2792 // Popover shell (hidden by CSS until .open). The chat fragment is fetched on
2793 // first open; afterwards open/close only toggles visibility so the
2794 // conversation survives. Moved to <body> on first use by the script below.
2795 $html .= '<div id="topmenu-ai-popover" class="ai-popover" role="dialog" aria-modal="false" aria-label="'.dol_escape_htmltag($langs->trans('AIAssistant')).'">
2796 <div class="ai-popover-body"><div class="ai-popover-loading"><i class="fa fa-circle-notch fa-spin"></i></div></div>
2797 </div>';
2798
2799 // Cache-busting version for the JS module: filemtime invalidates the browser
2800 // cache whenever the file actually changes (e.g. after a branch switch),
2801 // avoiding a stale module without the initAiAssistant() export.
2802 $aijsfile = DOL_DOCUMENT_ROOT.'/ai/js/ai_assistant.js';
2803 $aijsver = @filemtime($aijsfile);
2804 $aijsurl = DOL_URL_ROOT.'/ai/js/ai_assistant.js?v='.urlencode((string) ($aijsver ? $aijsver : DOL_VERSION));
2805
2806 $html .= '<script nonce="'.getNonce().'">
2807 jQuery(document).ready(function() {
2808 jQuery(document).on("click", function(event) {
2809 if (jQuery("#topmenu-ai-popover").hasClass("open")) {
2810 if (!$(event.target).closest("#topmenu-ai-toggle").length && !$(event.target).closest("#topmenu-ai-popover").length) {
2811 console.log("click close ai dropdown - we click outside");
2812 // Hide the dropdown.
2813 jQuery("#topmenu-ai-popover").removeClass("open");
2814 }
2815 }
2816 });
2817 });
2818
2819 (function () {
2820 var toggle = document.getElementById("topmenu-ai-toggle");
2821 var popover = document.getElementById("topmenu-ai-popover");
2822 if (!toggle || !popover) { return; }
2823 var body = popover.querySelector(".ai-popover-body");
2824 var loaded = false;
2825 var loading = false;
2826
2827 function positionPopover() {
2828 var top = document.getElementById("id-top");
2829 var anchor = (top ? top.getBoundingClientRect().bottom : 44) + 4;
2830 popover.style.setProperty("--ai-popover-top", anchor + "px");
2831 }
2832
2833 function loadChat() {
2834 if (loaded || loading) { return; }
2835 loading = true;
2836 fetch("'.DOL_URL_ROOT.'/ai/assistant/popover.php", { credentials: "same-origin" })
2837 .then(function (resp) {
2838 if (!resp.ok) { throw new Error("HTTP " + resp.status); }
2839 return resp.text();
2840 })
2841 .then(function (htmlcontent) {
2842 body.innerHTML = htmlcontent;
2843 return import("'.dol_escape_js($aijsurl).'").then(function (mod) {
2844 mod.initAiAssistant(body.querySelector(".ai-chat-container"));
2845 });
2846 })
2847 .then(function () {
2848 loaded = true;
2849 focusInput();
2850 })
2851 .catch(function (e) {
2852 console.error("AI Assistant popover load failed", e);
2853 body.innerHTML = "<div class=\"ai-popover-loading\">'.dol_escape_js($langs->trans('Error')).'</div>";
2854 })
2855 .finally(function () { loading = false; });
2856 }
2857
2858 function focusInput() {
2859 var input = body.querySelector("#user-input");
2860 if (input) { input.focus(); }
2861 }
2862
2863 toggle.addEventListener("click", function (event) {
2864 console.log("Click on #topmenu-ai-toggle");
2865 event.preventDefault();
2866 // position:fixed can be hijacked by a transformed ancestor: hosting the
2867 // panel directly under <body> guarantees viewport coordinates.
2868 if (popover.parentNode !== document.body) { document.body.appendChild(popover); }
2869 positionPopover();
2870 var isOpen = popover.classList.toggle("open");
2871 if (isOpen) {
2872 loadChat();
2873 if (loaded) { focusInput(); }
2874 }
2875 });
2876
2877 popover.addEventListener("click", function (event) {
2878 console.log("Click on #topmenu-ai-popover");
2879 var closeBtn = event.target.closest("#ai-close-btn");
2880 var expandBtn = event.target.closest("#ai-expand-btn");
2881 if (closeBtn) {
2882 popover.classList.remove("open");
2883 } else if (expandBtn) {
2884 var expanded = popover.classList.toggle("expanded");
2885 var icon = expandBtn.querySelector("i");
2886 if (icon) { icon.className = expanded ? "fa fa-compress-alt" : "fa fa-expand-alt"; }
2887 expandBtn.title = expanded ? (expandBtn.dataset.titleReduce || "") : (expandBtn.dataset.titleExpand || "");
2888 }
2889 });
2890
2891 document.addEventListener("keydown", function (event) {
2892 if (event.key === "Escape" && popover.classList.contains("open")) {
2893 popover.classList.remove("open");
2894 }
2895 });
2896 })();
2897 </script>';
2898
2899 return $html;
2900}
2901
2909{
2910 global $conf, $langs;
2911
2912 // Button disabled on text browser
2913 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2914 return '';
2915 }
2916
2917 $html = '';
2918
2919 if (!empty($conf->use_javascript_ajax)) {
2920 $html .= '<!-- div for quick add link -->
2921 <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
2922 <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>
2923 <div class="dropdown-menu">'.printDropdownQuickadd().'</div>
2924 </div>';
2925 if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) { // This may be set by some pages that use different jquery version to avoid errors
2926 $html .= '
2927 <!-- Code to show/hide the user drop-down for the quick add -->
2928 <script nonce="'.getNonce().'">
2929 jQuery(document).ready(function() {
2930 jQuery(document).on("click", function(event) {
2931 if (jQuery("#topmenu-quickadd-dropdown").hasClass("open")) {
2932 if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
2933 console.log("click close quick add - we click outside");
2934 // Hide the dropdown.
2935 $("#topmenu-quickadd-dropdown").removeClass("open");
2936 }
2937 }
2938 });
2939 $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
2940 console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
2941 openQuickAddDropDown(event);
2942 });
2943
2944 // Key map shortcut
2945 $(document).keydown(function(event){
2946 var ostype = \''.dol_escape_js($conf->browser->os).'\';
2947 if (ostype === "macintosh") {
2948 if ( event.which === 65 && event.ctrlKey ) {
2949 console.log(\'control + a : trigger open quick add dropdown\');
2950 openQuickAddDropDown(event);
2951 }
2952 } else {
2953 if ( event.which === 65 && event.ctrlKey && event.shiftKey ) {
2954 console.log(\'control + shift + a : trigger open quick add dropdown\');
2955 openQuickAddDropDown(event);
2956 }
2957 }
2958 });
2959
2960 var openQuickAddDropDown = function(event) {
2961 event.preventDefault();
2962 $("#topmenu-quickadd-dropdown").toggleClass("open");
2963 }
2964 });
2965 </script>
2966 ';
2967 }
2968 }
2969
2970 return $html;
2971}
2972
2973
2981{
2982 global $conf, $langs;
2983
2984 // Button disabled on text browser
2985 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2986 return '';
2987 }
2988
2989 $html = '';
2990
2991 if (!empty($conf->use_javascript_ajax)) {
2992 $urlforuploadpage = DOL_URL_ROOT.'/core/upload_page.php';
2993 if (!is_numeric(getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE'))) {
2994 $urlforuploadpage = getDolGlobalString('MAIN_USE_TOP_MENU_IMPORT_FILE');
2995 }
2996
2997 $html .= '<!-- div for link to upload file -->
2998 <div id="topmenu-uploadfile-dropdown" class="atoplogin dropdown inline-block">
2999 <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>
3000 </div>';
3001 }
3002
3003 return $html;
3004}
3005
3006
3013function printDropdownQuickadd($mode = 0)
3014{
3015 global $user, $langs, $hookmanager;
3016
3017 $items = array(
3018 'items' => array(
3019 array(
3020 "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
3021 "title" => "MenuNewMember@members",
3022 "name" => "Adherent@members",
3023 "picto" => "object_member",
3024 "activation" => isModEnabled('member') && $user->hasRight("adherent", "write"), // vs hooking
3025 "position" => 5,
3026 ),
3027 array(
3028 "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
3029 "title" => "MenuNewThirdParty@companies",
3030 "name" => "ThirdParty@companies",
3031 "picto" => "object_company",
3032 "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
3033 "position" => 10,
3034 ),
3035 array(
3036 "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
3037 "title" => "NewContactAddress@companies",
3038 "name" => "Contact@companies",
3039 "picto" => "object_contact",
3040 "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
3041 "position" => 20,
3042 ),
3043 array(
3044 "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
3045 "title" => "NewPropal@propal",
3046 "name" => "Proposal@propal",
3047 "picto" => "object_propal",
3048 "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
3049 "position" => 30,
3050 ),
3051
3052 array(
3053 "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
3054 "title" => "NewOrder@orders",
3055 "name" => "Order@orders",
3056 "picto" => "object_order",
3057 "activation" => isModEnabled('order') && $user->hasRight("commande", "write"), // vs hooking
3058 "position" => 40,
3059 ),
3060 array(
3061 "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
3062 "title" => "NewBill@bills",
3063 "name" => "Bill@bills",
3064 "picto" => "object_bill",
3065 "activation" => isModEnabled('invoice') && $user->hasRight("facture", "write"), // vs hooking
3066 "position" => 50,
3067 ),
3068 array(
3069 "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
3070 "title" => "NewContractSubscription@contracts",
3071 "name" => "Contract@contracts",
3072 "picto" => "object_contract",
3073 "activation" => isModEnabled('contract') && $user->hasRight("contrat", "write"), // vs hooking
3074 "position" => 60,
3075 ),
3076 array(
3077 "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
3078 "title" => "SupplierProposalNew@supplier_proposal",
3079 "name" => "SupplierProposal@supplier_proposal",
3080 "picto" => "supplier_proposal",
3081 "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
3082 "position" => 70,
3083 ),
3084 array(
3085 "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
3086 "title" => "NewSupplierOrderShort@orders",
3087 "name" => "SupplierOrder@orders",
3088 "picto" => "supplier_order",
3089 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "commande", "write")) || (isModEnabled("supplier_order") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3090 "position" => 80,
3091 ),
3092 array(
3093 "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
3094 "title" => "NewBill@bills",
3095 "name" => "SupplierBill@bills",
3096 "picto" => "supplier_invoice",
3097 "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "write")) || (isModEnabled("supplier_invoice") && $user->hasRight("supplier_invoice", "write")), // vs hooking
3098 "position" => 90,
3099 ),
3100 array(
3101 "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
3102 "title" => "NewTicket@ticket",
3103 "name" => "Ticket@ticket",
3104 "picto" => "ticket",
3105 "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
3106 "position" => 100,
3107 ),
3108 array(
3109 "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
3110 "title" => "NewIntervention@interventions",
3111 "name" => "Intervention@interventions",
3112 "picto" => "intervention",
3113 "activation" => isModEnabled('intervention') && $user->hasRight("ficheinter", "creer"), // vs hooking
3114 "position" => 110,
3115 ),
3116 array(
3117 "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
3118 "title" => "NewProduct@products",
3119 "name" => "Product@products",
3120 "picto" => "object_product",
3121 "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
3122 "position" => 400,
3123 ),
3124 array(
3125 "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
3126 "title" => "NewService@products",
3127 "name" => "Service@products",
3128 "picto" => "object_service",
3129 "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
3130 "position" => 410,
3131 ),
3132 array(
3133 "url" => "/product/stock/stocktransfer/stocktransfer_card.php?action=create&amp;mainmenu=products",
3134 "title" => "StockTransferNew@stocks",
3135 "name" => "StockTransfer@stocks",
3136 "picto" => "stock",
3137 "activation" => isModEnabled("stocktransfer") && $user->hasRight("stocktransfer", "stocktransfer", "write"), // vs hooking
3138 "position" => 415,
3139 ),
3140 array(
3141 "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
3142 "title" => "AddUser@users",
3143 "name" => "User@users",
3144 "picto" => "user",
3145 "activation" => $user->hasRight("user", "user", "write"), // vs hooking
3146 "position" => 500,
3147 ),
3148 ),
3149 );
3150
3151 $dropDownQuickAddHtml = '';
3152
3153 // Define $dropDownQuickAddHtml
3154 if (empty($mode)) {
3155 $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
3156 }
3157 $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
3158
3159 // Allow the $items of the menu to be manipulated by modules
3160 $parameters = array();
3161 $hook_items = $items;
3162 $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks @phan-suppress-current-line PhanTypeMismatchArgument
3163 if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
3164 if ($reshook == 0) {
3165 $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
3166 } else {
3167 $items = $hookmanager->resArray; // replace
3168 }
3169
3170 // Sort menu items by 'position' value
3171 $position = array();
3172 foreach ($items['items'] as $key => $row) {
3173 $position[$key] = $row['position'];
3174 }
3175 $array1_sort_order = SORT_ASC;
3176 array_multisort($position, $array1_sort_order, $items['items']);
3177 }
3178
3179 foreach ($items['items'] as $item) {
3180 if (!$item['activation']) {
3181 continue;
3182 }
3183 $langs->load(explode('@', $item['title'])[1]);
3184 $langs->load(explode('@', $item['name'])[1]);
3185 $dropDownQuickAddHtml .= '
3186 <a class="dropdown-item quickadd-item" href="'.DOL_URL_ROOT.$item['url'].'" title="'.$langs->trans(explode('@', $item['title'])[0]).'">
3187 '. img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
3188 ';
3189 }
3190
3191 if (empty($mode)) {
3192 $dropDownQuickAddHtml .= '</div>';
3193 }
3194 $dropDownQuickAddHtml .= '</div>';
3195
3196 return $dropDownQuickAddHtml;
3197}
3198
3205{
3206 global $langs, $conf, $user;
3207
3208 $html = '';
3209
3210 // Return empty in some case
3211 if (!isModEnabled('bookmark') || !$user->hasRight('bookmark', 'lire')) {
3212 return '';
3213 }
3214 /*
3215 if ($conf->browser->name == 'textbrowser') {
3216 return $html;
3217 }
3218 */
3219
3220 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
3221 include_once DOL_DOCUMENT_ROOT.'/bookmarks/bookmarks.lib.php';
3222 $langs->load("bookmarks");
3223
3224 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
3225 $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
3226 $html .= printDropdownBookmarksList();
3227 $html .= '</div>';
3228 } else {
3229 $html .= '<!-- div for bookmark link -->
3230 <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
3231 <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>
3232 <div class="dropdown-menu">
3234 </div>
3235 </div>';
3236
3237 $html .= '
3238 <!-- Code to show/hide the bookmark drop-down -->
3239 <script>
3240 jQuery(document).ready(function() {
3241 jQuery(document).on("click", function(event) {
3242 if (jQuery("#topmenu-bookmark-dropdown").hasClass("open")) {
3243 if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
3244 console.log("close bookmark dropdown - we click outside");
3245 // Hide the menus.
3246 $("#topmenu-bookmark-dropdown").removeClass("open");
3247 }
3248 }
3249 });
3250
3251 jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
3252 console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
3253 openBookMarkDropDown(event);
3254 });
3255
3256 // Key map shortcut
3257 jQuery(document).keydown(function(event) {
3258 var ostype = \''.dol_escape_js($conf->browser->os).'\';
3259 if (ostype === "macintosh") {
3260 if ( event.which === 66 && event.ctrlKey ) {
3261 console.log("Click on control + b : trigger open bookmark dropdown");
3262 openBookMarkDropDown(event);
3263 }
3264 } else {
3265 if ( event.which === 66 && event.ctrlKey && event.shiftKey ) {
3266 console.log("Click on control + shift + b : trigger open bookmark dropdown");
3267 openBookMarkDropDown(event);
3268 }
3269 }
3270 });
3271
3272 var openBookMarkDropDown = function(event) {
3273 console.log("toggle #topmenu-bookmark-dropdown and force focus");
3274 event.preventDefault();
3275 jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
3276 jQuery("#top-bookmark-search-input").focus();
3277 }
3278
3279 });
3280 </script>
3281 ';
3282 }
3283 }
3284 return $html;
3285}
3286
3292function top_menu_search()
3293{
3294 global $langs, $conf, $db, $user, $hookmanager; // used by htdocs/core/ajax/selectsearchbox.php
3295
3296 $html = '';
3297
3298 $usedbyinclude = 1; // Used by selectsearchbox.php
3299 $arrayresult = array();
3300 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This sets $arrayresult
3301
3302 $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">';
3303
3304 $defaultAction = '';
3305 $buttonList = '<div class="dropdown-global-search-button-list" >';
3306 // Menu with all searchable items
3307 // @phan-suppress-next-line PhanEmptyForeach // array is really empty
3308 foreach ($arrayresult as $keyItem => $item) {
3309 if (empty($defaultAction)) {
3310 $defaultAction = $item['url'];
3311 }
3312 $buttonList .= '<button class="dropdown-item global-search-item '.(empty($conf->dol_optimize_smallscreen) ? 'tdoverflowmax400' : 'tdoverflowmax300').'" data-target="'.dol_escape_htmltag($item['url']).'" >';
3313 $buttonList .= $item['text'];
3314 $buttonList .= '</button>';
3315 }
3316 $buttonList .= '</div>';
3317
3318 $dropDownHtml = '<form role="search" id="top-menu-action-search" name="actionsearch" method="GET" action="'.$defaultAction.'">';
3319
3320 $dropDownHtml .= '
3321 <!-- search input -->
3322 <div class="dropdown-header search-dropdown-header">
3323 ' . $searchInput.'
3324 </div>
3325 ';
3326
3327 $dropDownHtml .= '
3328 <!-- Menu Body search -->
3329 <div class="dropdown-body search-dropdown-body">
3330 '.$buttonList.'
3331 </div>
3332 ';
3333
3334 $dropDownHtml .= '</form>';
3335
3336 $html .= '<!-- div for Global Search -->
3337 <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
3338 <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="'.$langs->trans('Search').' ('.$conf->browser->stringforfirstkey.' s)">
3339 <i class="fa fa-search" aria-hidden="true" ></i>
3340 </a>
3341 <div class="dropdown-menu dropdown-search">
3342 '.$dropDownHtml.'
3343 </div>
3344 </div>';
3345
3346 $html .= '
3347 <!-- Code to show/hide the user drop-down -->
3348 <script>
3349 jQuery(document).ready(function() {
3350
3351 // prevent submitting form on press ENTER
3352 jQuery("#top-global-search-input").keydown(function (e) {
3353 if (e.keyCode == 13 || e.keyCode == 40) {
3354 var inputs = $(this).parents("form").eq(0).find(":button");
3355 if (inputs[inputs.index(this) + 1] != null) {
3356 console.log("Force focus after keydow on #top-global-search-input");
3357 inputs[inputs.index(this) + 1].focus();
3358 if (e.keyCode == 13){
3359 inputs[inputs.index(this) + 1].trigger("click");
3360 }
3361
3362 }
3363 e.preventDefault();
3364 return false;
3365 }
3366 });
3367
3368 // arrow key nav
3369 jQuery(document).keydown(function(e) {
3370 // Get the focused element:
3371 var $focused = $(":focus");
3372 if($focused.length && $focused.hasClass("global-search-item")){
3373
3374 // UP - move to the previous line
3375 if (e.keyCode == 38) {
3376 e.preventDefault();
3377 console.log("Force focus after keycode 38");
3378 $focused.prev().focus();
3379 }
3380
3381 // DOWN - move to the next line
3382 if (e.keyCode == 40) {
3383 e.preventDefault();
3384 console.log("Force focus after keycode 40");
3385 $focused.next().focus();
3386 }
3387 }
3388 });
3389
3390
3391 // submit form action
3392 jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
3393 jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
3394 jQuery("#top-menu-action-search").submit();
3395 });
3396
3397 // Close drop down
3398 jQuery(document).on("click", function(event) {
3399 if (jQuery("#topmenu-global-search-dropdown").hasClass("open")) {
3400 if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
3401 console.log("click close search - we click outside");
3402 // Hide the dropdown.
3403 jQuery("#topmenu-global-search-dropdown").removeClass("open");
3404 }
3405 }
3406 });
3407
3408 // Open drop down
3409 jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
3410 console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
3411 openGlobalSearchDropDown();
3412 });
3413
3414 // Key map shortcut
3415 jQuery(document).keydown(function(e){
3416 if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
3417 console.log(\'control + shift + f : trigger open global-search dropdown\');
3418 openGlobalSearchDropDown();
3419 }
3420 if ( e.which === 70 && e.alKey ) {
3421 console.log(\'alt + f : trigger open global-search dropdown\');
3422 openGlobalSearchDropDown();
3423 }
3424 });
3425
3426 var openGlobalSearchDropDown = function() {
3427 jQuery("#topmenu-global-search-dropdown").toggleClass("open");
3428 jQuery("#top-global-search-input").focus();
3429 }
3430
3431 });
3432 </script>
3433 ';
3434
3435 return $html;
3436}
3437
3452function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = array(), $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
3453{
3454 global $user, $conf, $langs, $db, $form;
3455 global $hookmanager, $menumanager;
3456
3457 $searchform = '';
3458
3459 if (!empty($menu_array_before)) {
3460 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);
3461 }
3462
3463 if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
3464 // Instantiate hooks for external modules
3465 $hookmanager->initHooks(array('leftblock'));
3466
3467 print "\n".'<!-- Begin side-nav id-left -->'."\n".'<div class="side-nav"><div id="id-left">'."\n";
3468 print "\n";
3469
3470 if (!is_object($form)) {
3471 $form = new Form($db);
3472 }
3473 $selected = -1;
3474 if (!getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
3475 // Select with select2 is awful on smartphone. TODO Is this still true with select2 v4 ?
3476 if ($conf->browser->layout == 'phone') {
3477 $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
3478 }
3479
3480 $usedbyinclude = 1;
3481 $arrayresult = array();
3482 include DOL_DOCUMENT_ROOT.'/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
3483
3484 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3485 //$textsearch = $langs->trans("Search");
3486 $textsearch = '<span class="fa fa-search paddingright pictofixedwidth"></span>'.$langs->trans("Search");
3487 $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');
3488 } else {
3489 if (is_array($arrayresult)) {
3490 // @phan-suppress-next-line PhanEmptyForeach // array is really empty in else case.
3491 foreach ($arrayresult as $key => $val) {
3492 $searchform .= printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft'.$key, $val['img']);
3493 }
3494 }
3495 }
3496
3497 // Execute hook printSearchForm
3498 $parameters = array('searchform' => $searchform);
3499 $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
3500 if (empty($reshook)) {
3501 $searchform .= $hookmanager->resPrint;
3502 } else {
3503 $searchform = $hookmanager->resPrint;
3504 }
3505
3506 // Force special value for $searchform for text browsers or very old search form
3507 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') || empty($conf->use_javascript_ajax)) {
3508 $urltosearch = DOL_URL_ROOT.'/core/search_page.php?showtitlebefore=1';
3509 $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>';
3510 } elseif ($conf->use_javascript_ajax && getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3511 $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>';
3512 $searchform .= '<script>
3513 jQuery(document).ready(function () {
3514 jQuery("#divsearchforms1").click(function(){
3515 jQuery("#divsearchforms2").toggle();
3516 });
3517 });
3518 </script>' . "\n";
3519 $searchform .= '</div>';
3520 }
3521
3522 // Key map shortcut
3523 $searchform .= '<script>
3524 jQuery(document).keydown(function(e){
3525 if( e.which === 70 && e.ctrlKey && e.shiftKey ){
3526 console.log(\'control + shift + f : trigger open global-search dropdown\');
3527 openGlobalSearchDropDown();
3528 }
3529 if( (e.which === 83 || e.which === 115) && e.altKey ){
3530 console.log(\'alt + s : trigger open global-search dropdown\');
3531 openGlobalSearchDropDown();
3532 }
3533 });
3534
3535 var openGlobalSearchDropDown = function() {
3536 jQuery("#searchselectcombo").select2(\'open\');
3537 }
3538 </script>';
3539 }
3540
3541 // Left column
3542 print '<!-- Begin left menu -->'."\n";
3543
3544 print '<div class="vmenu"'.(getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' alt="Left menu"' : '').'>'."\n\n";
3545
3546 // Show left menu with other forms
3547 // @phan-suppress-next-line PhanRedefinedClassReference
3548 $menumanager->menu_array = $menu_array_before;
3549 // @phan-suppress-next-line PhanRedefinedClassReference
3550 $menumanager->menu_array_after = $menu_array_after;
3551 if (getDolGlobalInt('MAIN_MENU_LEFT_DROPDOWN')) {
3552 // @phan-suppress-next-line PhanRedefinedClassReference
3553 $menumanager->showmenu('leftdropdown', array('searchform' => $searchform)); // output menu_array and menu found in database
3554 } else {
3555 // @phan-suppress-next-line PhanRedefinedClassReference
3556 $menumanager->showmenu('left', array('searchform' => $searchform)); // output menu_array and menu found in database
3557 }
3558
3559 // Dolibarr version + help + bug report link
3560 if (getDolGlobalString('MAIN_SHOW_VERSION') || getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3561 print "\n";
3562 print "<!-- Begin Help Block-->\n";
3563 print '<div id="blockvmenuhelp" class="blockvmenuhelp">'."\n";
3564
3565 // Version
3566 if (getDolGlobalString('MAIN_SHOW_VERSION')) { // Version is already on help picto and on login page.
3567 $doliurl = 'https://www.dolibarr.org';
3568 //local communities
3569 if (preg_match('/fr/i', $langs->defaultlang)) {
3570 $doliurl = 'https://www.dolibarr.fr';
3571 }
3572 if (preg_match('/es/i', $langs->defaultlang)) {
3573 $doliurl = 'https://www.dolibarr.es';
3574 }
3575 if (preg_match('/de/i', $langs->defaultlang)) {
3576 $doliurl = 'https://www.dolibarr.de';
3577 }
3578 if (preg_match('/it/i', $langs->defaultlang)) {
3579 $doliurl = 'https://www.dolibarr.it';
3580 }
3581 if (preg_match('/gr/i', $langs->defaultlang)) {
3582 $doliurl = 'https://www.dolibarr.gr';
3583 }
3584
3585 $appli = constant('DOL_APPLICATION_TITLE');
3586 $applicustom = getDolGlobalString('MAIN_APPLICATION_TITLE');
3587 if ($applicustom) {
3588 $appli = (preg_match('/^\+/', $applicustom) ? $appli : '').$applicustom;
3589 } else {
3590 $appli .= " ".DOL_VERSION;
3591 }
3592
3593 // Clean doliurl if we use a custom application name
3594 if ($applicustom) {
3595 $doliurl = '';
3596 }
3597
3598 print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
3599 if ($doliurl) {
3600 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$doliurl.'">';
3601 } else {
3602 print '<span class="help">';
3603 }
3604 print $appli;
3605 if ($doliurl) {
3606 print '</a>';
3607 } else {
3608 print '</span>';
3609 }
3610 print '</div>'."\n";
3611 }
3612
3613 // Link to bugtrack
3614 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3615 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
3616
3617 if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK') == 'github') {
3618 $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
3619 $bugbaseurl .= '&title=';
3620 $bugbaseurl .= urlencode("Bug: ");
3621 $bugbaseurl .= '&body=';
3622 $bugbaseurl .= urlencode("# Instructions\n");
3623 $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");
3624 $bugbaseurl .= urlencode("*Please:*\n");
3625 $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
3626 $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
3627 $bugbaseurl .= urlencode("\n");
3628 $bugbaseurl .= urlencode("\n");
3629 $bugbaseurl .= urlencode("# Bug\n");
3630 $bugbaseurl .= urlencode("[*Short description*]\n");
3631 $bugbaseurl .= urlencode("\n");
3632 $bugbaseurl .= urlencode("## Environment\n");
3633 $bugbaseurl .= urlencode("- **Version**: ".DOL_VERSION."\n");
3634 $bugbaseurl .= urlencode("- **OS**: ".php_uname('s')."\n");
3635 $bugbaseurl .= urlencode("- **Web server**: ".$_SERVER["SERVER_SOFTWARE"]."\n");
3636 $bugbaseurl .= urlencode("- **PHP**: ".php_sapi_name().' '.phpversion()."\n");
3637 $bugbaseurl .= urlencode("- **Database**: ".$db::LABEL.' '.$db->getVersion()."\n");
3638 $bugbaseurl .= urlencode("- **URL(s)**: ".$_SERVER["REQUEST_URI"]."\n");
3639 $bugbaseurl .= urlencode("\n");
3640 $bugbaseurl .= urlencode("## Expected and actual behavior\n");
3641 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3642 $bugbaseurl .= urlencode("\n");
3643 $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
3644 $bugbaseurl .= urlencode("[*Verbose description*]\n");
3645 $bugbaseurl .= urlencode("\n");
3646 $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, dolibarr.log, debugging information…)\n");
3647 $bugbaseurl .= urlencode("[*Files*]\n");
3648 $bugbaseurl .= urlencode("\n");
3649
3650 $bugbaseurl .= urlencode("\n");
3651 $bugbaseurl .= urlencode("## Report\n");
3652 } elseif (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3653 $bugbaseurl = getDolGlobalString('MAIN_BUGTRACK_ENABLELINK');
3654 } else {
3655 $bugbaseurl = "";
3656 }
3657
3658 // Execute hook printBugtrackInfo
3659 $parameters = array('bugbaseurl' => $bugbaseurl);
3660 $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
3661 if (empty($reshook)) {
3662 $bugbaseurl .= $hookmanager->resPrint;
3663 } else {
3664 $bugbaseurl = $hookmanager->resPrint;
3665 }
3666
3667 print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
3668 print '<a class="help" target="_blank" rel="noopener noreferrer" href="'.$bugbaseurl.'"><i class="fas fa-bug"></i> '.$langs->trans("FindBug").'</a>';
3669 print '</div>';
3670 }
3671
3672 print "</div>\n";
3673 print "<!-- End Help Block-->\n";
3674 print "\n";
3675 }
3676
3677 print "</div>\n";
3678 print "<!-- End left menu -->\n";
3679 print "\n";
3680
3681 // Execute hook printLeftBlock
3682 $parameters = array();
3683 $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
3684 print $hookmanager->resPrint;
3685
3686 print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
3687 }
3688
3689 print "\n";
3690 print '<!-- Begin right area -->'."\n";
3691
3692 if (empty($leftmenuwithoutmainarea)) {
3693 main_area($title);
3694 }
3695}
3696
3697
3704function main_area($title = '')
3705{
3706 global $conf, $langs, $hookmanager;
3707
3708 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3709 print '<div id="id-right">';
3710 }
3711
3712 print "\n";
3713
3714 print '<!-- Begin div class="fiche" -->'."\n".'<div class="fiche">'."\n";
3715
3716 $hookmanager->initHooks(array('main'));
3717 $parameters = array();
3718 $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
3719 print $hookmanager->resPrint;
3720
3721 if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
3722 print info_admin($langs->trans("WarningYouAreInMaintenanceMode", getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')), 0, 0, '1', 'warning maintenancemode');
3723 }
3724
3725 // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
3726 if (getDolGlobalString('SHOW_SOCINFO_ON_PRINT') && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'aZ09'))) {
3727 $parameters = array();
3728 $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
3729 if (empty($reshook)) {
3730 print '<!-- Begin show mysoc info header -->'."\n";
3731 print '<div id="mysoc-info-header">'."\n";
3732 print '<table class="centpercent div-table-responsive">'."\n";
3733 print '<tbody>';
3734 print '<tr><td rowspan="0" class="width20p">';
3735 if (getDolGlobalString('MAIN_SHOW_LOGO') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalString('MAIN_INFO_SOCIETE_LOGO')) {
3736 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'))).'">';
3737 }
3738 print '</td><td rowspan="0" class="width50p"></td></tr>'."\n";
3739 print '<tr><td class="titre bold">'.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_NOM')).'</td></tr>'."\n";
3740 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";
3741 if (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) {
3742 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Phone").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TEL')).'</td></tr>';
3743 }
3744 if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
3745 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Email").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')).'</td></tr>';
3746 }
3747 if (getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) {
3748 print '<tr><td style="padding-left: 1em" class="small">'.$langs->trans("Web").' : '.dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_WEB')).'</td></tr>';
3749 }
3750 print '</tbody>';
3751 print '</table>'."\n";
3752 print '</div>'."\n";
3753 print '<!-- End show mysoc info header -->'."\n";
3754 }
3755 }
3756}
3757
3758
3766function getHelpParamFor($helppagename, $langs)
3767{
3768 $helpbaseurl = '';
3769 $helppage = '';
3770 $mode = '';
3771
3772 if (preg_match('/^http/i', $helppagename)) {
3773 // If complete URL
3774 $helpbaseurl = '%s';
3775 $helppage = $helppagename;
3776 $mode = 'local';
3777 } else {
3778 // If WIKI URL
3779 $reg = array();
3780 if (preg_match('/^es/i', $langs->defaultlang)) {
3781 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3782 if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
3783 $helppage = $reg[1];
3784 }
3785 }
3786 if (preg_match('/^fr/i', $langs->defaultlang)) {
3787 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3788 if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
3789 $helppage = $reg[1];
3790 }
3791 }
3792 if (preg_match('/^de/i', $langs->defaultlang)) {
3793 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3794 if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
3795 $helppage = $reg[1];
3796 }
3797 }
3798 if (empty($helppage)) { // If help page not already found
3799 $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3800 if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
3801 $helppage = $reg[1];
3802 }
3803 }
3804 $mode = 'wiki';
3805 }
3806 return array('helpbaseurl' => $helpbaseurl, 'helppage' => $helppage, 'mode' => $mode);
3807}
3808
3809
3826function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
3827{
3828 global $langs, $user;
3829
3830 $ret = '';
3831 $ret .= '<form action="'.$urlaction.'" method="post" class="searchform nowraponall tagtr">';
3832 $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
3833 $ret .= '<input type="hidden" name="savelogin" value="'.dol_escape_htmltag($user->login).'">';
3834 if ($showtitlebefore) {
3835 $ret .= '<div class="tagtd left">'.$title.'</div> ';
3836 }
3837 $ret .= '<div class="tagtd">';
3838 $ret .= img_picto('', $img, '', 0, 0, 0, '', 'paddingright width20');
3839 $ret .= '<input type="text" class="flat '.$htmlmorecss.'"';
3840 $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
3841 $ret .= ($accesskey ? ' accesskey="'.$accesskey.'"' : '');
3842 $ret .= ' placeholder="'.strip_tags($title).'"';
3843 $ret .= ($autofocus ? ' autofocus' : '');
3844 $ret .= ' name="'.$htmlinputname.'" id="'.$prefhtmlinputname.$htmlinputname.'" />';
3845 $ret .= '<button type="submit" class="button bordertransp nohover" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
3846 $ret .= '<span class="fa fa-search"></span>';
3847 $ret .= '</button>';
3848 $ret .= '</div>';
3849 $ret .= "</form>\n";
3850 return $ret;
3851}
3852
3853
3854if (!function_exists("llxFooter")) {
3868 function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
3869 {
3870 global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager, $action;
3871 global $delayedhtmlcontent;
3872 global $contextpage, $page, $limit, $mode;
3873 global $dolibarr_distrib;
3874
3875 $ext = 'layout='.urlencode($conf->browser->layout).'&version='.urlencode(DOL_VERSION);
3876
3877 // Hook to add more things on all pages within fiche DIV
3878 $llxfooter = '';
3879 $parameters = array();
3880 $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3881 if (empty($reshook)) {
3882 $llxfooter .= $hookmanager->resPrint;
3883 } elseif ($reshook > 0) {
3884 $llxfooter = $hookmanager->resPrint;
3885 }
3886 if ($llxfooter) {
3887 print $llxfooter;
3888 }
3889
3890 // Global html output events ($mesgs, $errors, $warnings)
3891 dol_htmloutput_events($disabledoutputofmessages);
3892
3893 // Code for search criteria persistence.
3894 // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
3895 if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
3896 // Clean and save data
3897 foreach ($user->lastsearch_values_tmp as $key => $val) {
3898 unset($_SESSION['lastsearch_values_tmp_'.$key]); // Clean array to rebuild it just after
3899 if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
3900 if (empty($val['sortfield'])) {
3901 unset($val['sortfield']);
3902 }
3903 if (empty($val['sortorder'])) {
3904 unset($val['sortorder']);
3905 }
3906 dol_syslog('Save lastsearch_values_tmp_'.$key.'='.json_encode($val, 0)." (systematic recording of last search criteria)");
3907 $_SESSION['lastsearch_values_tmp_'.$key] = json_encode($val);
3908 unset($_SESSION['lastsearch_values_'.$key]);
3909 }
3910 }
3911 }
3912
3913
3914 $relativepathstring = $_SERVER["PHP_SELF"];
3915 // Clean $relativepathstring
3916 if (constant('DOL_URL_ROOT')) {
3917 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
3918 }
3919 $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3920 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3921 if (preg_match('/list\.php$/', $relativepathstring)) {
3922 unset($_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring]);
3923 unset($_SESSION['lastsearch_page_tmp_'.$relativepathstring]);
3924 unset($_SESSION['lastsearch_limit_tmp_'.$relativepathstring]);
3925 unset($_SESSION['lastsearch_mode_tmp_'.$relativepathstring]);
3926
3927 if (!empty($contextpage)) {
3928 $_SESSION['lastsearch_contextpage_tmp_'.$relativepathstring] = $contextpage;
3929 }
3930 if (!empty($page) && $page > 0) {
3931 $_SESSION['lastsearch_page_tmp_'.$relativepathstring] = $page;
3932 }
3933 if (!empty($limit) && $limit != $conf->liste_limit) {
3934 $_SESSION['lastsearch_limit_tmp_'.$relativepathstring] = $limit;
3935 }
3936 if (!empty($mode)) {
3937 $_SESSION['lastsearch_mode_tmp_'.$relativepathstring] = $mode;
3938 }
3939
3940 unset($_SESSION['lastsearch_contextpage_'.$relativepathstring]);
3941 unset($_SESSION['lastsearch_page_'.$relativepathstring]);
3942 unset($_SESSION['lastsearch_limit_'.$relativepathstring]);
3943 unset($_SESSION['lastsearch_mode_'.$relativepathstring]);
3944 }
3945
3946 // Core error message
3947 if (getDolGlobalString('MAIN_CORE_ERROR')) {
3948 // Ajax version
3949 if ($conf->use_javascript_ajax) {
3950 $title = img_warning().' '.$langs->trans('CoreErrorTitle');
3951 print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
3952 } else {
3953 // html version
3954 $msg = img_warning().' '.$langs->trans('CoreErrorMessage');
3955 print '<div class="error">'.$msg.'</div>';
3956 }
3957
3958 //define("MAIN_CORE_ERROR",0); // Constant was defined and we can't change value of a constant
3959 }
3960
3961 print "\n\n";
3962
3963 print '</div> <!-- End div class="fiche" -->'."\n"; // End div fiche
3964
3965 if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
3966 print '</div> <!-- End div id-right -->'."\n"; // End div id-right
3967 }
3968
3969 if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
3970 print '</div> <!-- End div id-container -->'."\n"; // End div container
3971 }
3972
3973 print "\n";
3974 if ($comment) {
3975 print '<!-- '.$comment.' -->'."\n";
3976 }
3977
3978 printCommonFooter($zone);
3979
3980 if (!empty($delayedhtmlcontent)) {
3981 print $delayedhtmlcontent;
3982 }
3983
3984 if (!empty($conf->use_javascript_ajax)) {
3985 print "\n".'<!-- Includes JS Footer of Dolibarr -->'."\n";
3986 print '<script src="'.DOL_URL_ROOT.'/core/js/lib_foot.js.php?lang='.$langs->defaultlang . '&' . $ext .'"></script>'."\n";
3987 }
3988
3989 // JS wrapper to add an unalterable log when clicking on Download or Preview
3990 // This is done on customer invoices only.
3991 // This add a log and increase the pos_print_counter too (done by block-add.php).
3992 /* NOTE: No more required, the trigger is now included into the call of the wrapper documents.php
3993 if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
3994 if (in_array($object->element, array('facture')) && $object->statut > 0) { // Restrict for the moment to element 'facture'
3995 print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
3996 ?>
3997 <script>
3998 jQuery(document).ready(function () {
3999 $('a.documentpreview').click(function() {
4000 console.log("Call /blockedlog/ajax/block-add on a.documentpreview (DOC_PREVIEW)");
4001 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
4002 , {
4003 id: <?php echo $object->id; ?>
4004 , element: '<?php echo dol_escape_js($object->element) ?>'
4005 , action: 'DOC_PREVIEW'
4006 , lang: '<?php echo dol_escape_js($langs->defaultlang); ?>'
4007 , token: '<?php echo currentToken(); ?>'
4008 }
4009 );
4010 });
4011 $('a.documentdownload').click(function() {
4012 console.log("Call /blockedlog/ajax/block-add on a.documentdownload (DOC_DOWNLOAD)");
4013 $.post('<?php echo DOL_URL_ROOT."/blockedlog/ajax/block-add.php" ?>'
4014 , {
4015 id: <?php echo $object->id; ?>
4016 , element: '<?php echo dol_escape_js($object->element) ?>'
4017 , action: 'DOC_DOWNLOAD'
4018 , lang: '<?php echo dol_escape_js($langs->defaultlang); ?>'
4019 , token: '<?php echo currentToken(); ?>'
4020 }
4021 );
4022 });
4023 });
4024 </script>
4025 <?php
4026 }
4027 }
4028 */
4029
4030 // A div for the #dialogforpopup popup
4031 print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
4032 print '<div id="dialogforpopup" style="display: none;"></div>'."\n";
4033
4034 // A div for the #uiblock
4035 print "\n<!-- A div to allow uiblock by dolBlockUI(message) -->\n";
4036 print '<div id="dol-block-ui" style="display: none;"><div class="message">Loading...</div></div>'."\n";
4037
4038
4039 // Add code for the asynchronous anonymous first ping (for telemetry)
4040 // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
4041 $forceping = GETPOSTINT('forceping');
4042
4043 if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceping) {
4044 require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
4045
4046 $hash_unique_id_ping = getHashUniqueIdOfRegistration('sha256');
4047 $constanttosavelastko = 'MAIN_LAST_PING_KO_DATE';
4048 $constanttosavefirstok = 'MAIN_FIRST_PING_OK_DATE';
4049 $constanttosavefirstokid = 'MAIN_FIRST_PING_OK_ID';
4050
4051 if (!getDolGlobalString($constanttosavefirstok)
4052 || (!empty($conf->file->instance_unique_id) && (($hash_unique_id_ping.' - '.DOL_VERSION) != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
4053 || $forceping) {
4054 // No ping done if we are into an alpha version
4055 if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
4056 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
4057 } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id_ping]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
4058 // Output code for ping
4059 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4060
4061 $arrayofmoredata = array(
4062 'action' => 'dolibarrping',
4063 'datesys' => dol_print_date(dol_now(), 'standard', 'gmt'),
4064
4065 'country_code' => ($mysoc->country_code ? $mysoc->country_code : 'unknown')
4066 );
4067 printCodeForPing($constanttosavelastko, $constanttosavefirstok, $arrayofmoredata, $forceping);
4068 } else {
4069 $now = dol_now();
4070 print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
4071 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
4072 dolibarr_set_const($db, $constanttosavefirstok, dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
4073 dolibarr_set_const($db, $constanttosavefirstokid, 'disabled', 'chaine', 0, '', $conf->entity);
4074 }
4075 } else {
4076 print "\n<!-- NO JS CODE TO call the ping. It was already done for this couple uniqueid and version -->\n";
4077 }
4078 }
4079
4080 // Add code for the asynchronous registration of the use of the BlockedLog module if not yet done but ready (in case past submission failed)
4081 // You can use &forceregistration=1 in parameters to force also the recall if the call was already sent.
4082 $forceregistration = GETPOSTINT('forceregistration');
4083
4084 if (isModEnabled('blockedlog') && (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceregistration)) {
4085 require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
4086
4087 if (!isALNEQualifiedVersion()) {
4088 print "\n<!-- NO JS CODE TO ENABLE the registration. Not a LNE qualified version -->\n";
4089 } elseif (!isRegistrationDataSaved()) {
4090 print "\n<!-- NO JS CODE TO ENABLE the registration. Registration data not saved -->\n";
4091 } else {
4092 $hash_unique_id_registration = getHashUniqueIdOfRegistration();
4093 $constanttosavelastko = 'MAIN_LAST_REGISTRATION_KO_DATE';
4094 $constanttosavefirstok = 'MAIN_FIRST_REGISTRATION_OK_DATE';
4095 $constanttosavefirstokid = 'MAIN_FIRST_REGISTRATION_OK_ID';
4096
4097 if (!getDolGlobalString($constanttosavefirstok)
4098 || (!empty($conf->file->instance_unique_id) && ($hash_unique_id_registration.' - '.DOL_VERSION != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
4099 || $forceregistration) {
4100 // No registration done if we are into an alpha or beta version
4101 if ((strpos('alpha', DOL_VERSION) > 0 || strpos('beta', DOL_VERSION) > 0) && !$forceregistration) {
4102 print "\n<!-- NO JS CODE TO ENABLE the registration. It is an alpha or beta version -->\n";
4103 } elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id_registration]) || $forceregistration) { // Cookie is set when we uncheck the checkbox in the installation wizard.
4104 // Output code for ping
4105 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
4106
4107 $arrayofdata = array(
4108 'action' => 'dolibarrregistration',
4109 'datesys' => dol_print_date(dol_now(), 'standard', 'gmt'),
4110
4111 'company_name' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_NAME', $mysoc->name),
4112 'company_email' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_EMAIL', $mysoc->email),
4113 'company_idprof1' => getDolGlobalString('MAIN_INFO_SIREN', $mysoc->idprof1),
4114 'company_idprof2' => getDolGlobalString('MAIN_INFO_SIRET', $mysoc->idprof2),
4115 'company_address' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_ADDRESS', $mysoc->address),
4116 'company_state' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_STATE', $mysoc->state),
4117 'company_zip' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_ZIP', $mysoc->zip),
4118 'company_town' => getDolGlobalString('BLOCKEDLOG_REGISTRATION_TOWN', $mysoc->town),
4119 'country_code' => $mysoc->country_code,
4120
4121 'provider_name' => getDolGlobalString('MAIN_INFO_ITPROVIDER_NAME'),
4122 'provider_email' => getDolGlobalString('MAIN_INFO_ITPROVIDER_MAIL'),
4123 'provider_phone' => getDolGlobalString('MAIN_INFO_ITPROVIDER_PHONE'),
4124 'provider_address' => getDolGlobalString('MAIN_INFO_ITPROVIDER_ADDRESS'),
4125 'provider_state' => getDolGlobalString('MAIN_INFO_ITPROVIDER_STATE'),
4126 'provider_zip' => getDolGlobalString('MAIN_INFO_ITPROVIDER_ZIP'),
4127 'provider_town' => getDolGlobalString('MAIN_INFO_ITPROVIDER_TOWN'),
4128 'provider_country' => getDolGlobalString('MAIN_INFO_ITPROVIDER_COUNTRY'),
4129 'provider_idprof1' => getDolGlobalString('MAIN_INFO_ITPROVIDER_IDPROF1'),
4130 'provider_idprof2' => getDolGlobalString('MAIN_INFO_ITPROVIDER_IDPROF2')
4131 );
4132 printCodeForPing($constanttosavelastko, $constanttosavefirstok, $arrayofdata, $forceregistration);
4133 } else {
4134 $now = dol_now();
4135 print "\n<!-- NO JS CODE TO ENABLE the registration. It was disabled -->\n";
4136 include_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
4137 dolibarr_set_const($db, $constanttosavefirstok, dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
4138 dolibarr_set_const($db, $constanttosavefirstokid, 'disabled', 'chaine', 0, '', $conf->entity);
4139 }
4140 } else {
4141 print "\n<!-- NO JS CODE TO call the registration. It was already done for this couple uniqueid and version -->\n";
4142 }
4143 }
4144 }
4145
4146 // Add code for the asynchronous emulation of pushing a tracking counter of the use of the BlockedLog module trigger(for test purposes)
4147 // You can use &forceregistration=1 in parameters to force also the recall if the call was already sent.
4148 /*
4149 $forcepushcounter = GETPOSTINT('forcepushcounter');
4150
4151 if (isModEnabled('blockedlog') && ($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') && $forcepushcounter) {
4152 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
4153 $islne = isALNEQualifiedVersion(1, 1);
4154 if (!$islne) {
4155 print "\n<!-- NO CALL TO API TO PUSH COUNTER. Not a LNE qualified version -->\n";
4156 } elseif (!isRegistrationDataSaved()) {
4157 print "\n<!-- NO CALL TO API TO PUSH COUNTER. Registration data not saved -->\n";
4158 } else {
4159 // Get last ID and hash into $tmpresult
4160 include_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
4161 $tmpblockedlog = new BlockedLog($db);
4162 $tmpresult = $tmpblockedlog->getPreviousHash(0, 0);
4163
4164 if ((int) $tmpresult['previousid']) {
4165 $tmpresult2 = $tmpblockedlog->getPreviousHash(0, (int) $tmpresult['previousid']); // Get previous record
4166
4167 if ((int) $tmpresult2['previousid']) {
4168 // Call remote API service to record the last counter
4169 $resultcall = callApiToPushCounter((int) $tmpresult['previousid'], $tmpresult['previoushash'], $tmpresult['previousdatecreation'], 1, (int) $tmpresult2['previousid'], $tmpresult2['previoushash'], $tmpresult2['previousdatecreation']);
4170
4171 $algo = 'sha256';
4172 $hash_unique_id = getHashUniqueIdOfRegistration($algo); // The hash of the unique IDof instance
4173
4174 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";
4175 }
4176 } else {
4177 print "\n<!-- NO CALL TO API TO PUSH COUNTER. Last rowid and signature not found -->\n";
4178 }
4179 }
4180 }
4181 */
4182
4183
4184
4185 $parameters = array();
4186 $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
4187 if ($reshook > 0) {
4188 print $hookmanager->resPrint;
4189 }
4190
4191 print "</body>\n";
4192 print "</html>\n";
4193 }
4194}
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.
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.
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.
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='classlink button bordertransp', $jsonopen='', $jsonclose='', $accesskey='')
Return HTML code to output a button to open a dialog popup box.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
dolSetCookie(string $cookiename, string $cookievalue, int $expire=-1)
Set a cookie.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
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', ...)
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).
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).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getNonce()
Return a random string to be used as a nonce value for js.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
top_menu_importfile()
Build the tooltip on top menu quick add.
top_menu_quickadd()
Build the tooltip on top menu quick add.
top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $disableforlogin=0, $disablenofollow=0, $disablenoindex=0)
Output html header of a page.
top_menu_ai()
Build the HTML for the AI Assistant entry of the top menu: a toggle icon and a floating popover panel...
top_menu_user($hideloginname=0, $urllogout='')
Build the tooltip on user login.
left_menu($menu_array_before, $helppagename='', $notused='', $menu_array_after=array(), $leftmenuwithoutmainarea=0, $title='', $acceptdelayedhtml=0)
Show left menu bar.
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
main_area($title='')
Begin main area.
getHelpParamFor($helppagename, $langs)
Return helpbaseurl, helppage and mode.
printDropdownQuickadd($mode=0)
Generate list of quickadd items.
printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey='', $prefhtmlinputname='', $img='', $showtitlebefore=0, $autofocus=0)
Show a search area.
top_menu($head, $title='', $target='', $disablejs=0, $disablehead=0, $arrayofjs=array(), $arrayofcss=array(), $morequerystring='', $helppagename='')
Show an HTML header + a BODY + The top menu bar.
top_menu_search()
Build the tooltip on top menu search.
top_menu_bookmark()
Build the tooltip on top menu bookmark.
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:134
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode, $context='')
Return a login if login/pass was successful.
checkIPInCidr($ip, $cidr)
Check if IP address is in CIDR range.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...