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