ViewMain::topMenuQuickAdd()   B
last analyzed

Complexity

Conditions 7

Size

Total Lines 76
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 22
c 1
b 0
f 0
nop 0
dl 0
loc 76
rs 8.6346

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2024       Rafael San José         <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
namespace Dolibarr\Lib;
20
21
use Dolibarr\Code\Core\Classes\Form;
22
use Dolibarr\Code\Core\Classes\HookManager;
23
24
abstract class ViewMain
25
{
26
    /**
27
     *  Show HTML header HTML + BODY + Top menu + left menu + DIV
28
     *
29
     * @param string $head Optional head lines
30
     * @param string $title HTML title
31
     * @param string $help_url Url links to help page
32
     *                                              Syntax is: For a wiki page: EN:EnglishPage|FR:FrenchPage|ES:SpanishPage|DE:GermanPage
33
     *                                              For other external page: http://server/url
34
     * @param string $target Target to use on links
35
     * @param int $disablejs More content into html header
36
     * @param int $disablehead More content into html header
37
     * @param array|string $arrayofjs Array of complementary js files
38
     * @param array|string $arrayofcss Array of complementary css files
39
     * @param string $morequerystring Query string to add to the link "print" to get same parameters (use only if autodetect fails)
40
     * @param string $morecssonbody More CSS on body tag. For example 'classforhorizontalscrolloftabs'.
41
     * @param string $replacemainareaby Replace call to static::mainArea() by a print of this string
42
     * @param int $disablenofollow Disable the "nofollow" on meta robot header
43
     * @param int $disablenoindex Disable the "noindex" on meta robot header
44
     * @return  void
45
     */
46
    public static function llxHeader($head = '', $title = '', $help_url = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = '', $arrayofcss = '', $morequerystring = '', $morecssonbody = '', $replacemainareaby = '', $disablenofollow = 0, $disablenoindex = 0)
47
    {
48
        global $conf, $hookmanager;
49
50
        $parameters = array(
51
            'head' => & $head,
52
            'title' => & $title,
53
            'help_url' => & $help_url,
54
            'target' => & $target,
55
            'disablejs' => & $disablejs,
56
            'disablehead' => & $disablehead,
57
            'arrayofjs' => & $arrayofjs,
58
            'arrayofcss' => & $arrayofcss,
59
            'morequerystring' => & $morequerystring,
60
            'morecssonbody' => & $morecssonbody,
61
            'replacemainareaby' => & $replacemainareaby,
62
            'disablenofollow' => & $disablenofollow,
63
            'disablenoindex' => & $disablenoindex
64
65
        );
66
        $reshook = $hookmanager->executeHooks('llxHeader', $parameters);
67
        if ($reshook > 0) {
68
            print $hookmanager->resPrint;
69
            return;
70
        }
71
72
        // html header
73
        static::topHtmlHead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow, $disablenoindex);
74
75
        $tmpcsstouse = 'sidebar-collapse' . ($morecssonbody ? ' ' . $morecssonbody : '');
76
        // If theme MD and classic layer, we open the menulayer by default.
77
        if ($conf->theme == 'md' && !in_array($conf->browser->layout, array('phone', 'tablet')) && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
78
            global $mainmenu;
79
            if ($mainmenu != 'website') {
80
                $tmpcsstouse = $morecssonbody; // We do not use sidebar-collpase by default to have menuhider open by default.
81
            }
82
        }
83
84
        if (getDolGlobalString('MAIN_OPTIMIZEFORCOLORBLIND')) {
85
            $tmpcsstouse .= ' colorblind-' . strip_tags($conf->global->MAIN_OPTIMIZEFORCOLORBLIND);
86
        }
87
88
        print '<body id="mainbody" class="' . $tmpcsstouse . '">' . "\n";
89
90
        // top menu and left menu area
91
        if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && !GETPOSTINT('dol_openinpopup')) {
92
            static::topMenu($head, $title, $target, $disablejs, $disablehead, $arrayofjs, $arrayofcss, $morequerystring, $help_url);
93
        }
94
95
        if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup', 'aZ09')) {
96
            static::leftMenu(array(), $help_url, '', '', 1, $title, 1); // $menumanager is retrieved with a global $menumanager inside this function
97
        }
98
99
        // main area
100
        if ($replacemainareaby) {
101
            print $replacemainareaby;
102
            return;
103
        }
104
        static::mainArea($title);
105
    }
106
107
    /**
108
     *  Show HTTP header. Called by static::topHtmlHead().
109
     *
110
     * @param string $contenttype Content type. For example, 'text/html'
111
     * @param int<0,1> $forcenocache Force disabling of cache for the page
112
     * @return void
113
     */
114
    public static function topHttpHead($contenttype = 'text/html', $forcenocache = 0)
115
    {
116
        global $db, $conf, $hookmanager;
117
118
        if ($contenttype == 'text/html') {
119
            header("Content-Type: text/html; charset=" . $conf->file->character_set_client);
120
        } else {
121
            header("Content-Type: " . $contenttype);
122
        }
123
124
        // Security options
125
126
        // X-Content-Type-Options
127
        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)
128
129
        // X-Frame-Options
130
        if (!defined('XFRAMEOPTIONS_ALLOWALL')) {
131
            header("X-Frame-Options: SAMEORIGIN"); // By default, frames allowed only if on same domain (stop some XSS attacks)
132
        } else {
133
            header("X-Frame-Options: ALLOWALL");
134
        }
135
136
        if (getDolGlobalString('MAIN_SECURITY_FORCE_ACCESS_CONTROL_ALLOW_ORIGIN')) {
137
            $tmpurl = constant('DOL_MAIN_URL_ROOT');
138
            $tmpurl = preg_replace('/^(https?:\/\/[^\/]+)\/.*$/', '\1', $tmpurl);
139
            header('Access-Control-Allow-Origin: ' . $tmpurl);
140
            header('Vary: Origin');
141
        }
142
143
        // X-XSS-Protection
144
        //header("X-XSS-Protection: 1");            // XSS filtering protection of some browsers (note: use of Content-Security-Policy is more efficient). Disabled as deprecated.
145
146
        // Content-Security-Policy-Report-Only
147
        if (!defined('MAIN_SECURITY_FORCECSPRO')) {
148
            // If CSP not forced from the page
149
150
            // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
151
            // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
152
            // 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: *;
153
            // For example, to restrict everything to itself except img that can be on other servers:
154
            // default-src 'self'; img-src *;
155
            // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
156
            // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
157
            //
158
            // $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;";
159
            // $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';";
160
            $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSPRO');
161
162
            if (!is_object($hookmanager)) {
163
                $hookmanager = new HookManager($db);
164
            }
165
            $hookmanager->initHooks(array("main"));
166
167
            $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'reportonly');
168
            $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
169
            if ($result > 0) {
170
                $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
171
            } else {
172
                $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
173
            }
174
175
            if (!empty($contentsecuritypolicy)) {
176
                header("Content-Security-Policy-Report-Only: " . $contentsecuritypolicy);
177
            }
178
        } else {
179
            header("Content-Security-Policy: " . constant('MAIN_SECURITY_FORCECSPRO'));
180
        }
181
182
        // Content-Security-Policy
183
        if (!defined('MAIN_SECURITY_FORCECSP')) {
184
            // If CSP not forced from the page
185
186
            // A default security policy that keep usage of js external component like ckeditor, stripe, google, working
187
            // For example: to restrict to only local resources, except for css (cloudflare+google), and js (transifex + google tags) and object/iframe (youtube)
188
            // 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: *;
189
            // For example, to restrict everything to itself except img that can be on other servers:
190
            // default-src 'self'; img-src *;
191
            // Pre-existing site that uses too much js code to fix but wants to ensure resources are loaded only over https and disable plugins:
192
            // default-src https: 'unsafe-inline' 'unsafe-eval'; object-src 'none'
193
            //
194
            // $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;";
195
            // $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';";
196
            $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP');
197
198
            if (!is_object($hookmanager)) {
199
                $hookmanager = new HookManager($db);
200
            }
201
            $hookmanager->initHooks(array("main"));
202
203
            $parameters = array('contentsecuritypolicy' => $contentsecuritypolicy, 'mode' => 'active');
204
            $result = $hookmanager->executeHooks('setContentSecurityPolicy', $parameters); // Note that $action and $object may have been modified by some hooks
205
            if ($result > 0) {
206
                $contentsecuritypolicy = $hookmanager->resPrint; // Replace CSP
207
            } else {
208
                $contentsecuritypolicy .= $hookmanager->resPrint; // Concat CSP
209
            }
210
211
            if (!empty($contentsecuritypolicy)) {
212
                header("Content-Security-Policy: " . $contentsecuritypolicy);
213
            }
214
        } else {
215
            header("Content-Security-Policy: " . constant('MAIN_SECURITY_FORCECSP'));
216
        }
217
218
        // Referrer-Policy
219
        // Say if we must provide the referrer when we jump onto another web page.
220
        // 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.
221
        // 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.
222
        if (!defined('MAIN_SECURITY_FORCERP')) {
223
            $referrerpolicy = getDolGlobalString('MAIN_SECURITY_FORCERP', "same-origin");
224
225
            header("Referrer-Policy: " . $referrerpolicy);
226
        }
227
228
        if ($forcenocache) {
229
            header("Cache-Control: no-cache, no-store, must-revalidate, max-age=0");
230
        }
231
232
        // No need to add this token in header, we use instead the one into the forms.
233
        //header("anti-csrf-token: ".newToken());
234
    }
235
236
237
    /**
238
     * Output html header of a page. It calls also static::topHttpHead()
239
     * This code is also duplicated into security2.lib.php::dol_loginfunction
240
     *
241
     * @param string $head Optional head lines
242
     * @param string $title HTML title
243
     * @param int<0,1> $disablejs Disable js output
244
     * @param int<0,1> $disablehead Disable head output
245
     * @param string[] $arrayofjs Array of complementary js files
246
     * @param string[] $arrayofcss Array of complementary css files
247
     * @param int<0,1> $disableforlogin Do not load heavy js and css for login pages
248
     * @param int<0,1> $disablenofollow Disable nofollow tag for meta robots
249
     * @param int<0,1> $disablenoindex Disable noindex tag for meta robots
250
     * @return  void
251
     */
252
    public static function topHtmlHead($head, $title = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $disableforlogin = 0, $disablenofollow = 0, $disablenoindex = 0)
253
    {
254
        global $db, $conf, $langs, $user, $mysoc, $hookmanager;
255
256
        static::topHttpHead();
257
258
        if (empty($conf->css)) {
259
            $conf->css = '/theme/eldy/style.css.php'; // If not defined, eldy by default
260
        }
261
262
        print '<!doctype html>' . "\n";
263
264
        print '<html lang="' . substr($langs->defaultlang, 0, 2) . '">' . "\n";
265
266
        //print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">'."\n";
267
        if (empty($disablehead)) {
268
            if (!is_object($hookmanager)) {
269
                $hookmanager = new HookManager($db);
270
            }
271
            $hookmanager->initHooks(array("main"));
272
273
            $ext = 'layout=' . (empty($conf->browser->layout) ? '' : $conf->browser->layout) . '&amp;version=' . urlencode(DOL_VERSION);
274
275
            print "<head>\n";
276
277
            if (GETPOST('dol_basehref', 'alpha')) {
278
                print '<base href="' . dol_escape_htmltag(GETPOST('dol_basehref', 'alpha')) . '">' . "\n";
279
            }
280
281
            // Displays meta
282
            print '<meta charset="utf-8">' . "\n";
283
            print '<meta name="robots" content="' . ($disablenoindex ? 'index' : 'noindex') . ($disablenofollow ? ',follow' : ',nofollow') . '">' . "\n"; // Do not index
284
            print '<meta name="viewport" content="width=device-width, initial-scale=1.0">' . "\n"; // Scale for mobile device
285
            print '<meta name="author" content="Dolibarr Development Team">' . "\n";
286
            print '<meta name="anti-csrf-newtoken" content="' . newToken() . '">' . "\n";
287
            print '<meta name="anti-csrf-currenttoken" content="' . currentToken() . '">' . "\n";
288
            if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
289
                print '<meta name="MAIN_FEATURES_LEVEL" content="' . getDolGlobalInt('MAIN_FEATURES_LEVEL') . '">' . "\n";
290
            }
291
            // Favicon
292
            $favicon = constant('DOL_URL_ROOT') . '/theme/alixar_square_logo_256x256_color.png';
293
            if (!empty($mysoc->logo_squarred_mini)) {
294
                $favicon = constant('BASE_URL') . '/viewimage.php?cache=1&modulepart=mycompany&file=' . urlencode('logos/thumbs/' . $mysoc->logo_squarred_mini);
295
            }
296
            if (getDolGlobalString('MAIN_FAVICON_URL')) {
297
                $favicon = getDolGlobalString('MAIN_FAVICON_URL');
298
            }
299
            if (empty($conf->dol_use_jmobile)) {
300
                print '<link rel="shortcut icon" type="image/x-icon" href="' . $favicon . '"/>' . "\n"; // Not required into an Android webview
301
            }
302
303
            // Mobile appli like icon
304
            $manifest = constant('DOL_URL_ROOT') . '/theme/' . $conf->theme . '/manifest.json.php';
305
            $parameters = array('manifest' => $manifest);
306
            $resHook = $hookmanager->executeHooks('hookSetManifest', $parameters); // Note that $action and $object may have been modified by some hooks
307
            if ($resHook > 0) {
308
                $manifest = $hookmanager->resPrint; // Replace manifest.json
309
            } else {
310
                $manifest .= $hookmanager->resPrint; // Concat to actual manifest declaration
311
            }
312
            if (!empty($manifest)) {
313
                print '<link rel="manifest" href="' . $manifest . '" />' . "\n";
314
            }
315
316
            if (getDolGlobalString('THEME_ELDY_TOPMENU_BACK1')) {
317
                // TODO: use auto theme color switch
318
                print '<meta name="theme-color" content="rgb(' . getDolGlobalString('THEME_ELDY_TOPMENU_BACK1') . ')">' . "\n";
319
            }
320
321
            // Auto refresh page
322
            if (GETPOSTINT('autorefresh') > 0) {
323
                print '<meta http-equiv="refresh" content="' . GETPOSTINT('autorefresh') . '">';
324
            }
325
326
            // Displays title
327
            $appli = constant('DOL_APPLICATION_TITLE');
328
            if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
329
                $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
330
            }
331
332
            print '<title>';
333
            $titletoshow = '';
334
            if ($title && preg_match('/showapp/', getDolGlobalString('MAIN_HTML_TITLE'))) {
335
                $titletoshow = dol_htmlentities($appli . ' - ' . $title);
336
            } elseif ($title) {
337
                $titletoshow = dol_htmlentities($title);
338
            } else {
339
                $titletoshow = dol_htmlentities($appli);
340
            }
341
342
            $parameters = array('title' => $titletoshow);
343
            $result = $hookmanager->executeHooks('setHtmlTitle', $parameters); // Note that $action and $object may have been modified by some hooks
344
            if ($result > 0) {
345
                $titletoshow = $hookmanager->resPrint; // Replace Title to show
346
            } else {
347
                $titletoshow .= $hookmanager->resPrint; // Concat to Title to show
348
            }
349
350
            print $titletoshow;
351
            print '</title>';
352
353
            print "\n";
354
355
            if (GETPOSTINT('version')) {
356
                $ext = 'version=' . GETPOSTINT('version'); // useful to force no cache on css/js
357
            }
358
            // Refresh value of MAIN_IHM_PARAMS_REV before forging the parameter line.
359
            if (GETPOST('dol_resetcache')) {
360
                dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
361
            }
362
363
            $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;
364
365
            $themeparam .= ($ext ? '&amp;' . $ext : '') . '&amp;revision=' . getDolGlobalInt("MAIN_IHM_PARAMS_REV");
366
            if (GETPOSTISSET('dol_hide_topmenu')) {
367
                $themeparam .= '&amp;dol_hide_topmenu=' . GETPOSTINT('dol_hide_topmenu');
368
            }
369
            if (GETPOSTISSET('dol_hide_leftmenu')) {
370
                $themeparam .= '&amp;dol_hide_leftmenu=' . GETPOSTINT('dol_hide_leftmenu');
371
            }
372
            if (GETPOSTISSET('dol_optimize_smallscreen')) {
373
                $themeparam .= '&amp;dol_optimize_smallscreen=' . GETPOSTINT('dol_optimize_smallscreen');
374
            }
375
            if (GETPOSTISSET('dol_no_mouse_hover')) {
376
                $themeparam .= '&amp;dol_no_mouse_hover=' . GETPOSTINT('dol_no_mouse_hover');
377
            }
378
            if (GETPOSTISSET('dol_use_jmobile')) {
379
                $themeparam .= '&amp;dol_use_jmobile=' . GETPOSTINT('dol_use_jmobile');
380
                $conf->dol_use_jmobile = GETPOSTINT('dol_use_jmobile');
381
            }
382
            if (GETPOSTISSET('THEME_DARKMODEENABLED')) {
383
                $themeparam .= '&amp;THEME_DARKMODEENABLED=' . GETPOSTINT('THEME_DARKMODEENABLED');
384
            }
385
            if (GETPOSTISSET('THEME_SATURATE_RATIO')) {
386
                $themeparam .= '&amp;THEME_SATURATE_RATIO=' . GETPOSTINT('THEME_SATURATE_RATIO');
387
            }
388
389
            if (getDolGlobalString('MAIN_ENABLE_FONT_ROBOTO')) {
390
                print '<link rel="preconnect" href="https://fonts.gstatic.com">' . "\n";
391
                print '<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;600&display=swap" rel="stylesheet">' . "\n";
392
            }
393
394
            if (!defined('DISABLE_JQUERY') && !$disablejs && $conf->use_javascript_ajax) {
395
                print '<!-- Includes CSS for JQuery (Ajax library) -->' . "\n";
396
                $jquerytheme = 'base';
397
                if (getDolGlobalString('MAIN_USE_JQUERY_THEME')) {
398
                    $jquerytheme = getDolGlobalString('MAIN_USE_JQUERY_THEME');
399
                }
400
                if (constant('JS_JQUERY_UI')) {
401
                    print '<link rel="stylesheet" type="text/css" href="' . JS_JQUERY_UI . 'css/' . $jquerytheme . '/jquery-ui.min.css' . ($ext ? '?' . $ext : '') . '">' . "\n"; // Forced JQuery
402
                } else {
403
                    print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . '/includes/jquery/css/' . $jquerytheme . '/jquery-ui.css' . ($ext ? '?' . $ext : '') . '">' . "\n"; // JQuery
404
                }
405
                if (!defined('DISABLE_JQUERY_JNOTIFY')) {
406
                    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
407
                }
408
                if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {     // jQuery plugin "mutiselect", "multiple-select", "select2"...
409
                    $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
410
                    print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/' . $tmpplugin . '/dist/css/' . $tmpplugin . '.css' . ($ext ? '?' . $ext : '') . '">' . "\n";
411
                }
412
            }
413
414
            if (!defined('DISABLE_FONT_AWSOME')) {
415
                print '<!-- Includes CSS for font awesome -->' . "\n";
416
                $fontawesome_directory = getDolGlobalString('MAIN_FONTAWESOME_DIRECTORY', '/theme/common/fontawesome-5');
417
                print '<link rel="stylesheet" type="text/css" href="' . constant('DOL_URL_ROOT') . $fontawesome_directory . '/css/all.min.css' . ($ext ? '?' . $ext : '') . '">' . "\n";
418
            }
419
420
            print '<!-- Includes CSS for Dolibarr theme -->' . "\n";
421
            // Output style sheets (optioncss='print' or ''). Note: $conf->css looks like '/theme/eldy/style.css.php'
422
            $themepath = dol_buildpath($conf->css, 1);
423
            $themesubdir = '';
424
            if (!empty($conf->modules_parts['theme'])) {    // This slow down
425
                foreach ($conf->modules_parts['theme'] as $reldir) {
426
                    if (file_exists(dol_buildpath($reldir . $conf->css, 0))) {
427
                        $themepath = dol_buildpath($reldir . $conf->css, 1);
428
                        $themesubdir = $reldir;
429
                        break;
430
                    }
431
                }
432
            }
433
434
            //print 'themepath='.$themepath.' themeparam='.$themeparam;exit;
435
            print '<link rel="stylesheet" type="text/css" href="' . $themepath . $themeparam . '">' . "\n";
436
            if (getDolGlobalString('MAIN_FIX_FLASH_ON_CHROME')) {
437
                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";
438
            }
439
440
            // LEAFLET AND GEOMAN
441
            if (getDolGlobalString('MAIN_USE_GEOPHP')) {
442
                print '<link rel="stylesheet" href="' . constant('BASE_URL') . '/includes/leaflet/leaflet.css' . ($ext ? '?' . $ext : '') . "\">\n";
443
                print '<link rel="stylesheet" href="' . constant('BASE_URL') . '/includes/leaflet/leaflet-geoman.css' . ($ext ? '?' . $ext : '') . "\">\n";
444
            }
445
446
            // CSS forced by modules (relative url starting with /)
447
            if (!empty($conf->modules_parts['css'])) {
448
                $arraycss = (array)$conf->modules_parts['css'];
449
                foreach ($arraycss as $modcss => $filescss) {
450
                    $filescss = (array)$filescss; // To be sure filecss is an array
451
                    foreach ($filescss as $cssfile) {
452
                        if (empty($cssfile)) {
453
                            dol_syslog("Warning: module " . $modcss . " declared a css path file into its descriptor that is empty.", LOG_WARNING);
454
                        }
455
                        // cssfile is a relative path
456
                        $urlforcss = dol_buildpath($cssfile, 1);
457
                        if ($urlforcss && $urlforcss != '/') {
458
                            print '<!-- Includes CSS added by module ' . $modcss . ' -->' . "\n" . '<link rel="stylesheet" type="text/css" href="' . $urlforcss;
459
                            // 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.
460
                            if (!preg_match('/\.css$/i', $cssfile)) {
461
                                print $themeparam;
462
                            }
463
                            print '">' . "\n";
464
                        } else {
465
                            dol_syslog("Warning: module " . $modcss . " declared a css path file for a file we can't find.", LOG_WARNING);
466
                        }
467
                    }
468
                }
469
            }
470
            // CSS forced by page in static::topHtmlHead( call (relative url starting with /)
471
            if (is_array($arrayofcss)) {
472
                foreach ($arrayofcss as $cssfile) {
473
                    // $cssfile = '/htdocs' . $cssfile;
474
                    if (preg_match('/^(http|\/\/)/i', $cssfile)) {
475
                        $urltofile = $cssfile;
476
                    } else {
477
                        $urltofile = dol_buildpath($cssfile, 1);
478
                    }
479
                    print '<!-- Includes CSS added by page -->' . "\n" . '<link rel="stylesheet" type="text/css" title="default" href="' . $urltofile;
480
                    // 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.
481
                    if (!preg_match('/\.css$/i', $cssfile)) {
482
                        print $themeparam;
483
                    }
484
                    print '">' . "\n";
485
                }
486
            }
487
488
            // Custom CSS
489
            if (getDolGlobalString('MAIN_IHM_CUSTOM_CSS')) {
490
                // If a custom CSS was set, we add link to the custom css php file
491
                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";
492
            }
493
494
            // Output standard javascript links
495
            if (!defined('DISABLE_JQUERY') && !$disablejs && !empty($conf->use_javascript_ajax)) {
496
                // JQuery. Must be before other includes
497
                print '<!-- Includes JS for JQuery -->' . "\n";
498
                if (defined('JS_JQUERY') && constant('JS_JQUERY')) {
499
                    print '<script nonce="' . getNonce() . '" src="' . JS_JQUERY . 'jquery.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
500
                } else {
501
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/js/jquery.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
502
                }
503
                if (defined('JS_JQUERY_UI') && constant('JS_JQUERY_UI')) {
504
                    print '<script nonce="' . getNonce() . '" src="' . JS_JQUERY_UI . 'jquery-ui.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
505
                } else {
506
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/js/jquery-ui.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
507
                }
508
                // jQuery jnotify
509
                if (!getDolGlobalString('MAIN_DISABLE_JQUERY_JNOTIFY') && !defined('DISABLE_JQUERY_JNOTIFY')) {
510
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jnotify/jquery.jnotify.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
511
                }
512
                // Table drag and drop lines
513
                if (empty($disableforlogin) && !defined('DISABLE_JQUERY_TABLEDND')) {
514
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/tablednd/jquery.tablednd.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
515
                }
516
                // Chart
517
                if (empty($disableforlogin) && (!getDolGlobalString('MAIN_JS_GRAPH') || getDolGlobalString('MAIN_JS_GRAPH') == 'chart') && !defined('DISABLE_JS_GRAPH')) {
518
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/nnnick/chartjs/dist/chart.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
519
                }
520
521
                // jQuery jeditable for Edit In Place features
522
                if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !defined('DISABLE_JQUERY_JEDITABLE')) {
523
                    print '<!-- JS to manage editInPlace feature -->' . "\n";
524
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
525
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.ui-datepicker.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
526
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.ui-autocomplete.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
527
                    print '<script>' . "\n";
528
                    print 'var urlSaveInPlace = \'' . constant('DOL_URL_ROOT') . '/core/ajax/saveinplace.php\';' . "\n";
529
                    print 'var urlLoadInPlace = \'' . constant('DOL_URL_ROOT') . '/core/ajax/loadinplace.php\';' . "\n";
530
                    print 'var tooltipInPlace = \'' . $langs->transnoentities('ClickToEdit') . '\';' . "\n"; // Added in title attribute of span
531
                    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 ?
532
                    print 'var cancelInPlace = \'' . $langs->trans("Cancel") . '\';' . "\n";
533
                    print 'var submitInPlace = \'' . $langs->trans('Ok') . '\';' . "\n";
534
                    print 'var indicatorInPlace = \'<img src="' . constant('DOL_URL_ROOT') . "/theme/" . $conf->theme . "/img/working.gif" . '">\';' . "\n";
535
                    print 'var withInPlace = 300;'; // width in pixel for default string edit
536
                    print '</script>' . "\n";
537
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/core/js/editinplace.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
538
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/jeditable/jquery.jeditable.ckeditor.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
539
                }
540
                // jQuery Timepicker
541
                if (getDolGlobalString('MAIN_USE_JQUERY_TIMEPICKER') || defined('REQUIRE_JQUERY_TIMEPICKER')) {
542
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/timepicker/jquery-ui-timepicker-addon.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
543
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/core/js/timepicker.js.php?lang=' . $langs->defaultlang . ($ext ? '&amp;' . $ext : '') . '"></script>' . "\n";
544
                }
545
                if (!defined('DISABLE_SELECT2') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
546
                    // jQuery plugin "mutiselect", "multiple-select", "select2", ...
547
                    $tmpplugin = !getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
548
                    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
549
                }
550
                if (!defined('DISABLE_MULTISELECT')) {     // jQuery plugin "mutiselect" to select with checkboxes. Can be removed once we have an enhanced search tool
551
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/jquery/plugins/multiselect/jquery.multi-select.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
552
                }
553
            }
554
555
            if (!$disablejs && !empty($conf->use_javascript_ajax)) {
556
                // CKEditor
557
                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...
558
                    print '<!-- Includes JS for CKEditor -->' . "\n";
559
                    $pathckeditor = constant('DOL_URL_ROOT') . '/includes/ckeditor/ckeditor/';
560
                    $jsckeditor = 'ckeditor.js';
561
                    if (constant('JS_CKEDITOR')) {
562
                        // To use external ckeditor 4 js lib
563
                        $pathckeditor = constant('JS_CKEDITOR');
564
                    }
565
                    print '<script nonce="' . getNonce() . '">';
566
                    print '/* enable ckeditor by main.inc.php */';
567
                    print 'var CKEDITOR_BASEPATH = \'' . dol_escape_js($pathckeditor) . '\';' . "\n";
568
                    print 'var ckeditorConfig = \'' . dol_escape_js(dol_buildpath('/htdocs' . $themesubdir . '/theme/' . $conf->theme . '/ckeditor/config.js' . ($ext ? '?' . $ext : ''), 1)) . '\';' . "\n"; // $themesubdir='' in standard usage
569
                    print 'var ckeditorFilebrowserBrowseUrl = \'' . constant('DOL_URL_ROOT') . '/core/filemanagerdol/browser/default/browser.php?Connector=' . constant('BASE_URL') . '/core/filemanagerdol/connectors/php/connector.php\';' . "\n";
570
                    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";
571
                    print '</script>' . "\n";
572
                    print '<script src="' . $pathckeditor . $jsckeditor . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
573
                    print '<script>';
574
                    if (GETPOST('mode', 'aZ09') == 'Full_inline') {
575
                        print 'CKEDITOR.disableAutoInline = false;' . "\n";
576
                    } else {
577
                        print 'CKEDITOR.disableAutoInline = true;' . "\n";
578
                    }
579
                    print '</script>' . "\n";
580
                }
581
582
                // 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).
583
                if (!defined('NOBROWSERNOTIF') && !defined('NOREQUIREMENU') && !defined('NOLOGIN')) {
584
                    $enablebrowsernotif = false;
585
                    if (isModEnabled('agenda') && getDolGlobalString('AGENDA_REMINDER_BROWSER')) {
586
                        $enablebrowsernotif = true;
587
                    }
588
                    if ($conf->browser->layout == 'phone') {
589
                        $enablebrowsernotif = false;
590
                    }
591
                    if ($enablebrowsernotif) {
592
                        print '<!-- Includes JS of Dolibarr (browser layout = ' . $conf->browser->layout . ')-->' . "\n";
593
                        print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/core/js/lib_notification.js.php' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
594
                    }
595
                }
596
597
                // Global js function
598
                print '<!-- Includes JS of Dolibarr -->' . "\n";
599
                print '<script nonce="' . getNonce() . '" src="' . BASE_URL . '/core/js/lib_head.js.php?lang=' . $langs->defaultlang . ($ext ? '&amp;' . $ext : '') . '"></script>' . "\n";
600
601
                // Leaflet TODO use dolibarr files
602
                if (getDolGlobalString('MAIN_USE_GEOPHP')) {
603
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/leaflet/leaflet.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
604
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/includes/leaflet/leaflet-geoman.min.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
605
                }
606
607
                // JS forced by modules (relative url starting with /)
608
                if (!empty($conf->modules_parts['js'])) {       // $conf->modules_parts['js'] is array('module'=>array('file1','file2'))
609
                    $arrayjs = (array)$conf->modules_parts['js'];
610
                    foreach ($arrayjs as $modjs => $filesjs) {
611
                        $filesjs = (array)$filesjs; // To be sure filejs is an array
612
                        foreach ($filesjs as $jsfile) {
613
                            // jsfile is a relative path
614
                            $urlforjs = dol_buildpath($jsfile, 3);
615
                            if ($urlforjs && $urlforjs != '/') {
616
                                print '<!-- Include JS added by module ' . $modjs . '-->' . "\n";
617
                                print '<script nonce="' . getNonce() . '" src="' . $urlforjs . ((strpos($jsfile, '?') === false) ? '?' : '&amp;') . 'lang=' . $langs->defaultlang . '"></script>' . "\n";
618
                            } else {
619
                                dol_syslog("Warning: module " . $modjs . " declared a js path file for a file we can't find.", LOG_WARNING);
620
                            }
621
                        }
622
                    }
623
                }
624
                // JS forced by page in static::topHtmlHead( (relative url starting with /)
625
                if (is_array($arrayofjs)) {
626
                    print '<!-- Includes JS added by page -->' . "\n";
627
                    foreach ($arrayofjs as $jsfile) {
628
                        // $jsfile = '/htdocs' . $jsfile;
629
                        if (preg_match('/^(http|\/\/)/i', $jsfile)) {
630
                            print '<script nonce="' . getNonce() . '" src="' . $jsfile . ((strpos($jsfile, '?') === false) ? '?' : '&amp;') . 'lang=' . $langs->defaultlang . '"></script>' . "\n";
631
                        } else {
632
                            print '<script nonce="' . getNonce() . '" src="' . dol_buildpath($jsfile, 3) . ((strpos($jsfile, '?') === false) ? '?' : '&amp;') . 'lang=' . $langs->defaultlang . '"></script>' . "\n";
633
                        }
634
                    }
635
                }
636
            }
637
638
            //If you want to load custom javascript file from your selected theme directory
639
            if (getDolGlobalString('ALLOW_THEME_JS')) {
640
                $theme_js = dol_buildpath('/theme/' . $conf->theme . '/' . $conf->theme . '.js', 0);
641
                if (file_exists($theme_js)) {
642
                    print '<script nonce="' . getNonce() . '" src="' . constant('DOL_URL_ROOT') . '/theme/' . $conf->theme . '/' . $conf->theme . '.js' . ($ext ? '?' . $ext : '') . '"></script>' . "\n";
643
                }
644
            }
645
646
            if (!empty($head)) {
647
                print $head . "\n";
648
            }
649
            if (getDolGlobalString('MAIN_HTML_HEADER')) {
650
                print getDolGlobalString('MAIN_HTML_HEADER') . "\n";
651
            }
652
653
            $parameters = array();
654
            $result = $hookmanager->executeHooks('addHtmlHeader', $parameters); // Note that $action and $object may have been modified by some hooks
655
            print $hookmanager->resPrint; // Replace Title to show
656
657
            print "</head>\n\n";
658
        }
659
660
        $conf->headerdone = 1; // To tell header was output
661
    }
662
663
664
    /**
665
     *  Show an HTML header + a BODY + The top menu bar
666
     *
667
     * @param string $head Lines in the HEAD
668
     * @param string $title Title of web page
669
     * @param string $target Target to use in menu links (Example: '' or '_top')
670
     * @param int<0,1> $disablejs Do not output links to js (Ex: qd fonction utilisee par sous formulaire Ajax)
671
     * @param int<0,1> $disablehead Do not output head section
672
     * @param string[] $arrayofjs Array of js files to add in header
673
     * @param string[] $arrayofcss Array of css files to add in header
674
     * @param string $morequerystring Query string to add to the link "print" to get same parameters (use only if autodetect fails)
675
     * @param string $helppagename Name of wiki page for help ('' by default).
676
     *                                                  Syntax is: For a wiki page: EN:EnglishPage|FR:FrenchPage|ES:SpanishPage|DE:GermanPage
677
     *                                                  For other external page: http://server/url
678
     * @return     void
679
     */
680
    public static function topMenu($head, $title = '', $target = '', $disablejs = 0, $disablehead = 0, $arrayofjs = array(), $arrayofcss = array(), $morequerystring = '', $helppagename = '')
681
    {
682
        global $user, $conf, $langs, $db, $form;
683
        global $dolibarr_main_authentication, $dolibarr_main_demo;
684
        global $hookmanager, $menumanager;
685
686
        $searchform = '';
687
688
        // Instantiate hooks for external modules
689
        $hookmanager->initHooks(array('toprightmenu'));
690
691
        $toprightmenu = '';
692
693
        // For backward compatibility with old modules
694
        if (empty($conf->headerdone)) {
695
            $disablenofollow = 0;
696
            static::topHtmlHead($head, $title, $disablejs, $disablehead, $arrayofjs, $arrayofcss, 0, $disablenofollow);
697
            print '<body id="mainbody">';
698
        }
699
700
        /*
701
         * Top menu
702
         */
703
        if ((empty($conf->dol_hide_topmenu) || GETPOSTINT('dol_invisible_topmenu')) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
704
            if (!isset($form) || !is_object($form)) {
705
                $form = new Form($db);
706
            }
707
708
            print "\n" . '<!-- Start top horizontal -->' . "\n";
709
710
            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.
711
712
            // Show menu entries
713
            print '<div id="tmenu_tooltip' . (!getDolGlobalString('MAIN_MENU_INVERT') ? '' : 'invert') . '" class="tmenu">' . "\n";
714
            $menumanager->atarget = $target;
715
            $menumanager->showmenu('top', array('searchform' => $searchform)); // This contains a \n
716
            print "</div>\n";
717
718
            // Define link to login card
719
            $appli = constant('DOL_APPLICATION_TITLE');
720
            if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
721
                $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
722
                if (preg_match('/\d\.\d/', $appli)) {
723
                    if (!preg_match('/' . preg_quote(DOL_VERSION) . '/', $appli)) {
724
                        $appli .= " (" . DOL_VERSION . ")"; // If new title contains a version that is different than core
725
                    }
726
                } else {
727
                    $appli .= " " . DOL_VERSION;
728
                }
729
            } else {
730
                $appli .= " " . DOL_VERSION;
731
            }
732
733
            if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
734
                $appli .= "<br>" . $langs->trans("LevelOfFeature") . ': ' . getDolGlobalInt('MAIN_FEATURES_LEVEL');
735
            }
736
737
            $logouttext = '';
738
            $logouthtmltext = '';
739
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
740
                //$logouthtmltext=$appli.'<br>';
741
                $stringforfirstkey = $langs->trans("KeyboardShortcut");
742
                if ($conf->browser->name == 'chrome') {
743
                    $stringforfirstkey .= ' ALT +';
744
                } elseif ($conf->browser->name == 'firefox') {
745
                    $stringforfirstkey .= ' ALT + SHIFT +';
746
                } else {
747
                    $stringforfirstkey .= ' CTL +';
748
                }
749
                if ($_SESSION["dol_authmode"] != 'forceuser' && $_SESSION["dol_authmode"] != 'http') {
750
                    $logouthtmltext .= $langs->trans("Logout") . '<br>';
751
                    $logouttext .= '<a accesskey="l" href="' . constant('BASE_URL') . '/user/logout.php?token=' . newToken() . '">';
752
                    $logouttext .= img_picto($langs->trans('Logout') . ' (' . $stringforfirstkey . ' l)', 'sign-out', '', false, 0, 0, '', 'atoplogin valignmiddle');
753
                    $logouttext .= '</a>';
754
                } else {
755
                    $logouthtmltext .= $langs->trans("NoLogoutProcessWithAuthMode", $_SESSION["dol_authmode"]);
756
                    $logouttext .= img_picto($langs->trans('Logout') . ' (' . $stringforfirstkey . ' l)', 'sign-out', '', false, 0, 0, '', 'atoplogin valignmiddle opacitymedium');
757
                }
758
            }
759
760
            print '<div class="login_block usedropdown">' . "\n";
761
762
            $toprightmenu .= '<div class="login_block_other">';
763
764
            // Execute hook printTopRightMenu (hooks should output string like '<div class="login"><a href="">mylink</a></div>')
765
            $parameters = array();
766
            $result = $hookmanager->executeHooks('printTopRightMenu', $parameters); // Note that $action and $object may have been modified by some hooks
767
            if (is_numeric($result)) {
768
                if ($result == 0) {
769
                    $toprightmenu .= $hookmanager->resPrint; // add
770
                } else {
771
                    $toprightmenu = $hookmanager->resPrint; // replace
772
                }
773
            } else {
774
                $toprightmenu .= $result; // For backward compatibility
775
            }
776
777
            // Link to module builder
778
            if (isModEnabled('modulebuilder')) {
779
                $text = '<a href="' . constant('BASE_URL') . '/modulebuilder/index.php?mainmenu=home&leftmenu=admintools" target="modulebuilder">';
780
                //$text.= img_picto(":".$langs->trans("ModuleBuilder"), 'printer_top.png', 'class="printer"');
781
                $text .= '<span class="fa fa-bug atoplogin valignmiddle"></span>';
782
                $text .= '</a>';
783
                // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
784
                $toprightmenu .= $form->textwithtooltip('', $langs->trans("ModuleBuilder"), 2, 1, $text, 'login_block_elem', 2);
785
            }
786
787
            // Link to print main content area (optioncss=print)
788
            if (!getDolGlobalString('MAIN_PRINT_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
789
                $qs = dol_escape_htmltag($_SERVER["QUERY_STRING"]);
790
791
                if (isset($_POST) && is_array($_POST)) {
792
                    foreach ($_POST as $key => $value) {
793
                        $key = preg_replace('/[^a-z0-9_\.\-\[\]]/i', '', $key);
794
                        if (in_array($key, array('action', 'massaction', 'password'))) {
795
                            continue;
796
                        }
797
                        if (!is_array($value)) {
798
                            if ($value !== '') {
799
                                $qs .= '&' . urlencode($key) . '=' . urlencode($value);
800
                            }
801
                        } else {
802
                            foreach ($value as $value2) {
803
                                if (($value2 !== '') && (!is_array($value2))) {
804
                                    $qs .= '&' . urlencode($key) . '[]=' . urlencode($value2);
805
                                }
806
                            }
807
                        }
808
                    }
809
                }
810
                $qs .= (($qs && $morequerystring) ? '&' : '') . $morequerystring;
811
                $text = '<a href="' . dol_escape_htmltag($_SERVER["PHP_SELF"]) . '?' . $qs . ($qs ? '&' : '') . 'optioncss=print" target="_blank" rel="noopener noreferrer">';
812
                //$text.= img_picto(":".$langs->trans("PrintContentArea"), 'printer_top.png', 'class="printer"');
813
                $text .= '<span class="fa fa-print atoplogin valignmiddle"></span>';
814
                $text .= '</a>';
815
                // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
816
                $toprightmenu .= $form->textwithtooltip('', $langs->trans("PrintContentArea"), 2, 1, $text, 'login_block_elem', 2);
817
            }
818
819
            // Link to Dolibarr wiki pages
820
            if (!getDolGlobalString('MAIN_HELP_DISABLELINK') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
821
                $langs->load("help");
822
823
                $helpbaseurl = '';
824
                $helppage = '';
825
                $mode = '';
826
                $helppresent = '';
827
828
                if (empty($helppagename)) {
829
                    $helppagename = 'EN:User_documentation|FR:Documentation_utilisateur|ES:Documentación_usuarios|DE:Benutzerdokumentation';
830
                } else {
831
                    $helppresent = 'helppresent';
832
                }
833
834
                // Get helpbaseurl, helppage and mode from helppagename and langs
835
                $arrayres = static::getHelpParamFor($helppagename, $langs);
836
                $helpbaseurl = $arrayres['helpbaseurl'];
837
                $helppage = $arrayres['helppage'];
838
                $mode = $arrayres['mode'];
839
840
                // Link to help pages
841
                if ($helpbaseurl && $helppage) {
842
                    $text = '';
843
                    $title = $langs->trans($mode == 'wiki' ? 'GoToWikiHelpPage' : 'GoToHelpPage') . ', ';
844
                    if ($mode == 'wiki') {
845
                        $title .= '<br>' . img_picto('', 'globe', 'class="pictofixedwidth"') . $langs->trans("PageWiki") . ' ' . dol_escape_htmltag('"' . strtr($helppage, '_', ' ') . '"');
846
                        if ($helppresent) {
847
                            $title .= ' <span class="opacitymedium">(' . $langs->trans("DedicatedPageAvailable") . ')</span>';
848
                        } else {
849
                            $title .= ' <span class="opacitymedium">(' . $langs->trans("HomePage") . ')</span>';
850
                        }
851
                    }
852
                    $text .= '<a class="help" target="_blank" rel="noopener noreferrer" href="';
853
                    if ($mode == 'wiki') {
854
                        // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
855
                        $text .= sprintf($helpbaseurl, urlencode(html_entity_decode($helppage)));
856
                    } else {
857
                        // @phan-suppress-next-line PhanPluginPrintfVariableFormatString
858
                        $text .= sprintf($helpbaseurl, $helppage);
859
                    }
860
                    $text .= '">';
861
                    $text .= '<span class="fa fa-question-circle atoplogin valignmiddle' . ($helppresent ? ' ' . $helppresent : '') . '"></span>';
862
                    $text .= '<span class="fa fa-long-arrow-alt-up helppresentcircle' . ($helppresent ? '' : ' unvisible') . '"></span>';
863
                    $text .= '</a>';
864
                    // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
865
                    $toprightmenu .= $form->textwithtooltip('', $title, 2, 1, $text, 'login_block_elem', 2);
866
                }
867
868
                // Version
869
                if (getDolGlobalString('MAIN_SHOWDATABASENAMEINHELPPAGESLINK')) {
870
                    $langs->load('admin');
871
                    $appli .= '<br>' . $langs->trans("Database") . ': ' . $db->database_name;
872
                }
873
            }
874
875
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
876
                $text = '<span class="aversion"><span class="hideonsmartphone small">' . DOL_VERSION . '</span></span>';
877
                // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
878
                $toprightmenu .= $form->textwithtooltip('', $appli, 2, 1, $text, 'login_block_elem', 2);
879
            }
880
881
            // Logout link
882
            $toprightmenu .= $form->textwithtooltip('', $logouthtmltext, 2, 1, $logouttext, 'login_block_elem logout-btn', 2);
883
884
            $toprightmenu .= '</div>'; // end div class="login_block_other"
885
886
887
            // Add login user link
888
            $toprightmenu .= '<div class="login_block_user">';
889
890
            // Login name with photo and tooltip
891
            $mode = -1;
892
            $toprightmenu .= '<div class="inline-block login_block_elem login_block_elem_name nowrap centpercent" style="padding: 0px;">';
893
894
            if (getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
895
                // Add search dropdown
896
                $toprightmenu .= static::topSearchMenu();
897
            }
898
899
            if (getDolGlobalString('MAIN_USE_TOP_MENU_QUICKADD_DROPDOWN')) {
900
                // Add search dropdown
901
                $toprightmenu .= static::topMenuQuickAdd();
902
            }
903
904
            // Add bookmark dropdown
905
            $toprightmenu .= static::topMenuBookmark();
906
907
            // Add user dropdown
908
            $toprightmenu .= static::topMenuUser();
909
910
            $toprightmenu .= '</div>';
911
912
            $toprightmenu .= '</div>' . "\n";
913
914
915
            print $toprightmenu;
916
917
            print "</div>\n"; // end div class="login_block"
918
919
            print '</header>';
920
            //print '<header class="header2">&nbsp;</header>';
921
922
            print '<div style="clear: both;"></div>';
923
            print "<!-- End top horizontal menu -->\n\n";
924
        }
925
926
        if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
927
            print '<!-- Begin div id-container --><div id="id-container" class="id-container">';
928
        }
929
    }
930
931
    /**
932
     * Build the tooltip on user login
933
     *
934
     * @param int<0,1> $hideloginname Hide login name. Show only the image.
935
     * @param string $urllogout URL for logout (Will use DOL_URL_ROOT.'/user/logout.php?token=...' if empty)
936
     * @return  string                          HTML content
937
     */
938
    public static function topMenuUser($hideloginname = 0, $urllogout = '')
939
    {
940
        global $langs, $conf, $db, $hookmanager, $user, $mysoc;
941
        global $dolibarr_main_authentication, $dolibarr_main_demo;
942
        global $menumanager;
943
944
        $langs->load('companies');
945
946
        $userImage = $userDropDownImage = '';
947
        if (!empty($user->photo)) {
948
            $userImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'photouserphoto userphoto', 'small', 0, 1);
949
            $userDropDownImage = Form::showphoto('userphoto', $user, 0, 0, 0, 'dropdown-user-image', 'small', 0, 1);
950
        } else {
951
            $nophoto = '/public/theme/common/user_anonymous.png';
952
            if ($user->gender == 'man') {
953
                $nophoto = '/public/theme/common/user_man.png';
954
            }
955
            if ($user->gender == 'woman') {
956
                $nophoto = '/public/theme/common/user_woman.png';
957
            }
958
959
            $userImage = '<img class="photo photouserphoto userphoto" alt="" src="' . constant('DOL_URL_ROOT') . $nophoto . '">';
960
            $userDropDownImage = '<img class="photo dropdown-user-image" alt="" src="' . constant('DOL_URL_ROOT') . $nophoto . '">';
961
        }
962
963
        $dropdownBody = '';
964
        $dropdownBody .= '<span id="topmenulogincompanyinfo-btn"><i class="fa fa-caret-right"></i> ' . $langs->trans("ShowCompanyInfos") . '</span>';
965
        $dropdownBody .= '<div id="topmenulogincompanyinfo" >';
966
967
        $dropdownBody .= '<br><b>' . $langs->trans("Company") . '</b>: <span>' . dol_escape_htmltag($mysoc->name) . '</span>';
968
        if ($langs->transcountry("ProfId1", $mysoc->country_code) != '-') {
969
            $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId1", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_SIREN"), 1) . '</span>';
970
        }
971
        if ($langs->transcountry("ProfId2", $mysoc->country_code) != '-') {
972
            $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId2", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_SIRET"), 2) . '</span>';
973
        }
974
        if ($langs->transcountry("ProfId3", $mysoc->country_code) != '-') {
975
            $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId3", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_APE"), 3) . '</span>';
976
        }
977
        if ($langs->transcountry("ProfId4", $mysoc->country_code) != '-') {
978
            $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId4", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_RCS"), 4) . '</span>';
979
        }
980
        if ($langs->transcountry("ProfId5", $mysoc->country_code) != '-') {
981
            $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId5", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_PROFID5"), 5) . '</span>';
982
        }
983
        if ($langs->transcountry("ProfId6", $mysoc->country_code) != '-') {
984
            $dropdownBody .= '<br><b>' . $langs->transcountry("ProfId6", $mysoc->country_code) . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_PROFID6"), 6) . '</span>';
985
        }
986
        $dropdownBody .= '<br><b>' . $langs->trans("VATIntraShort") . '</b>: <span>' . dol_print_profids(getDolGlobalString("MAIN_INFO_TVAINTRA"), 'VAT') . '</span>';
987
        $dropdownBody .= '<br><b>' . $langs->trans("Country") . '</b>: <span>' . ($mysoc->country_code ? $langs->trans("Country" . $mysoc->country_code) : '') . '</span>';
988
        if (isModEnabled('multicurrency')) {
989
            $dropdownBody .= '<br><b>' . $langs->trans("Currency") . '</b>: <span>' . $conf->currency . '</span>';
990
        }
991
        $dropdownBody .= '</div>';
992
993
        $dropdownBody .= '<br>';
994
        $dropdownBody .= '<span id="topmenuloginmoreinfo-btn"><i class="fa fa-caret-right"></i> ' . $langs->trans("ShowMoreInfos") . '</span>';
995
        $dropdownBody .= '<div id="topmenuloginmoreinfo" >';
996
997
        // login infos
998
        if (!empty($user->admin)) {
999
            $dropdownBody .= '<br><b>' . $langs->trans("Administrator") . '</b>: ' . yn($user->admin);
1000
        }
1001
        if (!empty($user->socid)) { // Add thirdparty for external users
1002
            $thirdpartystatic = new Societe($db);
1003
            $thirdpartystatic->fetch($user->socid);
1004
            $companylink = ' ' . $thirdpartystatic->getNomUrl(2); // picto only of company
1005
            $company = ' (' . $langs->trans("Company") . ': ' . $thirdpartystatic->name . ')';
1006
        }
1007
        $type = ($user->socid ? $langs->trans("External") . $company : $langs->trans("Internal"));
1008
        $dropdownBody .= '<br><b>' . $langs->trans("Type") . ':</b> ' . $type;
1009
        $dropdownBody .= '<br><b>' . $langs->trans("Status") . '</b>: ' . $user->getLibStatut(0);
1010
        $dropdownBody .= '<br>';
1011
1012
        $dropdownBody .= '<br><u>' . $langs->trans("Session") . '</u>';
1013
        $dropdownBody .= '<br><b>' . $langs->trans("IPAddress") . '</b>: ' . dol_escape_htmltag($_SERVER["REMOTE_ADDR"]);
1014
        if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
1015
            $dropdownBody .= '<br><b>' . $langs->trans("ConnectedOnMultiCompany") . ':</b> ' . $conf->entity . ' (user entity ' . $user->entity . ')';
1016
        }
1017
        $dropdownBody .= '<br><b>' . $langs->trans("AuthenticationMode") . ':</b> ' . $_SESSION["dol_authmode"] . (empty($dolibarr_main_demo) ? '' : ' (demo)');
1018
        $dropdownBody .= '<br><b>' . $langs->trans("ConnectedSince") . ':</b> ' . dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
1019
        $dropdownBody .= '<br><b>' . $langs->trans("PreviousConnexion") . ':</b> ' . dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
1020
        $dropdownBody .= '<br><b>' . $langs->trans("CurrentTheme") . ':</b> ' . $conf->theme;
1021
        $dropdownBody .= '<br><b>' . $langs->trans("CurrentMenuManager") . ':</b> ' . (isset($menumanager) ? $menumanager->name : 'unknown');
1022
        $langFlag = picto_from_langcode($langs->getDefaultLang());
1023
        $dropdownBody .= '<br><b>' . $langs->trans("CurrentUserLanguage") . ':</b> ' . ($langFlag ? $langFlag . ' ' : '') . $langs->getDefaultLang();
1024
1025
        $tz = (int)$_SESSION['dol_tz'] + (int)$_SESSION['dol_dst'];
1026
        $dropdownBody .= '<br><b>' . $langs->trans("ClientTZ") . ':</b> ' . ($tz ? ($tz >= 0 ? '+' : '') . $tz : '');
1027
        $dropdownBody .= ' (' . $_SESSION['dol_tz_string'] . ')';
1028
        //$dropdownBody .= ' &nbsp; &nbsp; &nbsp; '.$langs->trans("DaylingSavingTime").': ';
1029
        //if ($_SESSION['dol_dst'] > 0) $dropdownBody .= yn(1);
1030
        //else $dropdownBody .= yn(0);
1031
1032
        $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>';
1033
        $dropdownBody .= '<br><b>' . $langs->trans("Layout") . ':</b> ' . $conf->browser->layout;
1034
        $dropdownBody .= '<br><b>' . $langs->trans("Screen") . ':</b> ' . $_SESSION['dol_screenwidth'] . ' x ' . $_SESSION['dol_screenheight'];
1035
        if ($conf->browser->layout == 'phone') {
1036
            $dropdownBody .= '<br><b>' . $langs->trans("Phone") . ':</b> ' . $langs->trans("Yes");
1037
        }
1038
        if (!empty($_SESSION["disablemodules"])) {
1039
            $dropdownBody .= '<br><b>' . $langs->trans("DisabledModules") . ':</b> <br>' . implode(', ', explode(',', $_SESSION["disablemodules"]));
1040
        }
1041
        $dropdownBody .= '</div>';
1042
1043
        // Execute hook
1044
        $parameters = array('user' => $user, 'langs' => $langs);
1045
        $result = $hookmanager->executeHooks('printTopRightMenuLoginDropdownBody', $parameters); // Note that $action and $object may have been modified by some hooks
1046
        if (is_numeric($result)) {
1047
            if ($result == 0) {
1048
                $dropdownBody .= $hookmanager->resPrint; // add
1049
            } else {
1050
                $dropdownBody = $hookmanager->resPrint; // replace
1051
            }
1052
        }
1053
1054
        if (empty($urllogout)) {
1055
            $urllogout = BASE_URL . '/user/logout.php?token=' . newToken();
1056
        }
1057
1058
        // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
1059
        // accesskey is for Mac:               CTRL + key for all browsers
1060
        $stringforfirstkey = $langs->trans("KeyboardShortcut");
1061
        if ($conf->browser->name == 'chrome') {
1062
            $stringforfirstkey .= ' ALT +';
1063
        } elseif ($conf->browser->name == 'firefox') {
1064
            $stringforfirstkey .= ' ALT + SHIFT +';
1065
        } else {
1066
            $stringforfirstkey .= ' CTL +';
1067
        }
1068
1069
        // Defined the links for bottom of card
1070
        $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>';
1071
        $urltovirtualcard = '/user/virtualcard.php?id=' . ((int)$user->id);
1072
        $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');
1073
        $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>';
1074
1075
        $profilName = $user->getFullName($langs) . ' (' . $user->login . ')';
1076
        if (!empty($user->admin)) {
1077
            $profilName = '<i class="far fa-star classfortooltip" title="' . $langs->trans("Administrator") . '" ></i> ' . $profilName;
1078
        }
1079
1080
        // Define version to show
1081
        $appli = constant('DOL_APPLICATION_TITLE');
1082
        if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
1083
            $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
1084
            if (preg_match('/\d\.\d/', $appli)) {
1085
                if (!preg_match('/' . preg_quote(DOL_VERSION) . '/', $appli)) {
1086
                    $appli .= " (" . DOL_VERSION . ")"; // If new title contains a version that is different than core
1087
                }
1088
            } else {
1089
                $appli .= " " . DOL_VERSION;
1090
            }
1091
        } else {
1092
            $appli .= " " . DOL_VERSION;
1093
        }
1094
1095
        if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1096
            $btnUser = '<!-- div for user link -->
1097
	    <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
1098
	        <a href="' . constant('BASE_URL') . '/user/card.php?id=' . $user->id . '" class="dropdown-toggle login-dropdown-a valignmiddle" data-toggle="dropdown">
1099
	            ' . $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>' : '') . '
1100
	        </a>
1101
	        <div class="dropdown-menu">
1102
	            <!-- User image -->
1103
	            <div class="user-header">
1104
	                ' . $userDropDownImage . '
1105
	                <p>
1106
	                    ' . $profilName . '<br>';
1107
            if ($user->datelastlogin) {
1108
                $title = $langs->trans("ConnectedSince") . ' : ' . dol_print_date($user->datelastlogin, "dayhour", 'tzuser');
1109
                if ($user->datepreviouslogin) {
1110
                    $title .= '<br>' . $langs->trans("PreviousConnexion") . ' : ' . dol_print_date($user->datepreviouslogin, "dayhour", 'tzuser');
1111
                }
1112
            }
1113
            $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>';
1114
            if ($user->datepreviouslogin) {
1115
                $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>';
1116
            }
1117
1118
            //$btnUser .= '<small class="classfortooltip"><i class="fa fa-cog"></i> '.$langs->trans("Version").' '.$appli.'</small>';
1119
            $btnUser .= '
1120
	                </p>
1121
	            </div>
1122
1123
	            <!-- Menu Body user-->
1124
	            <div class="user-body">' . $dropdownBody . '</div>
1125
1126
	            <!-- Menu Footer-->
1127
	            <div class="user-footer">
1128
	                <div class="pull-left">
1129
	                    ' . $profilLink . '
1130
	                </div>
1131
	                <div class="pull-left">
1132
	                    ' . $virtuelcardLink . '
1133
	                </div>
1134
	                <div class="pull-right">
1135
	                    ' . $logoutLink . '
1136
	                </div>
1137
	                <div class="clearboth"></div>
1138
	            </div>
1139
1140
	        </div>
1141
	    </div>';
1142
        } else {
1143
            $btnUser = '<!-- div for user link text browser -->
1144
	    <div id="topmenu-login-dropdown" class="userimg atoplogin dropdown user user-menu inline-block">
1145
	    	<a href="' . constant('BASE_URL') . '/user/card.php?id=' . $user->id . '" class="valignmiddle" alt="' . $langs->trans("MyUserCard") . '">
1146
	    	' . $userImage . (empty($user->photo) ? '<span class="hidden-xs maxwidth200 atoploginusername hideonsmartphone paddingleft small">' . dol_trunc($user->firstname ? $user->firstname : $user->login, 10) . '</span>' : '') . '
1147
	    	</a>
1148
		</div>';
1149
        }
1150
1151
        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
1152
            $btnUser .= '
1153
        <!-- Code to show/hide the user drop-down -->
1154
        <script>
1155
		function closeTopMenuLoginDropdown() {
1156
			//console.log("close login dropdown");	// This is call at each click on page, so we disable the log
1157
			// Hide the menus.
1158
            jQuery("#topmenu-login-dropdown").removeClass("open");
1159
		}
1160
        jQuery(document).ready(function() {
1161
            jQuery(document).on("click", function(event) {
1162
				// console.log("Click somewhere on screen");
1163
                if (!$(event.target).closest("#topmenu-login-dropdown").length) {
1164
					closeTopMenuLoginDropdown();
1165
                }
1166
            });
1167
		';
1168
1169
1170
            //if ($conf->theme != 'md') {
1171
            $btnUser .= '
1172
	            jQuery("#topmenu-login-dropdown .dropdown-toggle").on("click", function(event) {
1173
					console.log("Click on #topmenu-login-dropdown .dropdown-toggle");
1174
					event.preventDefault();
1175
	                jQuery("#topmenu-login-dropdown").toggleClass("open");
1176
	            });
1177
1178
	            jQuery("#topmenulogincompanyinfo-btn").on("click", function() {
1179
					console.log("Click on #topmenulogincompanyinfo-btn");
1180
	                jQuery("#topmenulogincompanyinfo").slideToggle();
1181
	            });
1182
1183
	            jQuery("#topmenuloginmoreinfo-btn").on("click", function() {
1184
					console.log("Click on #topmenuloginmoreinfo-btn");
1185
	                jQuery("#topmenuloginmoreinfo").slideToggle();
1186
	            });';
1187
            //}
1188
1189
            $btnUser .= '
1190
        });
1191
        </script>
1192
        ';
1193
        }
1194
1195
        return $btnUser;
1196
    }
1197
1198
    /**
1199
     * Build the tooltip on top menu quick add
1200
     *
1201
     * @return  string                  HTML content
1202
     */
1203
    public static function topMenuQuickAdd()
1204
    {
1205
        global $conf, $langs;
1206
1207
        // Button disabled on text browser
1208
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1209
            return '';
1210
        }
1211
1212
        $html = '';
1213
1214
        // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
1215
        // accesskey is for Mac:               CTRL + key for all browsers
1216
        $stringforfirstkey = $langs->trans("KeyboardShortcut");
1217
        if ($conf->browser->os === 'macintosh') {
1218
            $stringforfirstkey .= ' CTL +';
1219
        } else {
1220
            if ($conf->browser->name == 'chrome') {
1221
                $stringforfirstkey .= ' ALT +';
1222
            } elseif ($conf->browser->name == 'firefox') {
1223
                $stringforfirstkey .= ' ALT + SHIFT +';
1224
            } else {
1225
                $stringforfirstkey .= ' CTL +';
1226
            }
1227
        }
1228
1229
        if (!empty($conf->use_javascript_ajax)) {
1230
            $html .= '<!-- div for quick add link -->
1231
    <div id="topmenu-quickadd-dropdown" class="atoplogin dropdown inline-block">
1232
        <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>
1233
        <div class="dropdown-menu">' . static::printDropdownQuickadd() . '</div>
1234
    </div>';
1235
            if (!defined('JS_JQUERY_DISABLE_DROPDOWN')) {    // This may be set by some pages that use different jquery version to avoid errors
1236
                $html .= '
1237
        <!-- Code to show/hide the user drop-down for the quick add -->
1238
        <script>
1239
        jQuery(document).ready(function() {
1240
            jQuery(document).on("click", function(event) {
1241
                if (!$(event.target).closest("#topmenu-quickadd-dropdown").length) {
1242
                    // Hide the menus.
1243
                    $("#topmenu-quickadd-dropdown").removeClass("open");
1244
                }
1245
            });
1246
            $("#topmenu-quickadd-dropdown .dropdown-toggle").on("click", function(event) {
1247
				console.log("Click on #topmenu-quickadd-dropdown .dropdown-toggle");
1248
                openQuickAddDropDown(event);
1249
            });
1250
1251
            // Key map shortcut
1252
            $(document).keydown(function(event){
1253
				var ostype = \'' . dol_escape_js($conf->browser->os) . '\';
1254
				if (ostype === "macintosh") {
1255
					if ( event.which === 65 && event.ctrlKey ) {
1256
						console.log(\'control + a : trigger open quick add dropdown\');
1257
						openQuickAddDropDown(event);
1258
					}
1259
				} else {
1260
					if ( event.which === 65 && event.ctrlKey && event.shiftKey ) {
1261
						console.log(\'control + shift + a : trigger open quick add dropdown\');
1262
						openQuickAddDropDown(event);
1263
					}
1264
				}
1265
            });
1266
1267
            var openQuickAddDropDown = function(event) {
1268
                event.preventDefault();
1269
                $("#topmenu-quickadd-dropdown").toggleClass("open");
1270
                //$("#top-quickadd-search-input").focus();
1271
            }
1272
        });
1273
        </script>
1274
        ';
1275
            }
1276
        }
1277
1278
        return $html;
1279
    }
1280
1281
    /**
1282
     * Generate list of quickadd items
1283
     *
1284
     * @return string HTML output
1285
     */
1286
    public static function printDropdownQuickadd()
1287
    {
1288
        global $user, $langs, $hookmanager;
1289
1290
        $items = array(
1291
            'items' => array(
1292
                array(
1293
                    "url" => "/adherents/card.php?action=create&amp;mainmenu=members",
1294
                    "title" => "MenuNewMember@members",
1295
                    "name" => "Adherent@members",
1296
                    "picto" => "object_member",
1297
                    "activation" => isModEnabled('member') && $user->hasRight("adherent", "write"), // vs hooking
1298
                    "position" => 5,
1299
                ),
1300
                array(
1301
                    "url" => "/societe/card.php?action=create&amp;mainmenu=companies",
1302
                    "title" => "MenuNewThirdParty@companies",
1303
                    "name" => "ThirdParty@companies",
1304
                    "picto" => "object_company",
1305
                    "activation" => isModEnabled("societe") && $user->hasRight("societe", "write"), // vs hooking
1306
                    "position" => 10,
1307
                ),
1308
                array(
1309
                    "url" => "/contact/card.php?action=create&amp;mainmenu=companies",
1310
                    "title" => "NewContactAddress@companies",
1311
                    "name" => "Contact@companies",
1312
                    "picto" => "object_contact",
1313
                    "activation" => isModEnabled("societe") && $user->hasRight("societe", "contact", "write"), // vs hooking
1314
                    "position" => 20,
1315
                ),
1316
                array(
1317
                    "url" => "/comm/propal/card.php?action=create&amp;mainmenu=commercial",
1318
                    "title" => "NewPropal@propal",
1319
                    "name" => "Proposal@propal",
1320
                    "picto" => "object_propal",
1321
                    "activation" => isModEnabled("propal") && $user->hasRight("propal", "write"), // vs hooking
1322
                    "position" => 30,
1323
                ),
1324
1325
                array(
1326
                    "url" => "/commande/card.php?action=create&amp;mainmenu=commercial",
1327
                    "title" => "NewOrder@orders",
1328
                    "name" => "Order@orders",
1329
                    "picto" => "object_order",
1330
                    "activation" => isModEnabled('order') && $user->hasRight("commande", "write"), // vs hooking
1331
                    "position" => 40,
1332
                ),
1333
                array(
1334
                    "url" => "/compta/facture/card.php?action=create&amp;mainmenu=billing",
1335
                    "title" => "NewBill@bills",
1336
                    "name" => "Bill@bills",
1337
                    "picto" => "object_bill",
1338
                    "activation" => isModEnabled('invoice') && $user->hasRight("facture", "write"), // vs hooking
1339
                    "position" => 50,
1340
                ),
1341
                array(
1342
                    "url" => "/contrat/card.php?action=create&amp;mainmenu=commercial",
1343
                    "title" => "NewContractSubscription@contracts",
1344
                    "name" => "Contract@contracts",
1345
                    "picto" => "object_contract",
1346
                    "activation" => isModEnabled('contract') && $user->hasRight("contrat", "write"), // vs hooking
1347
                    "position" => 60,
1348
                ),
1349
                array(
1350
                    "url" => "/supplier_proposal/card.php?action=create&amp;mainmenu=commercial",
1351
                    "title" => "SupplierProposalNew@supplier_proposal",
1352
                    "name" => "SupplierProposal@supplier_proposal",
1353
                    "picto" => "supplier_proposal",
1354
                    "activation" => isModEnabled('supplier_proposal') && $user->hasRight("supplier_invoice", "write"), // vs hooking
1355
                    "position" => 70,
1356
                ),
1357
                array(
1358
                    "url" => "/fourn/commande/card.php?action=create&amp;mainmenu=commercial",
1359
                    "title" => "NewSupplierOrderShort@orders",
1360
                    "name" => "SupplierOrder@orders",
1361
                    "picto" => "supplier_order",
1362
                    "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "commande", "write")) || (isModEnabled("supplier_order") && $user->hasRight("supplier_invoice", "write")), // vs hooking
1363
                    "position" => 80,
1364
                ),
1365
                array(
1366
                    "url" => "/fourn/facture/card.php?action=create&amp;mainmenu=billing",
1367
                    "title" => "NewBill@bills",
1368
                    "name" => "SupplierBill@bills",
1369
                    "picto" => "supplier_invoice",
1370
                    "activation" => (isModEnabled("fournisseur") && !getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "write")) || (isModEnabled("supplier_invoice") && $user->hasRight("supplier_invoice", "write")), // vs hooking
1371
                    "position" => 90,
1372
                ),
1373
                array(
1374
                    "url" => "/ticket/card.php?action=create&amp;mainmenu=ticket",
1375
                    "title" => "NewTicket@ticket",
1376
                    "name" => "Ticket@ticket",
1377
                    "picto" => "ticket",
1378
                    "activation" => isModEnabled('ticket') && $user->hasRight("ticket", "write"), // vs hooking
1379
                    "position" => 100,
1380
                ),
1381
                array(
1382
                    "url" => "/fichinter/card.php?action=create&mainmenu=commercial",
1383
                    "title" => "NewIntervention@interventions",
1384
                    "name" => "Intervention@interventions",
1385
                    "picto" => "intervention",
1386
                    "activation" => isModEnabled('intervention') && $user->hasRight("ficheinter", "creer"), // vs hooking
1387
                    "position" => 110,
1388
                ),
1389
                array(
1390
                    "url" => "/product/card.php?action=create&amp;type=0&amp;mainmenu=products",
1391
                    "title" => "NewProduct@products",
1392
                    "name" => "Product@products",
1393
                    "picto" => "object_product",
1394
                    "activation" => isModEnabled("product") && $user->hasRight("produit", "write"), // vs hooking
1395
                    "position" => 400,
1396
                ),
1397
                array(
1398
                    "url" => "/product/card.php?action=create&amp;type=1&amp;mainmenu=products",
1399
                    "title" => "NewService@products",
1400
                    "name" => "Service@products",
1401
                    "picto" => "object_service",
1402
                    "activation" => isModEnabled("service") && $user->hasRight("service", "write"), // vs hooking
1403
                    "position" => 410,
1404
                ),
1405
                array(
1406
                    "url" => "/user/card.php?action=create&amp;type=1&amp;mainmenu=home",
1407
                    "title" => "AddUser@users",
1408
                    "name" => "User@users",
1409
                    "picto" => "user",
1410
                    "activation" => $user->hasRight("user", "user", "write"), // vs hooking
1411
                    "position" => 500,
1412
                ),
1413
            ),
1414
        );
1415
1416
        $dropDownQuickAddHtml = '';
1417
1418
        // Define $dropDownQuickAddHtml
1419
        $dropDownQuickAddHtml .= '<div class="quickadd-body dropdown-body">';
1420
        $dropDownQuickAddHtml .= '<div class="dropdown-quickadd-list">';
1421
1422
        // Allow the $items of the menu to be manipulated by modules
1423
        $parameters = array();
1424
        $hook_items = $items;
1425
        $reshook = $hookmanager->executeHooks('menuDropdownQuickaddItems', $parameters, $hook_items); // Note that $action and $object may have been modified by some hooks
1426
        if (is_numeric($reshook) && !empty($hookmanager->resArray) && is_array($hookmanager->resArray)) {
1427
            if ($reshook == 0) {
1428
                $items['items'] = array_merge($items['items'], $hookmanager->resArray); // add
1429
            } else {
1430
                $items = $hookmanager->resArray; // replace
1431
            }
1432
1433
            // Sort menu items by 'position' value
1434
            $position = array();
1435
            foreach ($items['items'] as $key => $row) {
1436
                $position[$key] = $row['position'];
1437
            }
1438
            $array1_sort_order = SORT_ASC;
1439
            array_multisort($position, $array1_sort_order, $items['items']);
1440
        }
1441
1442
        foreach ($items['items'] as $item) {
1443
            if (!$item['activation']) {
1444
                continue;
1445
            }
1446
            $langs->load(explode('@', $item['title'])[1]);
1447
            $langs->load(explode('@', $item['name'])[1]);
1448
            $dropDownQuickAddHtml .= '
1449
			<a class="dropdown-item quickadd-item" href="' . constant('DOL_URL_ROOT') . $item['url'] . '" title="' . $langs->trans(explode('@', $item['title'])[0]) . '">
1450
			' . img_picto('', $item['picto'], 'style="width:18px;"') . ' ' . $langs->trans(explode('@', $item['name'])[0]) . '</a>
1451
		';
1452
        }
1453
1454
        $dropDownQuickAddHtml .= '</div>';
1455
        $dropDownQuickAddHtml .= '</div>';
1456
1457
        return $dropDownQuickAddHtml;
1458
    }
1459
1460
    /**
1461
     * Build the tooltip on top menu bookmark
1462
     *
1463
     * @return  string                  HTML content
1464
     */
1465
    public static function topMenuBookmark()
1466
    {
1467
        global $langs, $conf, $db, $user;
1468
1469
        $html = '';
1470
1471
        // Define $bookmarks
1472
        if (!isModEnabled('bookmark') || !$user->hasRight('bookmark', 'lire')) {
1473
            return $html;
1474
        }
1475
1476
        // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
1477
        // accesskey is for Mac:               CTRL + key for all browsers
1478
        $stringforfirstkey = $langs->trans("KeyboardShortcut");
1479
        if ($conf->browser->os === 'macintosh') {
1480
            $stringforfirstkey .= ' CTL +';
1481
        } else {
1482
            if ($conf->browser->name == 'chrome') {
1483
                $stringforfirstkey .= ' ALT +';
1484
            } elseif ($conf->browser->name == 'firefox') {
1485
                $stringforfirstkey .= ' ALT + SHIFT +';
1486
            } else {
1487
                $stringforfirstkey .= ' CTL +';
1488
            }
1489
        }
1490
1491
        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
1492
            include_once DOL_DOCUMENT_ROOT . '/bookmarks/lib/bookmarks.lib.php';
1493
            $langs->load("bookmarks");
1494
1495
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1496
                $html .= '<div id="topmenu-bookmark-dropdown" class="dropdown inline-block">';
1497
                $html .= printDropdownBookmarksList();
1498
                $html .= '</div>';
1499
            } else {
1500
                $html .= '<!-- div for bookmark link -->
1501
	        <div id="topmenu-bookmark-dropdown" class="dropdown inline-block">
1502
	            <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>
1503
	            <div class="dropdown-menu">
1504
	                ' . printDropdownBookmarksList() . '
1505
	            </div>
1506
	        </div>';
1507
1508
                $html .= '
1509
	        <!-- Code to show/hide the bookmark drop-down -->
1510
	        <script>
1511
	        jQuery(document).ready(function() {
1512
	            jQuery(document).on("click", function(event) {
1513
	                if (!$(event.target).closest("#topmenu-bookmark-dropdown").length) {
1514
						//console.log("close bookmark dropdown - we click outside");
1515
	                    // Hide the menus.
1516
	                    $("#topmenu-bookmark-dropdown").removeClass("open");
1517
	                }
1518
	            });
1519
1520
	            jQuery("#topmenu-bookmark-dropdown .dropdown-toggle").on("click", function(event) {
1521
					console.log("Click on #topmenu-bookmark-dropdown .dropdown-toggle");
1522
					openBookMarkDropDown(event);
1523
	            });
1524
1525
	            // Key map shortcut
1526
	            jQuery(document).keydown(function(event) {
1527
					var ostype = \'' . dol_escape_js($conf->browser->os) . '\';
1528
					if (ostype === "macintosh") {
1529
						if ( event.which === 66 && event.ctrlKey ) {
1530
							console.log("Click on control + b : trigger open bookmark dropdown");
1531
							openBookMarkDropDown(event);
1532
						}
1533
					} else {
1534
						if ( event.which === 66 && event.ctrlKey && event.shiftKey ) {
1535
							console.log("Click on control + shift + b : trigger open bookmark dropdown");
1536
							openBookMarkDropDown(event);
1537
						}
1538
					}
1539
	            });
1540
1541
	            var openBookMarkDropDown = function(event) {
1542
	                event.preventDefault();
1543
	                jQuery("#topmenu-bookmark-dropdown").toggleClass("open");
1544
	                jQuery("#top-bookmark-search-input").focus();
1545
	            }
1546
1547
	        });
1548
	        </script>
1549
	        ';
1550
            }
1551
        }
1552
        return $html;
1553
    }
1554
1555
    /**
1556
     * Build the tooltip on top menu tsearch
1557
     *
1558
     * @return  string                  HTML content
1559
     */
1560
    public static function topSearchMenu()
1561
    {
1562
        global $langs, $conf, $db, $user, $hookmanager;
1563
1564
        $html = '';
1565
1566
        $usedbyinclude = 1;
1567
        $arrayresult = array();
1568
        include DOL_DOCUMENT_ROOT . '/core/ajax/selectsearchbox.php'; // This sets $arrayresult
1569
1570
        // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
1571
        // accesskey is for Mac:               CTRL + key for all browsers
1572
        $stringforfirstkey = $langs->trans("KeyboardShortcut");
1573
        if ($conf->browser->name == 'chrome') {
1574
            $stringforfirstkey .= ' ALT +';
1575
        } elseif ($conf->browser->name == 'firefox') {
1576
            $stringforfirstkey .= ' ALT + SHIFT +';
1577
        } else {
1578
            $stringforfirstkey .= ' CTL +';
1579
        }
1580
1581
        $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">';
1582
1583
        $defaultAction = '';
1584
        $buttonList = '<div class="dropdown-global-search-button-list" >';
1585
        // Menu with all searchable items
1586
        foreach ($arrayresult as $keyItem => $item) {
1587
            if (empty($defaultAction)) {
1588
                $defaultAction = $item['url'];
1589
            }
1590
            $buttonList .= '<button class="dropdown-item global-search-item tdoverflowmax300" data-target="' . dol_escape_htmltag($item['url']) . '" >';
1591
            $buttonList .= $item['text'];
1592
            $buttonList .= '</button>';
1593
        }
1594
        $buttonList .= '</div>';
1595
1596
        $dropDownHtml = '<form role="search" id="top-menu-action-search" name="actionsearch" method="GET" action="' . $defaultAction . '">';
1597
1598
        $dropDownHtml .= '
1599
        <!-- search input -->
1600
        <div class="dropdown-header search-dropdown-header">
1601
            ' . $searchInput . '
1602
        </div>
1603
    ';
1604
1605
        $dropDownHtml .= '
1606
        <!-- Menu Body search -->
1607
        <div class="dropdown-body search-dropdown-body">
1608
        ' . $buttonList . '
1609
        </div>
1610
        ';
1611
1612
        $dropDownHtml .= '</form>';
1613
1614
        // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
1615
        // accesskey is for Mac:               CTRL + key for all browsers
1616
        $stringforfirstkey = $langs->trans("KeyboardShortcut");
1617
        if ($conf->browser->name == 'chrome') {
1618
            $stringforfirstkey .= ' ALT +';
1619
        } elseif ($conf->browser->name == 'firefox') {
1620
            $stringforfirstkey .= ' ALT + SHIFT +';
1621
        } else {
1622
            $stringforfirstkey .= ' CTL +';
1623
        }
1624
1625
        $html .= '<!-- div for Global Search -->
1626
    <div id="topmenu-global-search-dropdown" class="atoplogin dropdown inline-block">
1627
        <a accesskey="s" class="dropdown-toggle login-dropdown-a nofocusvisible" data-toggle="dropdown" href="#" title="' . $langs->trans('Search') . ' (' . $stringforfirstkey . ' s)">
1628
            <i class="fa fa-search" aria-hidden="true" ></i>
1629
        </a>
1630
        <div class="dropdown-menu dropdown-search">
1631
            ' . $dropDownHtml . '
1632
        </div>
1633
    </div>';
1634
1635
        $html .= '
1636
    <!-- Code to show/hide the user drop-down -->
1637
    <script>
1638
    jQuery(document).ready(function() {
1639
1640
        // prevent submitting form on press ENTER
1641
        jQuery("#top-global-search-input").keydown(function (e) {
1642
            if (e.keyCode == 13 || e.keyCode == 40) {
1643
                var inputs = $(this).parents("form").eq(0).find(":button");
1644
                if (inputs[inputs.index(this) + 1] != null) {
1645
                    inputs[inputs.index(this) + 1].focus();
1646
					 if (e.keyCode == 13){
1647
						 inputs[inputs.index(this) + 1].trigger("click");
1648
					 }
1649
1650
                }
1651
                e.preventDefault();
1652
                return false;
1653
            }
1654
        });
1655
1656
        // arrow key nav
1657
        jQuery(document).keydown(function(e) {
1658
			// Get the focused element:
1659
			var $focused = $(":focus");
1660
			if($focused.length && $focused.hasClass("global-search-item")){
1661
1662
           		// UP - move to the previous line
1663
				if (e.keyCode == 38) {
1664
				    e.preventDefault();
1665
					$focused.prev().focus();
1666
				}
1667
1668
				// DOWN - move to the next line
1669
				if (e.keyCode == 40) {
1670
				    e.preventDefault();
1671
					$focused.next().focus();
1672
				}
1673
			}
1674
        });
1675
1676
1677
        // submit form action
1678
        jQuery(".dropdown-global-search-button-list .global-search-item").on("click", function(event) {
1679
            jQuery("#top-menu-action-search").attr("action", $(this).data("target"));
1680
            jQuery("#top-menu-action-search").submit();
1681
        });
1682
1683
        // close drop down
1684
        jQuery(document).on("click", function(event) {
1685
			if (!$(event.target).closest("#topmenu-global-search-dropdown").length) {
1686
				console.log("click close search - we click outside");
1687
                // Hide the menus.
1688
                jQuery("#topmenu-global-search-dropdown").removeClass("open");
1689
            }
1690
        });
1691
1692
        // Open drop down
1693
        jQuery("#topmenu-global-search-dropdown .dropdown-toggle").on("click", function(event) {
1694
			console.log("click on toggle #topmenu-global-search-dropdown .dropdown-toggle");
1695
            openGlobalSearchDropDown();
1696
        });
1697
1698
        // Key map shortcut
1699
        jQuery(document).keydown(function(e){
1700
              if ( e.which === 70 && e.ctrlKey && e.shiftKey ) {
1701
                 console.log(\'control + shift + f : trigger open global-search dropdown\');
1702
                 openGlobalSearchDropDown();
1703
              }
1704
              if ( e.which === 70 && e.alKey ) {
1705
                 console.log(\'alt + f : trigger open global-search dropdown\');
1706
                 openGlobalSearchDropDown();
1707
              }
1708
        });
1709
1710
        var openGlobalSearchDropDown = function() {
1711
            jQuery("#topmenu-global-search-dropdown").toggleClass("open");
1712
            jQuery("#top-global-search-input").focus();
1713
        }
1714
1715
    });
1716
    </script>
1717
    ';
1718
1719
        return $html;
1720
    }
1721
1722
    /**
1723
     *  Show left menu bar
1724
     *
1725
     * @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 ''.
1726
     * @param string $helppagename Name of wiki page for help ('' by default).
1727
     *                                                  Syntax is: For a wiki page: EN:EnglishPage|FR:FrenchPage|ES:SpanishPage|DE:GermanPage
1728
     *                                                  For other external page: http://server/url
1729
     * @param string $notused Deprecated. Used in past to add content into left menu. Hooks can be used now.
1730
     * @param array $menu_array_after Table of menu entries to show after entries of menu handler
1731
     * @param int $leftmenuwithoutmainarea Must be set to 1. 0 by default for backward compatibility with old modules.
1732
     * @param string $title Title of web page
1733
     * @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)
1734
     * @return void
1735
     */
1736
    public static function leftMenu($menu_array_before, $helppagename = '', $notused = '', $menu_array_after = array(), $leftmenuwithoutmainarea = 0, $title = '', $acceptdelayedhtml = 0)
1737
    {
1738
        global $user, $conf, $langs, $db, $form;
1739
        global $hookmanager, $menumanager;
1740
1741
        $searchform = '';
1742
1743
        if (!empty($menu_array_before)) {
1744
            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);
1745
        }
1746
1747
        if (empty($conf->dol_hide_leftmenu) && (!defined('NOREQUIREMENU') || !constant('NOREQUIREMENU'))) {
1748
            // Instantiate hooks for external modules
1749
            $hookmanager->initHooks(array('leftblock'));
1750
1751
            print "\n" . '<!-- Begin side-nav id-left -->' . "\n" . '<div class="side-nav"><div id="id-left">' . "\n";
1752
            print "\n";
1753
1754
            if (!is_object($form)) {
1755
                $form = new Form($db);
1756
            }
1757
            $selected = -1;
1758
            if (!getDolGlobalString('MAIN_USE_TOP_MENU_SEARCH_DROPDOWN')) {
1759
                // Select with select2 is awful on smartphone. TODO Is this still true with select2 v4 ?
1760
                if ($conf->browser->layout == 'phone') {
1761
                    $conf->global->MAIN_USE_OLD_SEARCH_FORM = 1;
1762
                }
1763
1764
                $usedbyinclude = 1;
1765
                $arrayresult = array();
1766
                include DOL_DOCUMENT_ROOT . '/core/ajax/selectsearchbox.php'; // This make initHooks('searchform') then set $arrayresult
1767
1768
                if ($conf->use_javascript_ajax && !getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
1769
                    // accesskey is for Windows or Linux:  ALT + key for chrome, ALT + SHIFT + KEY for firefox
1770
                    // accesskey is for Mac:               CTRL + key for all browsers
1771
                    $stringforfirstkey = $langs->trans("KeyboardShortcut");
1772
                    if ($conf->browser->name == 'chrome') {
1773
                        $stringforfirstkey .= ' ALT +';
1774
                    } elseif ($conf->browser->name == 'firefox') {
1775
                        $stringforfirstkey .= ' ALT + SHIFT +';
1776
                    } else {
1777
                        $stringforfirstkey .= ' CTL +';
1778
                    }
1779
1780
                    //$textsearch = $langs->trans("Search");
1781
                    $textsearch = '<span class="fa fa-search paddingright pictofixedwidth"></span>' . $langs->trans("Search");
1782
                    $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');
1783
                } else {
1784
                    if (is_array($arrayresult)) {
1785
                        foreach ($arrayresult as $key => $val) {
1786
                            $searchform .= static::printSearchForm($val['url'], $val['url'], $val['label'], 'maxwidth125', 'search_all', (empty($val['shortcut']) ? '' : $val['shortcut']), 'searchleft' . $key, $val['img']);
1787
                        }
1788
                    }
1789
                }
1790
1791
                // Execute hook printSearchForm
1792
                $parameters = array('searchform' => $searchform);
1793
                $reshook = $hookmanager->executeHooks('printSearchForm', $parameters); // Note that $action and $object may have been modified by some hooks
1794
                if (empty($reshook)) {
1795
                    $searchform .= $hookmanager->resPrint;
1796
                } else {
1797
                    $searchform = $hookmanager->resPrint;
1798
                }
1799
1800
                // Force special value for $searchform for text browsers or very old search form
1801
                if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') || empty($conf->use_javascript_ajax)) {
1802
                    $urltosearch = constant('BASE_URL') . '/core/search_page.php?showtitlebefore=1';
1803
                    $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>';
1804
                } elseif ($conf->use_javascript_ajax && getDolGlobalString('MAIN_USE_OLD_SEARCH_FORM')) {
1805
                    $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>';
1806
                    $searchform .= '<script>
1807
            	jQuery(document).ready(function () {
1808
            		jQuery("#divsearchforms1").click(function(){
1809
	                   jQuery("#divsearchforms2").toggle();
1810
	               });
1811
            	});
1812
                </script>' . "\n";
1813
                    $searchform .= '</div>';
1814
                }
1815
1816
                // Key map shortcut
1817
                $searchform .= '<script>
1818
				jQuery(document).keydown(function(e){
1819
					if( e.which === 70 && e.ctrlKey && e.shiftKey ){
1820
						console.log(\'control + shift + f : trigger open global-search dropdown\');
1821
		                openGlobalSearchDropDown();
1822
		            }
1823
		            if( (e.which === 83 || e.which === 115) && e.altKey ){
1824
		                console.log(\'alt + s : trigger open global-search dropdown\');
1825
		                openGlobalSearchDropDown();
1826
		            }
1827
		        });
1828
1829
		        var openGlobalSearchDropDown = function() {
1830
		            jQuery("#searchselectcombo").select2(\'open\');
1831
		        }
1832
			</script>';
1833
            }
1834
1835
            // Left column
1836
            print '<!-- Begin left menu -->' . "\n";
1837
1838
            print '<div class="vmenu"' . (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' alt="Left menu"' : '') . '>' . "\n\n";
1839
1840
            // Show left menu with other forms
1841
            $menumanager->menu_array = $menu_array_before;
1842
            $menumanager->menu_array_after = $menu_array_after;
1843
            $menumanager->showmenu('left', array('searchform' => $searchform)); // output menu_array and menu found in database
1844
1845
            // Dolibarr version + help + bug report link
1846
            print "\n";
1847
            print "<!-- Begin Help Block-->\n";
1848
            print '<div id="blockvmenuhelp" class="blockvmenuhelp">' . "\n";
1849
1850
            // Version
1851
            if (getDolGlobalString('MAIN_SHOW_VERSION')) {    // Version is already on help picto and on login page.
1852
                $doliurl = 'https://www.dolibarr.org';
1853
                //local communities
1854
                if (preg_match('/fr/i', $langs->defaultlang)) {
1855
                    $doliurl = 'https://www.dolibarr.fr';
1856
                }
1857
                if (preg_match('/es/i', $langs->defaultlang)) {
1858
                    $doliurl = 'https://www.dolibarr.es';
1859
                }
1860
                if (preg_match('/de/i', $langs->defaultlang)) {
1861
                    $doliurl = 'https://www.dolibarr.de';
1862
                }
1863
                if (preg_match('/it/i', $langs->defaultlang)) {
1864
                    $doliurl = 'https://www.dolibarr.it';
1865
                }
1866
                if (preg_match('/gr/i', $langs->defaultlang)) {
1867
                    $doliurl = 'https://www.dolibarr.gr';
1868
                }
1869
1870
                $appli = constant('DOL_APPLICATION_TITLE');
1871
                if (getDolGlobalString('MAIN_APPLICATION_TITLE')) {
1872
                    $appli = getDolGlobalString('MAIN_APPLICATION_TITLE');
1873
                    $doliurl = '';
1874
                    if (preg_match('/\d\.\d/', $appli)) {
1875
                        if (!preg_match('/' . preg_quote(DOL_VERSION) . '/', $appli)) {
1876
                            $appli .= " (" . DOL_VERSION . ")"; // If new title contains a version that is different than core
1877
                        }
1878
                    } else {
1879
                        $appli .= " " . DOL_VERSION;
1880
                    }
1881
                } else {
1882
                    $appli .= " " . DOL_VERSION;
1883
                }
1884
                print '<div id="blockvmenuhelpapp" class="blockvmenuhelp">';
1885
                if ($doliurl) {
1886
                    print '<a class="help" target="_blank" rel="noopener noreferrer" href="' . $doliurl . '">';
1887
                } else {
1888
                    print '<span class="help">';
1889
                }
1890
                print $appli;
1891
                if ($doliurl) {
1892
                    print '</a>';
1893
                } else {
1894
                    print '</span>';
1895
                }
1896
                print '</div>' . "\n";
1897
            }
1898
1899
            // Link to bugtrack
1900
            if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
1901
                require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
1902
1903
                if (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK') == 'github') {
1904
                    $bugbaseurl = 'https://github.com/Dolibarr/dolibarr/issues/new?labels=Bug';
1905
                    $bugbaseurl .= '&title=';
1906
                    $bugbaseurl .= urlencode("Bug: ");
1907
                    $bugbaseurl .= '&body=';
1908
                    $bugbaseurl .= urlencode("# Instructions\n");
1909
                    $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");
1910
                    $bugbaseurl .= urlencode("*Please:*\n");
1911
                    $bugbaseurl .= urlencode("- *replace the bracket enclosed texts with meaningful information*\n");
1912
                    $bugbaseurl .= urlencode("- *remove any unused sub-section*\n");
1913
                    $bugbaseurl .= urlencode("\n");
1914
                    $bugbaseurl .= urlencode("\n");
1915
                    $bugbaseurl .= urlencode("# Bug\n");
1916
                    $bugbaseurl .= urlencode("[*Short description*]\n");
1917
                    $bugbaseurl .= urlencode("\n");
1918
                    $bugbaseurl .= urlencode("## Environment\n");
1919
                    $bugbaseurl .= urlencode("- **Version**: " . DOL_VERSION . "\n");
1920
                    $bugbaseurl .= urlencode("- **OS**: " . php_uname('s') . "\n");
1921
                    $bugbaseurl .= urlencode("- **Web server**: " . $_SERVER["SERVER_SOFTWARE"] . "\n");
1922
                    $bugbaseurl .= urlencode("- **PHP**: " . php_sapi_name() . ' ' . phpversion() . "\n");
1923
                    $bugbaseurl .= urlencode("- **Database**: " . $db::LABEL . ' ' . $db->getVersion() . "\n");
1924
                    $bugbaseurl .= urlencode("- **URL(s)**: " . $_SERVER["REQUEST_URI"] . "\n");
1925
                    $bugbaseurl .= urlencode("\n");
1926
                    $bugbaseurl .= urlencode("## Expected and actual behavior\n");
1927
                    $bugbaseurl .= urlencode("[*Verbose description*]\n");
1928
                    $bugbaseurl .= urlencode("\n");
1929
                    $bugbaseurl .= urlencode("## Steps to reproduce the behavior\n");
1930
                    $bugbaseurl .= urlencode("[*Verbose description*]\n");
1931
                    $bugbaseurl .= urlencode("\n");
1932
                    $bugbaseurl .= urlencode("## [Attached files](https://help.github.com/articles/issue-attachments) (Screenshots, screencasts, alixar.log, debugging information…)\n");
1933
                    $bugbaseurl .= urlencode("[*Files*]\n");
1934
                    $bugbaseurl .= urlencode("\n");
1935
1936
                    $bugbaseurl .= urlencode("\n");
1937
                    $bugbaseurl .= urlencode("## Report\n");
1938
                } elseif (getDolGlobalString('MAIN_BUGTRACK_ENABLELINK')) {
1939
                    $bugbaseurl = getDolGlobalString('MAIN_BUGTRACK_ENABLELINK');
1940
                } else {
1941
                    $bugbaseurl = "";
1942
                }
1943
1944
                // Execute hook printBugtrackInfo
1945
                $parameters = array('bugbaseurl' => $bugbaseurl);
1946
                $reshook = $hookmanager->executeHooks('printBugtrackInfo', $parameters); // Note that $action and $object may have been modified by some hooks
1947
                if (empty($reshook)) {
1948
                    $bugbaseurl .= $hookmanager->resPrint;
1949
                } else {
1950
                    $bugbaseurl = $hookmanager->resPrint;
1951
                }
1952
1953
                print '<div id="blockvmenuhelpbugreport" class="blockvmenuhelp">';
1954
                print '<a class="help" target="_blank" rel="noopener noreferrer" href="' . $bugbaseurl . '"><i class="fas fa-bug"></i> ' . $langs->trans("FindBug") . '</a>';
1955
                print '</div>';
1956
            }
1957
1958
            print "</div>\n";
1959
            print "<!-- End Help Block-->\n";
1960
            print "\n";
1961
1962
            print "</div>\n";
1963
            print "<!-- End left menu -->\n";
1964
            print "\n";
1965
1966
            // Execute hook printLeftBlock
1967
            $parameters = array();
1968
            $reshook = $hookmanager->executeHooks('printLeftBlock', $parameters); // Note that $action and $object may have been modified by some hooks
1969
            print $hookmanager->resPrint;
1970
1971
            print '</div></div> <!-- End side-nav id-left -->'; // End div id="side-nav" div id="id-left"
1972
        }
1973
1974
        print "\n";
1975
        print '<!-- Begin right area -->' . "\n";
1976
1977
        if (empty($leftmenuwithoutmainarea)) {
1978
            static::mainArea($title);
1979
        }
1980
    }
1981
1982
    /**
1983
     *  Begin main area
1984
     *
1985
     * @param string $title Title
1986
     * @return void
1987
     */
1988
    public static function mainArea($title = '')
1989
    {
1990
        global $conf, $langs, $hookmanager;
1991
1992
        if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup')) {
1993
            print '<div id="id-right">';
1994
        }
1995
1996
        print "\n";
1997
1998
        print '<!-- Begin div class="fiche" -->' . "\n" . '<div class="fiche">' . "\n";
1999
2000
        $hookmanager->initHooks(array('main'));
2001
        $parameters = array();
2002
        $reshook = $hookmanager->executeHooks('printMainArea', $parameters); // Note that $action and $object may have been modified by some hooks
2003
        print $hookmanager->resPrint;
2004
2005
        if (getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')) {
2006
            print info_admin($langs->trans("WarningYouAreInMaintenanceMode", getDolGlobalString('MAIN_ONLY_LOGIN_ALLOWED')), 0, 0, 1, 'warning maintenancemode');
2007
        }
2008
2009
        // Permit to add user company information on each printed document by setting SHOW_SOCINFO_ON_PRINT
2010
        if (getDolGlobalString('SHOW_SOCINFO_ON_PRINT') && GETPOST('optioncss', 'aZ09') == 'print' && empty(GETPOST('disable_show_socinfo_on_print', 'aZ09'))) {
2011
            $parameters = array();
2012
            $reshook = $hookmanager->executeHooks('showSocinfoOnPrint', $parameters);
2013
            if (empty($reshook)) {
2014
                print '<!-- Begin show mysoc info header -->' . "\n";
2015
                print '<div id="mysoc-info-header">' . "\n";
2016
                print '<table class="centpercent div-table-responsive">' . "\n";
2017
                print '<tbody>';
2018
                print '<tr><td rowspan="0" class="width20p">';
2019
                if (getDolGlobalString('MAIN_SHOW_LOGO') && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && getDolGlobalString('MAIN_INFO_SOCIETE_LOGO')) {
2020
                    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'))) . '">';
2021
                }
2022
                print '</td><td  rowspan="0" class="width50p"></td></tr>' . "\n";
2023
                print '<tr><td class="titre bold">' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_NOM')) . '</td></tr>' . "\n";
2024
                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";
2025
                if (getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) {
2026
                    print '<tr><td style="padding-left: 1em" class="small">' . $langs->trans("Phone") . ' : ' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_TEL')) . '</td></tr>';
2027
                }
2028
                if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
2029
                    print '<tr><td style="padding-left: 1em" class="small">' . $langs->trans("Email") . ' : ' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) . '</td></tr>';
2030
                }
2031
                if (getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) {
2032
                    print '<tr><td style="padding-left: 1em" class="small">' . $langs->trans("Web") . ' : ' . dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_WEB')) . '</td></tr>';
2033
                }
2034
                print '</tbody>';
2035
                print '</table>' . "\n";
2036
                print '</div>' . "\n";
2037
                print '<!-- End show mysoc info header -->' . "\n";
2038
            }
2039
        }
2040
    }
2041
2042
    /**
2043
     *  Return helpbaseurl, helppage and mode
2044
     *
2045
     * @param string $helppagename Page name ('EN:xxx,ES:eee,FR:fff,DE:ddd...' or 'http://localpage')
2046
     * @param Translate $langs Language
2047
     * @return array{helpbaseurl:string,helppage:string,mode:string}   Array of help urls
2048
     */
2049
    public static function getHelpParamFor($helppagename, $langs)
2050
    {
2051
        $helpbaseurl = '';
2052
        $helppage = '';
2053
        $mode = '';
2054
2055
        if (preg_match('/^http/i', $helppagename)) {
2056
            // If complete URL
2057
            $helpbaseurl = '%s';
2058
            $helppage = $helppagename;
2059
            $mode = 'local';
2060
        } else {
2061
            // If WIKI URL
2062
            $reg = array();
2063
            if (preg_match('/^es/i', $langs->defaultlang)) {
2064
                $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
2065
                if (preg_match('/ES:([^|]+)/i', $helppagename, $reg)) {
2066
                    $helppage = $reg[1];
2067
                }
2068
            }
2069
            if (preg_match('/^fr/i', $langs->defaultlang)) {
2070
                $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
2071
                if (preg_match('/FR:([^|]+)/i', $helppagename, $reg)) {
2072
                    $helppage = $reg[1];
2073
                }
2074
            }
2075
            if (preg_match('/^de/i', $langs->defaultlang)) {
2076
                $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
2077
                if (preg_match('/DE:([^|]+)/i', $helppagename, $reg)) {
2078
                    $helppage = $reg[1];
2079
                }
2080
            }
2081
            if (empty($helppage)) { // If help page not already found
2082
                $helpbaseurl = 'http://wiki.dolibarr.org/index.php/%s';
2083
                if (preg_match('/EN:([^|]+)/i', $helppagename, $reg)) {
2084
                    $helppage = $reg[1];
2085
                }
2086
            }
2087
            $mode = 'wiki';
2088
        }
2089
        return array('helpbaseurl' => $helpbaseurl, 'helppage' => $helppage, 'mode' => $mode);
2090
    }
2091
2092
    /**
2093
     *  Show a search area.
2094
     *  Used when the javascript quick search is not used.
2095
     *
2096
     * @param string $urlaction Url post
2097
     * @param string $urlobject Url of the link under the search box
2098
     * @param string $title Title search area
2099
     * @param string $htmlmorecss Add more css
2100
     * @param string $htmlinputname Field Name input form
2101
     * @param string $accesskey Accesskey
2102
     * @param string $prefhtmlinputname Complement for id to avoid multiple same id in the page
2103
     * @param string $img Image to use
2104
     * @param int $showtitlebefore Show title before input text instead of into placeholder. This can be set when output is dedicated for text browsers.
2105
     * @param int $autofocus Set autofocus on field
2106
     * @return string
2107
     */
2108
    public static function printSearchForm($urlaction, $urlobject, $title, $htmlmorecss, $htmlinputname, $accesskey = '', $prefhtmlinputname = '', $img = '', $showtitlebefore = 0, $autofocus = 0)
2109
    {
2110
        global $langs, $user;
2111
2112
        $ret = '';
2113
        $ret .= '<form action="' . $urlaction . '" method="post" class="searchform nowraponall tagtr">';
2114
        $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
2115
        $ret .= '<input type="hidden" name="savelogin" value="' . dol_escape_htmltag($user->login) . '">';
2116
        if ($showtitlebefore) {
2117
            $ret .= '<div class="tagtd left">' . $title . '</div> ';
2118
        }
2119
        $ret .= '<div class="tagtd">';
2120
        $ret .= img_picto('', $img, '', false, 0, 0, '', 'paddingright width20');
2121
        $ret .= '<input type="text" class="flat ' . $htmlmorecss . '"';
2122
        $ret .= ' style="background-repeat: no-repeat; background-position: 3px;"';
2123
        $ret .= ($accesskey ? ' accesskey="' . $accesskey . '"' : '');
2124
        $ret .= ' placeholder="' . strip_tags($title) . '"';
2125
        $ret .= ($autofocus ? ' autofocus' : '');
2126
        $ret .= ' name="' . $htmlinputname . '" id="' . $prefhtmlinputname . $htmlinputname . '" />';
2127
        $ret .= '<button type="submit" class="button bordertransp" style="padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 6px">';
2128
        $ret .= '<span class="fa fa-search"></span>';
2129
        $ret .= '</button>';
2130
        $ret .= '</div>';
2131
        $ret .= "</form>\n";
2132
        return $ret;
2133
    }
2134
2135
    /**
2136
     * Show HTML footer
2137
     * Close div /DIV class=fiche + /DIV id-right + /DIV id-container + /BODY + /HTML.
2138
     * If global var $delayedhtmlcontent was filled, we output it just before closing the body.
2139
     *
2140
     * @param string $comment A text to add as HTML comment into HTML generated page
2141
     * @param string $zone 'private' (for private pages) or 'public' (for public pages)
2142
     * @param int $disabledoutputofmessages Clear all messages stored into session without displaying them
2143
     * @return  void
2144
     */
2145
    public static function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages = 0)
2146
    {
2147
        global $conf, $db, $langs, $user, $mysoc, $object, $hookmanager, $action;
2148
        global $delayedhtmlcontent;
2149
        global $contextpage, $page, $limit, $mode;
2150
        global $dolibarr_distrib;
2151
2152
        $ext = 'layout=' . urlencode($conf->browser->layout) . '&version=' . urlencode(DOL_VERSION);
2153
2154
        // Hook to add more things on all pages within fiche DIV
2155
        $llxfooter = '';
2156
        $parameters = array();
2157
        $reshook = $hookmanager->executeHooks('llxFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2158
        if (empty($reshook)) {
2159
            $llxfooter .= $hookmanager->resPrint;
2160
        } elseif ($reshook > 0) {
2161
            $llxfooter = $hookmanager->resPrint;
2162
        }
2163
        if ($llxfooter) {
2164
            print $llxfooter;
2165
        }
2166
2167
        // Global html output events ($mesgs, $errors, $warnings)
2168
        dol_htmloutput_events($disabledoutputofmessages);
2169
2170
        // Code for search criteria persistence.
2171
        // $user->lastsearch_values was set by the GETPOST when form field search_xxx exists
2172
        if (is_object($user) && !empty($user->lastsearch_values_tmp) && is_array($user->lastsearch_values_tmp)) {
2173
            // Clean and save data
2174
            foreach ($user->lastsearch_values_tmp as $key => $val) {
2175
                unset($_SESSION['lastsearch_values_tmp_' . $key]); // Clean array to rebuild it just after
2176
                if (count($val) && empty($_POST['button_removefilter']) && empty($_POST['button_removefilter_x'])) {
2177
                    if (empty($val['sortfield'])) {
2178
                        unset($val['sortfield']);
2179
                    }
2180
                    if (empty($val['sortorder'])) {
2181
                        unset($val['sortorder']);
2182
                    }
2183
                    dol_syslog('Save lastsearch_values_tmp_' . $key . '=' . json_encode($val, 0) . " (systematic recording of last search criteria)");
2184
                    $_SESSION['lastsearch_values_tmp_' . $key] = json_encode($val);
2185
                    unset($_SESSION['lastsearch_values_' . $key]);
2186
                }
2187
            }
2188
        }
2189
2190
2191
        $relativepathstring = $_SERVER["PHP_SELF"];
2192
        // Clean $relativepathstring
2193
        if (constant('DOL_URL_ROOT')) {
2194
            $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_URL_ROOT'), '/') . '/', '', $relativepathstring);
2195
        }
2196
        $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
2197
        $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
2198
        if (preg_match('/list\.php$/', $relativepathstring)) {
2199
            unset($_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring]);
2200
            unset($_SESSION['lastsearch_page_tmp_' . $relativepathstring]);
2201
            unset($_SESSION['lastsearch_limit_tmp_' . $relativepathstring]);
2202
            unset($_SESSION['lastsearch_mode_tmp_' . $relativepathstring]);
2203
2204
            if (!empty($contextpage)) {
2205
                $_SESSION['lastsearch_contextpage_tmp_' . $relativepathstring] = $contextpage;
2206
            }
2207
            if (!empty($page) && $page > 0) {
2208
                $_SESSION['lastsearch_page_tmp_' . $relativepathstring] = $page;
2209
            }
2210
            if (!empty($limit) && $limit != $conf->liste_limit) {
2211
                $_SESSION['lastsearch_limit_tmp_' . $relativepathstring] = $limit;
2212
            }
2213
            if (!empty($mode)) {
2214
                $_SESSION['lastsearch_mode_tmp_' . $relativepathstring] = $mode;
2215
            }
2216
2217
            unset($_SESSION['lastsearch_contextpage_' . $relativepathstring]);
2218
            unset($_SESSION['lastsearch_page_' . $relativepathstring]);
2219
            unset($_SESSION['lastsearch_limit_' . $relativepathstring]);
2220
            unset($_SESSION['lastsearch_mode_' . $relativepathstring]);
2221
        }
2222
2223
        // Core error message
2224
        if (getDolGlobalString('MAIN_CORE_ERROR')) {
2225
            // Ajax version
2226
            if ($conf->use_javascript_ajax) {
2227
                $title = img_warning() . ' ' . $langs->trans('CoreErrorTitle');
2228
                print ajax_dialog($title, $langs->trans('CoreErrorMessage'));
2229
            } else {
2230
                // html version
2231
                $msg = img_warning() . ' ' . $langs->trans('CoreErrorMessage');
2232
                print '<div class="error">' . $msg . '</div>';
2233
            }
2234
2235
            //define("MAIN_CORE_ERROR",0);      // Constant was defined and we can't change value of a constant
2236
        }
2237
2238
        print "\n\n";
2239
2240
        print '</div> <!-- End div class="fiche" -->' . "\n"; // End div fiche
2241
2242
        if (empty($conf->dol_hide_leftmenu) && !GETPOST('dol_openinpopup')) {
2243
            print '</div> <!-- End div id-right -->' . "\n"; // End div id-right
2244
        }
2245
2246
        if (empty($conf->dol_hide_leftmenu) && empty($conf->dol_use_jmobile)) {
2247
            print '</div> <!-- End div id-container -->' . "\n"; // End div container
2248
        }
2249
2250
        print "\n";
2251
        if ($comment) {
2252
            print '<!-- ' . $comment . ' -->' . "\n";
2253
        }
2254
2255
        printCommonFooter($zone);
2256
2257
        if (!empty($delayedhtmlcontent)) {
2258
            print $delayedhtmlcontent;
2259
        }
2260
2261
        if (!empty($conf->use_javascript_ajax)) {
2262
            print "\n" . '<!-- Includes JS Footer of Dolibarr -->' . "\n";
2263
            print '<script src="' . constant('BASE_URL') . '/core/js/lib_foot.js.php?lang=' . $langs->defaultlang . ($ext ? '&' . $ext : '') . '"></script>' . "\n";
2264
        }
2265
2266
        // Wrapper to add log when clicking on download or preview
2267
        if (isModEnabled('blockedlog') && is_object($object) && !empty($object->id) && $object->id > 0) {
2268
            if (in_array($object->element, array('facture')) && $object->statut > 0) {       // Restrict for the moment to element 'facture'
2269
                print "\n<!-- JS CODE TO ENABLE log when making a download or a preview of a document -->\n";
2270
                ?>
2271
                <script>
2272
                    jQuery(document).ready(function () {
2273
                        $('a.documentpreview').click(function () {
2274
                            console.log("Call /blockedlog/ajax/block-add on a.documentpreview");
2275
                            $.post('<?php echo DOL_URL_ROOT . "/blockedlog/ajax/block-add.php" ?>'
2276
                                , {
2277
                                    id:<?php echo $object->id; ?>
2278
                                    , element: '<?php echo dol_escape_js($object->element) ?>'
2279
                                    , action: 'DOC_PREVIEW'
2280
                                    , token: '<?php echo currentToken(); ?>'
2281
                                }
2282
                            );
2283
                        });
2284
                        $('a.documentdownload').click(function () {
2285
                            console.log("Call /blockedlog/ajax/block-add a.documentdownload");
2286
                            $.post('<?php echo DOL_URL_ROOT . "/blockedlog/ajax/block-add.php" ?>'
2287
                                , {
2288
                                    id:<?php echo $object->id; ?>
2289
                                    , element: '<?php echo dol_escape_js($object->element) ?>'
2290
                                    , action: 'DOC_DOWNLOAD'
2291
                                    , token: '<?php echo currentToken(); ?>'
2292
                                }
2293
                            );
2294
                        });
2295
                    });
2296
                </script>
2297
                <?php
2298
            }
2299
        }
2300
2301
        // A div for the address popup
2302
        print "\n<!-- A div to allow dialog popup by jQuery('#dialogforpopup').dialog() -->\n";
2303
        print '<div id="dialogforpopup" style="display: none;"></div>' . "\n";
2304
2305
        // Add code for the asynchronous anonymous first ping (for telemetry)
2306
        // You can use &forceping=1 in parameters to force the ping if the ping was already sent.
2307
        $forceping = GETPOST('forceping', 'alpha');
2308
        if (($_SERVER["PHP_SELF"] == constant('BASE_URL') . '/index.php') || $forceping) {
2309
            //print '<!-- instance_unique_id='.$conf->file->instance_unique_id.' MAIN_FIRST_PING_OK_ID='.$conf->global->MAIN_FIRST_PING_OK_ID.' -->';
2310
            $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.
2311
2312
            if (
2313
                !getDolGlobalString('MAIN_FIRST_PING_OK_DATE')
2314
                || (!empty($conf->file->instance_unique_id) && ($hash_unique_id != $conf->global->MAIN_FIRST_PING_OK_ID) && (getDolGlobalString('MAIN_FIRST_PING_OK_ID') != 'disabled'))
2315
                || $forceping
2316
            ) {
2317
                // No ping done if we are into an alpha version
2318
                if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
2319
                    print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
2320
                } elseif (empty($_COOKIE['DOLINSTALLNOPING_' . $hash_unique_id]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
2321
                    // MAIN_LAST_PING_KO_DATE
2322
                    // Disable ping if MAIN_LAST_PING_KO_DATE is set and is recent (this month)
2323
                    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) {
2324
                        print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. An error already occurred this month, we will try later. -->\n";
2325
                    } else {
2326
                        include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
2327
2328
                        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";
2329
                        print "\n<!-- JS CODE TO ENABLE the anonymous Ping -->\n";
2330
                        $url_for_ping = getDolGlobalString('MAIN_URL_FOR_PING', "https://ping.dolibarr.org/");
2331
                        // Try to guess the distrib used
2332
                        $distrib = 'standard';
2333
                        if ($_SERVER["SERVER_ADMIN"] == 'doliwamp@localhost') {
2334
                            $distrib = 'doliwamp';
2335
                        }
2336
                        if (!empty($dolibarr_distrib)) {
2337
                            $distrib = $dolibarr_distrib;
2338
                        }
2339
                        ?>
2340
                        <script>
2341
                            jQuery(document).ready(function (tmp) {
2342
                                console.log("Try Ping with hash_unique_id is dol_hash('dolibarr'+instance_unique_id, 'sha256')");
2343
                                $.ajax({
2344
                                    method: "POST",
2345
                                    url: "<?php echo $url_for_ping ?>",
2346
                                    timeout: 500,     // timeout milliseconds
2347
                                    cache: false,
2348
                                    data: {
2349
                                        hash_algo: 'dol_hash-sha256',
2350
                                        hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
2351
                                        action: 'dolibarrping',
2352
                                        version: '<?php echo (float)DOL_VERSION; ?>',
2353
                                        entity: '<?php echo (int)$conf->entity; ?>',
2354
                                        dbtype: '<?php echo dol_escape_js($db->type); ?>',
2355
                                        country_code: '<?php echo $mysoc->country_code ? dol_escape_js($mysoc->country_code) : 'unknown'; ?>',
2356
                                        php_version: '<?php echo dol_escape_js(phpversion()); ?>',
2357
                                        os_version: '<?php echo dol_escape_js(version_os('smr')); ?>',
2358
                                        db_version: '<?php echo dol_escape_js(version_db()); ?>',
2359
                                        distrib: '<?php echo $distrib ? dol_escape_js($distrib) : 'unknown'; ?>',
2360
                                        token: 'notrequired'
2361
                                    },
2362
                                    success: function (data, status, xhr) {   // success callback function (data contains body of response)
2363
                                        console.log("Ping ok");
2364
                                        $.ajax({
2365
                                            method: 'GET',
2366
                                            url: '<?php echo constant('BASE_URL') . '/core/ajax/pingresult.php'; ?>',
2367
                                            timeout: 500,     // timeout milliseconds
2368
                                            cache: false,
2369
                                            data: {
2370
                                                hash_algo: 'dol_hash-sha256',
2371
                                                hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
2372
                                                action: 'firstpingok',
2373
                                                token: '<?php echo currentToken(); ?>'
2374
                                            }, // for update
2375
                                        });
2376
                                    },
2377
                                    error: function (data, status, xhr) {   // error callback function
2378
                                        console.log("Ping ko: " + data);
2379
                                        $.ajax({
2380
                                            method: 'GET',
2381
                                            url: '<?php echo constant('BASE_URL') . '/core/ajax/pingresult.php'; ?>',
2382
                                            timeout: 500,     // timeout milliseconds
2383
                                            cache: false,
2384
                                            data: {
2385
                                                hash_algo: 'dol_hash-sha256',
2386
                                                hash_unique_id: '<?php echo dol_escape_js($hash_unique_id); ?>',
2387
                                                action: 'firstpingko',
2388
                                                token: '<?php echo currentToken(); ?>'
2389
                                            },
2390
                                        });
2391
                                    }
2392
                                });
2393
                            });
2394
                        </script>
2395
                        <?php
2396
                    }
2397
                } else {
2398
                    $now = dol_now();
2399
                    print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It was disabled -->\n";
2400
                    include_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php';
2401
                    dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_DATE', dol_print_date($now, 'dayhourlog', 'gmt'), 'chaine', 0, '', $conf->entity);
2402
                    dolibarr_set_const($db, 'MAIN_FIRST_PING_OK_ID', 'disabled', 'chaine', 0, '', $conf->entity);
2403
                }
2404
            }
2405
        }
2406
2407
        $parameters = array();
2408
        $reshook = $hookmanager->executeHooks('beforeBodyClose', $parameters); // Note that $action and $object may have been modified by some hooks
2409
        if ($reshook > 0) {
2410
            print $hookmanager->resPrint;
2411
        }
2412
2413
        print "</body>\n";
2414
        print "</html>\n";
2415
    }
2416
}