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