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