Test Setup Failed
Push — dev ( 608138...99eb65 )
by Rafael
61:41 queued 16s
created

getConstants()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 16
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 23
rs 9.7333
1
<?php
2
3
/* Copyright (C) 2002-2007  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2003       Xavier Dutoit               <[email protected]>
5
 * Copyright (C) 2004-2021  Laurent Destailleur         <[email protected]>
6
 * Copyright (C) 2004       Sebastien Di Cintio         <[email protected]>
7
 * Copyright (C) 2004       Benoit Mortier              <[email protected]>
8
 * Copyright (C) 2005-2021  Regis Houssin               <[email protected]>
9
 * Copyright (C) 2011-2014  Philippe Grand              <[email protected]>
10
 * Copyright (C) 2008       Matteli
11
 * Copyright (C) 2011-2016  Juanjo Menent               <[email protected]>
12
 * Copyright (C) 2012       Christophe Battarel         <[email protected]>
13
 * Copyright (C) 2014-2015  Marcos García               <[email protected]>
14
 * Copyright (C) 2015       Raphaël Doursenaud          <[email protected]>
15
 * Copyright (C) 2020       Demarest Maxime             <[email protected]>
16
 * Copyright (C) 2020       Charlene Benke              <[email protected]>
17
 * Copyright (C) 2021-2024  Frédéric France             <[email protected]>
18
 * Copyright (C) 2021       Alexandre Spangaro          <[email protected]>
19
 * Copyright (C) 2023       Joachim Küter      		    <[email protected]>
20
 * Copyright (C) 2023       Eric Seigne      		    <[email protected]>
21
 * Copyright (C) 2024		MDW							<[email protected]>
22
 * Copyright (C) 2024       Rafael San José             <[email protected]>
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
38
use Dolibarr\Code\Core\Classes\Form;
39
use Dolibarr\Code\Core\Classes\HookManager;
40
use Dolibarr\Code\Core\Classes\Translate;
41
use Dolibarr\Lib\Filters;
42
use Dolibarr\Tools\Debug;
43
44
/**
45
 *  \file       htdocs/main.inc.php
46
 *  \ingroup    core
47
 *  \brief      File that defines environment for Dolibarr GUI pages only (file not required by scripts)
48
 */
49
50
//@ini_set('memory_limit', '128M'); // This may be useless if memory is hard limited by your PHP
51
52
// For optional tuning. Enabled if environment variable MAIN_SHOW_TUNING_INFO is defined.
53
54
$micro_start_time = 0;
55
if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) {
56
    list($usec, $sec) = explode(" ", microtime());
57
    $micro_start_time = ((float)$usec + (float)$sec);
58
    // Add Xdebug code coverage
59
    //define('XDEBUGCOVERAGE',1);
60
    if (defined('XDEBUGCOVERAGE')) {
61
        xdebug_start_code_coverage();
62
    }
63
}
64
65
// To disable the WAF for GET and POST and PHP_SELF, uncomment this
66
//define('NOSCANPHPSELFFORINJECTION', 1);
67
//define('NOSCANGETFORINJECTION', 1);
68
//define('NOSCANPOSTFORINJECTION', 1 or 2);
69
70
// Check consistency of NOREQUIREXXX DEFINES
71
if ((defined('NOREQUIREDB') || defined('NOREQUIRETRAN')) && !defined('NOREQUIREMENU')) {
72
    print 'If define NOREQUIREDB or NOREQUIRETRAN are set, you must also set NOREQUIREMENU or not set them.';
73
    exit;
74
}
75
if (defined('NOREQUIREUSER') && !defined('NOREQUIREMENU')) {
76
    print 'If define NOREQUIREUSER is set, you must also set NOREQUIREMENU or not set it.';
77
    exit;
78
}
79
80
// Sanity check on URL
81
/*
82
if (!defined('NOSCANPHPSELFFORINJECTION') && !empty($_SERVER["PHP_SELF"])) {
83
    $morevaltochecklikepost = array($_SERVER["PHP_SELF"]);
84
    analyseVarsForSqlAndScriptsInjection($morevaltochecklikepost, 2);
85
}
86
*/
87
88
// Sanity check on GET parameters
89
if (!defined('NOSCANGETFORINJECTION') && !empty($_SERVER["QUERY_STRING"])) {
90
    // Note: QUERY_STRING is url encoded, but $_GET and $_POST are already decoded
91
    // Because the analyseVarsForSqlAndScriptsInjection is designed for already url decoded value, we must decode QUERY_STRING
92
    // Another solution is to provide $_GET as parameter with analyseVarsForSqlAndScriptsInjection($_GET, 1);
93
    $morevaltochecklikeget = array(urldecode($_SERVER["QUERY_STRING"]));
94
    Filters::analyseVarsForSqlAndScriptsInjection($morevaltochecklikeget, 1);
95
}
96
97
// Sanity check on POST
98
if (!defined('NOSCANPOSTFORINJECTION') || is_array(constant('NOSCANPOSTFORINJECTION'))) {
99
    Filters::analyseVarsForSqlAndScriptsInjection($_POST, 0);
100
}
101
102
// This is to make Dolibarr working with Plesk
103
if (!empty($_SERVER['DOCUMENT_ROOT']) && substr($_SERVER['DOCUMENT_ROOT'], -6) !== 'htdocs') {
104
    set_include_path($_SERVER['DOCUMENT_ROOT'] . '/htdocs');
105
}
106
107
// 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...
108
require_once 'filefunc.inc.php';
109
110
// If there is a POST parameter to tell to save automatically some POST parameters into cookies, we do it.
111
// This is used for example by form of boxes to save personalization of some options.
112
// DOL_AUTOSET_COOKIE=cookiename:val1,val2 and  cookiename_val1=aaa cookiename_val2=bbb will set cookie_name with value json_encode(array('val1'=> , ))
113
if (GETPOST("DOL_AUTOSET_COOKIE")) {
114
    $tmpautoset = explode(':', GETPOST("DOL_AUTOSET_COOKIE"), 2);
115
    $tmplist = explode(',', $tmpautoset[1]);
116
    $cookiearrayvalue = array();
117
    foreach ($tmplist as $tmpkey) {
118
        $postkey = $tmpautoset[0] . '_' . $tmpkey;
119
        //var_dump('tmpkey='.$tmpkey.' postkey='.$postkey.' value='.GETPOST($postkey);
120
        if (GETPOST($postkey)) {
121
            $cookiearrayvalue[$tmpkey] = GETPOST($postkey);
122
        }
123
    }
124
    $cookiename = $tmpautoset[0];
125
    $cookievalue = json_encode($cookiearrayvalue);
126
    //var_dump('setcookie cookiename='.$cookiename.' cookievalue='.$cookievalue);
127
    if (PHP_VERSION_ID < 70300) {
128
        setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, empty($cookievalue) ? 0 : (time() + (86400 * 354)), '/', '', ((empty($dolibarr_main_force_https) && isHTTPS() === false) ? false : true), true); // keep cookie 1 year and add tag httponly
129
    } else {
130
        // Only available for php >= 7.3
131
        $cookieparams = array(
132
            'expires' => empty($cookievalue) ? 0 : (time() + (86400 * 354)),
133
            'path' => '/',
134
            //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
135
            'secure' => ((empty($dolibarr_main_force_https) && isHTTPS() === false) ? false : true),
136
            'httponly' => true,
137
            'samesite' => 'Lax' // None || Lax  || Strict
138
        );
139
        setcookie($cookiename, empty($cookievalue) ? '' : $cookievalue, $cookieparams);
140
    }
141
    if (empty($cookievalue)) {
142
        unset($_COOKIE[$cookiename]);
143
    }
144
}
145
146
// Set the handler of session
147
// if (ini_get('session.save_handler') == 'user')
148
if (!empty($php_session_save_handler) && $php_session_save_handler == 'db') {
149
    require_once 'core/lib/phpsessionin' . $php_session_save_handler . '.lib.php';
150
}
151
152
// Init session. Name of session is specific to Dolibarr instance.
153
// 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).
154
// 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.
155
$prefix = dol_getprefix('');
156
$sessionname = 'DOLSESSID_' . $prefix;
157
$sessiontimeout = 'DOLSESSTIMEOUT_' . $prefix;
158
if (!empty($_COOKIE[$sessiontimeout])) {
159
    ini_set('session.gc_maxlifetime', $_COOKIE[$sessiontimeout]);
160
}
161
162
// This create lock, released by session_write_close() or end of page.
163
// We need this lock as long as we read/write $_SESSION ['vars']. We can remove lock when finished.
164
if (!defined('NOSESSION')) {
165
    if (PHP_VERSION_ID < 70300) {
166
        session_set_cookie_params(0, '/', null, ((empty($dolibarr_main_force_https) && isHTTPS() === false) ? false : true), 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.
167
    } else {
168
        // Only available for php >= 7.3
169
        $sessioncookieparams = array(
170
            'lifetime' => 0,
171
            'path' => '/',
172
            //'domain' => '.mywebsite.com', // the dot at the beginning allows compatibility with subdomains
173
            'secure' => ((empty($dolibarr_main_force_https) && isHTTPS() === false) ? false : true),
174
            'httponly' => true,
175
            'samesite' => 'Lax' // None || Lax  || Strict
176
        );
177
        session_set_cookie_params($sessioncookieparams);
178
    }
179
    session_name($sessionname);
180
    dol_session_start();    // This call the open and read of session handler
181
    //exit; // this exist generates a call to write and close
182
}
183
184
185
// Init the 6 global objects, this include will make the 'new Xxx()' and set properties for: $conf, $db, $langs, $user, $mysoc, $hookmanager
186
require_once 'master.inc.php';
187
188
// Uncomment this and set session.save_handler = user to use local session storing
189
// include DOL_DOCUMENT_ROOT.'/core/lib/phpsessionindb.inc.php
190
191
// If software has been locked. Only login $conf->global->MAIN_ONLY_LOGIN_ALLOWED is allowed.
192
if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
193
    $ok = 0;
194
    if ((!session_id() || !isset($_SESSION["dol_login"])) && !isset($_POST["username"]) && !empty($_SERVER["GATEWAY_INTERFACE"])) {
195
        $ok = 1; // We let working pages if not logged and inside a web browser (login form, to allow login by admin)
196
    } elseif (isset($_POST["username"]) && $_POST["username"] == $conf->global->MAIN_ONLY_LOGIN_ALLOWED) {
197
        $ok = 1; // We let working pages that is a login submission (login submit, to allow login by admin)
198
    } elseif (defined('NOREQUIREDB')) {
199
        $ok = 1; // We let working pages that don't need database access (xxx.css.php)
200
    } elseif (defined('EVEN_IF_ONLY_LOGIN_ALLOWED')) {
201
        $ok = 1; // We let working pages that ask to work even if only login enabled (logout.php)
202
    } elseif (session_id() && isset($_SESSION["dol_login"]) && $_SESSION["dol_login"] == $conf->global->MAIN_ONLY_LOGIN_ALLOWED) {
203
        $ok = 1; // We let working if user is allowed admin
204
    }
205
    if (!$ok) {
206
        if (session_id() && isset($_SESSION["dol_login"]) && $_SESSION["dol_login"] != $conf->global->MAIN_ONLY_LOGIN_ALLOWED) {
207
            print 'Sorry, your application is offline.' . "\n";
208
            print 'You are logged with user "' . $_SESSION["dol_login"] . '" and only administrator user "' . getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED') . '" is allowed to connect for the moment.' . "\n";
209
            $nexturl = constant('BASE_URL') . '/user/logout.php?token=' . newToken();
210
            print 'Please try later or <a href="' . $nexturl . '">click here to disconnect and change login user</a>...' . "\n";
211
        } else {
212
            print 'Sorry, your application is offline. Only administrator user "' . getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED') . '" is allowed to connect for the moment.' . "\n";
213
            $nexturl = constant('BASE_URL') . '/';
214
            print 'Please try later or <a href="' . $nexturl . '">click here to change login user</a>...' . "\n";
215
        }
216
        exit;
217
    }
218
}
219
220
221
// Activate end of page function
222
register_shutdown_function('dol_shutdown');
223
224
// Load debugbar
225
if (isModEnabled('debugbar') && !GETPOST('dol_use_jmobile') && empty($_SESSION['dol_use_jmobile'])) {
226
    global $debugbar;
227
228
    $debugbar = new Debug();
229
    $renderer = $debugbar->getJavascriptRenderer();
230
    if (!getDolGlobalString('MAIN_HTML_HEADER')) {
231
        $conf->global->MAIN_HTML_HEADER = '';
232
    }
233
    $conf->global->MAIN_HTML_HEADER .= $renderer->renderHead();
234
235
    $debugbar['time']->startMeasure('pageaftermaster', 'Page generation (after environment init)');
0 ignored issues
show
Bug introduced by
The method startMeasure() does not exist on DebugBar\DataCollector\DataCollectorInterface. It seems like you code against a sub-type of DebugBar\DataCollector\DataCollectorInterface such as DebugBar\DataCollector\TimeDataCollector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

235
    $debugbar['time']->/** @scrutinizer ignore-call */ 
236
                       startMeasure('pageaftermaster', 'Page generation (after environment init)');
Loading history...
236
}
237
238
// Detection browser
239
if (isset($_SERVER["HTTP_USER_AGENT"])) {
240
    $tmp = getBrowserInfo($_SERVER["HTTP_USER_AGENT"]);
241
    $conf->browser->name = $tmp['browsername'];
242
    $conf->browser->os = $tmp['browseros'];
243
    $conf->browser->version = $tmp['browserversion'];
244
    $conf->browser->ua = $tmp['browserua'];
245
    $conf->browser->layout = $tmp['layout']; // 'classic', 'phone', 'tablet'
246
    //var_dump($conf->browser);
247
248
    if ($conf->browser->layout == 'phone') {
249
        $conf->dol_no_mouse_hover = 1;
250
    }
251
}
252
253
// If theme is forced
254
if (GETPOST('theme', 'aZ09')) {
255
    $conf->theme = GETPOST('theme', 'aZ09');
256
    $conf->css = "/theme/" . $conf->theme . "/style.css.php";
257
}
258
259
// Set global MAIN_OPTIMIZEFORTEXTBROWSER (must be before login part)
260
if (GETPOSTINT('textbrowser') || (!empty($conf->browser->name) && $conf->browser->name == 'lynxlinks')) {   // If we must enable text browser
261
    $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = 2;
262
}
263
264
// Force HTTPS if required ($conf->file->main_force_https is 0/1 or 'https dolibarr root url')
265
// $_SERVER["HTTPS"] is 'on' when link is https, otherwise $_SERVER["HTTPS"] is empty or 'off'
266
if (!empty($conf->file->main_force_https) && !isHTTPS() && !defined('NOHTTPSREDIRECT')) {
267
    $newurl = '';
268
    if (is_numeric($conf->file->main_force_https)) {
269
        if ($conf->file->main_force_https == '1' && !empty($_SERVER["SCRIPT_URI"])) {   // If SCRIPT_URI supported by server
270
            if (preg_match('/^http:/i', $_SERVER["SCRIPT_URI"]) && !preg_match('/^https:/i', $_SERVER["SCRIPT_URI"])) { // If link is http
271
                $newurl = preg_replace('/^http:/i', 'https:', $_SERVER["SCRIPT_URI"]);
272
            }
273
        } else {
274
            // If HTTPS is not defined in DOL_MAIN_URL_ROOT,
275
            // Check HTTPS environment variable (Apache/mod_ssl only)
276
            $newurl = preg_replace('/^http:/i', 'https:', DOL_MAIN_URL_ROOT) . $_SERVER["REQUEST_URI"];
277
        }
278
    } else {
279
        // Check HTTPS environment variable (Apache/mod_ssl only)
280
        $newurl = $conf->file->main_force_https . $_SERVER["REQUEST_URI"];
281
    }
282
    // Start redirect
283
    if ($newurl) {
284
        header_remove(); // Clean header already set to be sure to remove any header like "Set-Cookie: DOLSESSID_..." from non HTTPS answers
285
        dol_syslog("main.inc: dolibarr_main_force_https is on, we make a redirect to " . $newurl);
286
        header("Location: " . $newurl);
287
        exit;
288
    } else {
289
        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);
290
    }
291
}
292
293
if (!defined('NOLOGIN') && !defined('NOIPCHECK') && !empty($dolibarr_main_restrict_ip)) {
294
    $listofip = explode(',', $dolibarr_main_restrict_ip);
295
    $found = false;
296
    foreach ($listofip as $ip) {
297
        $ip = trim($ip);
298
        if ($ip == $_SERVER['REMOTE_ADDR']) {
299
            $found = true;
300
            break;
301
        }
302
    }
303
    if (!$found) {
304
        print 'Access refused by IP protection. Your detected IP is ' . $_SERVER['REMOTE_ADDR'];
305
        exit;
306
    }
307
}
308
309
// Loading of additional presentation includes
310
if (!defined('NOREQUIREAJAX')) {
311
    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/ajax.lib.php'; // Need 22ko memory
312
}
313
314
// If install or upgrade process not done or not completely finished, we call the install page.
315
if (getDolGlobalString('MAIN_NOT_INSTALLED') || getDolGlobalString('MAIN_NOT_UPGRADED')) {
316
    dol_syslog("main.inc: A previous install or upgrade was not complete. Redirect to install page.", LOG_WARNING);
317
    header("Location: " . constant('BASE_URL') . "/install/index.php");
318
    exit;
319
}
320
// If an upgrade process is required, we call the install page.
321
$checkifupgraderequired = false;
322
if (getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') && getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') != DOL_VERSION) {
323
    $checkifupgraderequired = true;
324
}
325
if (!getDolGlobalString('MAIN_VERSION_LAST_UPGRADE') && getDolGlobalString('MAIN_VERSION_LAST_INSTALL') && getDolGlobalString('MAIN_VERSION_LAST_INSTALL') != DOL_VERSION) {
326
    $checkifupgraderequired = true;
327
}
328
if ($checkifupgraderequired) {
329
    $versiontocompare = getDolGlobalString('MAIN_VERSION_LAST_UPGRADE', getDolGlobalString('MAIN_VERSION_LAST_INSTALL'));
330
    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/admin.lib.php';
331
    $dolibarrversionlastupgrade = preg_split('/[.-]/', $versiontocompare);
332
    $dolibarrversionprogram = preg_split('/[.-]/', DOL_VERSION);
333
    $rescomp = versioncompare($dolibarrversionprogram, $dolibarrversionlastupgrade);
334
    if ($rescomp > 0) {   // Programs have a version higher than database.
335
        if (!getDolGlobalString('MAIN_NO_UPGRADE_REDIRECT_ON_LEVEL_3_CHANGE') || $rescomp < 3) {
336
            // We did not add "&& $rescomp < 3" because we want upgrade process for build upgrades
337
            dol_syslog("main.inc: database version " . $versiontocompare . " is lower than programs version " . DOL_VERSION . ". Redirect to install/upgrade page.", LOG_WARNING);
338
            if (php_sapi_name() === "cli") {
339
                print "main.inc: database version " . $versiontocompare . " is lower than programs version " . DOL_VERSION . ". Try to run upgrade process.\n";
340
            } else {
341
                header("Location: " . constant('BASE_URL') . "/install/index.php");
342
            }
343
            exit;
344
        }
345
    }
346
}
347
348
// Creation of a token against CSRF vulnerabilities
349
if (!defined('NOTOKENRENEWAL') && !defined('NOSESSION')) {
350
    // No token renewal on .css.php, .js.php and .json.php (even if the NOTOKENRENEWAL was not provided)
351
    if (!preg_match('/\.(css|js|json)\.php$/', $_SERVER["PHP_SELF"])) {
352
        // Rolling token at each call ($_SESSION['token'] contains token of previous page)
353
        if (isset($_SESSION['newtoken'])) {
354
            $_SESSION['token'] = $_SESSION['newtoken'];
355
        }
356
357
        if (!isset($_SESSION['newtoken']) || getDolGlobalInt('MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL')) {
358
            // Note: Using MAIN_SECURITY_CSRF_TOKEN_RENEWAL_ON_EACH_CALL is not recommended: if a user succeed in entering a data from
359
            // a public page with a link that make a token regeneration, it can make use of the backoffice no more possible !
360
            // Save in $_SESSION['newtoken'] what will be next token. Into forms, we will add param token = $_SESSION['newtoken']
361
            $token = dol_hash(uniqid((string)mt_rand(), false), 'md5'); // Generates a hash of a random number. We don't need a secured hash, just a changing random value.
362
            $_SESSION['newtoken'] = $token;
363
            dol_syslog("NEW TOKEN generated by : " . $_SERVER['PHP_SELF'], LOG_DEBUG);
364
        }
365
    }
366
}
367
368
//dol_syslog("CSRF info: ".defined('NOCSRFCHECK')." - ".$dolibarr_nocsrfcheck." - ".$conf->global->MAIN_SECURITY_CSRF_WITH_TOKEN." - ".$_SERVER['REQUEST_METHOD']." - ".GETPOST('token', 'alpha'));
369
370
// Check validity of token, only if option MAIN_SECURITY_CSRF_WITH_TOKEN enabled or if constant CSRFCHECK_WITH_TOKEN is set into page
371
if ((!defined('NOCSRFCHECK') && empty($dolibarr_nocsrfcheck) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) || defined('CSRFCHECK_WITH_TOKEN')) {
372
    // Array of action code where CSRFCHECK with token will be forced (so token must be provided on url request)
373
    $sensitiveget = false;
374
    if ((GETPOSTISSET('massaction') || GETPOST('action', 'aZ09')) && getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 3) {
375
        // 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.
376
        if (GETPOSTISSET('massaction') || !in_array(GETPOST('action', 'aZ09'), array('create', 'createsite', 'createcard', 'edit', 'editvalidator', 'file_manager', 'presend', 'presend_addmessage', 'preview', 'specimen'))) { // We exclude some action that are not sensitive so legitimate
377
            $sensitiveget = true;
378
        }
379
    } elseif (getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN') >= 2) {
380
        // Few GET actions coded with a &token into url are also processed as sensitive.
381
        $arrayofactiontoforcetokencheck = array(
382
            'activate',
383
            'doprev', 'donext', 'dvprev', 'dvnext',
384
            'freezone', 'install',
385
            'reopen'
386
        );
387
        if (in_array(GETPOST('action', 'aZ09'), $arrayofactiontoforcetokencheck)) {
388
            $sensitiveget = true;
389
        }
390
        // We also need a valid token for actions matching one of these values
391
        if (preg_match('/^(confirm_)?(add|classify|close|confirm|copy|del|disable|enable|remove|set|unset|update|save)/', GETPOST('action', 'aZ09'))) {
392
            $sensitiveget = true;
393
        }
394
    }
395
396
    // Check a token is provided for all cases that need a mandatory token
397
    // (all POST actions + all sensitive GET actions + all mass actions + all login/actions/logout on pages with CSRFCHECK_WITH_TOKEN set)
398
    if (
399
        (!empty($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') ||
400
        $sensitiveget ||
401
        GETPOSTISSET('massaction') ||
402
        ((GETPOSTISSET('actionlogin') || GETPOSTISSET('action')) && defined('CSRFCHECK_WITH_TOKEN'))
403
    ) {
404
        // If token is not provided or empty, error (we are in case it is mandatory)
405
        if (!GETPOST('token', 'alpha') || GETPOST('token', 'alpha') == 'notrequired') {
406
            top_httphead();
407
            if (GETPOSTINT('uploadform')) {
408
                dol_syslog("--- Access to " . (empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"] . ' ') . $_SERVER["PHP_SELF"] . " refused. File size too large or not provided.");
409
                $langs->loadLangs(array("errors", "install"));
410
                print $langs->trans("ErrorFileSizeTooLarge") . ' ';
411
                print $langs->trans("ErrorGoBackAndCorrectParameters");
412
            } else {
413
                http_response_code(403);
414
                if (defined('CSRFCHECK_WITH_TOKEN')) {
415
                    dd(['debug_backtrace in CSRF error in main.inc.php ' => debug_backtrace()]);
416
                    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);
417
                    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";
418
                } else {
419
                    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);
420
                    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";
421
                    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";
422
                    if (getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN')) {
423
                        print " instead of " . getDolGlobalString('MAIN_SECURITY_CSRF_WITH_TOKEN');
424
                    }
425
                    print " into setup).\n";
426
                }
427
            }
428
            die;
429
        }
430
    }
431
432
    $sessiontokenforthisurl = (empty($_SESSION['token']) ? '' : $_SESSION['token']);
433
    // 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)
434
    if (GETPOSTISSET('token') && GETPOST('token') != 'notrequired' && GETPOST('token', 'alpha') != $sessiontokenforthisurl) {
435
        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);
436
        //dol_syslog("_SESSION['token']=".$sessiontokenforthisurl, LOG_DEBUG);
437
        // 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.
438
        if (!defined('NOTOKENRENEWAL')) {
439
            // If the page is not a page that disable the token renewal, we report a warning message to explain token has epired.
440
            setEventMessages('SecurityTokenHasExpiredSoActionHasBeenCanceledPleaseRetry', null, 'warnings', '', 1);
441
        }
442
        $savid = null;
443
        if (isset($_POST['id'])) {
444
            $savid = ((int)$_POST['id']);
445
        }
446
        unset($_POST);
447
        unset($_GET['confirm']);
448
        unset($_GET['action']);
449
        unset($_GET['confirmmassaction']);
450
        unset($_GET['massaction']);
451
        unset($_GET['token']);          // TODO Make a redirect if we have a token in url to remove it ?
452
        if (isset($savid)) {
453
            $_POST['id'] = ((int)$savid);
454
        }
455
        // So rest of code can know something was wrong here
456
        $_GET['errorcode'] = 'InvalidToken';
457
    }
458
459
    // Note: There is another CSRF protection into the filefunc.inc.php
460
}
461
462
// Disable modules (this must be after session_start and after conf has been loaded)
463
if (GETPOSTISSET('disablemodules')) {
464
    $_SESSION["disablemodules"] = GETPOST('disablemodules', 'alpha');
465
}
466
if (!empty($_SESSION["disablemodules"])) {
467
    $modulepartkeys = array('css', 'js', 'tabs', 'triggers', 'login', 'substitutions', 'menus', 'theme', 'sms', 'tpl', 'barcode', 'models', 'societe', 'hooks', 'dir', 'syslog', 'tpllinkable', 'contactelement', 'moduleforexternal', 'websitetemplates');
468
469
    $disabled_modules = explode(',', $_SESSION["disablemodules"]);
470
    foreach ($disabled_modules as $module) {
471
        if ($module) {
472
            if (empty($conf->$module)) {
473
                $conf->$module = new stdClass(); // To avoid warnings
474
            }
475
            $conf->$module->enabled = false;
476
            foreach ($modulepartkeys as $modulepartkey) {
477
                unset($conf->modules_parts[$modulepartkey][$module]);
478
            }
479
            if ($module == 'fournisseur') {     // Special case
480
                $conf->supplier_order->enabled = 0;
481
                $conf->supplier_invoice->enabled = 0;
482
            }
483
        }
484
    }
485
}
486
487
// Set current modulepart
488
$modulepart = explode("/", $_SERVER["PHP_SELF"]);
489
if (is_array($modulepart) && count($modulepart) > 0) {
490
    foreach ($conf->modules as $module) {
491
        if (in_array($module, $modulepart)) {
492
            $modulepart = $module;
493
            break;
494
        }
495
    }
496
}
497
if (is_array($modulepart)) {
498
    $modulepart = '';
499
}
500
501
502
/*
503
 * Phase authentication / login
504
 */
505
506
$login = '';
507
$error = 0;
508
if (!defined('NOLOGIN')) {
509
    // $authmode lists the different method of identification to be tested in order of preference.
510
    // Example: 'http', 'dolibarr', 'ldap', 'http,forceuser', '...'
511
512
    if (defined('MAIN_AUTHENTICATION_MODE')) {
513
        $dolibarr_main_authentication = constant('MAIN_AUTHENTICATION_MODE');
514
    } else {
515
        // Authentication mode
516
        if (empty($dolibarr_main_authentication)) {
517
            $dolibarr_main_authentication = 'dolibarr';
518
        }
519
        // Authentication mode: forceuser
520
        if ($dolibarr_main_authentication == 'forceuser' && empty($dolibarr_auto_user)) {
521
            $dolibarr_auto_user = 'auto';
522
        }
523
    }
524
    // Set authmode
525
    $authmode = explode(',', $dolibarr_main_authentication);
526
527
    // No authentication mode
528
    if (!count($authmode)) {
529
        $langs->load('main');
530
        dol_print_error(null, $langs->trans("ErrorConfigParameterNotDefined", 'dolibarr_main_authentication'));
531
        exit;
532
    }
533
534
    // If login request was already post, we retrieve login from the session
535
    // Call module if not realized that his request.
536
    // At the end of this phase, the variable $login is defined.
537
    $resultFetchUser = '';
538
    $test = true;
539
    if (!isset($_SESSION["dol_login"])) {
540
        // It is not already authenticated and it requests the login / password
541
        include_once DOL_DOCUMENT_ROOT . '/core/lib/security2.lib.php';
542
543
        $dol_dst_observed = GETPOSTINT("dst_observed", 3);
544
        $dol_dst_first = GETPOSTINT("dst_first", 3);
545
        $dol_dst_second = GETPOSTINT("dst_second", 3);
546
        $dol_screenwidth = GETPOSTINT("screenwidth", 3);
547
        $dol_screenheight = GETPOSTINT("screenheight", 3);
548
        $dol_hide_topmenu = GETPOSTINT('dol_hide_topmenu', 3);
549
        $dol_hide_leftmenu = GETPOSTINT('dol_hide_leftmenu', 3);
550
        $dol_optimize_smallscreen = GETPOSTINT('dol_optimize_smallscreen', 3);
551
        $dol_no_mouse_hover = GETPOSTINT('dol_no_mouse_hover', 3);
552
        $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
553
554
        // If in demo mode, we check we go to home page through the public/demo/index.php page
555
        if (!empty($dolibarr_main_demo) && $_SERVER['PHP_SELF'] == constant('BASE_URL') . '/index.php') {  // We ask index page
556
            if (empty($_SERVER['HTTP_REFERER']) || !preg_match('/public/', $_SERVER['HTTP_REFERER'])) {
557
                dol_syslog("Call index page from another url than demo page (call is done from page " . (empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFER']) . ")");
558
                $url = '';
559
                $url .= ($url ? '&' : '') . ($dol_hide_topmenu ? 'dol_hide_topmenu=' . $dol_hide_topmenu : '');
560
                $url .= ($url ? '&' : '') . ($dol_hide_leftmenu ? 'dol_hide_leftmenu=' . $dol_hide_leftmenu : '');
561
                $url .= ($url ? '&' : '') . ($dol_optimize_smallscreen ? 'dol_optimize_smallscreen=' . $dol_optimize_smallscreen : '');
562
                $url .= ($url ? '&' : '') . ($dol_no_mouse_hover ? 'dol_no_mouse_hover=' . $dol_no_mouse_hover : '');
563
                $url .= ($url ? '&' : '') . ($dol_use_jmobile ? 'dol_use_jmobile=' . $dol_use_jmobile : '');
564
                $url = constant('BASE_URL') . '/public/demo/index.php' . ($url ? '?' . $url : '');
565
                header("Location: " . $url);
566
                exit;
567
            }
568
        }
569
570
        // Hooks for security access
571
        $action = '';
572
        $hookmanager->initHooks(array('login'));
573
        $parameters = array();
574
        $reshook = $hookmanager->executeHooks('beforeLoginAuthentication', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
575
        if ($reshook < 0) {
576
            $test = false;
577
            $error++;
578
        }
579
580
        // Verification security graphic code
581
        if ($test && GETPOST("username", "alpha", 2) && getDolGlobalString('MAIN_SECURITY_ENABLECAPTCHA') && !isset($_SESSION['dol_bypass_antispam'])) {
582
            $sessionkey = 'dol_antispam_value';
583
            $ok = (array_key_exists($sessionkey, $_SESSION) === true && (strtolower($_SESSION[$sessionkey]) === strtolower(GETPOST('code', 'restricthtml'))));
584
585
            // Check code
586
            if (!$ok) {
587
                dol_syslog('Bad value for code, connection refused', LOG_NOTICE);
588
                // Load translation files required by page
589
                $langs->loadLangs(array('main', 'errors'));
590
591
                $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadValueForCode");
592
                $test = false;
593
594
                // Call trigger for the "security events" log
595
                $user->context['audit'] = 'ErrorBadValueForCode - login=' . GETPOST("username", "alpha", 2);
596
597
                // Call trigger
598
                $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
599
                if ($result < 0) {
600
                    $error++;
601
                }
602
                // End call triggers
603
604
                // Hooks on failed login
605
                $action = '';
606
                $hookmanager->initHooks(array('login'));
607
                $parameters = array('dol_authmode' => $authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
608
                $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
609
                if ($reshook < 0) {
610
                    $error++;
611
                }
612
613
                // Note: exit is done later
614
            }
615
        }
616
617
        $allowedmethodtopostusername = 3;
618
        if (defined('MAIN_AUTHENTICATION_POST_METHOD')) {
619
            $allowedmethodtopostusername = constant('MAIN_AUTHENTICATION_POST_METHOD'); // Note a value of 2 is not compatible with some authentication methods that put username as GET parameter
620
        }
621
        // TODO Remove use of $_COOKIE['login_dolibarr'] ? Replace $usertotest = with $usertotest = GETPOST("username", "alpha", $allowedmethodtopostusername);
622
        $usertotest = (!empty($_COOKIE['login_dolibarr']) ? preg_replace('/[^a-zA-Z0-9_@\-\.]/', '', $_COOKIE['login_dolibarr']) : GETPOST("username", "alpha", $allowedmethodtopostusername));
623
        $passwordtotest = GETPOST('password', 'none', $allowedmethodtopostusername);
624
        $entitytotest = (GETPOSTINT('entity') ? GETPOSTINT('entity') : (!empty($conf->entity) ? $conf->entity : 1));
625
626
        // Define if we received the correct data to go into the test of the login with the checkLoginPassEntity().
627
        $goontestloop = false;
628
        if (isset($_SERVER["REMOTE_USER"]) && in_array('http', $authmode)) {    // For http basic login test
629
            $goontestloop = true;
630
        }
631
        if ($dolibarr_main_authentication == 'forceuser' && !empty($dolibarr_auto_user)) {  // For automatic login with a forced user
632
            $goontestloop = true;
633
        }
634
        if (GETPOST("username", "alpha", $allowedmethodtopostusername)) {   // For posting the login form
635
            $goontestloop = true;
636
        }
637
        if (GETPOST('openid_mode', 'alpha', 1)) {   // For openid_connect ?
638
            $goontestloop = true;
639
        }
640
        if (GETPOST('beforeoauthloginredirect') || GETPOST('afteroauthloginreturn')) {  // For oauth login
641
            $goontestloop = true;
642
        }
643
        if (!empty($_COOKIE['login_dolibarr'])) {   // TODO For ? Remove this ?
644
            $goontestloop = true;
645
        }
646
647
        if (!is_object($langs)) { // This can occurs when calling page with NOREQUIRETRAN defined, however we need langs for error messages.
648
            $langs = new Translate("", $conf);
649
            $langcode = (GETPOST('lang', 'aZ09', 1) ? GETPOST('lang', 'aZ09', 1) : getDolGlobalString('MAIN_LANG_DEFAULT', 'auto'));
650
            if (defined('MAIN_LANG_DEFAULT')) {
651
                $langcode = constant('MAIN_LANG_DEFAULT');
652
            }
653
            $langs->setDefaultLang($langcode);
654
        }
655
656
        // Validation of login/pass/entity
657
        // If ok, the variable login will be returned
658
        // If error, we will put error message in session under the name dol_loginmesg
659
        if ($test && $goontestloop && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {
660
            // Loop on each test mode defined into $authmode
661
            // $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');
662
            $oauthmodetotestarray = array('google');
663
            foreach ($oauthmodetotestarray as $oauthmodetotest) {
664
                if (in_array($oauthmodetotest . 'oauth', $authmode)) {    // This is an authmode that is currently qualified. Do we have to remove it ?
665
                    // If we click on the link to use OAuth authentication or if we goes after callback return, we do nothing
666
                    if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn')) {
667
                        // TODO Use: if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {
668
                        continue;
669
                    }
670
                    dol_syslog("User did not click on link for OAuth or is not on the OAuth return, so we disable check using " . $oauthmodetotest);
671
                    foreach ($authmode as $tmpkey => $tmpval) {
672
                        if ($tmpval == $oauthmodetotest . 'oauth') {
673
                            unset($authmode[$tmpkey]);
674
                            break;
675
                        }
676
                    }
677
                }
678
            }
679
680
            // Check login for all qualified modes in array $authmode.
681
            $login = checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode);
682
            if ($login === '--bad-login-validity--') {
683
                $login = '';
684
            }
685
686
            $dol_authmode = '';
687
688
            if ($login) {
689
                $dol_authmode = $conf->authmode; // This properties is defined only when logged, to say what mode was successfully used
690
                $dol_tz = empty($_POST["tz"]) ? (empty($_SESSION["tz"]) ? '' : $_SESSION["tz"]) : $_POST["tz"];
691
                $dol_tz_string = empty($_POST["tz_string"]) ? (empty($_SESSION["tz_string"]) ? '' : $_SESSION["tz_string"]) : $_POST["tz_string"];
692
                $dol_tz_string = preg_replace('/\s*\(.+\)$/', '', $dol_tz_string);
693
                $dol_tz_string = preg_replace('/,/', '/', $dol_tz_string);
694
                $dol_tz_string = preg_replace('/\s/', '_', $dol_tz_string);
695
                $dol_dst = 0;
696
                // Keep $_POST here. Do not use GETPOSTISSET
697
                $dol_dst_first = empty($_POST["dst_first"]) ? (empty($_SESSION["dst_first"]) ? '' : $_SESSION["dst_first"]) : $_POST["dst_first"];
698
                $dol_dst_second = empty($_POST["dst_second"]) ? (empty($_SESSION["dst_second"]) ? '' : $_SESSION["dst_second"]) : $_POST["dst_second"];
699
                if ($dol_dst_first && $dol_dst_second) {
700
                    include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
701
                    $datenow = dol_now();
702
                    $datefirst = dol_stringtotime($dol_dst_first);
703
                    $datesecond = dol_stringtotime($dol_dst_second);
704
                    if ($datenow >= $datefirst && $datenow < $datesecond) {
705
                        $dol_dst = 1;
706
                    }
707
                }
708
                $dol_screenheight = empty($_POST["screenheight"]) ? (empty($_SESSION["dol_screenheight"]) ? '' : $_SESSION["dol_screenheight"]) : $_POST["screenheight"];
709
                $dol_screenwidth = empty($_POST["screenwidth"]) ? (empty($_SESSION["dol_screenwidth"]) ? '' : $_SESSION["dol_screenwidth"]) : $_POST["screenwidth"];
710
                //print $datefirst.'-'.$datesecond.'-'.$datenow.'-'.$dol_tz.'-'.$dol_tzstring.'-'.$dol_dst.'-'.sdol_screenheight.'-'.sdol_screenwidth; exit;
711
            }
712
713
            if (!$login) {
714
                dol_syslog('Bad password, connection refused (see a previous notice message for more info)', LOG_NOTICE);
715
                // Load translation files required by page
716
                $langs->loadLangs(array('main', 'errors'));
717
718
                // Bad password. No authmode has found a good password.
719
                // We set a generic message if not defined inside function checkLoginPassEntity or subfunctions
720
                if (empty($_SESSION["dol_loginmesg"])) {
721
                    $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorBadLoginPassword");
722
                }
723
724
                // Call trigger for the "security events" log
725
                $user->context['audit'] = $langs->trans("ErrorBadLoginPassword") . ' - login=' . GETPOST("username", "alpha", 2);
726
727
                // Call trigger
728
                $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
729
                if ($result < 0) {
730
                    $error++;
731
                }
732
                // End call triggers
733
734
                // Hooks on failed login
735
                $action = '';
736
                $hookmanager->initHooks(array('login'));
737
                $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
738
                $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
739
                if ($reshook < 0) {
740
                    $error++;
741
                }
742
743
                // Note: exit is done in next chapter
744
            }
745
        }
746
747
        // End test login / passwords
748
        if (!$login || (in_array('ldap', $authmode) && empty($passwordtotest))) {   // With LDAP we refused empty password because some LDAP are "opened" for anonymous access so connection is a success.
749
            // No data to test login, so we show the login page.
750
            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);
751
            if (defined('NOREDIRECTBYMAINTOLOGIN')) {
752
                // When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
753
                // See example with selectsearchbox.php. This case is reserved for the selectesearchbox.php so we can
754
                // report a message to ask to login when search ajax component is used after a timeout.
755
                //top_httphead();
756
                return 'ERROR_NOT_LOGGED';
757
            } else {
758
                if (!empty($_SERVER["HTTP_USER_AGENT"]) && $_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
759
                    http_response_code(401); // It makes easier to understand if session was broken during security tests
760
                }
761
                dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : ''));   // This include http headers
762
            }
763
            exit;
764
        }
765
766
        $resultFetchUser = $user->fetch('', $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.
767
        if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
768
            dol_syslog('User not found or not valid, connection refused');
769
            session_destroy();
770
            session_set_cookie_params(0, '/', null, (empty($dolibarr_main_force_https) ? false : true), true); // Add tag secure and httponly on session cookie
771
            session_name($sessionname);
772
            dol_session_start();
773
774
            if ($resultFetchUser == 0) {
775
                // Load translation files required by page
776
                $langs->loadLangs(array('main', 'errors'));
777
778
                $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
779
780
                $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login=' . $login;
781
            } elseif ($resultFetchUser < 0) {
782
                $_SESSION["dol_loginmesg"] = $user->error;
783
784
                $user->context['audit'] = $user->error;
785
            } else {
786
                // Load translation files required by the page
787
                $langs->loadLangs(array('main', 'errors'));
788
789
                $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginDateValidity");
790
791
                $user->context['audit'] = $langs->trans("ErrorLoginDateValidity") . ' - login=' . $login;
792
            }
793
794
            // Call trigger
795
            $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
796
            if ($result < 0) {
797
                $error++;
798
            }
799
            // End call triggers
800
801
802
            // Hooks on failed login
803
            $action = '';
804
            $hookmanager->initHooks(array('login'));
805
            $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
806
            $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
807
            if ($reshook < 0) {
808
                $error++;
809
            }
810
811
            $paramsurl = array();
812
            if (GETPOSTINT('textbrowser')) {
813
                $paramsurl[] = 'textbrowser=' . GETPOSTINT('textbrowser');
814
            }
815
            if (GETPOSTINT('nojs')) {
816
                $paramsurl[] = 'nojs=' . GETPOSTINT('nojs');
817
            }
818
            if (GETPOST('lang', 'aZ09')) {
819
                $paramsurl[] = 'lang=' . GETPOST('lang', 'aZ09');
820
            }
821
            header('Location: ' . constant('BASE_URL') . '/index.php' . (count($paramsurl) ? '?' . implode('&', $paramsurl) : ''));
822
            exit;
823
        } else {
824
            // User is loaded, we may need to change language for him according to its choice
825
            if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
826
                $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
827
            }
828
        }
829
    } else {
830
        // We are already into an authenticated session
831
        $login = $_SESSION["dol_login"];
832
        $entity = isset($_SESSION["dol_entity"]) ? $_SESSION["dol_entity"] : 0;
833
        dol_syslog("- This is an already logged session. _SESSION['dol_login']=" . $login . " _SESSION['dol_entity']=" . $entity, LOG_DEBUG);
834
835
        $resultFetchUser = $user->fetch('', $login, '', 1, ($entity > 0 ? $entity : -1));
836
837
        //var_dump(dol_print_date($user->flagdelsessionsbefore, 'dayhour', 'gmt')." ".dol_print_date($_SESSION["dol_logindate"], 'dayhour', 'gmt'));
838
839
        if (
840
            $resultFetchUser <= 0
841
            || ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"])
842
            || ($user->status != $user::STATUS_ENABLED)
843
            || ($user->isNotIntoValidityDateRange())
844
        ) {
845
            if ($resultFetchUser <= 0) {
846
                // Account has been removed after login
847
                dol_syslog("Can't load user even if session logged. _SESSION['dol_login']=" . $login, LOG_WARNING);
848
            } elseif ($user->flagdelsessionsbefore && !empty($_SESSION["dol_logindate"]) && $user->flagdelsessionsbefore > $_SESSION["dol_logindate"]) {
849
                // Session is no more valid
850
                dol_syslog("The user has a date for session invalidation = " . $user->flagdelsessionsbefore . " and a session date = " . $_SESSION["dol_logindate"] . ". We must invalidate its sessions.");
851
            } elseif ($user->status != $user::STATUS_ENABLED) {
852
                // User is not enabled
853
                dol_syslog("The user login is disabled");
854
            } else {
855
                // User validity dates are no more valid
856
                dol_syslog("The user login has a validity between [" . $user->datestartvalidity . " and " . $user->dateendvalidity . "], current date is " . dol_now());
857
            }
858
            session_destroy();
859
            session_set_cookie_params(0, '/', null, (empty($dolibarr_main_force_https) ? false : true), true); // Add tag secure and httponly on session cookie
860
            session_name($sessionname);
861
            dol_session_start();
862
863
            if ($resultFetchUser == 0) {
864
                $langs->loadLangs(array('main', 'errors'));
865
866
                $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorCantLoadUserFromDolibarrDatabase", $login);
867
868
                $user->context['audit'] = 'ErrorCantLoadUserFromDolibarrDatabase - login=' . $login;
869
            } elseif ($resultFetchUser < 0) {
870
                $_SESSION["dol_loginmesg"] = $user->error;
871
872
                $user->context['audit'] = $user->error;
873
            } else {
874
                $langs->loadLangs(array('main', 'errors'));
875
876
                $_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorSessionInvalidatedAfterPasswordChange");
877
878
                $user->context['audit'] = 'ErrorUserSessionWasInvalidated - login=' . $login;
879
            }
880
881
            // Call trigger
882
            $result = $user->call_trigger('USER_LOGIN_FAILED', $user);
883
            if ($result < 0) {
884
                $error++;
885
            }
886
            // End call triggers
887
888
            // Hooks on failed login
889
            $action = '';
890
            $hookmanager->initHooks(array('login'));
891
            $parameters = array('dol_authmode' => (isset($dol_authmode) ? $dol_authmode : ''), 'dol_loginmesg' => $_SESSION["dol_loginmesg"]);
892
            $reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
893
            if ($reshook < 0) {
894
                $error++;
895
            }
896
897
            $paramsurl = array();
898
            if (GETPOSTINT('textbrowser')) {
899
                $paramsurl[] = 'textbrowser=' . GETPOSTINT('textbrowser');
900
            }
901
            if (GETPOSTINT('nojs')) {
902
                $paramsurl[] = 'nojs=' . GETPOSTINT('nojs');
903
            }
904
            if (GETPOST('lang', 'aZ09')) {
905
                $paramsurl[] = 'lang=' . GETPOST('lang', 'aZ09');
906
            }
907
908
            header('Location: ' . constant('BASE_URL') . '/index.php' . (count($paramsurl) ? '?' . implode('&', $paramsurl) : ''));
909
            exit;
910
        } else {
911
            // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
912
            $hookmanager->initHooks(array('main'));
913
914
            // Code for search criteria persistence.
915
            if (!empty($_GET['save_lastsearch_values']) && !empty($_SERVER["HTTP_REFERER"])) {    // We must use $_GET here
916
                $relativepathstring = preg_replace('/\?.*$/', '', $_SERVER["HTTP_REFERER"]);
917
                $relativepathstring = preg_replace('/^https?:\/\/[^\/]*/', '', $relativepathstring); // Get full path except host server
918
                // Clean $relativepathstring
919
                if (constant('DOL_URL_ROOT')) {
920
                    $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
921
                }
922
                $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
923
                $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
924
                //var_dump($relativepathstring);
925
926
                // 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
927
                if (!empty($_SESSION['lastsearch_values_tmp_' . $relativepathstring])) {
928
                    $_SESSION['lastsearch_values_' . $relativepathstring] = $_SESSION['lastsearch_values_tmp_' . $relativepathstring];
929
                    unset($_SESSION['lastsearch_values_tmp_' . $relativepathstring]);
930
                }
931
                if (!empty($_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring])) {
932
                    $_SESSION['lastsearch_contextpage_' . $relativepathstring] = $_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring];
933
                    unset($_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring]);
934
                }
935
                if (!empty($_SESSION['lastsearch_limit_tmp_' . $relativepathstring]) && $_SESSION['lastsearch_limit_tmp_' . $relativepathstring] != $conf->liste_limit) {
936
                    $_SESSION['lastsearch_limit_' . $relativepathstring] = $_SESSION['lastsearch_limit_tmp_' . $relativepathstring];
937
                    unset($_SESSION['lastsearch_limit_tmp_' . $relativepathstring]);
938
                }
939
                if (!empty($_SESSION['lastsearch_page_tmp_' . $relativepathstring]) && $_SESSION['lastsearch_page_tmp_' . $relativepathstring] > 0) {
940
                    $_SESSION['lastsearch_page_' . $relativepathstring] = $_SESSION['lastsearch_page_tmp_' . $relativepathstring];
941
                    unset($_SESSION['lastsearch_page_tmp_' . $relativepathstring]);
942
                }
943
                if (!empty($_SESSION['lastsearch_mode_tmp_' . $relativepathstring])) {
944
                    $_SESSION['lastsearch_mode_' . $relativepathstring] = $_SESSION['lastsearch_mode_tmp_' . $relativepathstring];
945
                    unset($_SESSION['lastsearch_mode_tmp_' . $relativepathstring]);
946
                }
947
            }
948
            if (!empty($_GET['save_pageforbacktolist']) && !empty($_SERVER["HTTP_REFERER"])) {    // We must use $_GET here
949
                if (empty($_SESSION['pageforbacktolist'])) {
950
                    $pageforbacktolistarray = array();
951
                } else {
952
                    $pageforbacktolistarray = $_SESSION['pageforbacktolist'];
953
                }
954
                $tmparray = explode(':', $_GET['save_pageforbacktolist'], 2);
955
                if (!empty($tmparray[0]) && !empty($tmparray[1])) {
956
                    $pageforbacktolistarray[$tmparray[0]] = $tmparray[1];
957
                    $_SESSION['pageforbacktolist'] = $pageforbacktolistarray;
958
                }
959
            }
960
961
            $action = '';
962
            $parameters = array();
963
            $reshook = $hookmanager->executeHooks('updateSession', $parameters, $user, $action);
964
            if ($reshook < 0) {
965
                setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
966
            }
967
        }
968
    }
969
970
    // Is it a new session that has started ?
971
    // If we are here, this means authentication was successful.
972
    if (!isset($_SESSION["dol_login"])) {
973
        // New session for this login has started.
974
        $error = 0;
975
976
        // Store value into session (values always stored)
977
        $_SESSION["dol_login"] = $user->login;
978
        $_SESSION["dol_logindate"] = dol_now('gmt');
979
        $_SESSION["dol_authmode"] = isset($dol_authmode) ? $dol_authmode : '';
980
        $_SESSION["dol_tz"] = isset($dol_tz) ? $dol_tz : '';
981
        $_SESSION["dol_tz_string"] = isset($dol_tz_string) ? $dol_tz_string : '';
982
        $_SESSION["dol_dst"] = isset($dol_dst) ? $dol_dst : '';
983
        $_SESSION["dol_dst_observed"] = isset($dol_dst_observed) ? $dol_dst_observed : '';
984
        $_SESSION["dol_dst_first"] = isset($dol_dst_first) ? $dol_dst_first : '';
985
        $_SESSION["dol_dst_second"] = isset($dol_dst_second) ? $dol_dst_second : '';
986
        $_SESSION["dol_screenwidth"] = isset($dol_screenwidth) ? $dol_screenwidth : '';
987
        $_SESSION["dol_screenheight"] = isset($dol_screenheight) ? $dol_screenheight : '';
988
        $_SESSION["dol_company"] = getDolGlobalString("MAIN_INFO_SOCIETE_NOM");
989
        $_SESSION["dol_entity"] = $conf->entity;
990
        // Store value into session (values stored only if defined)
991
        if (!empty($dol_hide_topmenu)) {
992
            $_SESSION['dol_hide_topmenu'] = $dol_hide_topmenu;
993
        }
994
        if (!empty($dol_hide_leftmenu)) {
995
            $_SESSION['dol_hide_leftmenu'] = $dol_hide_leftmenu;
996
        }
997
        if (!empty($dol_optimize_smallscreen)) {
998
            $_SESSION['dol_optimize_smallscreen'] = $dol_optimize_smallscreen;
999
        }
1000
        if (!empty($dol_no_mouse_hover)) {
1001
            $_SESSION['dol_no_mouse_hover'] = $dol_no_mouse_hover;
1002
        }
1003
        if (!empty($dol_use_jmobile)) {
1004
            $_SESSION['dol_use_jmobile'] = $dol_use_jmobile;
1005
        }
1006
1007
        dol_syslog("This is a new started user session. _SESSION['dol_login']=" . $_SESSION["dol_login"] . " Session id=" . session_id());
1008
1009
        $db->begin();
1010
1011
        $user->update_last_login_date();
1012
1013
        $loginfo = 'TZ=' . $_SESSION["dol_tz"] . ';TZString=' . $_SESSION["dol_tz_string"] . ';Screen=' . $_SESSION["dol_screenwidth"] . 'x' . $_SESSION["dol_screenheight"];
1014
        $loginfo .= ' - authmode=' . $dol_authmode . ' - entity=' . $conf->entity;
1015
1016
        // Call triggers for the "security events" log
1017
        $user->context['audit'] = $loginfo;
1018
        $user->context['authentication_method'] = $dol_authmode;
1019
1020
        // Call trigger
1021
        $result = $user->call_trigger('USER_LOGIN', $user);
1022
        if ($result < 0) {
1023
            $error++;
1024
        }
1025
        // End call triggers
1026
1027
        // Hooks on successful login
1028
        $action = '';
1029
        $hookmanager->initHooks(array('login'));
1030
        $parameters = array('dol_authmode' => $dol_authmode, 'dol_loginfo' => $loginfo);
1031
        $reshook = $hookmanager->executeHooks('afterLogin', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
1032
        if ($reshook < 0) {
1033
            $error++;
1034
        }
1035
1036
        if ($error) {
1037
            $db->rollback();
1038
            session_destroy();
1039
            dol_print_error($db, 'Error in some triggers USER_LOGIN or in some hooks afterLogin');
1040
            exit;
1041
        } else {
1042
            $db->commit();
1043
        }
1044
1045
        // Change landing page if defined.
1046
        $landingpage = (empty($user->conf->MAIN_LANDING_PAGE) ? (!getDolGlobalString('MAIN_LANDING_PAGE') ? '' : $conf->global->MAIN_LANDING_PAGE) : $user->conf->MAIN_LANDING_PAGE);
1047
        if (!empty($landingpage)) {    // Example: /index.php
1048
            $newpath = dol_buildpath($landingpage, 1);
1049
            if ($_SERVER["PHP_SELF"] != $newpath) {   // not already on landing page (avoid infinite loop)
1050
                header('Location: ' . $newpath);
1051
                exit;
1052
            }
1053
        }
1054
    }
1055
1056
1057
    // If user admin, we force the rights-based modules
1058
    if ($user->admin) {
1059
        $user->rights->user->user->lire = 1;
1060
        $user->rights->user->user->creer = 1;
1061
        $user->rights->user->user->password = 1;
1062
        $user->rights->user->user->supprimer = 1;
1063
        $user->rights->user->self->creer = 1;
1064
        $user->rights->user->self->password = 1;
1065
1066
        //Required if advanced permissions are used with MAIN_USE_ADVANCED_PERMS
1067
        if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
1068
            if (!$user->hasRight('user', 'user_advance')) {
1069
                $user->rights->user->user_advance = new stdClass(); // To avoid warnings
1070
            }
1071
            if (!$user->hasRight('user', 'self_advance')) {
1072
                $user->rights->user->self_advance = new stdClass(); // To avoid warnings
1073
            }
1074
            if (!$user->hasRight('user', 'group_advance')) {
1075
                $user->rights->user->group_advance = new stdClass(); // To avoid warnings
1076
            }
1077
1078
            $user->rights->user->user_advance->readperms = 1;
1079
            $user->rights->user->user_advance->write = 1;
1080
            $user->rights->user->self_advance->readperms = 1;
1081
            $user->rights->user->self_advance->writeperms = 1;
1082
            $user->rights->user->group_advance->read = 1;
1083
            $user->rights->user->group_advance->readperms = 1;
1084
            $user->rights->user->group_advance->write = 1;
1085
            $user->rights->user->group_advance->delete = 1;
1086
        }
1087
    }
1088
1089
    /*
1090
     * Overwrite some configs globals (try to avoid this and have code to use instead $user->conf->xxx)
1091
     */
1092
1093
    // Set liste_limit
1094
    if (isset($user->conf->MAIN_SIZE_LISTE_LIMIT)) {
1095
        $conf->liste_limit = $user->conf->MAIN_SIZE_LISTE_LIMIT; // Can be 0
1096
    }
1097
    if (isset($user->conf->PRODUIT_LIMIT_SIZE)) {
1098
        $conf->product->limit_size = $user->conf->PRODUIT_LIMIT_SIZE; // Can be 0
1099
    }
1100
1101
    // Replace conf->css by personalized value if theme not forced
1102
    if (!getDolGlobalString('MAIN_FORCETHEME') && !empty($user->conf->MAIN_THEME)) {
1103
        $conf->theme = $user->conf->MAIN_THEME;
1104
        $conf->css = "/theme/" . $conf->theme . "/style.css.php";
1105
    }
1106
} else {
1107
    // We may have NOLOGIN set, but NOREQUIREUSER not
1108
    if (!empty($user) && method_exists($user, 'loadDefaultValues') && !defined('NODEFAULTVALUES')) {
1109
        $user->loadDefaultValues();     // Load default values for everybody (works even if $user->id = 0
1110
    }
1111
}
1112
1113
1114
// Case forcing style from url
1115
if (GETPOST('theme', 'aZ09')) {
1116
    $conf->theme = GETPOST('theme', 'aZ09', 1);
1117
    $conf->css = "/theme/" . $conf->theme . "/style.css.php";
1118
}
1119
1120
// Set javascript option
1121
if (GETPOSTINT('nojs')) {  // If javascript was not disabled on URL
1122
    $conf->use_javascript_ajax = 0;
1123
} else {
1124
    if (!empty($user->conf->MAIN_DISABLE_JAVASCRIPT)) {
1125
        $conf->use_javascript_ajax = !$user->conf->MAIN_DISABLE_JAVASCRIPT;
1126
    }
1127
}
1128
1129
// Set MAIN_OPTIMIZEFORTEXTBROWSER for user (must be after login part)
1130
if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && !empty($user->conf->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1131
    $conf->global->MAIN_OPTIMIZEFORTEXTBROWSER = $user->conf->MAIN_OPTIMIZEFORTEXTBROWSER;
1132
    if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') == 1) {
1133
        $conf->global->THEME_TOPMENU_DISABLE_IMAGE = 1;
1134
    }
1135
}
1136
//var_dump($conf->global->THEME_TOPMENU_DISABLE_IMAGE);
1137
//var_dump($user->conf->THEME_TOPMENU_DISABLE_IMAGE);
1138
1139
// set MAIN_OPTIMIZEFORCOLORBLIND for user
1140
$conf->global->MAIN_OPTIMIZEFORCOLORBLIND = empty($user->conf->MAIN_OPTIMIZEFORCOLORBLIND) ? '' : $user->conf->MAIN_OPTIMIZEFORCOLORBLIND;
1141
1142
// Set terminal output option according to conf->browser.
1143
if (GETPOSTINT('dol_hide_leftmenu') || !empty($_SESSION['dol_hide_leftmenu'])) {
1144
    $conf->dol_hide_leftmenu = 1;
1145
}
1146
if (GETPOSTINT('dol_hide_topmenu') || !empty($_SESSION['dol_hide_topmenu'])) {
1147
    $conf->dol_hide_topmenu = 1;
1148
}
1149
if (GETPOSTINT('dol_optimize_smallscreen') || !empty($_SESSION['dol_optimize_smallscreen'])) {
1150
    $conf->dol_optimize_smallscreen = 1;
1151
}
1152
if (GETPOSTINT('dol_no_mouse_hover') || !empty($_SESSION['dol_no_mouse_hover'])) {
1153
    $conf->dol_no_mouse_hover = 1;
1154
}
1155
if (GETPOSTINT('dol_use_jmobile') || !empty($_SESSION['dol_use_jmobile'])) {
1156
    $conf->dol_use_jmobile = 1;
1157
}
1158
// If not on Desktop
1159
if (!empty($conf->browser->layout) && $conf->browser->layout != 'classic') {
1160
    $conf->dol_no_mouse_hover = 1;
1161
}
1162
1163
// If on smartphone or optimized for small screen
1164
if (
1165
    (!empty($conf->browser->layout) && $conf->browser->layout == 'phone')
1166
    || (!empty($_SESSION['dol_screenwidth']) && $_SESSION['dol_screenwidth'] < 400)
1167
    || (!empty($_SESSION['dol_screenheight']) && $_SESSION['dol_screenheight'] < 400
1168
        || getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER'))
1169
) {
1170
    $conf->dol_optimize_smallscreen = 1;
1171
1172
    if (getDolGlobalInt('PRODUIT_DESC_IN_FORM') == 1) {
1173
        $conf->global->PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE = 0;
1174
    }
1175
}
1176
// Replace themes bugged with jmobile with eldy
1177
if (!empty($conf->dol_use_jmobile) && in_array($conf->theme, array('bureau2crea', 'cameleo', 'amarok'))) {
1178
    $conf->theme = 'eldy';
1179
    $conf->css = "/theme/" . $conf->theme . "/style.css.php";
1180
}
1181
1182
if (!defined('NOREQUIRETRAN')) {
1183
    if (!GETPOST('lang', 'aZ09')) { // If language was not forced on URL
1184
        // If user has chosen its own language
1185
        if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
1186
            // If different than current language
1187
            //print ">>>".$langs->getDefaultLang()."-".$user->conf->MAIN_LANG_DEFAULT;
1188
            if ($langs->getDefaultLang() != $user->conf->MAIN_LANG_DEFAULT) {
1189
                $langs->setDefaultLang($user->conf->MAIN_LANG_DEFAULT);
1190
            }
1191
        }
1192
    }
1193
}
1194
1195
if (!defined('NOLOGIN')) {
1196
    // If the login is not recovered, it is identified with an account that does not exist.
1197
    // Hacking attempt?
1198
    if (!$user->login) {
1199
        accessforbidden();
1200
    }
1201
1202
    // Check if user is active
1203
    if ($user->statut < 1) {
1204
        // If not active, we refuse the user
1205
        $langs->loadLangs(array("errors", "other"));
1206
        dol_syslog("Authentication KO as login is disabled", LOG_NOTICE);
1207
        accessforbidden("ErrorLoginDisabled");
1208
    }
1209
1210
    // Load permissions
1211
    $user->getrights();
1212
}
1213
1214
dol_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);
1215
//Another call for easy debug
1216
//dol_syslog("Access to ".$_SERVER["PHP_SELF"].' '.$_SERVER["HTTP_REFERER"].' GET='.join(',',array_keys($_GET)).'->'.join(',',$_GET).' POST:'.join(',',array_keys($_POST)).'->'.join(',',$_POST));
1217
1218
// Load main languages files
1219
if (!defined('NOREQUIRETRAN')) {
1220
    // Load translation files required by page
1221
    $langs->loadLangs(array('main', 'dict'));
1222
}
1223
1224
// Define some constants used for style of arrays
1225
$bc = array(0 => 'class="impair"', 1 => 'class="pair"');
1226
$bcdd = array(0 => 'class="drag drop oddeven"', 1 => 'class="drag drop oddeven"');
1227
$bcnd = array(0 => 'class="nodrag nodrop nohover"', 1 => 'class="nodrag nodrop nohoverpair"'); // Used for tr to add new lines
1228
$bctag = array(0 => 'class="impair tagtr"', 1 => 'class="pair tagtr"');
1229
1230
// Define messages variables
1231
$mesg = '';
1232
$warning = '';
1233
$error = 0;
1234
// deprecated, see setEventMessages() and dol_htmloutput_events()
1235
$mesgs = array();
1236
$warnings = array();
1237
$errors = array();
1238
1239
// Constants used to defined number of lines in textarea
1240
if (empty($conf->browser->firefox)) {
1241
    define('ROWS_1', 1);
1242
    define('ROWS_2', 2);
1243
    define('ROWS_3', 3);
1244
    define('ROWS_4', 4);
1245
    define('ROWS_5', 5);
1246
    define('ROWS_6', 6);
1247
    define('ROWS_7', 7);
1248
    define('ROWS_8', 8);
1249
    define('ROWS_9', 9);
1250
} else {
1251
    define('ROWS_1', 0);
1252
    define('ROWS_2', 1);
1253
    define('ROWS_3', 2);
1254
    define('ROWS_4', 3);
1255
    define('ROWS_5', 4);
1256
    define('ROWS_6', 5);
1257
    define('ROWS_7', 6);
1258
    define('ROWS_8', 7);
1259
    define('ROWS_9', 8);
1260
}
1261
1262
$heightforframes = 50;
1263
1264
// Init menu manager
1265
if (!defined('NOREQUIREMENU')) {
1266
    if (empty($user->socid)) {    // If internal user or not defined
1267
        $conf->standard_menu = (!getDolGlobalString('MAIN_MENU_STANDARD_FORCED') ? (!getDolGlobalString('MAIN_MENU_STANDARD') ? 'eldy_menu.php' : $conf->global->MAIN_MENU_STANDARD) : $conf->global->MAIN_MENU_STANDARD_FORCED);
1268
    } else {
1269
        // If external user
1270
        $conf->standard_menu = (!getDolGlobalString('MAIN_MENUFRONT_STANDARD_FORCED') ? (!getDolGlobalString('MAIN_MENUFRONT_STANDARD') ? 'eldy_menu.php' : $conf->global->MAIN_MENUFRONT_STANDARD) : $conf->global->MAIN_MENUFRONT_STANDARD_FORCED);
1271
    }
1272
1273
    // Load the menu manager (only if not already done)
1274
    $file_menu = $conf->standard_menu;
1275
    if (GETPOST('menu', 'alpha')) {
1276
        $file_menu = GETPOST('menu', 'alpha'); // example: menu=eldy_menu.php
1277
    }
1278
    if (!class_exists('MenuManager')) {
1279
        $menufound = 0;
1280
        $dirmenus = array_merge(array("/core/menus/"), (array)$conf->modules_parts['menus']);
1281
        foreach ($dirmenus as $dirmenu) {
1282
            $menufound = dol_include_once($dirmenu . "standard/" . $file_menu);
1283
            if (class_exists('MenuManager')) {
1284
                break;
1285
            }
1286
        }
1287
        if (!class_exists('MenuManager')) { // If failed to include, we try with standard eldy_menu.php
1288
            dol_syslog("You define a menu manager '" . $file_menu . "' that can not be loaded.", LOG_WARNING);
1289
            $file_menu = 'eldy_menu.php';
1290
            include_once DOL_DOCUMENT_ROOT . "/core/menus/standard/" . $file_menu;
1291
        }
1292
    }
1293
    $menumanager = new MenuManager($db, empty($user->socid) ? 0 : 1);
1294
    $menumanager->loadMenu();
1295
}
1296
1297
if (!empty(GETPOST('seteventmessages', 'alpha'))) {
1298
    $message = GETPOST('seteventmessages', 'alpha');
1299
    $messages = explode(',', $message);
1300
    foreach ($messages as $key => $msg) {
1301
        $tmp = explode(':', $msg);
1302
        setEventMessages($tmp[0], null, !empty($tmp[1]) ? $tmp[1] : 'mesgs');
1303
    }
1304
}
1305
1306
// Functions
1307
1308
if (!function_exists("llxHeader")) {
1309
    /**
1310
     *  Show HTML header HTML + BODY + Top menu + left menu + DIV
1311
     *
1312
     * @param string $head Optional head lines
1313
     * @param string $title HTML title
1314
     * @param string $help_url Url links to help page
1315
     *                                              Syntax is: For a wiki page: EN:EnglishPage|FR:FrenchPage|ES:SpanishPage|DE:GermanPage
1316
     *                                              For other external page: http://server/url
1317
     * @param string $target Target to use on links
1318
     * @param int $disablejs More content into html header
1319
     * @param int $disablehead More content into html header
1320
     * @param array|string $arrayofjs Array of complementary js files
1321
     * @param array|string $arrayofcss Array of complementary css files
1322
     * @param string $morequerystring Query string to add to the link "print" to get same parameters (use only if autodetect fails)
1323
     * @param string $morecssonbody More CSS on body tag. For example 'classforhorizontalscrolloftabs'.
1324
     * @param string $replacemainareaby Replace call to main_area() by a print of this string
1325
     * @param int $disablenofollow Disable the "nofollow" on meta robot header
1326
     * @param int $disablenoindex Disable the "noindex" on meta robot header
1327
     * @return  void
1328
     */
1329
    function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
1330
    {
1331
        global $conf, $hookmanager;
1332
1333
        $parameters = array(
1334
            'head' => & $head,
1335
            'title' => & $title,
1336
            'help_url' => & $help_url,
1337
            'target' => & $target,
1338
            'disablejs' => & $disablejs,
1339
            'disablehead' => & $disablehead,
1340
            'arrayofjs' => & $arrayofjs,
1341
            'arrayofcss' => & $arrayofcss,
1342
            'morequerystring' => & $morequerystring,
1343
            'morecssonbody' => & $morecssonbody,
1344
            'replacemainareaby' => & $replacemainareaby,
1345
            'disablenofollow' => & $disablenofollow,
1346
            'disablenoindex' => & $disablenoindex
1347
1348
        );
1349
        $reshook = $hookmanager->executeHooks('llxHeader', $parameters);
1350
        if ($reshook > 0) {
1351
            print $hookmanager->resPrint;
1352
            return;
1353
        }
1354
1355
        // html header
1356
        top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
1357
1358
        $tmpcsstouse = 'sidebar-collapse' . ($morecssonbody ? ' ' . $morecssonbody : '');
1359
        // If theme MD and classic layer, we open the menulayer by default.
1360
        if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1361
            global $mainmenu;
1362
            if ($mainmenu != 'website') {
1363
                $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
1364
            }
1365
        }
1366
1367
        if (getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND')) {
1368
            $tmpcsstouse .= ' colorblind-' . strip_tags($conf->global->MAIN_OPTIMIZEFORCOLORBLIND);
1369
        }
1370
1371
        print '<body id="mainbody" class="' . $tmpcsstouse . '">' . "\n";
1372
1373
        // top menu and left menu area
1374
        if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && !GETPOSTINT('dol_openinpopup')) {
1375
            top_menu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
1376
        }
1377
1378
        if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
1379
            left_menu(array(), $help_url, '', '', 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
1380
        }
1381
1382
        // main area
1383
        if ($replacemainareaby) {
1384
            print $replacemainareaby;
1385
            return;
1386
        }
1387
        main_area($title);
1388
    }
1389
}
1390
1391
1392
/**
1393
 *  Show HTTP header. Called by top_htmlhead().
1394
 *
1395
 * @param string $contenttype Content type. For example, 'text/html'
1396
 * @param int<0,1> $forcenocache Force disabling of cache for the page
1397
 * @return void
1398
 */
1399
function top_httphead($contenttype = 'text/html', $forcenocache = 0)
1400
{
1401
    global $db, $conf, $hookmanager;
1402
1403
    if ($contenttype == 'text/html') {
1404
        header("Content-Type: text/html; charset=" . $conf->file->character_set_client);
1405
    } else {
1406
        header("Content-Type: " . $contenttype);
1407
    }
1408
1409
    // Security options
1410
1411
    // X-Content-Type-Options
1412
    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)
1413
1414
    // X-Frame-Options
1415
    if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
1416
        header("X-Frame-Options: SAMEORIGIN"); // By default, frames allowed only if on same domain (stop some XSS attacks)
1417
    } else {
1418
        header("X-Frame-Options: ALLOWALL");
1419
    }
1420
1421
    if (getDolGlobalString('MAIN_SECURITY_FORCE_ACCESS_CONTROL_ALLOW_ORIGIN')) {
1422
        $tmpurl = constant('DOL_MAIN_URL_ROOT');
1423
        $tmpurl = preg_replace('/^(https?:\/\/[^\/]+)\/.*$/', '\1', $tmpurl);
1424
        header('Access-Control-Allow-Origin: ' . $tmpurl);
1425
        header('Vary: Origin');
1426
    }
1427
1428
    // X-XSS-Protection
1429
    //header("X-XSS-Protection: 1");            // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
1430
1431
    // Content-Security-Policy-Report-Only
1432
    if (!defined('MAIN_SECURITY_FORCECSPRO')) {
1433
        // If CSP not forced from the page
1434
1435
        // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1436
        // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1437
        // 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: *;
1438
        // For example, to restrict everything to itself except img that can be on other servers:
1439
        // default-src 'self'; img-src *;
1440
        // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1441
        // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1442
        //
1443
        // $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;";
1444
        // $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';";
1445
        $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
1446
1447
        if (!is_object($hookmanager)) {
1448
            $hookmanager = new HookManager($db);
1449
        }
1450
        $hookmanager->initHooks(array("main"));
1451
1452
        $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'reportonly');
1453
        $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1454
        if ($result > 0) {
1455
            $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1456
        } else {
1457
            $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1458
        }
1459
1460
        if (!empty($contentsecuritypolicy)) {
1461
            header("Content-Security-Policy-Report-Only: " . $contentsecuritypolicy);
1462
        }
1463
    } else {
1464
        header("Content-Security-Policy: " . constant('MAIN_SECURITY_FORCECSPRO'));
1465
    }
1466
1467
    // Content-Security-Policy
1468
    if (!defined('MAIN_SECURITY_FORCECSP')) {
1469
        // If CSP not forced from the page
1470
1471
        // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
1472
        // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
1473
        // 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: *;
1474
        // For example, to restrict everything to itself except img that can be on other servers:
1475
        // default-src 'self'; img-src *;
1476
        // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
1477
        // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
1478
        //
1479
        // $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;";
1480
        // $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';";
1481
        $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
1482
1483
        if (!is_object($hookmanager)) {
1484
            $hookmanager = new HookManager($db);
1485
        }
1486
        $hookmanager->initHooks(array("main"));
1487
1488
        $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'active');
1489
        $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
1490
        if ($result > 0) {
1491
            $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
1492
        } else {
1493
            $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
1494
        }
1495
1496
        if (!empty($contentsecuritypolicy)) {
1497
            header("Content-Security-Policy: " . $contentsecuritypolicy);
1498
        }
1499
    } else {
1500
        header("Content-Security-Policy: " . constant('MAIN_SECURITY_FORCECSP'));
1501
    }
1502
1503
    // Referrer-Policy
1504
    // Say if we must provide the referrer when we jump onto another web page.
1505
    // 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.
1506
    // 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.
1507
    if (!defined('MAIN_SECURITY_FORCERP')) {
1508
        $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
1509
1510
        header("Referrer-Policy: " . $referrerpolicy);
1511
    }
1512
1513
    if ($forcenocache) {
1514
        header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
1515
    }
1516
1517
    // No need to add this token in header, we use instead the one into the forms.
1518
    //header("anti-csrf-token: ".newToken());
1519
}
1520
1521
/**
1522
 * Output html header of a page. It calls also top_httphead()
1523
 * This code is also duplicated into security2.lib.php::dol_loginfunction
1524
 *
1525
 * @param string $head Optional head lines
1526
 * @param string $title HTML title
1527
 * @param int<0,1> $disablejs Disable js output
1528
 * @param int<0,1> $disablehead Disable head output
1529
 * @param string[] $arrayofjs Array of complementary js files
1530
 * @param string[] $arrayofcss Array of complementary css files
1531
 * @param int<0,1> $disableforlogin Do not load heavy js and css for login pages
1532
 * @param int<0,1> $disablenofollow Disable nofollow tag for meta robots
1533
 * @param int<0,1> $disablenoindex Disable noindex tag for meta robots
1534
 * @return  void
1535
 */
1536
function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
1537
{
1538
    global $db, $conf, $langs, $user, $mysoc, $hookmanager;
1539
1540
    top_httphead();
1541
1542
    if (empty($conf->css)) {
1543
        $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
1544
    }
1545
1546
    print '<!doctype html>' . "\n";
1547
1548
    print '<html lang="' . substr($langs->defaultlang, 0, 2) . '">' . "\n";
1549
1550
    //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
1551
    if (empty($disablehead)) {
1552
        if (!is_object($hookmanager)) {
1553
            $hookmanager = new HookManager($db);
1554
        }
1555
        $hookmanager->initHooks(array("main"));
1556
1557
        $ext = 'layout=' . (empty($conf->browser->layout) ? '' : $conf->browser->layout) . '&amp;version=' . urlencode(DOL_VERSION);
1558
1559
        print "<head>\n";
1560
1561
        if (GETPOST('dol_basehref', 'alpha')) {
1562
            print '<base href="' . dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')) . '">' . "\n";
1563
        }
1564
1565
        // Displays meta
1566
        print '<meta charset="utf-8">' . "\n";
1567
        print '<meta name="robots" content="' . ($disablenoindex ? 'index' : 'noindex') . ($disablenofollow ? ',follow' : ',nofollow') . '">' . "\n"; // Do not index
1568
        print '<meta name="viewport" content="width=device-width, initial-scale=1.0">' . "\n"; // Scale for mobile device
1569
        print '<meta name="author" content="Dolibarr Development Team">' . "\n";
1570
        print '<meta name="anti-csrf-newtoken" content="' . newToken() . '">' . "\n";
1571
        print '<meta name="anti-csrf-currenttoken" content="' . currentToken() . '">' . "\n";
1572
        if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
1573
            print '<meta name="MAIN_FEATURES_LEVEL" content="' . getDolGlobalInt('MAIN_FEATURES_LEVEL') . '">' . "\n";
1574
        }
1575
        // Favicon
1576
        $favicon = constant('DOL_URL_ROOT') . '/theme/alixar_square_logo_256x256_color.png';
1577
        if (!empty($mysoc->logo_squarred_mini)) {
1578
            $favicon = constant('BASE_URL') . '/viewimage.php?cache=1&modulepart=mycompany&file=' . urlencode('logos/thumbs/' . $mysoc->logo_squarred_mini);
1579
        }
1580
        if (getDolGlobalString('MAIN_FAVICON_URL')) {
1581
            $favicon = getDolGlobalString('MAIN_FAVICON_URL');
1582
        }
1583
        if (empty($conf->dol_use_jmobile)) {
1584
            print '<link rel="shortcut icon" type="image/x-icon" href="' . $favicon . '"/>' . "\n"; // Not required into an Android webview
1585
        }
1586
1587
        // Mobile appli like icon
1588
        $manifest = constant('DOL_URL_ROOT') . '/theme/' . $conf->theme . '/manifest.json.php';
1589
        $parameters = array('manifest' => $manifest);
1590
        $resHook = $hookmanager->executeHooks('hookSetManifest', $parameters); // Note that $action and $object may have been modified by some hooks
1591
        if ($resHook > 0) {
1592
            $manifest = $hookmanager->resPrint; // Replace manifest.json
1593
        } else {
1594
            $manifest .= $hookmanager->resPrint; // Concat to actual manifest declaration
1595
        }
1596
        if (!empty($manifest)) {
1597
            print '<link rel="manifest" href="' . $manifest . '" />' . "\n";
1598
        }
1599
1600
        if (getDolGlobalString('THEME_ELDY_TOPMENU_BACK1')) {
1601
            // TODO: use auto theme color switch
1602
            print '<meta name="theme-color" content="rgb(' . getDolGlobalString('THEME_ELDY_TOPMENU_BACK1') . ')">' . "\n";
1603
        }
1604
1605
        // Auto refresh page
1606
        if (GETPOSTINT('autorefresh') > 0) {
1607
            print '<meta http-equiv="refresh" content="' . GETPOSTINT('autorefresh') . '">';
1608
        }
1609
1610
        // Displays title
1611
        $appli = constant('DOL_APPLICATION_TITLE');
1612
        if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
1613
            $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
1614
        }
1615
1616
        print '<title>';
1617
        $titletoshow = '';
1618
        if ($title && preg_match('/showapp/', getDolGlobalString('MAIN_HTML_TITLE'))) {
1619
            $titletoshow = dol_htmlentities($appli . ' - ' . $title);
1620
        } elseif ($title) {
1621
            $titletoshow = dol_htmlentities($title);
1622
        } else {
1623
            $titletoshow = dol_htmlentities($appli);
1624
        }
1625
1626
        $parameters = array('title' => $titletoshow);
1627
        $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
1628
        if ($result > 0) {
1629
            $titletoshow = $hookmanager->resPrint; // Replace Title to show
1630
        } else {
1631
            $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
1632
        }
1633
1634
        print $titletoshow;
1635
        print '</title>';
1636
1637
        print "\n";
1638
1639
        if (GETPOSTINT('version')) {
1640
            $ext = 'version=' . GETPOSTINT('version'); // useful to force no cache on css/js
1641
        }
1642
        // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
1643
        if (GETPOST('dol_resetcache')) {
1644
            dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
1645
        }
1646
1647
        $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;
1648
1649
        $themeparam .= ($ext ? '&amp;' . $ext : '') . '&amp;revision=' . getDolGlobalInt("MAIN_IHM_PARAMS_REV");
1650
        if (GETPOSTISSET('dol_hide_topmenu')) {
1651
            $themeparam .= '&amp;dol_hide_topmenu=' . GETPOSTINT('dol_hide_topmenu');
1652
        }
1653
        if (GETPOSTISSET('dol_hide_leftmenu')) {
1654
            $themeparam .= '&amp;dol_hide_leftmenu=' . GETPOSTINT('dol_hide_leftmenu');
1655
        }
1656
        if (GETPOSTISSET('dol_optimize_smallscreen')) {
1657
            $themeparam .= '&amp;dol_optimize_smallscreen=' . GETPOSTINT('dol_optimize_smallscreen');
1658
        }
1659
        if (GETPOSTISSET('dol_no_mouse_hover')) {
1660
            $themeparam .= '&amp;dol_no_mouse_hover=' . GETPOSTINT('dol_no_mouse_hover');
1661
        }
1662
        if (GETPOSTISSET('dol_use_jmobile')) {
1663
            $themeparam .= '&amp;dol_use_jmobile=' . GETPOSTINT('dol_use_jmobile');
1664
            $conf->dol_use_jmobile = GETPOSTINT('dol_use_jmobile');
1665
        }
1666
        if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
1667
            $themeparam .= '&amp;THEME_DARKMODEENABLED=' . GETPOSTINT('THEME_DARKMODEENABLED');
1668
        }
1669
        if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
1670
            $themeparam .= '&amp;THEME_SATURATE_RATIO=' . GETPOSTINT('THEME_SATURATE_RATIO');
1671
        }
1672
1673
        if (getDolGlobalString('MAIN_ENABLE_FONT_ROBOTO')) {
1674
            print '<link rel="preconnect" href="https://fonts.gstatic.com">' . "\n";
1675
            print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">' . "\n";
1676
        }
1677
1678
        if (!defined('DISABLE_JQUERY') && !$disablejs && $conf->use_javascript_ajax) {
1679
            print '<!-- Includes CSS for JQuery (Ajax library) -->' . "\n";
1680
            $jquerytheme = 'base';
1681
            if (getDolGlobalString('MAIN_USE_JQUERY_THEME')) {
1682
                $jquerytheme = getDolGlobalString('MAIN_USE_JQUERY_THEME');
1683
            }
1684
            if (constant('JS_JQUERY_UI')) {
1685
                print '<link rel="stylesheet" type="text/css" href="' . JS_JQUERY_UI . 'css/' . $jquerytheme . '/jquery-ui.min.css' . ($ext ? '?' . $ext : '') . '">' . "\n"; // Forced JQuery
1686
            } else {
1687
                print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . '/includes/jquery/css/' . $jquerytheme . '/jquery-ui.css' . ($ext ? '?' . $ext : '') . '">' . "\n"; // JQuery
1688
            }
1689
            if (!defined('DISABLE_JQUERY_JNOTIFY')) {
1690
                print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css' . ($ext ? '?' . $ext : '') . '">' . "\n"; // JNotify
1691
            }
1692
            if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {     // jQuery plugin "mutiselect", "multiple-select", "select2"...
1693
                $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
1694
                print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/' . $tmpplugin . '/dist/css/' . $tmpplugin . '.css' . ($ext ? '?' . $ext : '') . '">' . "\n";
1695
            }
1696
        }
1697
1698
        if (!defined('DISABLE_FONT_AWSOME')) {
1699
            print '<!-- Includes CSS for font awesome -->' . "\n";
1700
            $fontawesome_directory = getDolGlobalString('MAIN_FONTAWESOME_DIRECTORY', '/theme/common/fontawesome-5');
1701
            print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . $fontawesome_directory . '/css/all.min.css' . ($ext ? '?' . $ext : '') . '">' . "\n";
1702
        }
1703
1704
        print '<!-- Includes CSS for Dolibarr theme -->' . "\n";
1705
        // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
1706
        $themepath = dol_buildpath($conf->css, 1);
1707
        $themesubdir = '';
1708
        if (!empty($conf->modules_parts['theme'])) {    // This slow down
1709
            foreach ($conf->modules_parts['theme'] as $reldir) {
1710
                if (file_exists(dol_buildpath($reldir . $conf->css, 0))) {
1711
                    $themepath = dol_buildpath($reldir . $conf->css, 1);
1712
                    $themesubdir = $reldir;
1713
                    break;
1714
                }
1715
            }
1716
        }
1717
1718
        //print 'themepath='.$themepath.' themeparam='.$themeparam;exit;
1719
        print '<link rel="stylesheet" type="text/css" href="' . $themepath . $themeparam . '">' . "\n";
1720
        if (getDolGlobalString('MAIN_FIX_FLASH_ON_CHROME')) {
1721
            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";
1722
        }
1723
1724
        // LEAFLET AND GEOMAN
1725
        if (getDolGlobalString('MAIN_USE_GEOPHP')) {
1726
            print '<link rel="stylesheet" href="' . constant('BASE_URL') . '/includes/leaflet/leaflet.css' . ($ext ? '?' . $ext : '') . "\">\n";
1727
            print '<link rel="stylesheet" href="' . constant('BASE_URL') . '/includes/leaflet/leaflet-geoman.css' . ($ext ? '?' . $ext : '') . "\">\n";
1728
        }
1729
1730
        // CSS forced by modules (relative url starting with /)
1731
        if (!empty($conf->modules_parts['css'])) {
1732
            $arraycss = (array)$conf->modules_parts['css'];
1733
            foreach ($arraycss as $modcss => $filescss) {
1734
                $filescss = (array)$filescss; // To be sure filecss is an array
1735
                foreach ($filescss as $cssfile) {
1736
                    if (empty($cssfile)) {
1737
                        dol_syslog("Warning: module " . $modcss . " declared a css path file into its descriptor that is empty.", LOG_WARNING);
1738
                    }
1739
                    // cssfile is a relative path
1740
                    $urlforcss = dol_buildpath($cssfile, 1);
1741
                    if ($urlforcss && $urlforcss != '/') {
1742
                        print '<!-- Includes CSS added by module ' . $modcss . ' -->' . "\n" . '<link rel="stylesheet" type="text/css" href="' . $urlforcss;
1743
                        // 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.
1744
                        if (!preg_match('/\.css$/i', $cssfile)) {
1745
                            print $themeparam;
1746
                        }
1747
                        print '">' . "\n";
1748
                    } else {
1749
                        dol_syslog("Warning: module " . $modcss . " declared a css path file for a file we can't find.", LOG_WARNING);
1750
                    }
1751
                }
1752
            }
1753
        }
1754
        // CSS forced by page in top_htmlhead call (relative url starting with /)
1755
        if (is_array($arrayofcss)) {
1756
            foreach ($arrayofcss as $cssfile) {
1757
                // $cssfile = '/htdocs' . $cssfile;
1758
                if (preg_match('/^(http|\/\/)/i', $cssfile)) {
1759
                    $urltofile = $cssfile;
1760
                } else {
1761
                    $urltofile = dol_buildpath($cssfile, 1);
1762
                }
1763
                print '<!-- Includes CSS added by page -->' . "\n" . '<link rel="stylesheet" type="text/css" title="default" href="' . $urltofile;
1764
                // 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.
1765
                if (!preg_match('/\.css$/i', $cssfile)) {
1766
                    print $themeparam;
1767
                }
1768
                print '">' . "\n";
1769
            }
1770
        }
1771
1772
        // Custom CSS
1773
        if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
1774
            // If a custom CSS was set, we add link to the custom css php file
1775
            print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . '/theme/custom.css.php' . ($ext ? '?' . $ext : '') . '&amp;revision=' . getDolGlobalInt("MAIN_IHM_PARAMS_REV") . '">' . "\n";
1776
        }
1777
1778
        // Output standard javascript links
1779
        if (!defined('DISABLE_JQUERY') && !$disablejs && !empty($conf->use_javascript_ajax)) {
1780
            // JQuery. Must be before other includes
1781
            print '<!-- Includes JS for JQuery -->' . "\n";
1782
            if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
1783
                print '<script nonce="' . getNonce() . '" src="' . JS_JQUERY . 'jquery.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1784
            } else {
1785
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/js/jquery.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1786
            }
1787
            if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
1788
                print '<script nonce="' . getNonce() . '" src="' . JS_JQUERY_UI . 'jquery-ui.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1789
            } else {
1790
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/js/jquery-ui.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1791
            }
1792
            // jQuery jnotify
1793
            if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
1794
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jnotify/jquery.jnotify.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1795
            }
1796
            // Table drag and drop lines
1797
            if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
1798
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/tablednd/jquery.tablednd.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1799
            }
1800
            // Chart
1801
            if (empty($disableforlogin) && (!getDolGlobalString('MAIN_JS_GRAPH') || getDolGlobalString('MAIN_JS_GRAPH') == 'chart') && !defined('DISABLE_JS_GRAPH')) {
1802
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/nnnick/chartjs/dist/chart.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1803
            }
1804
1805
            // jQuery jeditable for Edit In Place features
1806
            if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !defined('DISABLE_JQUERY_JEDITABLE')) {
1807
                print '<!-- JS to manage editInPlace feature -->' . "\n";
1808
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1809
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1810
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1811
                print '<script>' . "\n";
1812
                print 'var urlSaveInPlace = \'' . constant('DOL_URL_ROOT') . '/core/ajax/saveinplace.php\';' . "\n";
1813
                print 'var urlLoadInPlace = \'' . constant('DOL_URL_ROOT') . '/core/ajax/loadinplace.php\';' . "\n";
1814
                print 'var tooltipInPlace = \'' . $langs->transnoentities('ClickToEdit') . '\';' . "\n"; // Added in title attribute of span
1815
                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 ?
1816
                print 'var cancelInPlace = \'' . $langs->trans("Cancel") . '\';' . "\n";
1817
                print 'var submitInPlace = \'' . $langs->trans('Ok') . '\';' . "\n";
1818
                print 'var indicatorInPlace = \'<img src="' . constant('DOL_URL_ROOT') . "/theme/" . $conf->theme . "/img/working.gif" . '">\';' . "\n";
1819
                print 'var withInPlace = 300;'; // width in pixel for default string edit
1820
                print '</script>' . "\n";
1821
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/core/js/editinplace.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1822
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1823
            }
1824
            // jQuery Timepicker
1825
            if (getDolGlobalString('MAIN_USE_JQUERY_TIMEPICKER') || defined('REQUIRE_JQUERY_TIMEPICKER')) {
1826
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/timepicker/jquery-ui-timepicker-addon.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1827
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/core/js/timepicker.js.php?lang=' . $langs->defaultlang . ($ext ? '&amp;' . $ext : '') . '"></script>' . "\n";
1828
            }
1829
            if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
1830
                // jQuery plugin "mutiselect", "multiple-select", "select2", ...
1831
                $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
1832
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/' . $tmpplugin . '/dist/js/' . $tmpplugin . '.full.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n"; // We include full because we need the support of containerCssClass
1833
            }
1834
            if (!defined('DISABLE_MULTISELECT')) {     // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
1835
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/multiselect/jquery.multi-select.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1836
            }
1837
        }
1838
1839
        if (!$disablejs && !empty($conf->use_javascript_ajax)) {
1840
            // CKEditor
1841
            if (empty($disableforlogin) && (isModEnabled('fckeditor') && (!getDolGlobalString('FCKEDITOR_EDITORNAME') || getDolGlobalString('FCKEDITOR_EDITORNAME') == 'ckeditor') && !defined('DISABLE_CKEDITOR')) || defined('FORCE_CKEDITOR')) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (empty($disableforlogin)...fined('FORCE_CKEDITOR'), Probably Intended Meaning: empty($disableforlogin) ...ined('FORCE_CKEDITOR'))
Loading history...
1842
                print '<!-- Includes JS for CKEditor -->' . "\n";
1843
                $pathckeditor = constant('DOL_URL_ROOT') . '/includes/ckeditor/ckeditor/';
1844
                $jsckeditor = 'ckeditor.js';
1845
                if (constant('JS_CKEDITOR')) {
1846
                    // To use external ckeditor 4 js lib
1847
                    $pathckeditor = constant('JS_CKEDITOR');
1848
                }
1849
                print '<script nonce="' . getNonce() . '">';
1850
                print '/* enable ckeditor by main.inc.php */';
1851
                print 'var CKEDITOR_BASEPATH = \'' . dol_escape_js($pathckeditor) . '\';' . "\n";
1852
                print 'var ckeditorConfig = \'' . dol_escape_js(dol_buildpath('/htdocs' . $themesubdir . '/theme/' . $conf->theme . '/ckeditor/config.js' . ($ext ? '?' . $ext : ''), 1)) . '\';' . "\n"; // $themesubdir='' in standard usage
1853
                print 'var ckeditorFilebrowserBrowseUrl = \'' . constant('DOL_URL_ROOT') . '/core/filemanagerdol/browser/default/browser.php?Connector=' . constant('BASE_URL') . '/core/filemanagerdol/connectors/php/connector.php\';' . "\n";
1854
                print 'var ckeditorFilebrowserImageBrowseUrl = \'' . constant('DOL_URL_ROOT') . '/core/filemanagerdol/browser/default/browser.php?Type=Image&Connector=' . constant('BASE_URL') . '/core/filemanagerdol/connectors/php/connector.php\';' . "\n";
1855
                print '</script>' . "\n";
1856
                print '<script src="' . $pathckeditor . $jsckeditor . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1857
                print '<script>';
1858
                if (GETPOST('mode', 'aZ09') == 'Full_inline') {
1859
                    print 'CKEDITOR.disableAutoInline = false;' . "\n";
1860
                } else {
1861
                    print 'CKEDITOR.disableAutoInline = true;' . "\n";
1862
                }
1863
                print '</script>' . "\n";
1864
            }
1865
1866
            // 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).
1867
            if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
1868
                $enablebrowsernotif = false;
1869
                if (isModEnabled('agenda') && getDolGlobalString('AGENDA_REMINDER_BROWSER')) {
1870
                    $enablebrowsernotif = true;
1871
                }
1872
                if ($conf->browser->layout == 'phone') {
1873
                    $enablebrowsernotif = false;
1874
                }
1875
                if ($enablebrowsernotif) {
1876
                    print '<!-- Includes JS of Dolibarr (browser layout = ' . $conf->browser->layout . ')-->' . "\n";
1877
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/core/js/lib_notification.js.php' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1878
                }
1879
            }
1880
1881
            // Global js function
1882
            print '<!-- Includes JS of Dolibarr -->' . "\n";
1883
            print '<script nonce="' . getNonce() . '" src="' . BASE_URL . '/core/js/lib_head.js.php?lang=' . $langs->defaultlang . ($ext ? '&amp;' . $ext : '') . '"></script>' . "\n";
1884
1885
            // Leaflet TODO use dolibarr files
1886
            if (getDolGlobalString('MAIN_USE_GEOPHP')) {
1887
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/leaflet/leaflet.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1888
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/leaflet/leaflet-geoman.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1889
            }
1890
1891
            // JS forced by modules (relative url starting with /)
1892
            if (!empty($conf->modules_parts['js'])) {       // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
1893
                $arrayjs = (array)$conf->modules_parts['js'];
1894
                foreach ($arrayjs as $modjs => $filesjs) {
1895
                    $filesjs = (array)$filesjs; // To be sure filejs is an array
1896
                    foreach ($filesjs as $jsfile) {
1897
                        // jsfile is a relative path
1898
                        $urlforjs = dol_buildpath($jsfile, 3);
1899
                        if ($urlforjs && $urlforjs != '/') {
1900
                            print '<!-- Include JS added by module ' . $modjs . '-->' . "\n";
1901
                            print '<script nonce="' . getNonce() . '" src="' . $urlforjs . ((strpos($jsfile, '?') === false) ? '?' : '&amp;') . 'lang=' . $langs->defaultlang . '"></script>' . "\n";
1902
                        } else {
1903
                            dol_syslog("Warning: module " . $modjs . " declared a js path file for a file we can't find.", LOG_WARNING);
1904
                        }
1905
                    }
1906
                }
1907
            }
1908
            // JS forced by page in top_htmlhead (relative url starting with /)
1909
            if (is_array($arrayofjs)) {
1910
                print '<!-- Includes JS added by page -->' . "\n";
1911
                foreach ($arrayofjs as $jsfile) {
1912
                    // $jsfile = '/htdocs' . $jsfile;
1913
                    if (preg_match('/^(http|\/\/)/i', $jsfile)) {
1914
                        print '<script nonce="' . getNonce() . '" src="' . $jsfile . ((strpos($jsfile, '?') === false) ? '?' : '&amp;') . 'lang=' . $langs->defaultlang . '"></script>' . "\n";
1915
                    } else {
1916
                        print '<script nonce="' . getNonce() . '" src="' . dol_buildpath($jsfile, 3) . ((strpos($jsfile, '?') === false) ? '?' : '&amp;') . 'lang=' . $langs->defaultlang . '"></script>' . "\n";
1917
                    }
1918
                }
1919
            }
1920
        }
1921
1922
        //If you want to load custom javascript file from your selected theme directory
1923
        if (getDolGlobalString('ALLOW_THEME_JS')) {
1924
            $theme_js = dol_buildpath('/theme/' . $conf->theme . '/' . $conf->theme . '.js', 0);
1925
            if (file_exists($theme_js)) {
1926
                print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/theme/' . $conf->theme . '/' . $conf->theme . '.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
1927
            }
1928
        }
1929
1930
        if (!empty($head)) {
1931
            print $head . "\n";
1932
        }
1933
        if (getDolGlobalString('MAIN_HTML_HEADER')) {
1934
            print getDolGlobalString('MAIN_HTML_HEADER') . "\n";
1935
        }
1936
1937
        $parameters = array();
1938
        $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
1939
        print $hookmanager->resPrint; // Replace Title to show
1940
1941
        print "</head>\n\n";
1942
    }
1943
1944
    $conf->headerdone = 1; // To tell header was output
1945
}
1946
1947
1948
/**
1949
 *  Show an HTML header + a BODY + The top menu bar
1950
 *
1951
 * @param string $head Lines in the HEAD
1952
 * @param string $title Title of web page
1953
 * @param string $target Target to use in menu links (Example: '' or '_top')
1954
 * @param int<0,1> $disablejs Do not output links to js (Ex: qd fonction utilisee par sous formulaire Ajax)
1955
 * @param int<0,1> $disablehead Do not output head section
1956
 * @param string[] $arrayofjs Array of js files to add in header
1957
 * @param string[] $arrayofcss Array of css files to add in header
1958
 * @param string $morequerystring Query string to add to the link "print" to get same parameters (use only if autodetect fails)
1959
 * @param string $helppagename Name of wiki page for help ('' by default).
1960
 *                                                  Syntax is: For a wiki page: EN:EnglishPage|FR:FrenchPage|ES:SpanishPage|DE:GermanPage
1961
 *                                                  For other external page: http://server/url
1962
 * @return     void
1963
 */
1964
function top_menu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $morequerystring = '', $helppagename = '')
1965
{
1966
    global $user, $conf, $langs, $db, $form;
1967
    global $dolibarr_main_authentication, $dolibarr_main_demo;
1968
    global $hookmanager, $menumanager;
1969
1970
    $searchform = '';
1971
1972
    // Instantiate hooks for external modules
1973
    $hookmanager->initHooks(array('toprightmenu'));
1974
1975
    $toprightmenu = '';
1976
1977
    // For backward compatibility with old modules
1978
    if (empty($conf->headerdone)) {
1979
        $disablenofollow = 0;
1980
        top_htmlhead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
1981
        print '<body id="mainbody">';
1982
    }
1983
1984
    /*
1985
     * Top menu
1986
     */
1987
    if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
1988
        if (!isset($form) || !is_object($form)) {
1989
            $form = new Form($db);
1990
        }
1991
1992
        print "\n" . '<!-- Start top horizontal -->' . "\n";
1993
1994
        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.
1995
1996
        // Show menu entries
1997
        print '<div id="tmenu_tooltip' . (!getDolGlobalString('MAIN_MENU_INVERT') ? '' : 'invert') . '" class="tmenu">' . "\n";
1998
        $menumanager->atarget = $target;
1999
        $menumanager->showmenu('top', array('searchform' => $searchform)); // This contains a \n
2000
        print "</div>\n";
2001
2002
        // Define link to login card
2003
        $appli = constant('DOL_APPLICATION_TITLE');
2004
        if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
2005
            $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
2006
            if (preg_match('/\d\.\d/', $appli)) {
2007
                if (!preg_match('/' . preg_quote(DOL_VERSION) . '/', $appli)) {
2008
                    $appli .= " (" . DOL_VERSION . ")"; // If new title contains a version that is different than core
2009
                }
2010
            } else {
2011
                $appli .= " " . DOL_VERSION;
2012
            }
2013
        } else {
2014
            $appli .= " " . DOL_VERSION;
2015
        }
2016
2017
        if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
2018
            $appli .= "<br>" . $langs->trans("LevelOfFeature") . ': ' . getDolGlobalInt('MAIN_FEATURES_LEVEL');
2019
        }
2020
2021
        $logouttext = '';
2022
        $logouthtmltext = '';
2023
        if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2024
            //$logouthtmltext=$appli.'<br>';
2025
            $stringforfirstkey = $langs->trans("KeyboardShortcut");
2026
            if ($conf->browser->name == 'chrome') {
2027
                $stringforfirstkey .= ' ALT +';
2028
            } elseif ($conf->browser->name == 'firefox') {
2029
                $stringforfirstkey .= ' ALT + SHIFT +';
2030
            } else {
2031
                $stringforfirstkey .= ' CTL +';
2032
            }
2033
            if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') {
2034
                $logouthtmltext .= $langs->trans("Logout") . '<br>';
2035
                $logouttext .= '<a accesskey="l" href="' . constant('BASE_URL') . '/user/logout.php?token=' . newToken() . '">';
2036
                $logouttext .= img_picto($langs->trans('Logout') . ' (' . $stringforfirstkey . ' l)', 'sign-out', '', false, 0, 0, '', 'atoplogin valignmiddle');
2037
                $logouttext .= '</a>';
2038
            } else {
2039
                $logouthtmltext .= $langs->trans("NoLogoutProcessWithAuthMode", $_SESSION["dol_authmode"]);
2040
                $logouttext .= img_picto($langs->trans('Logout') . ' (' . $stringforfirstkey . ' l)', 'sign-out', '', false, 0, 0, '', 'atoplogin valignmiddle opacitymedium');
2041
            }
2042
        }
2043
2044
        print '<div class="login_block usedropdown">' . "\n";
2045
2046
        $toprightmenu .= '<div class="login_block_other">';
2047
2048
        // Execute hook printTopRightMenu (hooks should output string like '<div class="login"><a href="">mylink</a></div>')
2049
        $parameters = array();
2050
        $result = $hookmanager->executeHooks('printTopRightMenu', $parameters); // Note that $action and $object may have been modified by some hooks
2051
        if (is_numeric($result)) {
2052
            if ($result == 0) {
2053
                $toprightmenu .= $hookmanager->resPrint; // add
2054
            } else {
2055
                $toprightmenu = $hookmanager->resPrint; // replace
2056
            }
2057
        } else {
2058
            $toprightmenu .= $result; // For backward compatibility
2059
        }
2060
2061
        // Link to module builder
2062
        if (isModEnabled('modulebuilder')) {
2063
            $text = '<a href="' . constant('BASE_URL') . '/modulebuilder/index.php?mainmenu=home&leftmenu=admintools" target="modulebuilder">';
2064
            //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
2065
            $text .= '<span class="fa fa-bug atoplogin valignmiddle"></span>';
2066
            $text .= '</a>';
2067
            // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2068
            $toprightmenu .= $form->textwithtooltip('', $langs->trans("ModuleBuilder"), 2, 1, $text, 'login_block_elem', 2);
2069
        }
2070
2071
        // Link to print main content area (optioncss=print)
2072
        if (!getDolGlobalString('MAIN_PRINT_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2073
            $qs = dol_escape_htmltag($_SERVER["QUERY_STRING"]);
2074
2075
            if (isset($_POST) && is_array($_POST)) {
2076
                foreach ($_POST as $key => $value) {
2077
                    $key = preg_replace('/[^a-z0-9_\.\-\[\]]/i', '', $key);
2078
                    if (in_array($key, array('action', 'massaction', 'password'))) {
2079
                        continue;
2080
                    }
2081
                    if (!is_array($value)) {
2082
                        if ($value !== '') {
2083
                            $qs .= '&' . urlencode($key) . '=' . urlencode($value);
2084
                        }
2085
                    } else {
2086
                        foreach ($value as $value2) {
2087
                            if (($value2 !== '') && (!is_array($value2))) {
2088
                                $qs .= '&' . urlencode($key) . '[]=' . urlencode($value2);
2089
                            }
2090
                        }
2091
                    }
2092
                }
2093
            }
2094
            $qs .= (($qs && $morequerystring) ? '&' : '') . $morequerystring;
2095
            $text = '<a href="' . dol_escape_htmltag($_SERVER["PHP_SELF"]) . '?' . $qs . ($qs ? '&' : '') . 'optioncss=print" target="_blank" rel="noopener noreferrer">';
2096
            //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
2097
            $text .= '<span class="fa fa-print atoplogin valignmiddle"></span>';
2098
            $text .= '</a>';
2099
            // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2100
            $toprightmenu .= $form->textwithtooltip('', $langs->trans("PrintContentArea"), 2, 1, $text, 'login_block_elem', 2);
2101
        }
2102
2103
        // Link to Dolibarr wiki pages
2104
        if (!getDolGlobalString('MAIN_HELP_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2105
            $langs->load("help");
2106
2107
            $helpbaseurl = '';
2108
            $helppage = '';
2109
            $mode = '';
2110
            $helppresent = '';
2111
2112
            if (empty($helppagename)) {
2113
                $helppagename = 'EN:User_documentation|FR:Documentation_utilisateur|ES:Documentación_usuarios|DE:Benutzerdokumentation';
2114
            } else {
2115
                $helppresent = 'helppresent';
2116
            }
2117
2118
            // Get helpbaseurl, helppage and mode from helppagename and langs
2119
            $arrayres = getHelpParamFor($helppagename, $langs);
2120
            $helpbaseurl = $arrayres['helpbaseurl'];
2121
            $helppage = $arrayres['helppage'];
2122
            $mode = $arrayres['mode'];
2123
2124
            // Link to help pages
2125
            if ($helpbaseurl && $helppage) {
2126
                $text = '';
2127
                $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage') . ', ';
2128
                if ($mode == 'wiki') {
2129
                    $title .= '<br>' . img_picto('', 'globe', 'class="pictofixedwidth"') . $langs->trans("PageWiki") . ' ' . dol_escape_htmltag('"' . strtr($helppage, '_', ' ') . '"');
2130
                    if ($helppresent) {
2131
                        $title .= ' <span class="opacitymedium">(' . $langs->trans("DedicatedPageAvailable") . ')</span>';
2132
                    } else {
2133
                        $title .= ' <span class="opacitymedium">(' . $langs->trans("HomePage") . ')</span>';
2134
                    }
2135
                }
2136
                $text .= '<a class="help" target="_blank" rel="noopener noreferrer" href="';
2137
                if ($mode == 'wiki') {
2138
                    // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2139
                    $text .= sprintf($helpbaseurl, urlencode(html_entity_decode($helppage)));
2140
                } else {
2141
                    // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
2142
                    $text .= sprintf($helpbaseurl, $helppage);
2143
                }
2144
                $text .= '">';
2145
                $text .= '<span class="fa fa-question-circle atoplogin valignmiddle' . ($helppresent ? ' ' . $helppresent : '') . '"></span>';
2146
                $text .= '<span class="fa fa-long-arrow-alt-up helppresentcircle' . ($helppresent ? '' : ' unvisible') . '"></span>';
2147
                $text .= '</a>';
2148
                // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2149
                $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2);
2150
            }
2151
2152
            // Version
2153
            if (getDolGlobalString('MAIN_SHOWDATABASENAMEINHELPPAGESLINK')) {
2154
                $langs->load('admin');
2155
                $appli .= '<br>' . $langs->trans("Database") . ': ' . $db->database_name;
2156
            }
2157
        }
2158
2159
        if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2160
            $text = '<span class="aversion"><span class="hideonsmartphone small">' . DOL_VERSION . '</span></span>';
2161
            // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
2162
            $toprightmenu .= $form->textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
2163
        }
2164
2165
        // Logout link
2166
        $toprightmenu .= $form->textwithtooltip('', $logouthtmltext, 2, 1, $logouttext, 'login_block_elem logout-btn', 2);
2167
2168
        $toprightmenu .= '</div>'; // end div class="login_block_other"
2169
2170
2171
        // Add login user link
2172
        $toprightmenu .= '<div class="login_block_user">';
2173
2174
        // Login name with photo and tooltip
2175
        $mode = -1;
2176
        $toprightmenu .= '<div class="inline-block login_block_elem login_block_elem_name nowrap centpercent" style="padding: 0px;">';
2177
2178
        if (getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
2179
            // Add search dropdown
2180
            $toprightmenu .= top_menu_search();
2181
        }
2182
2183
        if (getDolGlobalString('MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN')) {
2184
            // Add search dropdown
2185
            $toprightmenu .= top_menu_quickadd();
2186
        }
2187
2188
        // Add bookmark dropdown
2189
        $toprightmenu .= top_menu_bookmark();
2190
2191
        // Add user dropdown
2192
        $toprightmenu .= top_menu_user();
2193
2194
        $toprightmenu .= '</div>';
2195
2196
        $toprightmenu .= '</div>' . "\n";
2197
2198
2199
        print $toprightmenu;
2200
2201
        print "</div>\n"; // end div class="login_block"
2202
2203
        print '</header>';
2204
        //print '<header class="header2">&nbsp;</header>';
2205
2206
        print '<div style="clear: both;"></div>';
2207
        print "<!-- End top horizontal menu -->\n\n";
2208
    }
2209
2210
    if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
2211
        print '<!-- Begin div id-container --><div id="id-container" class="id-container">';
2212
    }
2213
}
2214
2215
2216
/**
2217
 * Build the tooltip on user login
2218
 *
2219
 * @param int<0,1> $hideloginname Hide login name. Show only the image.
2220
 * @param string $urllogout URL for logout (Will use DOL_URL_ROOT.'/user/logout.php?token=...' if empty)
2221
 * @return  string                          HTML content
2222
 */
2223
function top_menu_user($hideloginname = 0, $urllogout = '')
2224
{
2225
    global $langs, $conf, $db, $hookmanager, $user, $mysoc;
2226
    global $dolibarr_main_authentication, $dolibarr_main_demo;
2227
    global $menumanager;
2228
2229
    $langs->load('companies');
2230
2231
    $userImage = $userDropDownImage = '';
2232
    if (!empty($user->photo)) {
2233
        $userImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'photouserphoto userphoto', 'small', 0, 1);
2234
        $userDropDownImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'dropdown-user-image', 'small', 0, 1);
2235
    } else {
2236
        $nophoto = '/public/theme/common/user_anonymous.png';
2237
        if ($user->gender == 'man') {
2238
            $nophoto = '/public/theme/common/user_man.png';
2239
        }
2240
        if ($user->gender == 'woman') {
2241
            $nophoto = '/public/theme/common/user_woman.png';
2242
        }
2243
2244
        $userImage = '<img class="photo photouserphoto userphoto" alt="" src="' . constant('DOL_URL_ROOT') . $nophoto . '">';
2245
        $userDropDownImage = '<img class="photo dropdown-user-image" alt="" src="' . constant('DOL_URL_ROOT') . $nophoto . '">';
2246
    }
2247
2248
    $dropdownBody = '';
2249
    $dropdownBody .= '<span id="topmenulogincompanyinfo-btn"><i class="fa fa-caret-right"></i> ' . $langs->trans("ShowCompanyInfos") . '</span>';
2250
    $dropdownBody .= '<div id="topmenulogincompanyinfo" >';
2251
2252
    $dropdownBody .= '<br><b>' . $langs->trans("Company") . '</b>: <span>' . dol_escape_htmltag($mysoc->name) . '</span>';
2253
    if ($langs->transcountry("ProfId1", $mysoc->country_code) != '-') {
2254
        $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId1", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_SIREN"), 1) . '</span>';
2255
    }
2256
    if ($langs->transcountry("ProfId2", $mysoc->country_code) != '-') {
2257
        $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId2", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_SIRET"), 2) . '</span>';
2258
    }
2259
    if ($langs->transcountry("ProfId3", $mysoc->country_code) != '-') {
2260
        $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId3", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_APE"), 3) . '</span>';
2261
    }
2262
    if ($langs->transcountry("ProfId4", $mysoc->country_code) != '-') {
2263
        $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId4", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_RCS"), 4) . '</span>';
2264
    }
2265
    if ($langs->transcountry("ProfId5", $mysoc->country_code) != '-') {
2266
        $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId5", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_PROFID5"), 5) . '</span>';
2267
    }
2268
    if ($langs->transcountry("ProfId6", $mysoc->country_code) != '-') {
2269
        $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId6", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_PROFID6"), 6) . '</span>';
2270
    }
2271
    $dropdownBody .= '<br><b>' . $langs->trans("VATIntraShort") . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT') . '</span>';
2272
    $dropdownBody .= '<br><b>' . $langs->trans("Country") . '</b>: <span>' . ($mysoc->country_code ? $langs->trans("Country" . $mysoc->country_code) : '') . '</span>';
2273
    if (isModEnabled('multicurrency')) {
2274
        $dropdownBody .= '<br><b>' . $langs->trans("Currency") . '</b>: <span>' . $conf->currency . '</span>';
2275
    }
2276
    $dropdownBody .= '</div>';
2277
2278
    $dropdownBody .= '<br>';
2279
    $dropdownBody .= '<span id="topmenuloginmoreinfo-btn"><i class="fa fa-caret-right"></i> ' . $langs->trans("ShowMoreInfos") . '</span>';
2280
    $dropdownBody .= '<div id="topmenuloginmoreinfo" >';
2281
2282
    // login infos
2283
    if (!empty($user->admin)) {
2284
        $dropdownBody .= '<br><b>' . $langs->trans("Administrator") . '</b>: ' . yn($user->admin);
2285
    }
2286
    if (!empty($user->socid)) { // Add thirdparty for external users
2287
        $thirdpartystatic = new Societe($db);
2288
        $thirdpartystatic->fetch($user->socid);
2289
        $companylink = ' ' . $thirdpartystatic->getNomUrl(2); // picto only of company
2290
        $company = ' (' . $langs->trans("Company") . ': ' . $thirdpartystatic->name . ')';
2291
    }
2292
    $type = ($user->socid ? $langs->trans("External") . $company : $langs->trans("Internal"));
2293
    $dropdownBody .= '<br><b>' . $langs->trans("Type") . ':</b> ' . $type;
2294
    $dropdownBody .= '<br><b>' . $langs->trans("Status") . '</b>: ' . $user->getLibStatut(0);
2295
    $dropdownBody .= '<br>';
2296
2297
    $dropdownBody .= '<br><u>' . $langs->trans("Session") . '</u>';
2298
    $dropdownBody .= '<br><b>' . $langs->trans("IPAddress") . '</b>: ' . dol_escape_htmltag($_SERVER["REMOTE_ADDR"]);
2299
    if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
2300
        $dropdownBody .= '<br><b>' . $langs->trans("ConnectedOnMultiCompany") . ':</b> ' . $conf->entity . ' (user entity ' . $user->entity . ')';
2301
    }
2302
    $dropdownBody .= '<br><b>' . $langs->trans("AuthenticationMode") . ':</b> ' . $_SESSION["dol_authmode"] . (empty($dolibarr_main_demo) ? '' : ' (demo)');
2303
    $dropdownBody .= '<br><b>' . $langs->trans("ConnectedSince") . ':</b> ' . dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2304
    $dropdownBody .= '<br><b>' . $langs->trans("PreviousConnexion") . ':</b> ' . dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2305
    $dropdownBody .= '<br><b>' . $langs->trans("CurrentTheme") . ':</b> ' . $conf->theme;
2306
    $dropdownBody .= '<br><b>' . $langs->trans("CurrentMenuManager") . ':</b> ' . (isset($menumanager) ? $menumanager->name : 'unknown');
2307
    $langFlag = picto_from_langcode($langs->getDefaultLang());
2308
    $dropdownBody .= '<br><b>' . $langs->trans("CurrentUserLanguage") . ':</b> ' . ($langFlag ? $langFlag . ' ' : '') . $langs->getDefaultLang();
2309
2310
    $tz = (int)$_SESSION['dol_tz'] + (int)$_SESSION['dol_dst'];
2311
    $dropdownBody .= '<br><b>' . $langs->trans("ClientTZ") . ':</b> ' . ($tz ? ($tz >= 0 ? '+' : '') . $tz : '');
2312
    $dropdownBody .= ' (' . $_SESSION['dol_tz_string'] . ')';
2313
    //$dropdownBody .= ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
2314
    //if ($_SESSION['dol_dst'] > 0) $dropdownBody .= yn(1);
2315
    //else $dropdownBody .= yn(0);
2316
2317
    $dropdownBody .= '<br><b>' . $langs->trans("Browser") . ':</b> ' . $conf->browser->name . ($conf->browser->version ? ' ' . $conf->browser->version : '') . ' <small class="opacitymedium">(' . dol_escape_htmltag($_SERVER['HTTP_USER_AGENT']) . ')</small>';
2318
    $dropdownBody .= '<br><b>' . $langs->trans("Layout") . ':</b> ' . $conf->browser->layout;
2319
    $dropdownBody .= '<br><b>' . $langs->trans("Screen") . ':</b> ' . $_SESSION['dol_screenwidth'] . ' x ' . $_SESSION['dol_screenheight'];
2320
    if ($conf->browser->layout == 'phone') {
2321
        $dropdownBody .= '<br><b>' . $langs->trans("Phone") . ':</b> ' . $langs->trans("Yes");
2322
    }
2323
    if (!empty($_SESSION["disablemodules"])) {
2324
        $dropdownBody .= '<br><b>' . $langs->trans("DisabledModules") . ':</b> <br>' . implode(', ', explode(',', $_SESSION["disablemodules"]));
2325
    }
2326
    $dropdownBody .= '</div>';
2327
2328
    // Execute hook
2329
    $parameters = array('user' => $user, 'langs' => $langs);
2330
    $result = $hookmanager->executeHooks('printTopRightMenuLoginDropdownBody', $parameters); // Note that $action and $object may have been modified by some hooks
2331
    if (is_numeric($result)) {
2332
        if ($result == 0) {
2333
            $dropdownBody .= $hookmanager->resPrint; // add
2334
        } else {
2335
            $dropdownBody = $hookmanager->resPrint; // replace
2336
        }
2337
    }
2338
2339
    if (empty($urllogout)) {
2340
        $urllogout = BASE_URL . '/user/logout.php?token=' . newToken();
2341
    }
2342
2343
    // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
2344
    // accesskey is for Mac:               CTRL + key for all browsers
2345
    $stringforfirstkey = $langs->trans("KeyboardShortcut");
2346
    if ($conf->browser->name == 'chrome') {
2347
        $stringforfirstkey .= ' ALT +';
2348
    } elseif ($conf->browser->name == 'firefox') {
2349
        $stringforfirstkey .= ' ALT + SHIFT +';
2350
    } else {
2351
        $stringforfirstkey .= ' CTL +';
2352
    }
2353
2354
    // Defined the links for bottom of card
2355
    $profilLink = '<a accesskey="u" href="' . BASE_URL . '/user/card.php?id=' . $user->id . '" class="button-top-menu-dropdown" title="' . dol_escape_htmltag($langs->trans("YourUserFile") . ' (' . $stringforfirstkey . ' u)') . '"><i class="fa fa-user"></i>  ' . $langs->trans("Card") . '</a>';
2356
    $urltovirtualcard = '/user/virtualcard.php?id=' . ((int)$user->id);
2357
    $virtuelcardLink = dolButtonToOpenUrlInDialogPopup('publicvirtualcardmenu', $langs->transnoentitiesnoconv("PublicVirtualCardUrl") . (is_object($user) ? ' - ' . $user->getFullName($langs) : '') . ' (' . $stringforfirstkey . ' v)', img_picto($langs->trans("PublicVirtualCardUrl") . ' (' . $stringforfirstkey . ' v)', 'card', ''), $urltovirtualcard, '', 'button-top-menu-dropdown marginleftonly nohover', "closeTopMenuLoginDropdown()", '', 'v');
2358
    $logoutLink = '<a accesskey="l" href="' . $urllogout . '" class="button-top-menu-dropdown" title="' . dol_escape_htmltag($langs->trans("Logout") . ' (' . $stringforfirstkey . ' l)') . '"><i class="fa fa-sign-out-alt padingright"></i><span class="hideonsmartphone">' . $langs->trans("Logout") . '</span></a>';
2359
2360
    $profilName = $user->getFullName($langs) . ' (' . $user->login . ')';
2361
    if (!empty($user->admin)) {
2362
        $profilName = '<i class="far fa-star classfortooltip" title="' . $langs->trans("Administrator") . '" ></i> ' . $profilName;
2363
    }
2364
2365
    // Define version to show
2366
    $appli = constant('DOL_APPLICATION_TITLE');
2367
    if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
2368
        $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
2369
        if (preg_match('/\d\.\d/', $appli)) {
2370
            if (!preg_match('/' . preg_quote(DOL_VERSION) . '/', $appli)) {
2371
                $appli .= " (" . DOL_VERSION . ")"; // If new title contains a version that is different than core
2372
            }
2373
        } else {
2374
            $appli .= " " . DOL_VERSION;
2375
        }
2376
    } else {
2377
        $appli .= " " . DOL_VERSION;
2378
    }
2379
2380
    if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2381
        $btnUser = '<!-- div for user link -->
2382
	    <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2383
	        <a href="' . constant('BASE_URL') . '/user/card.php?id=' . $user->id . '" class="dropdown-toggle login-dropdown-a valignmiddle" data-toggle="dropdown">
2384
	            ' . $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>' : '') . '
2385
	        </a>
2386
	        <div class="dropdown-menu">
2387
	            <!-- User image -->
2388
	            <div class="user-header">
2389
	                ' . $userDropDownImage . '
2390
	                <p>
2391
	                    ' . $profilName . '<br>';
2392
        if ($user->datelastlogin) {
2393
            $title = $langs->trans("ConnectedSince") . ' : ' . dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
2394
            if ($user->datepreviouslogin) {
2395
                $title .= '<br>' . $langs->trans("PreviousConnexion") . ' : ' . dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
2396
            }
2397
        }
2398
        $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>';
2399
        if ($user->datepreviouslogin) {
2400
            $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>';
2401
        }
2402
2403
        //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
2404
        $btnUser .= '
2405
	                </p>
2406
	            </div>
2407
2408
	            <!-- Menu Body user-->
2409
	            <div class="user-body">' . $dropdownBody . '</div>
2410
2411
	            <!-- Menu Footer-->
2412
	            <div class="user-footer">
2413
	                <div class="pull-left">
2414
	                    ' . $profilLink . '
2415
	                </div>
2416
	                <div class="pull-left">
2417
	                    ' . $virtuelcardLink . '
2418
	                </div>
2419
	                <div class="pull-right">
2420
	                    ' . $logoutLink . '
2421
	                </div>
2422
	                <div class="clearboth"></div>
2423
	            </div>
2424
2425
	        </div>
2426
	    </div>';
2427
    } else {
2428
        $btnUser = '<!-- div for user link text browser -->
2429
	    <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
2430
	    	<a href="' . constant('BASE_URL') . '/user/card.php?id=' . $user->id . '" class="valignmiddle" alt="' . $langs->trans("MyUserCard") . '">
2431
	    	' . $userImage . (empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft small">' . dol_trunc($user->firstname ? $user->firstname : $user->login, 10) . '</span>' : '') . '
2432
	    	</a>
2433
		</div>';
2434
    }
2435
2436
    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
2437
        $btnUser .= '
2438
        <!-- Code to show/hide the user drop-down -->
2439
        <script>
2440
		function closeTopMenuLoginDropdown() {
2441
			//console.log("close login dropdown");	// This is call at each click on page, so we disable the log
2442
			// Hide the menus.
2443
            jQuery("#topmenu-login-dropdown").removeClass("open");
2444
		}
2445
        jQuery(document).ready(function() {
2446
            jQuery(document).on("click", function(event) {
2447
				// console.log("Click somewhere on screen");
2448
                if (!$(event.target).closest("#topmenu-login-dropdown").length) {
2449
					closeTopMenuLoginDropdown();
2450
                }
2451
            });
2452
		';
2453
2454
2455
        //if ($conf->theme != 'md') {
2456
        $btnUser .= '
2457
	            jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
2458
					console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
2459
					event.preventDefault();
2460
	                jQuery("#topmenu-login-dropdown").toggleClass("open");
2461
	            });
2462
2463
	            jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
2464
					console.log("Click on #topmenulogincompanyinfo-btn");
2465
	                jQuery("#topmenulogincompanyinfo").slideToggle();
2466
	            });
2467
2468
	            jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
2469
					console.log("Click on #topmenuloginmoreinfo-btn");
2470
	                jQuery("#topmenuloginmoreinfo").slideToggle();
2471
	            });';
2472
        //}
2473
2474
        $btnUser .= '
2475
        });
2476
        </script>
2477
        ';
2478
    }
2479
2480
    return $btnUser;
2481
}
2482
2483
/**
2484
 * Build the tooltip on top menu quick add
2485
 *
2486
 * @return  string                  HTML content
2487
 */
2488
function top_menu_quickadd()
2489
{
2490
    global $conf, $langs;
2491
2492
    // Button disabled on text browser
2493
    if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2494
        return '';
2495
    }
2496
2497
    $html = '';
2498
2499
    // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
2500
    // accesskey is for Mac:               CTRL + key for all browsers
2501
    $stringforfirstkey = $langs->trans("KeyboardShortcut");
2502
    if ($conf->browser->os === 'macintosh') {
2503
        $stringforfirstkey .= ' CTL +';
2504
    } else {
2505
        if ($conf->browser->name == 'chrome') {
2506
            $stringforfirstkey .= ' ALT +';
2507
        } elseif ($conf->browser->name == 'firefox') {
2508
            $stringforfirstkey .= ' ALT + SHIFT +';
2509
        } else {
2510
            $stringforfirstkey .= ' CTL +';
2511
        }
2512
    }
2513
2514
    if (!empty($conf->use_javascript_ajax)) {
2515
        $html .= '<!-- div for quick add link -->
2516
    <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
2517
        <a accesskey="a" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="' . $langs->trans('QuickAdd') . ' (' . $stringforfirstkey . ' a)"><i class="fa fa-plus-circle"></i></a>
2518
        <div class="dropdown-menu">' . printDropdownQuickadd() . '</div>
2519
    </div>';
2520
        if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) {    // This may be set by some pages that use different jquery version to avoid errors
2521
            $html .= '
2522
        <!-- Code to show/hide the user drop-down for the quick add -->
2523
        <script>
2524
        jQuery(document).ready(function() {
2525
            jQuery(document).on("click", function(event) {
2526
                if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
2527
                    // Hide the menus.
2528
                    $("#topmenu-quickadd-dropdown").removeClass("open");
2529
                }
2530
            });
2531
            $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
2532
				console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
2533
                openQuickAddDropDown(event);
2534
            });
2535
2536
            // Key map shortcut
2537
            $(document).keydown(function(event){
2538
				var ostype = \'' . dol_escape_js($conf->browser->os) . '\';
2539
				if (ostype === "macintosh") {
2540
					if ( event.which === 65 && event.ctrlKey ) {
2541
						console.log(\'control + a : trigger open quick add dropdown\');
2542
						openQuickAddDropDown(event);
2543
					}
2544
				} else {
2545
					if ( event.which === 65 && event.ctrlKey && event.shiftKey ) {
2546
						console.log(\'control + shift + a : trigger open quick add dropdown\');
2547
						openQuickAddDropDown(event);
2548
					}
2549
				}
2550
            });
2551
2552
            var openQuickAddDropDown = function(event) {
2553
                event.preventDefault();
2554
                $("#topmenu-quickadd-dropdown").toggleClass("open");
2555
                //$("#top-quickadd-search-input").focus();
2556
            }
2557
        });
2558
        </script>
2559
        ';
2560
        }
2561
    }
2562
2563
    return $html;
2564
}
2565
2566
/**
2567
 * Generate list of quickadd items
2568
 *
2569
 * @return string HTML output
2570
 */
2571
function printDropdownQuickadd()
2572
{
2573
    global $user, $langs, $hookmanager;
2574
2575
    $items = array(
2576
        'items' => array(
2577
            array(
2578
                "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
2579
                "title" => "MenuNewMember@members",
2580
                "name" => "Adherent@members",
2581
                "picto" => "object_member",
2582
                "activation" => isModEnabled('member') && $user->hasRight("adherent", "write"), // vs hooking
2583
                "position" => 5,
2584
            ),
2585
            array(
2586
                "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
2587
                "title" => "MenuNewThirdParty@companies",
2588
                "name" => "ThirdParty@companies",
2589
                "picto" => "object_company",
2590
                "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
2591
                "position" => 10,
2592
            ),
2593
            array(
2594
                "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
2595
                "title" => "NewContactAddress@companies",
2596
                "name" => "Contact@companies",
2597
                "picto" => "object_contact",
2598
                "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
2599
                "position" => 20,
2600
            ),
2601
            array(
2602
                "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
2603
                "title" => "NewPropal@propal",
2604
                "name" => "Proposal@propal",
2605
                "picto" => "object_propal",
2606
                "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
2607
                "position" => 30,
2608
            ),
2609
2610
            array(
2611
                "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
2612
                "title" => "NewOrder@orders",
2613
                "name" => "Order@orders",
2614
                "picto" => "object_order",
2615
                "activation" => isModEnabled('order') && $user->hasRight("commande", "write"), // vs hooking
2616
                "position" => 40,
2617
            ),
2618
            array(
2619
                "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
2620
                "title" => "NewBill@bills",
2621
                "name" => "Bill@bills",
2622
                "picto" => "object_bill",
2623
                "activation" => isModEnabled('invoice') && $user->hasRight("facture", "write"), // vs hooking
2624
                "position" => 50,
2625
            ),
2626
            array(
2627
                "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
2628
                "title" => "NewContractSubscription@contracts",
2629
                "name" => "Contract@contracts",
2630
                "picto" => "object_contract",
2631
                "activation" => isModEnabled('contract') && $user->hasRight("contrat", "write"), // vs hooking
2632
                "position" => 60,
2633
            ),
2634
            array(
2635
                "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
2636
                "title" => "SupplierProposalNew@supplier_proposal",
2637
                "name" => "SupplierProposal@supplier_proposal",
2638
                "picto" => "supplier_proposal",
2639
                "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
2640
                "position" => 70,
2641
            ),
2642
            array(
2643
                "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
2644
                "title" => "NewSupplierOrderShort@orders",
2645
                "name" => "SupplierOrder@orders",
2646
                "picto" => "supplier_order",
2647
                "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "commande", "write")) || (isModEnabled("supplier_order") && $user->hasRight("supplier_invoice", "write")), // vs hooking
2648
                "position" => 80,
2649
            ),
2650
            array(
2651
                "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
2652
                "title" => "NewBill@bills",
2653
                "name" => "SupplierBill@bills",
2654
                "picto" => "supplier_invoice",
2655
                "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "write")) || (isModEnabled("supplier_invoice") && $user->hasRight("supplier_invoice", "write")), // vs hooking
2656
                "position" => 90,
2657
            ),
2658
            array(
2659
                "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
2660
                "title" => "NewTicket@ticket",
2661
                "name" => "Ticket@ticket",
2662
                "picto" => "ticket",
2663
                "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
2664
                "position" => 100,
2665
            ),
2666
            array(
2667
                "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
2668
                "title" => "NewIntervention@interventions",
2669
                "name" => "Intervention@interventions",
2670
                "picto" => "intervention",
2671
                "activation" => isModEnabled('intervention') && $user->hasRight("ficheinter", "creer"), // vs hooking
2672
                "position" => 110,
2673
            ),
2674
            array(
2675
                "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
2676
                "title" => "NewProduct@products",
2677
                "name" => "Product@products",
2678
                "picto" => "object_product",
2679
                "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
2680
                "position" => 400,
2681
            ),
2682
            array(
2683
                "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
2684
                "title" => "NewService@products",
2685
                "name" => "Service@products",
2686
                "picto" => "object_service",
2687
                "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
2688
                "position" => 410,
2689
            ),
2690
            array(
2691
                "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
2692
                "title" => "AddUser@users",
2693
                "name" => "User@users",
2694
                "picto" => "user",
2695
                "activation" => $user->hasRight("user", "user", "write"), // vs hooking
2696
                "position" => 500,
2697
            ),
2698
        ),
2699
    );
2700
2701
    $dropDownQuickAddHtml = '';
2702
2703
    // Define $dropDownQuickAddHtml
2704
    $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
2705
    $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
2706
2707
    // Allow the $items of the menu to be manipulated by modules
2708
    $parameters = array();
2709
    $hook_items = $items;
2710
    $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks
2711
    if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
2712
        if ($reshook == 0) {
2713
            $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
2714
        } else {
2715
            $items = $hookmanager->resArray; // replace
2716
        }
2717
2718
        // Sort menu items by 'position' value
2719
        $position = array();
2720
        foreach ($items['items'] as $key => $row) {
2721
            $position[$key] = $row['position'];
2722
        }
2723
        $array1_sort_order = SORT_ASC;
2724
        array_multisort($position, $array1_sort_order, $items['items']);
2725
    }
2726
2727
    foreach ($items['items'] as $item) {
2728
        if (!$item['activation']) {
2729
            continue;
2730
        }
2731
        $langs->load(explode('@', $item['title'])[1]);
2732
        $langs->load(explode('@', $item['name'])[1]);
2733
        $dropDownQuickAddHtml .= '
2734
			<a class="dropdown-item quickadd-item" href="' . constant('DOL_URL_ROOT') . $item['url'] . '" title="' . $langs->trans(explode('@', $item['title'])[0]) . '">
2735
			' . img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
2736
		';
2737
    }
2738
2739
    $dropDownQuickAddHtml .= '</div>';
2740
    $dropDownQuickAddHtml .= '</div>';
2741
2742
    return $dropDownQuickAddHtml;
2743
}
2744
2745
/**
2746
 * Build the tooltip on top menu bookmark
2747
 *
2748
 * @return  string                  HTML content
2749
 */
2750
function top_menu_bookmark()
2751
{
2752
    global $langs, $conf, $db, $user;
2753
2754
    $html = '';
2755
2756
    // Define $bookmarks
2757
    if (!isModEnabled('bookmark') || !$user->hasRight('bookmark', 'lire')) {
2758
        return $html;
2759
    }
2760
2761
    // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
2762
    // accesskey is for Mac:               CTRL + key for all browsers
2763
    $stringforfirstkey = $langs->trans("KeyboardShortcut");
2764
    if ($conf->browser->os === 'macintosh') {
2765
        $stringforfirstkey .= ' CTL +';
2766
    } else {
2767
        if ($conf->browser->name == 'chrome') {
2768
            $stringforfirstkey .= ' ALT +';
2769
        } elseif ($conf->browser->name == 'firefox') {
2770
            $stringforfirstkey .= ' ALT + SHIFT +';
2771
        } else {
2772
            $stringforfirstkey .= ' CTL +';
2773
        }
2774
    }
2775
2776
    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
2777
        include_once DOL_DOCUMENT_ROOT . '/bookmarks/lib/bookmarks.lib.php';
2778
        $langs->load("bookmarks");
2779
2780
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2781
            $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
2782
            $html .= printDropdownBookmarksList();
2783
            $html .= '</div>';
2784
        } else {
2785
            $html .= '<!-- div for bookmark link -->
2786
	        <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
2787
	            <a accesskey="b" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="' . $langs->trans('Bookmarks') . ' (' . $stringforfirstkey . ' b)"><i class="fa fa-star"></i></a>
2788
	            <div class="dropdown-menu">
2789
	                ' . printDropdownBookmarksList() . '
2790
	            </div>
2791
	        </div>';
2792
2793
            $html .= '
2794
	        <!-- Code to show/hide the bookmark drop-down -->
2795
	        <script>
2796
	        jQuery(document).ready(function() {
2797
	            jQuery(document).on("click", function(event) {
2798
	                if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
2799
						//console.log("close bookmark dropdown - we click outside");
2800
	                    // Hide the menus.
2801
	                    $("#topmenu-bookmark-dropdown").removeClass("open");
2802
	                }
2803
	            });
2804
2805
	            jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
2806
					console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
2807
					openBookMarkDropDown(event);
2808
	            });
2809
2810
	            // Key map shortcut
2811
	            jQuery(document).keydown(function(event) {
2812
					var ostype = \'' . dol_escape_js($conf->browser->os) . '\';
2813
					if (ostype === "macintosh") {
2814
						if ( event.which === 66 && event.ctrlKey ) {
2815
							console.log("Click on control + b : trigger open bookmark dropdown");
2816
							openBookMarkDropDown(event);
2817
						}
2818
					} else {
2819
						if ( event.which === 66 && event.ctrlKey && event.shiftKey ) {
2820
							console.log("Click on control + shift + b : trigger open bookmark dropdown");
2821
							openBookMarkDropDown(event);
2822
						}
2823
					}
2824
	            });
2825
2826
	            var openBookMarkDropDown = function(event) {
2827
	                event.preventDefault();
2828
	                jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
2829
	                jQuery("#top-bookmark-search-input").focus();
2830
	            }
2831
2832
	        });
2833
	        </script>
2834
	        ';
2835
        }
2836
    }
2837
    return $html;
2838
}
2839
2840
/**
2841
 * Build the tooltip on top menu tsearch
2842
 *
2843
 * @return  string                  HTML content
2844
 */
2845
function top_menu_search()
2846
{
2847
    global $langs, $conf, $db, $user, $hookmanager;
2848
2849
    $html = '';
2850
2851
    $usedbyinclude = 1;
2852
    $arrayresult = array();
2853
    include DOL_DOCUMENT_ROOT . '/core/ajax/selectsearchbox.php'; // This sets $arrayresult
2854
2855
    // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
2856
    // accesskey is for Mac:               CTRL + key for all browsers
2857
    $stringforfirstkey = $langs->trans("KeyboardShortcut");
2858
    if ($conf->browser->name == 'chrome') {
2859
        $stringforfirstkey .= ' ALT +';
2860
    } elseif ($conf->browser->name == 'firefox') {
2861
        $stringforfirstkey .= ' ALT + SHIFT +';
2862
    } else {
2863
        $stringforfirstkey .= ' CTL +';
2864
    }
2865
2866
    $searchInput = '<input type="search" name="search_all"' . ($stringforfirstkey ? ' title="' . dol_escape_htmltag($stringforfirstkey . ' s') . '"' : '') . ' id="top-global-search-input" class="dropdown-search-input search_component_input" placeholder="' . $langs->trans('Search') . '" autocomplete="off">';
2867
2868
    $defaultAction = '';
2869
    $buttonList = '<div class="dropdown-global-search-button-list" >';
2870
    // Menu with all searchable items
2871
    foreach ($arrayresult as $keyItem => $item) {
2872
        if (empty($defaultAction)) {
2873
            $defaultAction = $item['url'];
2874
        }
2875
        $buttonList .= '<button class="dropdown-item global-search-item tdoverflowmax300" data-target="' . dol_escape_htmltag($item['url']) . '" >';
2876
        $buttonList .= $item['text'];
2877
        $buttonList .= '</button>';
2878
    }
2879
    $buttonList .= '</div>';
2880
2881
    $dropDownHtml = '<form role="search" id="top-menu-action-search" name="actionsearch" method="GET" action="' . $defaultAction . '">';
2882
2883
    $dropDownHtml .= '
2884
        <!-- search input -->
2885
        <div class="dropdown-header search-dropdown-header">
2886
            ' . $searchInput . '
2887
        </div>
2888
    ';
2889
2890
    $dropDownHtml .= '
2891
        <!-- Menu Body search -->
2892
        <div class="dropdown-body search-dropdown-body">
2893
        ' . $buttonList . '
2894
        </div>
2895
        ';
2896
2897
    $dropDownHtml .= '</form>';
2898
2899
    // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
2900
    // accesskey is for Mac:               CTRL + key for all browsers
2901
    $stringforfirstkey = $langs->trans("KeyboardShortcut");
2902
    if ($conf->browser->name == 'chrome') {
2903
        $stringforfirstkey .= ' ALT +';
2904
    } elseif ($conf->browser->name == 'firefox') {
2905
        $stringforfirstkey .= ' ALT + SHIFT +';
2906
    } else {
2907
        $stringforfirstkey .= ' CTL +';
2908
    }
2909
2910
    $html .= '<!-- div for Global Search -->
2911
    <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
2912
        <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="' . $langs->trans('Search') . ' (' . $stringforfirstkey . ' s)">
2913
            <i class="fa fa-search" aria-hidden="true" ></i>
2914
        </a>
2915
        <div class="dropdown-menu dropdown-search">
2916
            ' . $dropDownHtml . '
2917
        </div>
2918
    </div>';
2919
2920
    $html .= '
2921
    <!-- Code to show/hide the user drop-down -->
2922
    <script>
2923
    jQuery(document).ready(function() {
2924
2925
        // prevent submitting form on press ENTER
2926
        jQuery("#top-global-search-input").keydown(function (e) {
2927
            if (e.keyCode == 13 || e.keyCode == 40) {
2928
                var inputs = $(this).parents("form").eq(0).find(":button");
2929
                if (inputs[inputs.index(this) + 1] != null) {
2930
                    inputs[inputs.index(this) + 1].focus();
2931
					 if (e.keyCode == 13){
2932
						 inputs[inputs.index(this) + 1].trigger("click");
2933
					 }
2934
2935
                }
2936
                e.preventDefault();
2937
                return false;
2938
            }
2939
        });
2940
2941
        // arrow key nav
2942
        jQuery(document).keydown(function(e) {
2943
			// Get the focused element:
2944
			var $focused = $(":focus");
2945
			if($focused.length && $focused.hasClass("global-search-item")){
2946
2947
           		// UP - move to the previous line
2948
				if (e.keyCode == 38) {
2949
				    e.preventDefault();
2950
					$focused.prev().focus();
2951
				}
2952
2953
				// DOWN - move to the next line
2954
				if (e.keyCode == 40) {
2955
				    e.preventDefault();
2956
					$focused.next().focus();
2957
				}
2958
			}
2959
        });
2960
2961
2962
        // submit form action
2963
        jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
2964
            jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
2965
            jQuery("#top-menu-action-search").submit();
2966
        });
2967
2968
        // close drop down
2969
        jQuery(document).on("click", function(event) {
2970
			if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
2971
				console.log("click close search - we click outside");
2972
                // Hide the menus.
2973
                jQuery("#topmenu-global-search-dropdown").removeClass("open");
2974
            }
2975
        });
2976
2977
        // Open drop down
2978
        jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
2979
			console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
2980
            openGlobalSearchDropDown();
2981
        });
2982
2983
        // Key map shortcut
2984
        jQuery(document).keydown(function(e){
2985
              if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
2986
                 console.log(\'control + shift + f : trigger open global-search dropdown\');
2987
                 openGlobalSearchDropDown();
2988
              }
2989
              if ( e.which === 70 && e.alKey ) {
2990
                 console.log(\'alt + f : trigger open global-search dropdown\');
2991
                 openGlobalSearchDropDown();
2992
              }
2993
        });
2994
2995
        var openGlobalSearchDropDown = function() {
2996
            jQuery("#topmenu-global-search-dropdown").toggleClass("open");
2997
            jQuery("#top-global-search-input").focus();
2998
        }
2999
3000
    });
3001
    </script>
3002
    ';
3003
3004
    return $html;
3005
}
3006
3007
/**
3008
 *  Show left menu bar
3009
 *
3010
 * @param string $menu_array_before Table of menu entries to show before entries of menu handler. This param is deprecated and must be provided to ''.
3011
 * @param string $helppagename Name of wiki page for help ('' by default).
3012
 *                                                  Syntax is: For a wiki page: EN:EnglishPage|FR:FrenchPage|ES:SpanishPage|DE:GermanPage
3013
 *                                                  For other external page: http://server/url
3014
 * @param string $notused Deprecated. Used in past to add content into left menu. Hooks can be used now.
3015
 * @param array $menu_array_after Table of menu entries to show after entries of menu handler
3016
 * @param int $leftmenuwithoutmainarea Must be set to 1. 0 by default for backward compatibility with old modules.
3017
 * @param string $title Title of web page
3018
 * @param int<0,1> $acceptdelayedhtml 1 if caller request to have html delayed content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
3019
 * @return void
3020
 */
3021
function left_menu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = array(), $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
3022
{
3023
    global $user, $conf, $langs, $db, $form;
3024
    global $hookmanager, $menumanager;
3025
3026
    $searchform = '';
3027
3028
    if (!empty($menu_array_before)) {
3029
        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);
3030
    }
3031
3032
    if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
3033
        // Instantiate hooks for external modules
3034
        $hookmanager->initHooks(array('leftblock'));
3035
3036
        print "\n" . '<!-- Begin side-nav id-left -->' . "\n" . '<div class="side-nav"><div id="id-left">' . "\n";
3037
        print "\n";
3038
3039
        if (!is_object($form)) {
3040
            $form = new Form($db);
3041
        }
3042
        $selected = -1;
3043
        if (!getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
3044
            // Select with select2 is awful on smartphone. TODO Is this still true with select2 v4 ?
3045
            if ($conf->browser->layout == 'phone') {
3046
                $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
3047
            }
3048
3049
            $usedbyinclude = 1;
3050
            $arrayresult = array();
3051
            include DOL_DOCUMENT_ROOT . '/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
3052
3053
            if ($conf->use_javascript_ajax && !getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3054
                // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
3055
                // accesskey is for Mac:               CTRL + key for all browsers
3056
                $stringforfirstkey = $langs->trans("KeyboardShortcut");
3057
                if ($conf->browser->name == 'chrome') {
3058
                    $stringforfirstkey .= ' ALT +';
3059
                } elseif ($conf->browser->name == 'firefox') {
3060
                    $stringforfirstkey .= ' ALT + SHIFT +';
3061
                } else {
3062
                    $stringforfirstkey .= ' CTL +';
3063
                }
3064
3065
                //$textsearch = $langs->trans("Search");
3066
                $textsearch = '<span class="fa fa-search paddingright pictofixedwidth"></span>' . $langs->trans("Search");
3067
                $searchform .= $form->selectArrayFilter('searchselectcombo', $arrayresult, $selected, 'accesskey="s"', 1, 0, (!getDolGlobalString('MAIN_SEARCHBOX_CONTENT_LOADED_BEFORE_KEY') ? 1 : 0), 'vmenusearchselectcombo', 1, $textsearch, 1, $stringforfirstkey . ' s');
3068
            } else {
3069
                if (is_array($arrayresult)) {
3070
                    foreach ($arrayresult as $key => $val) {
3071
                        $searchform .= printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft' . $key, $val['img']);
3072
                    }
3073
                }
3074
            }
3075
3076
            // Execute hook printSearchForm
3077
            $parameters = array('searchform' => $searchform);
3078
            $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
3079
            if (empty($reshook)) {
3080
                $searchform .= $hookmanager->resPrint;
3081
            } else {
3082
                $searchform = $hookmanager->resPrint;
3083
            }
3084
3085
            // Force special value for $searchform for text browsers or very old search form
3086
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') || empty($conf->use_javascript_ajax)) {
3087
                $urltosearch = constant('BASE_URL') . '/core/search_page.php?showtitlebefore=1';
3088
                $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>';
3089
            } elseif ($conf->use_javascript_ajax && getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
3090
                $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>';
3091
                $searchform .= '<script>
3092
            	jQuery(document).ready(function () {
3093
            		jQuery("#divsearchforms1").click(function(){
3094
	                   jQuery("#divsearchforms2").toggle();
3095
	               });
3096
            	});
3097
                </script>' . "\n";
3098
                $searchform .= '</div>';
3099
            }
3100
3101
            // Key map shortcut
3102
            $searchform .= '<script>
3103
				jQuery(document).keydown(function(e){
3104
					if( e.which === 70 && e.ctrlKey && e.shiftKey ){
3105
						console.log(\'control + shift + f : trigger open global-search dropdown\');
3106
		                openGlobalSearchDropDown();
3107
		            }
3108
		            if( (e.which === 83 || e.which === 115) && e.altKey ){
3109
		                console.log(\'alt + s : trigger open global-search dropdown\');
3110
		                openGlobalSearchDropDown();
3111
		            }
3112
		        });
3113
3114
		        var openGlobalSearchDropDown = function() {
3115
		            jQuery("#searchselectcombo").select2(\'open\');
3116
		        }
3117
			</script>';
3118
        }
3119
3120
        // Left column
3121
        print '<!-- Begin left menu -->' . "\n";
3122
3123
        print '<div class="vmenu"' . (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' alt="Left menu"' : '') . '>' . "\n\n";
3124
3125
        // Show left menu with other forms
3126
        $menumanager->menu_array = $menu_array_before;
3127
        $menumanager->menu_array_after = $menu_array_after;
3128
        $menumanager->showmenu('left', array('searchform' => $searchform)); // output menu_array and menu found in database
3129
3130
        // Dolibarr version + help + bug report link
3131
        print "\n";
3132
        print "<!-- Begin Help Block-->\n";
3133
        print '<div id="blockvmenuhelp" class="blockvmenuhelp">' . "\n";
3134
3135
        // Version
3136
        if (getDolGlobalString('MAIN_SHOW_VERSION')) {    // Version is already on help picto and on login page.
3137
            $doliurl = 'https://www.dolibarr.org';
3138
            //local communities
3139
            if (preg_match('/fr/i', $langs->defaultlang)) {
3140
                $doliurl = 'https://www.dolibarr.fr';
3141
            }
3142
            if (preg_match('/es/i', $langs->defaultlang)) {
3143
                $doliurl = 'https://www.dolibarr.es';
3144
            }
3145
            if (preg_match('/de/i', $langs->defaultlang)) {
3146
                $doliurl = 'https://www.dolibarr.de';
3147
            }
3148
            if (preg_match('/it/i', $langs->defaultlang)) {
3149
                $doliurl = 'https://www.dolibarr.it';
3150
            }
3151
            if (preg_match('/gr/i', $langs->defaultlang)) {
3152
                $doliurl = 'https://www.dolibarr.gr';
3153
            }
3154
3155
            $appli = constant('DOL_APPLICATION_TITLE');
3156
            if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
3157
                $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
3158
                $doliurl = '';
3159
                if (preg_match('/\d\.\d/', $appli)) {
3160
                    if (!preg_match('/' . preg_quote(DOL_VERSION) . '/', $appli)) {
3161
                        $appli .= " (" . DOL_VERSION . ")"; // If new title contains a version that is different than core
3162
                    }
3163
                } else {
3164
                    $appli .= " " . DOL_VERSION;
3165
                }
3166
            } else {
3167
                $appli .= " " . DOL_VERSION;
3168
            }
3169
            print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
3170
            if ($doliurl) {
3171
                print '<a class="help" target="_blank" rel="noopener noreferrer" href="' . $doliurl . '">';
3172
            } else {
3173
                print '<span class="help">';
3174
            }
3175
            print $appli;
3176
            if ($doliurl) {
3177
                print '</a>';
3178
            } else {
3179
                print '</span>';
3180
            }
3181
            print '</div>' . "\n";
3182
        }
3183
3184
        // Link to bugtrack
3185
        if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3186
            require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
3187
3188
            if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK') == 'github') {
3189
                $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
3190
                $bugbaseurl .= '&title=';
3191
                $bugbaseurl .= urlencode("Bug: ");
3192
                $bugbaseurl .= '&body=';
3193
                $bugbaseurl .= urlencode("# Instructions\n");
3194
                $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");
3195
                $bugbaseurl .= urlencode("*Please:*\n");
3196
                $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
3197
                $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
3198
                $bugbaseurl .= urlencode("\n");
3199
                $bugbaseurl .= urlencode("\n");
3200
                $bugbaseurl .= urlencode("# Bug\n");
3201
                $bugbaseurl .= urlencode("[*Short description*]\n");
3202
                $bugbaseurl .= urlencode("\n");
3203
                $bugbaseurl .= urlencode("## Environment\n");
3204
                $bugbaseurl .= urlencode("- **Version**: " . DOL_VERSION . "\n");
3205
                $bugbaseurl .= urlencode("- **OS**: " . php_uname('s') . "\n");
3206
                $bugbaseurl .= urlencode("- **Web server**: " . $_SERVER["SERVER_SOFTWARE"] . "\n");
3207
                $bugbaseurl .= urlencode("- **PHP**: " . php_sapi_name() . ' ' . phpversion() . "\n");
3208
                $bugbaseurl .= urlencode("- **Database**: " . $db::LABEL . ' ' . $db->getVersion() . "\n");
3209
                $bugbaseurl .= urlencode("- **URL(s)**: " . $_SERVER["REQUEST_URI"] . "\n");
3210
                $bugbaseurl .= urlencode("\n");
3211
                $bugbaseurl .= urlencode("## Expected and actual behavior\n");
3212
                $bugbaseurl .= urlencode("[*Verbose description*]\n");
3213
                $bugbaseurl .= urlencode("\n");
3214
                $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
3215
                $bugbaseurl .= urlencode("[*Verbose description*]\n");
3216
                $bugbaseurl .= urlencode("\n");
3217
                $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, alixar.log, debugging information…)\n");
3218
                $bugbaseurl .= urlencode("[*Files*]\n");
3219
                $bugbaseurl .= urlencode("\n");
3220
3221
                $bugbaseurl .= urlencode("\n");
3222
                $bugbaseurl .= urlencode("## Report\n");
3223
            } elseif (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
3224
                $bugbaseurl = getDolGlobalString('MAIN_BUGTRACK_ENABLELINK');
3225
            } else {
3226
                $bugbaseurl = "";
3227
            }
3228
3229
            // Execute hook printBugtrackInfo
3230
            $parameters = array('bugbaseurl' => $bugbaseurl);
3231
            $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
3232
            if (empty($reshook)) {
3233
                $bugbaseurl .= $hookmanager->resPrint;
3234
            } else {
3235
                $bugbaseurl = $hookmanager->resPrint;
3236
            }
3237
3238
            print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
3239
            print '<a class="help" target="_blank" rel="noopener noreferrer" href="' . $bugbaseurl . '"><i class="fas fa-bug"></i> ' . $langs->trans("FindBug") . '</a>';
3240
            print '</div>';
3241
        }
3242
3243
        print "</div>\n";
3244
        print "<!-- End Help Block-->\n";
3245
        print "\n";
3246
3247
        print "</div>\n";
3248
        print "<!-- End left menu -->\n";
3249
        print "\n";
3250
3251
        // Execute hook printLeftBlock
3252
        $parameters = array();
3253
        $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
3254
        print $hookmanager->resPrint;
3255
3256
        print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
3257
    }
3258
3259
    print "\n";
3260
    print '<!-- Begin right area -->' . "\n";
3261
3262
    if (empty($leftmenuwithoutmainarea)) {
3263
        main_area($title);
3264
    }
3265
}
3266
3267
3268
/**
3269
 *  Begin main area
3270
 *
3271
 * @param string $title Title
3272
 * @return void
3273
 */
3274
function main_area($title = '')
3275
{
3276
    global $conf, $langs, $hookmanager;
3277
3278
    if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup')) {
3279
        print '<div id="id-right">';
3280
    }
3281
3282
    print "\n";
3283
3284
    print '<!-- Begin div class="fiche" -->' . "\n" . '<div class="fiche">' . "\n";
3285
3286
    $hookmanager->initHooks(array('main'));
3287
    $parameters = array();
3288
    $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
3289
    print $hookmanager->resPrint;
3290
3291
    if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
3292
        print info_admin($langs->trans("WarningYouAreInMaintenanceMode", getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')), 0, 0, 1, 'warning maintenancemode');
3293
    }
3294
3295
    // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
3296
    if (getDolGlobalString('SHOW_SOCINFO_ON_PRINT') && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'aZ09'))) {
3297
        $parameters = array();
3298
        $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
3299
        if (empty($reshook)) {
3300
            print '<!-- Begin show mysoc info header -->' . "\n";
3301
            print '<div id="mysoc-info-header">' . "\n";
3302
            print '<table class="centpercent div-table-responsive">' . "\n";
3303
            print '<tbody>';
3304
            print '<tr><td rowspan="0" class="width20p">';
3305
            if (getDolGlobalString('MAIN_SHOW_LOGO') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalString('MAIN_INFO_SOCIETE_LOGO')) {
3306
                print '<img id="mysoc-info-header-logo" style="max-width:100%" alt="" src="' . constant('BASE_URL') . '/viewimage.php?cache=1&modulepart=mycompany&file=' . urlencode('logos/' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_LOGO'))) . '">';
3307
            }
3308
            print '</td><td  rowspan="0" class="width50p"></td></tr>' . "\n";
3309
            print '<tr><td class="titre bold">' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_NOM')) . '</td></tr>' . "\n";
3310
            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";
3311
            if (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) {
3312
                print '<tr><td style="padding-left: 1em" class="small">' . $langs->trans("Phone") . ' : ' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) . '</td></tr>';
3313
            }
3314
            if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
3315
                print '<tr><td style="padding-left: 1em" class="small">' . $langs->trans("Email") . ' : ' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) . '</td></tr>';
3316
            }
3317
            if (getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) {
3318
                print '<tr><td style="padding-left: 1em" class="small">' . $langs->trans("Web") . ' : ' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) . '</td></tr>';
3319
            }
3320
            print '</tbody>';
3321
            print '</table>' . "\n";
3322
            print '</div>' . "\n";
3323
            print '<!-- End show mysoc info header -->' . "\n";
3324
        }
3325
    }
3326
}
3327
3328
3329
/**
3330
 *  Return helpbaseurl, helppage and mode
3331
 *
3332
 * @param string $helppagename Page name ('EN:xxx,ES:eee,FR:fff,DE:ddd...' or 'http://localpage')
3333
 * @param Translate $langs Language
3334
 * @return array{helpbaseurl:string,helppage:string,mode:string}   Array of help urls
3335
 */
3336
function getHelpParamFor($helppagename, $langs)
3337
{
3338
    $helpbaseurl = '';
3339
    $helppage = '';
3340
    $mode = '';
3341
3342
    if (preg_match('/^http/i', $helppagename)) {
3343
        // If complete URL
3344
        $helpbaseurl = '%s';
3345
        $helppage = $helppagename;
3346
        $mode = 'local';
3347
    } else {
3348
        // If WIKI URL
3349
        $reg = array();
3350
        if (preg_match('/^es/i', $langs->defaultlang)) {
3351
            $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3352
            if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
3353
                $helppage = $reg[1];
3354
            }
3355
        }
3356
        if (preg_match('/^fr/i', $langs->defaultlang)) {
3357
            $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3358
            if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
3359
                $helppage = $reg[1];
3360
            }
3361
        }
3362
        if (preg_match('/^de/i', $langs->defaultlang)) {
3363
            $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3364
            if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
3365
                $helppage = $reg[1];
3366
            }
3367
        }
3368
        if (empty($helppage)) { // If help page not already found
3369
            $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
3370
            if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
3371
                $helppage = $reg[1];
3372
            }
3373
        }
3374
        $mode = 'wiki';
3375
    }
3376
    return array('helpbaseurl' => $helpbaseurl, 'helppage' => $helppage, 'mode' => $mode);
3377
}
3378
3379
3380
/**
3381
 *  Show a search area.
3382
 *  Used when the javascript quick search is not used.
3383
 *
3384
 * @param string $urlaction Url post
3385
 * @param string $urlobject Url of the link under the search box
3386
 * @param string $title Title search area
3387
 * @param string $htmlmorecss Add more css
3388
 * @param string $htmlinputname Field Name input form
3389
 * @param string $accesskey Accesskey
3390
 * @param string $prefhtmlinputname Complement for id to avoid multiple same id in the page
3391
 * @param string $img Image to use
3392
 * @param int $showtitlebefore Show title before input text instead of into placeholder. This can be set when output is dedicated for text browsers.
3393
 * @param int $autofocus Set autofocus on field
3394
 * @return string
3395
 */
3396
function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
3397
{
3398
    global $langs, $user;
3399
3400
    $ret = '';
3401
    $ret .= '<form action="' . $urlaction . '" method="post" class="searchform nowraponall tagtr">';
3402
    $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
3403
    $ret .= '<input type="hidden" name="savelogin" value="' . dol_escape_htmltag($user->login) . '">';
3404
    if ($showtitlebefore) {
3405
        $ret .= '<div class="tagtd left">' . $title . '</div> ';
3406
    }
3407
    $ret .= '<div class="tagtd">';
3408
    $ret .= img_picto('', $img, '', false, 0, 0, '', 'paddingright width20');
3409
    $ret .= '<input type="text" class="flat ' . $htmlmorecss . '"';
3410
    $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
3411
    $ret .= ($accesskey ? ' accesskey="' . $accesskey . '"' : '');
3412
    $ret .= ' placeholder="' . strip_tags($title) . '"';
3413
    $ret .= ($autofocus ? ' autofocus' : '');
3414
    $ret .= ' name="' . $htmlinputname . '" id="' . $prefhtmlinputname . $htmlinputname . '" />';
3415
    $ret .= '<button type="submit" class="button bordertransp" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
3416
    $ret .= '<span class="fa fa-search"></span>';
3417
    $ret .= '</button>';
3418
    $ret .= '</div>';
3419
    $ret .= "</form>\n";
3420
    return $ret;
3421
}
3422
3423
3424
if (!function_exists("llxFooter")) {
3425
    /**
3426
     * Show HTML footer
3427
     * Close div /DIV class=fiche + /DIV id-right + /DIV id-container + /BODY + /HTML.
3428
     * If global var $delayedhtmlcontent was filled, we output it just before closing the body.
3429
     *
3430
     * @param string $comment A text to add as HTML comment into HTML generated page
3431
     * @param string $zone 'private' (for private pages) or 'public' (for public pages)
3432
     * @param int $disabledoutputofmessages Clear all messages stored into session without displaying them
3433
     * @return  void
3434
     */
3435
    function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
3436
    {
3437
        global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager, $action;
3438
        global $delayedhtmlcontent;
3439
        global $contextpage, $page, $limit, $mode;
3440
        global $dolibarr_distrib;
3441
3442
        $ext = 'layout=' . urlencode($conf->browser->layout) . '&version=' . urlencode(DOL_VERSION);
3443
3444
        // Hook to add more things on all pages within fiche DIV
3445
        $llxfooter = '';
3446
        $parameters = array();
3447
        $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3448
        if (empty($reshook)) {
3449
            $llxfooter .= $hookmanager->resPrint;
3450
        } elseif ($reshook > 0) {
3451
            $llxfooter = $hookmanager->resPrint;
3452
        }
3453
        if ($llxfooter) {
3454
            print $llxfooter;
3455
        }
3456
3457
        // Global html output events ($mesgs, $errors, $warnings)
3458
        dol_htmloutput_events($disabledoutputofmessages);
3459
3460
        // Code for search criteria persistence.
3461
        // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
3462
        if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
3463
            // Clean and save data
3464
            foreach ($user->lastsearch_values_tmp as $key => $val) {
3465
                unset($_SESSION['lastsearch_values_tmp_' . $key]); // Clean array to rebuild it just after
3466
                if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
3467
                    if (empty($val['sortfield'])) {
3468
                        unset($val['sortfield']);
3469
                    }
3470
                    if (empty($val['sortorder'])) {
3471
                        unset($val['sortorder']);
3472
                    }
3473
                    dol_syslog('Save lastsearch_values_tmp_' . $key . '=' . json_encode($val, 0) . " (systematic recording of last search criteria)");
3474
                    $_SESSION['lastsearch_values_tmp_' . $key] = json_encode($val);
3475
                    unset($_SESSION['lastsearch_values_' . $key]);
3476
                }
3477
            }
3478
        }
3479
3480
3481
        $relativepathstring = $_SERVER["PHP_SELF"];
3482
        // Clean $relativepathstring
3483
        if (constant('DOL_URL_ROOT')) {
3484
            $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
3485
        }
3486
        $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
3487
        $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
3488
        if (preg_match('/list\.php$/', $relativepathstring)) {
3489
            unset($_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring]);
3490
            unset($_SESSION['lastsearch_page_tmp_' . $relativepathstring]);
3491
            unset($_SESSION['lastsearch_limit_tmp_' . $relativepathstring]);
3492
            unset($_SESSION['lastsearch_mode_tmp_' . $relativepathstring]);
3493
3494
            if (!empty($contextpage)) {
3495
                $_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring] = $contextpage;
3496
            }
3497
            if (!empty($page) && $page > 0) {
3498
                $_SESSION['lastsearch_page_tmp_' . $relativepathstring] = $page;
3499
            }
3500
            if (!empty($limit) && $limit != $conf->liste_limit) {
3501
                $_SESSION['lastsearch_limit_tmp_' . $relativepathstring] = $limit;
3502
            }
3503
            if (!empty($mode)) {
3504
                $_SESSION['lastsearch_mode_tmp_' . $relativepathstring] = $mode;
3505
            }
3506
3507
            unset($_SESSION['lastsearch_contextpage_' . $relativepathstring]);
3508
            unset($_SESSION['lastsearch_page_' . $relativepathstring]);
3509
            unset($_SESSION['lastsearch_limit_' . $relativepathstring]);
3510
            unset($_SESSION['lastsearch_mode_' . $relativepathstring]);
3511
        }
3512
3513
        // Core error message
3514
        if (getDolGlobalString('MAIN_CORE_ERROR')) {
3515
            // Ajax version
3516
            if ($conf->use_javascript_ajax) {
3517
                $title = img_warning() . ' ' . $langs->trans('CoreErrorTitle');
3518
                print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
3519
            } else {
3520
                // html version
3521
                $msg = img_warning() . ' ' . $langs->trans('CoreErrorMessage');
3522
                print '<div class="error">' . $msg . '</div>';
3523
            }
3524
3525
            //define("MAIN_CORE_ERROR",0);      // Constant was defined and we can't change value of a constant
3526
        }
3527
3528
        print "\n\n";
3529
3530
        print '</div> <!-- End div class="fiche" -->' . "\n"; // End div fiche
3531
3532
        if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup')) {
3533
            print '</div> <!-- End div id-right -->' . "\n"; // End div id-right
3534
        }
3535
3536
        if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
3537
            print '</div> <!-- End div id-container -->' . "\n"; // End div container
3538
        }
3539
3540
        print "\n";
3541
        if ($comment) {
3542
            print '<!-- ' . $comment . ' -->' . "\n";
3543
        }
3544
3545
        printCommonFooter($zone);
3546
3547
        if (!empty($delayedhtmlcontent)) {
3548
            print $delayedhtmlcontent;
3549
        }
3550
3551
        if (!empty($conf->use_javascript_ajax)) {
3552
            print "\n" . '<!-- Includes JS Footer of Dolibarr -->' . "\n";
3553
            print '<script src="' . constant('BASE_URL') . '/core/js/lib_foot.js.php?lang=' . $langs->defaultlang . ($ext ? '&' . $ext : '') . '"></script>' . "\n";
3554
        }
3555
3556
        // Wrapper to add log when clicking on download or preview
3557
        if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
3558
            if (in_array($object->element, array('facture')) && $object->statut > 0) {       // Restrict for the moment to element 'facture'
3559
                print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
3560
                ?>
3561
                <script>
3562
                    jQuery(document).ready(function () {
3563
                        $('a.documentpreview').click(function () {
3564
                            console.log("Call /blockedlog/ajax/block-add on a.documentpreview");
3565
                            $.post('<?php echo DOL_URL_ROOT . "/blockedlog/ajax/block-add.php" ?>'
3566
                                , {
3567
                                    id:<?php echo $object->id; ?>
3568
                                    , element: '<?php echo dol_escape_js($object->element) ?>'
3569
                                    , action: 'DOC_PREVIEW'
3570
                                    , token: '<?php echo currentToken(); ?>'
3571
                                }
3572
                            );
3573
                        });
3574
                        $('a.documentdownload').click(function () {
3575
                            console.log("Call /blockedlog/ajax/block-add a.documentdownload");
3576
                            $.post('<?php echo DOL_URL_ROOT . "/blockedlog/ajax/block-add.php" ?>'
3577
                                , {
3578
                                    id:<?php echo $object->id; ?>
3579
                                    , element: '<?php echo dol_escape_js($object->element) ?>'
3580
                                    , action: 'DOC_DOWNLOAD'
3581
                                    , token: '<?php echo currentToken(); ?>'
3582
                                }
3583
                            );
3584
                        });
3585
                    });
3586
                </script>
3587
                <?php
3588
            }
3589
        }
3590
3591
        // A div for the address popup
3592
        print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
3593
        print '<div id="dialogforpopup" style="display: none;"></div>' . "\n";
3594
3595
        // Add code for the asynchronous anonymous first ping (for telemetry)
3596
        // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
3597
        $forceping = GETPOST('forceping', 'alpha');
3598
        if (($_SERVER["PHP_SELF"] == constant('BASE_URL') . '/index.php') || $forceping) {
3599
            //print '<!-- instance_unique_id='.$conf->file->instance_unique_id.' MAIN_FIRST_PING_OK_ID='.$conf->global->MAIN_FIRST_PING_OK_ID.' -->';
3600
            $hash_unique_id = dol_hash('dolibarr' . $conf->file->instance_unique_id, 'sha256');   // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
3601
3602
            if (
3603
                !getDolGlobalString('MAIN_FIRST_PING_OK_DATE')
3604
                || (!empty($conf->file->instance_unique_id) && ($hash_unique_id != $conf->global->MAIN_FIRST_PING_OK_ID) && (getDolGlobalString('MAIN_FIRST_PING_OK_ID') != 'disabled'))
3605
                || $forceping
3606
            ) {
3607
                // No ping done if we are into an alpha version
3608
                if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
3609
                    print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
3610
                } elseif (empty($_COOKIE['DOLINSTALLNOPING_' . $hash_unique_id]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
3611
                    // MAIN_LAST_PING_KO_DATE
3612
                    // Disable ping if MAIN_LAST_PING_KO_DATE is set and is recent (this month)
3613
                    if (getDolGlobalString('MAIN_LAST_PING_KO_DATE') && substr($conf->global->MAIN_LAST_PING_KO_DATE, 0, 6) == dol_print_date(dol_now(), '%Y%m') && !$forceping) {
3614
                        print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. An error already occurred this month, we will try later. -->\n";
3615
                    } else {
3616
                        include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
3617
3618
                        print "\n" . '<!-- Includes JS for Ping of Dolibarr forceping=' . $forceping . ' MAIN_FIRST_PING_OK_DATE=' . getDolGlobalString("MAIN_FIRST_PING_OK_DATE") . ' MAIN_FIRST_PING_OK_ID=' . getDolGlobalString("MAIN_FIRST_PING_OK_ID") . ' MAIN_LAST_PING_KO_DATE=' . getDolGlobalString("MAIN_LAST_PING_KO_DATE") . ' -->' . "\n";
3619
                        print "\n<!-- JS CODE TO ENABLE the anonymous Ping -->\n";
3620
                        $url_for_ping = getDolGlobalString('MAIN_URL_FOR_PING', "https://ping.dolibarr.org/");
3621
                        // Try to guess the distrib used
3622
                        $distrib = 'standard';
3623
                        if ($_SERVER["SERVER_ADMIN"] == 'doliwamp@localhost') {
3624
                            $distrib = 'doliwamp';
3625
                        }
3626
                        if (!empty($dolibarr_distrib)) {
3627
                            $distrib = $dolibarr_distrib;
3628
                        }
3629
                        ?>
3630
                        <script>
3631
                            jQuery(document).ready(function (tmp) {
3632
                                console.log("Try Ping with hash_unique_id is dol_hash('dolibarr'+instance_unique_id, 'sha256')");
3633
                                $.ajax({
3634
                                    method: "POST",
3635
                                    url: "<?php echo $url_for_ping ?>",
3636
                                    timeout: 500,     // timeout milliseconds
3637
                                    cache: false,
3638
                                    data: {
3639
                                        hash_algo: 'dol_hash-sha256',
3640
                                        hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
3641
                                        action: 'dolibarrping',
3642
                                        version: '<?php echo (float)DOL_VERSION; ?>',
3643
                                        entity: '<?php echo (int)$conf->entity; ?>',
3644
                                        dbtype: '<?php echo dol_escape_js($db->type); ?>',
3645
                                        country_code: '<?php echo $mysoc->country_code ? dol_escape_js($mysoc->country_code) : 'unknown'; ?>',
3646
                                        php_version: '<?php echo dol_escape_js(phpversion()); ?>',
3647
                                        os_version: '<?php echo dol_escape_js(version_os('smr')); ?>',
3648
                                        db_version: '<?php echo dol_escape_js(version_db()); ?>',
3649
                                        distrib: '<?php echo $distrib ? dol_escape_js($distrib) : 'unknown'; ?>',
3650
                                        token: 'notrequired'
3651
                                    },
3652
                                    success: function (data, status, xhr) {   // success callback function (data contains body of response)
3653
                                        console.log("Ping ok");
3654
                                        $.ajax({
3655
                                            method: 'GET',
3656
                                            url: '<?php echo constant('BASE_URL') . '/core/ajax/pingresult.php'; ?>',
3657
                                            timeout: 500,     // timeout milliseconds
3658
                                            cache: false,
3659
                                            data: {
3660
                                                hash_algo: 'dol_hash-sha256',
3661
                                                hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
3662
                                                action: 'firstpingok',
3663
                                                token: '<?php echo currentToken(); ?>'
3664
                                            }, // for update
3665
                                        });
3666
                                    },
3667
                                    error: function (data, status, xhr) {   // error callback function
3668
                                        console.log("Ping ko: " + data);
3669
                                        $.ajax({
3670
                                            method: 'GET',
3671
                                            url: '<?php echo constant('BASE_URL') . '/core/ajax/pingresult.php'; ?>',
3672
                                            timeout: 500,     // timeout milliseconds
3673
                                            cache: false,
3674
                                            data: {
3675
                                                hash_algo: 'dol_hash-sha256',
3676
                                                hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
3677
                                                action: 'firstpingko',
3678
                                                token: '<?php echo currentToken(); ?>'
3679
                                            },
3680
                                        });
3681
                                    }
3682
                                });
3683
                            });
3684
                        </script>
3685
                        <?php
3686
                    }
3687
                } else {
3688
                    $now = dol_now();
3689
                    print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
3690
                    include_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
3691
                    dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_DATE', dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
3692
                    dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_ID', 'disabled', 'chaine', 0, '', $conf->entity);
3693
                }
3694
            }
3695
        }
3696
3697
        $parameters = array();
3698
        $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters); // Note that $action and $object may have been modified by some hooks
3699
        if ($reshook > 0) {
3700
            print $hookmanager->resPrint;
3701
        }
3702
3703
        print "</body>\n";
3704
        print "</html>\n";
3705
    }
3706
}
3707