Issues (2811)

public/htdocs/admin/modules.php (3 issues)

1
<?php
2
3
/* Copyright (C) 2003-2007  Rodolphe Quiedeville        <[email protected]>
4
 * Copyright (C) 2003		Jean-Louis Bergamo		    <[email protected]>
5
 * Copyright (C) 2004-2017	Laurent Destailleur		    <[email protected]>
6
 * Copyright (C) 2004		Eric Seigne				    <[email protected]>
7
 * Copyright (C) 2005-2017	Regis Houssin			    <[email protected]>
8
 * Copyright (C) 2011-2023	Juanjo Menent			    <[email protected]>
9
 * Copyright (C) 2015		Jean-François Ferry		    <[email protected]>
10
 * Copyright (C) 2015		Raphaël Doursenaud		    <[email protected]>
11
 * Copyright (C) 2018		Nicolas ZABOURI 		    <[email protected]>
12
 * Copyright (C) 2021-2023  Frédéric France             <[email protected]>
13
 * Copyright (C) 2024		MDW							<[email protected]>
14
 * Copyright (C) 2024       Rafael San José             <[email protected]>
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 3 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
28
 */
29
30
use Dolibarr\Code\Core\Classes\Form;
31
use Dolibarr\Core\Base\DolibarrModules;
32
use Dolibarr\Lib\ViewMain;
33
34
/**
35
 *  \file       htdocs/admin/modules.php
36
 *  \brief      Page to activate/disable all modules
37
 */
38
39
if (!defined('CSRFCHECK_WITH_TOKEN') && (empty($_GET['action']) || $_GET['action'] != 'reset')) {   // We force security except to disable modules so we can do it if a problem occurs on a module
40
    define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET
41
}
42
43
// Load Dolibarr environment
44
require constant('DOL_DOCUMENT_ROOT') . '/main.inc.php';
45
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/admin.lib.php';
46
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
47
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/geturl.lib.php';
48
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/functions2.lib.php';
49
require_once constant('DOL_DOCUMENT_ROOT') . '/admin/dolistore/class/dolistore.class.php';
50
51
// Load translation files required by the page
52
$langs->loadLangs(array("errors", "admin", "modulebuilder"));
53
54
// if we set another view list mode, we keep it (till we change one more time)
55
if (GETPOSTISSET('mode')) {
56
    $mode = GETPOST('mode', 'alpha');
57
    if ($mode == 'common' || $mode == 'commonkanban') {
58
        dolibarr_set_const($db, "MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT", $mode, 'chaine', 0, '', $conf->entity);
59
    }
60
} else {
61
    $mode = (!getDolGlobalString('MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT') ? 'commonkanban' : $conf->global->MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT);
62
}
63
64
$action = GETPOST('action', 'aZ09');
65
$value = GETPOST('value', 'alpha');
66
$page_y = GETPOSTINT('page_y');
67
$search_keyword = GETPOST('search_keyword', 'alpha');
68
$search_status = GETPOST('search_status', 'alpha');
69
$search_nature = GETPOST('search_nature', 'alpha');
70
$search_version = GETPOST('search_version', 'alpha');
71
72
73
// For dolistore search
74
$options = array();
75
$options['per_page'] = 20;
76
$options['categorie'] = ((int)(GETPOSTINT('categorie') ? GETPOSTINT('categorie') : 0));
77
$options['start'] = ((int)(GETPOSTINT('start') ? GETPOSTINT('start') : 0));
78
$options['end'] = ((int)(GETPOSTINT('end') ? GETPOSTINT('end') : 0));
79
$options['search'] = GETPOST('search_keyword', 'alpha');
80
$dolistore = new Dolistore(false);
81
82
83
if (!$user->admin) {
84
    accessforbidden();
85
}
86
87
$familyinfo = array(
88
    'hr' => array('position' => '001', 'label' => $langs->trans("ModuleFamilyHr")),
89
    'crm' => array('position' => '006', 'label' => $langs->trans("ModuleFamilyCrm")),
90
    'srm' => array('position' => '007', 'label' => $langs->trans("ModuleFamilySrm")),
91
    'financial' => array('position' => '009', 'label' => $langs->trans("ModuleFamilyFinancial")),
92
    'products' => array('position' => '012', 'label' => $langs->trans("ModuleFamilyProducts")),
93
    'projects' => array('position' => '015', 'label' => $langs->trans("ModuleFamilyProjects")),
94
    'ecm' => array('position' => '018', 'label' => $langs->trans("ModuleFamilyECM")),
95
    'technic' => array('position' => '021', 'label' => $langs->trans("ModuleFamilyTechnic")),
96
    'portal' => array('position' => '040', 'label' => $langs->trans("ModuleFamilyPortal")),
97
    'interface' => array('position' => '050', 'label' => $langs->trans("ModuleFamilyInterface")),
98
    'base' => array('position' => '060', 'label' => $langs->trans("ModuleFamilyBase")),
99
    'other' => array('position' => '100', 'label' => $langs->trans("ModuleFamilyOther")),
100
);
101
102
$param = '';
103
if (!GETPOST('buttonreset', 'alpha')) {
104
    if ($search_keyword) {
105
        $param .= '&search_keyword=' . urlencode($search_keyword);
106
    }
107
    if ($search_status && $search_status != '-1') {
108
        $param .= '&search_status=' . urlencode($search_status);
109
    }
110
    if ($search_nature && $search_nature != '-1') {
111
        $param .= '&search_nature=' . urlencode($search_nature);
112
    }
113
    if ($search_version && $search_version != '-1') {
114
        $param .= '&search_version=' . urlencode($search_version);
115
    }
116
}
117
118
$dirins = DOL_DOCUMENT_ROOT . '/custom';
119
$urldolibarrmodules = 'https://www.dolistore.com/';
120
121
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
122
$hookmanager->initHooks(array('adminmodules', 'globaladmin'));
123
124
// Increase limit of time. Works only if we are not in safe mode
125
$max_execution_time_for_deploy = getDolGlobalInt('MODULE_UPLOAD_MAX_EXECUTION_TIME', 300); // 5mn if not defined
126
if (!empty($max_execution_time_for_deploy)) {
127
    $err = error_reporting();
128
    error_reporting(0); // Disable all errors
129
    //error_reporting(E_ALL);
130
    @set_time_limit($max_execution_time_for_deploy);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

130
    /** @scrutinizer ignore-unhandled */ @set_time_limit($max_execution_time_for_deploy);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
131
    error_reporting($err);
132
}
133
// Other method - TODO is this required ?
134
$max_time = @ini_get("max_execution_time");
135
if ($max_time && $max_time < $max_execution_time_for_deploy) {
136
    dol_syslog("max_execution_time=" . $max_time . " is lower than max_execution_time_for_deploy=" . $max_execution_time_for_deploy . ". We try to increase it dynamically.");
137
    @ini_set("max_execution_time", $max_execution_time_for_deploy); // This work only if safe mode is off. also web servers has timeout of 300
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

137
    /** @scrutinizer ignore-unhandled */ @ini_set("max_execution_time", $max_execution_time_for_deploy); // This work only if safe mode is off. also web servers has timeout of 300

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
138
}
139
140
141
$dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT);
142
$allowonlineinstall = true;
143
$allowfromweb = 1;
144
if (dol_is_file($dolibarrdataroot . '/installmodules.lock')) {
145
    $allowonlineinstall = false;
146
}
147
148
149
/*
150
 * Actions
151
 */
152
153
$formconfirm = '';
154
155
$parameters = array();
156
$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
157
if ($reshook < 0) {
158
    setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
159
}
160
161
if (GETPOST('buttonreset', 'alpha')) {
162
    $search_keyword = '';
163
    $search_status = '';
164
    $search_nature = '';
165
    $search_version = '';
166
}
167
168
if ($action == 'install' && $allowonlineinstall) {
169
    $error = 0;
170
171
    // $original_file should match format module_modulename-x.y[.z].zip
172
    $original_file = basename($_FILES["fileinstall"]["name"]);
173
    $original_file = preg_replace('/\s*\(\d+\)\.zip$/i', '.zip', $original_file);
174
    $newfile = $conf->admin->dir_temp . '/' . $original_file . '/' . $original_file;
175
176
    if (!$original_file) {
177
        $langs->load("Error");
178
        setEventMessages($langs->trans("ErrorModuleFileRequired"), null, 'warnings');
179
        $error++;
180
    } else {
181
        if (!$error && !preg_match('/\.zip$/i', $original_file)) {
182
            $langs->load("errors");
183
            setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage", $original_file), null, 'errors');
184
            $error++;
185
        }
186
        if (!$error && !preg_match('/^(module[a-zA-Z0-9]*|theme)_.*\-([0-9][0-9\.]*)\.zip$/i', $original_file)) {
187
            $langs->load("errors");
188
            setEventMessages($langs->trans("ErrorFilenameDosNotMatchDolibarrPackageRules", $original_file, 'module_*-x.y*.zip'), null, 'errors');
189
            $error++;
190
        }
191
        if (empty($_FILES['fileinstall']['tmp_name'])) {
192
            $langs->load("errors");
193
            setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
194
            $error++;
195
        }
196
    }
197
198
    if (!$error) {
199
        if ($original_file) {
200
            @dol_delete_dir_recursive($conf->admin->dir_temp . '/' . $original_file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for dol_delete_dir_recursive(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

200
            /** @scrutinizer ignore-unhandled */ @dol_delete_dir_recursive($conf->admin->dir_temp . '/' . $original_file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
201
            dol_mkdir($conf->admin->dir_temp . '/' . $original_file);
202
        }
203
204
        $tmpdir = preg_replace('/\.zip$/i', '', $original_file) . '.dir';
205
        if ($tmpdir) {
206
            @dol_delete_dir_recursive($conf->admin->dir_temp . '/' . $tmpdir);
207
            dol_mkdir($conf->admin->dir_temp . '/' . $tmpdir);
208
        }
209
210
        $result = dol_move_uploaded_file($_FILES['fileinstall']['tmp_name'], $newfile, 1, 0, $_FILES['fileinstall']['error']);
211
        if ($result > 0) {
212
            $result = dol_uncompress($newfile, $conf->admin->dir_temp . '/' . $tmpdir);
213
214
            if (!empty($result['error'])) {
215
                $langs->load("errors");
216
                setEventMessages($langs->trans($result['error'], $original_file), null, 'errors');
217
                $error++;
218
            } else {
219
                // Now we move the dir of the module
220
                $modulename = preg_replace('/module_/', '', $original_file);
221
                $modulename = preg_replace('/\-([0-9][0-9\.]*)\.zip$/i', '', $modulename);
222
                // Search dir $modulename
223
                $modulenamedir = $conf->admin->dir_temp . '/' . $tmpdir . '/' . $modulename; // Example ./mymodule
224
225
                if (!dol_is_dir($modulenamedir)) {
226
                    $modulenamedir = $conf->admin->dir_temp . '/' . $tmpdir . '/htdocs/' . $modulename; // Example ./htdocs/mymodule
227
                    //var_dump($modulenamedir);
228
                    if (!dol_is_dir($modulenamedir)) {
229
                        setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat") . '<br>' . $langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat2", $modulename, 'htdocs/' . $modulename), null, 'errors');
230
                        $error++;
231
                    }
232
                }
233
234
                if (!$error) {
235
                    // TODO Make more test
236
                }
237
238
                dol_syslog("Uncompress of module file is a success.");
239
240
                // We check if this is a metapackage
241
                $modulenamearrays = array();
242
                if (dol_is_file($modulenamedir . '/metapackage.conf')) {
243
                    // This is a meta package
244
                    $metafile = file_get_contents($modulenamedir . '/metapackage.conf');
245
                    $modulenamearrays = explode("\n", $metafile);
246
                }
247
                $modulenamearrays[$modulename] = $modulename;
248
                //var_dump($modulenamearrays);exit;
249
250
                // Lop on each package of the metapackage
251
                foreach ($modulenamearrays as $modulenameval) {
252
                    if (strpos($modulenameval, '#') === 0) {
253
                        continue; // Discard comments
254
                    }
255
                    if (strpos($modulenameval, '//') === 0) {
256
                        continue; // Discard comments
257
                    }
258
                    if (!trim($modulenameval)) {
259
                        continue;
260
                    }
261
262
                    // Now we install the module
263
                    if (!$error) {
264
                        @dol_delete_dir_recursive($dirins . '/' . $modulenameval); // delete the target directory
265
                        $submodulenamedir = $conf->admin->dir_temp . '/' . $tmpdir . '/' . $modulenameval;
266
                        if (!dol_is_dir($submodulenamedir)) {
267
                            $submodulenamedir = $conf->admin->dir_temp . '/' . $tmpdir . '/htdocs/' . $modulenameval;
268
                        }
269
                        dol_syslog("We copy now directory " . $submodulenamedir . " into target dir " . $dirins . '/' . $modulenameval);
270
                        $result = dolCopyDir($submodulenamedir, $dirins . '/' . $modulenameval, '0444', 1);
271
                        if ($result <= 0) {
272
                            dol_syslog('Failed to call dolCopyDir result=' . $result . " with param " . $submodulenamedir . " and " . $dirins . '/' . $modulenameval, LOG_WARNING);
273
                            $langs->load("errors");
274
                            setEventMessages($langs->trans("ErrorFailToCopyDir", $submodulenamedir, $dirins . '/' . $modulenameval), null, 'errors');
275
                            $error++;
276
                        }
277
                    }
278
                }
279
            }
280
        } else {
281
            setEventMessages($langs->trans("ErrorFailToRenameFile", $_FILES['fileinstall']['tmp_name'], $newfile), null, 'errors');
282
            $error++;
283
        }
284
    }
285
286
    if (!$error) {
287
        $message = $langs->trans("SetupIsReadyForUse", constant('BASE_URL') . '/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home") . ' - ' . $langs->transnoentitiesnoconv("Setup") . ' - ' . $langs->transnoentitiesnoconv("Modules"));
288
        setEventMessages($message, null, 'warnings');
289
    }
290
} elseif ($action == 'install' && !$allowonlineinstall) {
291
    httponly_accessforbidden("You try to bypass the protection to disallow deployment of an external module. Hack attempt ?");
292
}
293
294
if ($action == 'set' && $user->admin) {
295
    $checkOldValue = getDolGlobalInt('CHECKLASTVERSION_EXTERNALMODULE');
296
    $csrfCheckOldValue = getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN');
297
    $resarray = activateModule($value);
298
    if ($checkOldValue != getDolGlobalInt('CHECKLASTVERSION_EXTERNALMODULE')) {
299
        setEventMessage($langs->trans('WarningModuleHasChangedLastVersionCheckParameter', $value), 'warnings');
300
    }
301
    if ($csrfCheckOldValue != getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) {
302
        setEventMessage($langs->trans('WarningModuleHasChangedSecurityCsrfParameter', $value), 'warnings');
303
    }
304
    dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
305
    if (!empty($resarray['errors'])) {
306
        setEventMessages('', $resarray['errors'], 'errors');
307
    } else {
308
        //var_dump($resarray);exit;
309
        if ($resarray['nbperms'] > 0) {
310
            $tmpsql = "SELECT COUNT(rowid) as nb FROM " . MAIN_DB_PREFIX . "user WHERE admin <> 1";
311
            $resqltmp = $db->query($tmpsql);
312
            if ($resqltmp) {
313
                $obj = $db->fetch_object($resqltmp);
314
                //var_dump($obj->nb);exit;
315
                if ($obj && $obj->nb > 1) {
316
                    $msg = $langs->trans('ModuleEnabledAdminMustCheckRights');
317
                    setEventMessages($msg, null, 'warnings');
318
                }
319
            } else {
320
                dol_print_error($db);
321
            }
322
        }
323
    }
324
    header("Location: " . $_SERVER["PHP_SELF"] . "?mode=" . $mode . $param . ($page_y ? '&page_y=' . $page_y : ''));
325
    exit;
326
} elseif ($action == 'reset' && $user->admin && GETPOST('confirm') == 'yes') {
327
    $result = unActivateModule($value);
328
    dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
329
    if ($result) {
330
        setEventMessages($result, null, 'errors');
331
    }
332
    header("Location: " . $_SERVER["PHP_SELF"] . "?mode=" . $mode . $param . ($page_y ? '&page_y=' . $page_y : ''));
333
    exit;
334
} elseif (getDolGlobalInt("MAIN_FEATURES_LEVEL") > 1 && $action == 'reload' && $user->admin && GETPOST('confirm') == 'yes') {
335
    $result = unActivateModule($value, 0);
336
    dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", (int)$conf->global->MAIN_IHM_PARAMS_REV + 1, 'chaine', 0, '', $conf->entity);
337
    if ($result) {
338
        setEventMessages($result, null, 'errors');
339
        header("Location: " . $_SERVER["PHP_SELF"] . "?mode=" . $mode . $param . ($page_y ? '&page_y=' . $page_y : ''));
340
    }
341
    $resarray = activateModule($value, 0, 1);
342
    dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", (int)$conf->global->MAIN_IHM_PARAMS_REV + 1, 'chaine', 0, '', $conf->entity);
343
    if (!empty($resarray['errors'])) {
344
        setEventMessages('', $resarray['errors'], 'errors');
345
    } else {
346
        if ($resarray['nbperms'] > 0) {
347
            $tmpsql = "SELECT COUNT(rowid) as nb FROM " . MAIN_DB_PREFIX . "user WHERE admin <> 1";
348
            $resqltmp = $db->query($tmpsql);
349
            if ($resqltmp) {
350
                $obj = $db->fetch_object($resqltmp);
351
                if ($obj && $obj->nb > 1) {
352
                    $msg = $langs->trans('ModuleEnabledAdminMustCheckRights');
353
                    setEventMessages($msg, null, 'warnings');
354
                }
355
            } else {
356
                dol_print_error($db);
357
            }
358
        }
359
    }
360
    header("Location: " . $_SERVER["PHP_SELF"] . "?mode=" . $mode . $param . ($page_y ? '&page_y=' . $page_y : ''));
361
    exit;
362
}
363
364
/*
365
 * View
366
 */
367
368
$form = new Form($db);
369
370
$morejs = array();
371
$morecss = array("/htdocs/admin/dolistore/css/dolistore.css");
372
373
// Set dir where external modules are installed
374
if (!dol_is_dir($dirins)) {
375
    dol_mkdir($dirins);
376
}
377
$dirins_ok = (dol_is_dir($dirins));
378
379
$help_url = 'EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones';
380
ViewMain::llxHeader('', $langs->trans("Setup"), $help_url, '', '', '', $morejs, $morecss, 0, 'mod-admin page-modules');
381
382
383
// Search modules dirs
384
$modulesdir = dolGetModulesDirs();
385
386
$arrayofnatures = array(
387
    'core' => array('label' => $langs->transnoentitiesnoconv("NativeModules")),
388
    'external' => array('label' => $langs->transnoentitiesnoconv("External") . ' - [' . $langs->trans("AllPublishers") . ']')
389
);
390
$arrayofwarnings = array(); // Array of warning each module want to show when activated
391
$arrayofwarningsext = array(); // Array of warning each module want to show when we activate an external module
392
$filenames = array();
393
$modules = array();
394
$orders = array();
395
$categ = array();
396
$publisherlogoarray = array();
397
398
$i = 0; // is a sequencer of modules found
399
$j = 0; // j is module number. Automatically affected if module number not defined.
400
$modNameLoaded = array();
401
402
$allModules = DolibarrModules::getModules($modulesdir);
403
$main_features_level = getDolGlobalInt('MAIN_FEATURES_LEVEL');
404
foreach ($allModules as $modName => $filename) {
405
    $dir = dirname($filename) . DIRECTORY_SEPARATOR;
406
407
    if (!empty($modNameLoaded[$modName])) {   // In cache of already loaded modules ?
408
        $mesg = "Error: Module " . $modName . " was found twice: Into " . $modNameLoaded[$modName] . " and " . $dir . ". You probably have an old file on your disk.<br>";
409
        dump(['msg ' . $modName => $mesg]);
410
        setEventMessages($mesg, null, 'warnings');
411
        dol_syslog($mesg, LOG_ERR);
412
        continue;
413
    }
414
415
    try {
416
        $objMod = DolibarrModules::getObj($db, $modName, $filename);
417
        if (!isset($objMod)) {
418
            print info_admin("admin/modules.php Warning bad descriptor file : " . $filename . " (Class " . $modName . " not found into file)", 0, 0, '1', 'warning');
419
            continue;
420
        }
421
422
        '@phan-var-force DolibarrModules $objMod';
423
        $modNameLoaded[$modName] = $dir;
424
        if (!$objMod->numero > 0 && $modName != 'modUser') {
425
            dol_syslog('The module descriptor ' . $modName . ' must have a numero property', LOG_ERR);
426
        }
427
        $j = $objMod->numero;
428
429
        $modulequalified = 1;
430
431
        // We discard modules according to features level (PS: if module is activated we always show it)
432
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', $modName));
433
434
        if (!DolibarrModules::isActivated($modName)) {
435
            if ($objMod->isDevelopment() && $main_features_level < 2) {
436
                dol_syslog("Module " . $modName . " is not active");
437
                continue;
438
            }
439
440
            if ($objMod->isExperimental() && $main_features_level < 1) {
441
                dol_syslog("Module " . $modName . " is experimental");
442
                continue;
443
            }
444
445
            if ($objMod->isDeprecated() && $main_features_level >= 0) {
446
                dol_syslog("Module " . $modName . " is deprecated");
447
                continue;
448
            }
449
        }
450
451
        // We discard modules according to property->hidden
452
        if (!empty($objMod->hidden)) {
453
            dol_syslog("Module " . $modName . " is hidden");
454
            continue;
455
        }
456
457
        $publisher = dol_escape_htmltag($objMod->getPublisher());
458
        $external = ($objMod->isCoreOrExternalModule() == 'external');
459
        if ($external) {
460
            if ($publisher) {
461
                // Check if there is a logo forpublisher
462
                /* Do not show the company logo in combo. Make combo list dirty.
463
                if (!empty($objMod->editor_squarred_logo)) {
464
                    $publisherlogoarray['external_'.$publisher] = img_picto('', $objMod->editor_squarred_logo, 'class="publisherlogoinline"');
465
                }
466
                $publisherlogo = empty($publisherlogoarray['external_'.$publisher]) ? '' : $publisherlogoarray['external_'.$publisher];
467
                */
468
                $arrayofnatures['external_' . $publisher] = array('label' => $langs->trans("External") . ' - ' . $publisher, 'data-html' => $langs->trans("External") . ' - <span class="opacitymedium inine-block valignmiddle">' . $publisher . '</span>');
469
            } else {
470
                $arrayofnatures['external_'] = array('label' => $langs->trans("External") . ' - [' . $langs->trans("UnknownPublishers") . ']');
471
            }
472
        }
473
        ksort($arrayofnatures);
474
475
        // Define array $categ with categ with at least one qualified module
476
        $filenames[$i] = $modName;
477
        $modules[$modName] = $objMod;
478
479
        // Gives the possibility to the module, to provide his own family info and position of this family
480
        if (is_array($objMod->familyinfo) && !empty($objMod->familyinfo)) {
481
            $familyinfo = array_merge($familyinfo, $objMod->familyinfo);
482
            $familykey = key($objMod->familyinfo);
483
        } else {
484
            $familykey = $objMod->family;
485
        }
486
        '@phan-var-force string $familykey';  // if not, phan considers $familykey may be null
487
488
        $moduleposition = ($objMod->module_position ? $objMod->module_position : '50');
489
        if ($objMod->isCoreOrExternalModule() == 'external' && $moduleposition < 100000) {
490
            // an external module should never return a value lower than '80'.
491
            $moduleposition = '80'; // External modules at end by default
492
        }
493
494
        // Add list of warnings to show into arrayofwarnings and arrayofwarningsext
495
        if (!empty($objMod->warnings_activation)) {
496
            $arrayofwarnings[$modName] = $objMod->warnings_activation;
497
        }
498
        if (!empty($objMod->warnings_activation_ext)) {
499
            $arrayofwarningsext[$modName] = $objMod->warnings_activation_ext;
500
        }
501
502
        $familyposition = (empty($familyinfo[$familykey]['position']) ? '0' : $familyinfo[$familykey]['position']);
503
        $listOfOfficialModuleGroups = array('hr', 'technic', 'interface', 'technic', 'portal', 'financial', 'crm', 'base', 'products', 'srm', 'ecm', 'projects', 'other');
504
        if ($external && !in_array($familykey, $listOfOfficialModuleGroups)) {
505
            // If module is extern and into a custom group (not into an official predefined one), it must appear at end (custom groups should not be before official groups).
506
            if (is_numeric($familyposition)) {
507
                $familyposition = sprintf("%03d", (int)$familyposition + 100);
508
            }
509
        }
510
511
        $orders[$i] = $familyposition . "_" . $familykey . "_" . $moduleposition . "_" . $j; // Sort by family, then by module position then number
512
513
        // Set categ[$i]
514
        $specialstring = 'unknown';
515
        if ($objMod->version == 'development' || $objMod->version == 'experimental') {
516
            $specialstring = 'expdev';
517
        }
518
        if (isset($categ[$specialstring])) {
519
            $categ[$specialstring]++; // Array of all different modules categories
520
        } else {
521
            $categ[$specialstring] = 1;
522
        }
523
        $j++;
524
        $i++;
525
    } catch (Exception $e) {
526
        dol_syslog("Failed to load " . $filename . " " . $e->getMessage(), LOG_ERR);
527
    }
528
}
529
530
if ($action == 'reset_confirm' && $user->admin) {
531
    if (!empty($modules[$value])) {
532
        $objMod = $modules[$value];
533
534
        if (!empty($objMod->langfiles)) {
535
            $langs->loadLangs($objMod->langfiles);
536
        }
537
538
        $form = new Form($db);
539
        $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?value=' . $value . '&mode=' . $mode . $param, $langs->trans('ConfirmUnactivation'), $langs->trans(GETPOST('confirm_message_code')), 'reset', '', 'no', 1);
540
    }
541
}
542
543
if ($action == 'reload_confirm' && $user->admin) {
544
    if (!empty($modules[$value])) {
545
        $objMod = $modules[$value];
546
547
        if (!empty($objMod->langfiles)) {
548
            $langs->loadLangs($objMod->langfiles);
549
        }
550
551
        $form = new Form($db);
552
        $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?value=' . $value . '&mode=' . $mode . $param, $langs->trans('ConfirmReload'), $langs->trans(GETPOST('confirm_message_code')), 'reload', '', 'no', 1);
553
    }
554
}
555
556
print $formconfirm;
557
558
asort($orders);
559
//var_dump($orders);
560
//var_dump($categ);
561
//var_dump($modules);
562
563
$nbofactivatedmodules = count($conf->modules);
564
565
// Define $nbmodulesnotautoenabled - TODO This code is at different places
566
$nbmodulesnotautoenabled = count($conf->modules);
567
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
568
foreach ($listofmodulesautoenabled as $moduleautoenable) {
569
    if (in_array($moduleautoenable, $conf->modules)) {
570
        $nbmodulesnotautoenabled--;
571
    }
572
}
573
574
print load_fiche_titre($langs->trans("ModulesSetup"), '', 'title_setup');
575
576
// Start to show page
577
$deschelp = '';
578
if ($mode == 'common' || $mode == 'commonkanban') {
579
    $desc = $langs->trans("ModulesDesc", '{picto}');
580
    $desc .= ' ' . $langs->trans("ModulesDesc2", '{picto2}');
581
    $desc = str_replace('{picto}', img_picto('', 'switch_off', 'class="size15x"'), $desc);
582
    $desc = str_replace('{picto2}', img_picto('', 'setup', 'class="size15x"'), $desc);
583
    if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
584
        $deschelp .= '<div class="info hideonsmartphone">' . $desc . "<br></div>\n";
585
    }
586
    if (getDolGlobalString('MAIN_SETUP_MODULES_INFO')) {    // Show a custom message
587
        $deschelp .= '<div class="info">' . $langs->trans(getDolGlobalString('MAIN_SETUP_MODULES_INFO')) . "<br></div>\n";
588
    }
589
    if ($deschelp) {
590
        $deschelp .= '<br>';
591
    }
592
}
593
594
if ($mode == 'marketplace') {
595
    //$deschelp = '<div class="info hideonsmartphone">'.$langs->trans("ModulesMarketPlaceDesc")."<br></div><br>\n";
596
}
597
598
if ($mode == 'deploy') {
599
    $deschelp = '<div class="info hideonsmartphone">' . $langs->trans("ModulesDeployDesc", $langs->transnoentitiesnoconv("AvailableModules")) . "<br></div><br>\n";
600
}
601
602
if ($mode == 'develop') {
603
    $deschelp = '<div class="info hideonsmartphone">' . $langs->trans("ModulesDevelopDesc") . "<br></div><br>\n";
604
}
605
606
$head = modules_prepare_head($nbofactivatedmodules, count($modules), $nbmodulesnotautoenabled);
607
608
if ($mode == 'common' || $mode == 'commonkanban') {
609
    dol_set_focus('#search_keyword');
610
611
    print '<form method="POST" id="searchFormList" action="' . $_SERVER["PHP_SELF"] . '">';
612
    print '<input type="hidden" name="token" value="' . newToken() . '">';
613
    if (isset($optioncss) && $optioncss != '') {
614
        print '<input type="hidden" name="optioncss" value="' . $optioncss . '">';
615
    }
616
    if (isset($sortfield) && $sortfield != '') {
617
        print '<input type="hidden" name="sortfield" value="' . $sortfield . '">';
618
    }
619
    if (isset($sortorder) && $sortorder != '') {
620
        print '<input type="hidden" name="sortorder" value="' . $sortorder . '">';
621
    }
622
    if (isset($page) && $page != '') {
623
        print '<input type="hidden" name="page" value="' . $page . '">';
624
    }
625
    print '<input type="hidden" name="mode" value="' . $mode . '">';
626
627
    print dol_get_fiche_head($head, 'modules', '', -1);
628
629
    print $deschelp;
630
631
    $moreforfilter = '<div class="valignmiddle">';
632
633
    $moreforfilter .= '<div class="floatright right pagination paddingtop --module-list"><ul><li>';
634
    $moreforfilter .= dolGetButtonTitle($langs->trans('CheckForModuleUpdate'), $langs->trans('CheckForModuleUpdate') . '<br>' . $langs->trans('CheckForModuleUpdateHelp'), 'fa fa-sync', $_SERVER["PHP_SELF"] . '?action=checklastversion&token=' . newToken() . '&mode=' . $mode . $param, '', 1, array('morecss' => 'reposition'));
635
    $moreforfilter .= dolGetButtonTitleSeparator();
636
    $moreforfilter .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars imgforviewmode', $_SERVER["PHP_SELF"] . '?mode=common' . $param, '', ($mode == 'common' ? 2 : 1), array('morecss' => 'reposition'));
637
    $moreforfilter .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', $_SERVER["PHP_SELF"] . '?mode=commonkanban' . $param, '', ($mode == 'commonkanban' ? 2 : 1), array('morecss' => 'reposition'));
638
    $moreforfilter .= '</li></ul></div>';
639
640
    $moreforfilter .= '<div class="divfilteralone colorbacktimesheet float valignmiddle">';
641
    $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
642
    $moreforfilter .= img_picto($langs->trans("Filter"), 'filter', 'class="paddingright opacityhigh hideonsmartphone"') . '<input type="text" id="search_keyword" name="search_keyword" class="maxwidth125" value="' . dol_escape_htmltag($search_keyword) . '" placeholder="' . dol_escape_htmltag($langs->trans('Keyword')) . '">';
643
    $moreforfilter .= '</div>';
644
    $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
645
    $moreforfilter .= $form->selectarray('search_nature', $arrayofnatures, dol_escape_htmltag($search_nature), $langs->trans('Origin'), 0, 0, '', 0, 0, 0, '', 'maxwidth250', 1);
646
    $moreforfilter .= '</div>';
647
648
    if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
649
        $array_version = array('stable' => $langs->transnoentitiesnoconv("Stable"));
650
        if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 0) {
651
            $array_version['deprecated'] = $langs->trans("Deprecated");
652
        }
653
        if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
654
            $array_version['experimental'] = $langs->trans("Experimental");
655
        }
656
        if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 1) {
657
            $array_version['development'] = $langs->trans("Development");
658
        }
659
        $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
660
        $moreforfilter .= $form->selectarray('search_version', $array_version, $search_version, $langs->transnoentitiesnoconv('Version'), 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1);
661
        $moreforfilter .= '</div>';
662
    }
663
    $array_status = array('active' => $langs->transnoentitiesnoconv("Enabled"), 'disabled' => $langs->transnoentitiesnoconv("Disabled"));
664
    $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
665
    $moreforfilter .= $form->selectarray('search_status', $array_status, $search_status, $langs->transnoentitiesnoconv('Status'), 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1);
666
    $moreforfilter .= '</div>';
667
    $moreforfilter .= ' ';
668
    $moreforfilter .= '<div class="divsearchfield valignmiddle inline-block">';
669
    $moreforfilter .= '<input type="submit" name="buttonsubmit" class="button small nomarginleft" value="' . dol_escape_htmltag($langs->trans("Refresh")) . '">';
670
    if ($search_keyword || ($search_nature && $search_nature != '-1') || ($search_version && $search_version != '-1') || ($search_status && $search_status != '-1')) {
671
        $moreforfilter .= ' ';
672
        $moreforfilter .= '<input type="submit" name="buttonreset" class="buttonreset noborderbottom" value="' . dol_escape_htmltag($langs->trans("Reset")) . '">';
673
    }
674
    $moreforfilter .= '</div>';
675
    $moreforfilter .= '</div>';
676
677
    $moreforfilter .= '</div>';
678
679
    if (!empty($moreforfilter)) {
680
        print $moreforfilter;
681
        $parameters = array();
682
        $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
683
        print $hookmanager->resPrint;
684
    }
685
686
    $moreforfilter = '';
687
688
    print '<div class="clearboth"></div><br>';
689
690
    $object = new stdClass();
691
    $parameters = array();
692
    $reshook = $hookmanager->executeHooks('insertExtraHeader', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
693
    if ($reshook < 0) {
694
        setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
695
    }
696
697
    $disabled_modules = array();
698
    if (!empty($_SESSION["disablemodules"])) {
699
        $disabled_modules = explode(',', $_SESSION["disablemodules"]);
700
    }
701
702
    // Show list of modules
703
    $oldfamily = '';
704
    $foundoneexternalmodulewithupdate = 0;
705
    $linenum = 0;
706
    $atleastonequalified = 0;
707
    $atleastoneforfamily = 0;
708
709
    foreach ($orders as $key => $value) {
710
        $linenum++;
711
        $tab = explode('_', $value);
712
        $familykey = $tab[1];
713
        $module_position = $tab[2];
714
715
        $modName = $filenames[$key];
716
717
        if (!isset($modules[$modName])) {
718
            continue;
719
        }
720
721
        /** @var DolibarrModules $objMod */
722
        $objMod = $modules[$modName];
723
724
        //print $objMod->name." - ".$key." - ".$objMod->version."<br>";
725
        if ($mode == 'expdev' && $objMod->version != 'development' && $objMod->version != 'experimental') {
726
            continue; // Discard if not for current tab
727
        }
728
729
        if (!$objMod->getName()) {
730
            dol_syslog("Error for module " . $key . " - Property name of module looks empty", LOG_WARNING);
731
            continue;
732
        }
733
734
        $modulenameshort = strtolower(preg_replace('/^mod/i', '', get_only_class($objMod)));
735
        $const_name = 'MAIN_MODULE_' . strtoupper(preg_replace('/^mod/i', '', get_only_class($objMod)));
736
737
        // Check filters
738
        $modulename = $objMod->getName();
739
        $moduletechnicalname = $objMod->name;
740
        $moduledesc = $objMod->getDesc();
741
        $moduledesclong = $objMod->getDescLong();
742
        $moduleauthor = $objMod->getPublisher();
743
744
        // We discard showing according to filters
745
        if ($search_keyword) {
746
            $qualified = 0;
747
            if (
748
                preg_match('/' . preg_quote($search_keyword, '/') . '/i', $modulename)
749
                || preg_match('/' . preg_quote($search_keyword, '/') . '/i', $moduletechnicalname)
750
                || ($moduledesc && preg_match('/' . preg_quote($search_keyword, '/') . '/i', $moduledesc))
751
                || ($moduledesclong && preg_match('/' . preg_quote($search_keyword, '/') . '/i', $moduledesclong))
752
                || ($moduleauthor && preg_match('/' . preg_quote($search_keyword, '/') . '/i', $moduleauthor))
753
            ) {
754
                $qualified = 1;
755
            }
756
            if (!$qualified) {
757
                continue;
758
            }
759
        }
760
        if ($search_status) {
761
            if ($search_status == 'active' && !getDolGlobalString($const_name)) {
762
                continue;
763
            }
764
            if ($search_status == 'disabled' && getDolGlobalString($const_name)) {
765
                continue;
766
            }
767
        }
768
        if ($search_nature) {
769
            if (preg_match('/^external/', $search_nature) && $objMod->isCoreOrExternalModule() != 'external') {
770
                continue;
771
            }
772
            $reg = array();
773
            if (preg_match('/^external_(.*)$/', $search_nature, $reg)) {
774
                //print $reg[1].'-'.dol_escape_htmltag($objMod->getPublisher());
775
                $publisher = dol_escape_htmltag($objMod->getPublisher());
776
                if ($reg[1] && dol_escape_htmltag($reg[1]) != $publisher) {
777
                    continue;
778
                }
779
                if (!$reg[1] && !empty($publisher)) {
780
                    continue;
781
                }
782
            }
783
            if ($search_nature == 'core' && $objMod->isCoreOrExternalModule() == 'external') {
784
                continue;
785
            }
786
        }
787
        if ($search_version) {
788
            if (($objMod->version == 'development' || $objMod->version == 'experimental' || preg_match('/deprecated/', $objMod->version)) && $search_version == 'stable') {
789
                continue;
790
            }
791
            if ($objMod->version != 'development' && ($search_version == 'development')) {
792
                continue;
793
            }
794
            if ($objMod->version != 'experimental' && ($search_version == 'experimental')) {
795
                continue;
796
            }
797
            if (!preg_match('/deprecated/', $objMod->version) && ($search_version == 'deprecated')) {
798
                continue;
799
            }
800
        }
801
802
        $atleastonequalified++;
803
804
        // Load all language files of the qualified module
805
        if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
806
            foreach ($objMod->langfiles as $domain) {
807
                $langs->load($domain);
808
            }
809
        }
810
811
        // Print a separator if we change family
812
        if ($familykey != $oldfamily) {
813
            if ($oldfamily) {
814
                print '</table></div><br>';
815
            }
816
817
            $familytext = empty($familyinfo[$familykey]['label']) ? $familykey : $familyinfo[$familykey]['label'];
818
819
            print load_fiche_titre($familytext, '', '', 0, '', 'modulefamilygroup');
820
821
            if ($mode == 'commonkanban') {
822
                print '<div class="box-flex-container kanban">';
823
            } else {
824
                print '<div class="div-table-responsive">';
825
                print '<table class="tagtable liste" summary="list_of_modules">' . "\n";
826
            }
827
828
            $atleastoneforfamily = 0;
829
        }
830
831
        $atleastoneforfamily++;
832
833
        if ($familykey != $oldfamily) {
834
            $familytext = empty($familyinfo[$familykey]['label']) ? $familykey : $familyinfo[$familykey]['label'];
835
            $oldfamily = $familykey;
836
        }
837
838
        // Version (with picto warning or not)
839
        $version = $objMod->getVersion(0);
840
        $versiontrans = '';
841
        $warningstring = '';
842
        if (preg_match('/development/i', $version)) {
843
            $warningstring = $langs->trans("Development");
844
        }
845
        if (preg_match('/experimental/i', $version)) {
846
            $warningstring = $langs->trans("Experimental");
847
        }
848
        if (preg_match('/deprecated/i', $version)) {
849
            $warningstring = $langs->trans("Deprecated");
850
        }
851
852
        if ($objMod->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
853
            $versiontrans .= $objMod->getVersion(1);
854
        }
855
856
        if (
857
            $objMod->isCoreOrExternalModule() == 'external'
858
            && (
859
                $action == 'checklastversion'
860
                // This is a bad practice to activate a check on an external access during the building of the admin page. 1 external module can hang the application.
861
                // Adding a cron job could be a good idea: see DolibarrModules::checkForUpdate()
862
                || getDolGlobalString('CHECKLASTVERSION_EXTERNALMODULE')
863
            )
864
        ) {
865
            $checkRes = $objMod->checkForUpdate();
866
            if ($checkRes > 0) {
867
                setEventMessage($objMod->getName() . ' : ' . $versiontrans . ' -> ' . $objMod->lastVersion);
868
            } elseif ($checkRes < 0) {
869
                setEventMessage($objMod->getName() . ' ' . $langs->trans('CheckVersionFail'), 'warnings');
870
            }
871
        }
872
873
        // Define imginfo
874
        $imginfo = "info";
875
        if ($objMod->isCoreOrExternalModule() == 'external') {
876
            $imginfo = "info_black";
877
        }
878
879
        $codeenabledisable = '';
880
        $codetoconfig = '';
881
882
        // Force disable of module disabled into session (for demo for example)
883
        if (in_array($modulenameshort, $disabled_modules)) {
884
            $objMod->disabled = true;
885
        }
886
887
        // Activate/Disable and Setup (2 columns)
888
        if (DolibarrModules::isActivated($modName)) {
889
            // Set $codeenabledisable
890
            $disableSetup = 0;
891
            if (!empty($arrayofwarnings[$modName])) {
892
                $codeenabledisable .= '<!-- This module has a warning to show when we activate it (note: your country is ' . $mysoc->country_code . ') -->' . "\n";
893
            }
894
895
            if (!empty($objMod->disabled)) {
896
                $codeenabledisable .= $langs->trans("Disabled");
897
            } elseif (!empty($objMod->always_enabled) || ((isModEnabled('multicompany') && $objMod->core_enabled) && ($user->entity || $conf->entity != 1))) {
898
                // @phan-suppress-next-line PhanUndeclaredMethodCall
899
                if (method_exists($objMod, 'alreadyUsed') && $objMod->alreadyUsed()) {
900
                    $codeenabledisable .= $langs->trans("Used");
901
                } else {
902
                    $codeenabledisable .= img_picto($langs->trans("Required"), 'switch_on', '', false, 0, 0, '', 'opacitymedium valignmiddle');
903
                    //print $langs->trans("Required");
904
                }
905
                if (isModEnabled('multicompany') && $user->entity) {
906
                    $disableSetup++;
907
                }
908
            } else {
909
                // @phan-suppress-next-line PhanUndeclaredMethodCall
910
                if (!empty($objMod->warnings_unactivation[$mysoc->country_code]) && method_exists($objMod, 'alreadyUsed') && $objMod->alreadyUsed()) {
911
                    $codeenabledisable .= '<a class="reposition valignmiddle" href="' . $_SERVER["PHP_SELF"] . '?id=' . $objMod->numero . '&amp;token=' . newToken() . '&amp;module_position=' . $module_position . '&amp;action=reset_confirm&amp;confirm_message_code=' . urlencode($objMod->warnings_unactivation[$mysoc->country_code]) . '&amp;value=' . $modName . '&amp;mode=' . $mode . $param . '">';
912
                    $codeenabledisable .= img_picto($langs->trans("Activated") . ($warningstring ? ' ' . $warningstring : ''), 'switch_on');
913
                    $codeenabledisable .= '</a>';
914
                    if (getDolGlobalInt("MAIN_FEATURES_LEVEL") > 1) {
915
                        $codeenabledisable .= '&nbsp;';
916
                        $codeenabledisable .= '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $objMod->numero . '&amp;token=' . newToken() . '&amp;module_position=' . $module_position . '&amp;action=reload_confirm&amp;value=' . $modName . '&amp;mode=' . $mode . '&amp;confirm=yes' . $param . '">';
917
                        $codeenabledisable .= img_picto($langs->trans("Reload"), 'refresh', 'class="opacitymedium"');
918
                        $codeenabledisable .= '</a>';
919
                    }
920
                } else {
921
                    $codeenabledisable .= '<a class="reposition valignmiddle" href="' . $_SERVER["PHP_SELF"] . '?id=' . $objMod->numero . '&amp;token=' . newToken() . '&amp;module_position=' . $module_position . '&amp;action=reset&amp;value=' . $modName . '&amp;mode=' . $mode . '&amp;confirm=yes' . $param . '">';
922
                    $codeenabledisable .= img_picto($langs->trans("Activated") . ($warningstring ? ' ' . $warningstring : ''), 'switch_on');
923
                    $codeenabledisable .= '</a>';
924
                    if (getDolGlobalInt("MAIN_FEATURES_LEVEL") > 1) {
925
                        $codeenabledisable .= '&nbsp;';
926
                        $codeenabledisable .= '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $objMod->numero . '&amp;token=' . newToken() . '&amp;module_position=' . $module_position . '&amp;action=reload&amp;value=' . $modName . '&amp;mode=' . $mode . '&amp;confirm=yes' . $param . '">';
927
                        $codeenabledisable .= img_picto($langs->trans("Reload"), 'refresh', 'class="opacitymedium"');
928
                        $codeenabledisable .= '</a>';
929
                    }
930
                }
931
            }
932
933
            // Set $codetoconfig
934
            if (!empty($objMod->config_page_url) && !$disableSetup) {
935
                $backtourlparam = '';
936
                if ($search_keyword != '') {
937
                    $backtourlparam .= ($backtourlparam ? '&' : '?') . 'search_keyword=' . urlencode($search_keyword); // No urlencode here, done later
938
                }
939
                if ($search_nature > -1) {
940
                    $backtourlparam .= ($backtourlparam ? '&' : '?') . 'search_nature=' . urlencode($search_nature); // No urlencode here, done later
941
                }
942
                if ($search_version > -1) {
943
                    $backtourlparam .= ($backtourlparam ? '&' : '?') . 'search_version=' . urlencode($search_version); // No urlencode here, done later
944
                }
945
                if ($search_status > -1) {
946
                    $backtourlparam .= ($backtourlparam ? '&' : '?') . 'search_status=' . urlencode($search_status); // No urlencode here, done later
947
                }
948
                $backtourl = $_SERVER["PHP_SELF"] . $backtourlparam;
949
950
                $regs = array();
951
                if (is_array($objMod->config_page_url)) {
952
                    $i = 0;
953
                    foreach ($objMod->config_page_url as $page) {
954
                        $urlpage = $page;
955
                        if ($i++) {
956
                            $codetoconfig .= '<a href="' . $urlpage . '" title="' . $langs->trans($page) . '">' . img_picto(ucfirst($page), "setup") . '</a>';
957
                            //    print '<a href="'.$page.'">'.ucfirst($page).'</a>&nbsp;';
958
                        } else {
959
                            if (preg_match('/^([^@]+)@([^@]+)$/i', $urlpage, $regs)) {
960
                                $urltouse = dol_buildpath('/' . $regs[2] . '/admin/' . $regs[1], 1);
961
                                $codetoconfig .= '<a href="' . $urltouse . (preg_match('/\?/', $urltouse) ? '&' : '?') . 'save_lastsearch_values=1&backtopage=' . urlencode($backtourl) . '" title="' . $langs->trans("Setup") . '">' . img_picto($langs->trans("Setup"), "setup", 'style="padding-right: 6px"', false, 0, 0, '', 'fa-15') . '</a>';
962
                            } else {
963
                                $urltouse = $urlpage;
964
                                $codetoconfig .= '<a href="' . $urltouse . (preg_match('/\?/', $urltouse) ? '&' : '?') . 'save_lastsearch_values=1&backtopage=' . urlencode($backtourl) . '" title="' . $langs->trans("Setup") . '">' . img_picto($langs->trans("Setup"), "setup", 'style="padding-right: 6px"', false, 0, 0, '', 'fa-15') . '</a>';
965
                            }
966
                        }
967
                    }
968
                } elseif (preg_match('/^([^@]+)@([^@]+)$/i', (string)$objMod->config_page_url, $regs)) {
969
                    $codetoconfig .= '<a class="valignmiddle" href="' . dol_buildpath('/' . $regs[2] . '/admin/' . $regs[1], 1) . '?save_lastsearch_values=1&backtopage=' . urlencode($backtourl) . '" title="' . $langs->trans("Setup") . '">' . img_picto($langs->trans("Setup"), "setup", 'style="padding-right: 6px"', false, 0, 0, '', 'fa-15') . '</a>';
970
                } else {
971
                    $codetoconfig .= '<a class="valignmiddle" href="' . ((string)$objMod->config_page_url) . '?save_lastsearch_values=1&backtopage=' . urlencode($backtourl) . '" title="' . $langs->trans("Setup") . '">' . img_picto($langs->trans("Setup"), "setup", 'style="padding-right: 6px"', false, 0, 0, '', 'fa-15') . '</a>';
972
                }
973
            } else {
974
                $codetoconfig .= img_picto($langs->trans("NothingToSetup"), "setup", 'class="opacitytransp" style="padding-right: 6px"', false, 0, 0, '', 'fa-15');
975
            }
976
        } else { // Module not yet activated
977
            // Set $codeenabledisable
978
            if (!empty($objMod->always_enabled)) {
979
                // A 'always_enabled' module should not never be disabled. If this happen, we keep a link to re-enable it.
980
                $codeenabledisable .= '<!-- Message to show: an always_enabled module has been disabled -->' . "\n";
981
                $codeenabledisable .= '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $objMod->numero . '&token=' . newToken() . '&module_position=' . $module_position . '&action=set&token=' . newToken() . '&value=' . $modName . '&mode=' . $mode . $param . '"';
982
                $codeenabledisable .= '>';
983
                $codeenabledisable .= img_picto($langs->trans("Disabled"), 'switch_off');
984
                $codeenabledisable .= "</a>\n";
985
            } elseif (!empty($objMod->disabled)) {
986
                $codeenabledisable .= $langs->trans("Disabled");
987
            } else {
988
                // Module qualified for activation
989
                $warningmessage = '';
990
                if (!empty($arrayofwarnings[$modName])) {
991
                    $codeenabledisable .= '<!-- This module is a core module and it may have a warning to show when we activate it (note: your country is ' . $mysoc->country_code . ') -->' . "\n";
992
                    foreach ($arrayofwarnings[$modName] as $keycountry => $cursorwarningmessage) {
993
                        if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^' . $mysoc->country_code . '/', $keycountry))) {
994
                            $warningmessage .= ($warningmessage ? "\n" : "") . $langs->trans($cursorwarningmessage, $objMod->getName(), $mysoc->country_code);
995
                        }
996
                    }
997
                }
998
                if ($objMod->isCoreOrExternalModule() == 'external' && !empty($arrayofwarningsext)) {
999
                    $codeenabledisable .= '<!-- This module is an external module and it may have a warning to show (note: your country is ' . $mysoc->country_code . ') -->' . "\n";
1000
                    foreach ($arrayofwarningsext as $keymodule => $arrayofwarningsextbycountry) {
1001
                        $keymodulelowercase = strtolower(preg_replace('/^mod/', '', $keymodule));
1002
                        if (in_array($keymodulelowercase, $conf->modules)) {    // If module that request warning is on
1003
                            foreach ($arrayofwarningsextbycountry as $keycountry => $cursorwarningmessage) {
1004
                                if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^' . $mysoc->country_code . '/', $keycountry))) {
1005
                                    $warningmessage .= ($warningmessage ? "\n" : "") . $langs->trans($cursorwarningmessage, $objMod->getName(), $mysoc->country_code, $modules[$keymodule]->getName());
1006
                                    $warningmessage .= ($warningmessage ? "\n" : "") . ($warningmessage ? "\n" : "") . $langs->trans("Module") . ' : ' . $objMod->getName();
1007
                                    if (!empty($objMod->editor_name)) {
1008
                                        $warningmessage .= ($warningmessage ? "\n" : "") . $langs->trans("Publisher") . ' : ' . $objMod->editor_name;
1009
                                    }
1010
                                    if (!empty($objMod->editor_name)) {
1011
                                        $warningmessage .= ($warningmessage ? "\n" : "") . $langs->trans("ModuleTriggeringThisWarning") . ' : ' . $modules[$keymodule]->getName();
1012
                                    }
1013
                                }
1014
                            }
1015
                        }
1016
                    }
1017
                }
1018
                $codeenabledisable .= '<!-- Message to show: ' . $warningmessage . ' -->' . "\n";
1019
                $codeenabledisable .= '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $objMod->numero . '&token=' . newToken() . '&module_position=' . $module_position . '&action=set&token=' . newToken() . '&value=' . $modName . '&mode=' . $mode . $param . '"';
1020
                if ($warningmessage) {
1021
                    $codeenabledisable .= ' onclick="return confirm(\'' . dol_escape_js($warningmessage) . '\');"';
1022
                }
1023
                $codeenabledisable .= '>';
1024
                $codeenabledisable .= img_picto($langs->trans("Disabled"), 'switch_off');
1025
                $codeenabledisable .= "</a>\n";
1026
            }
1027
1028
            // Set $codetoconfig
1029
            $codetoconfig .= img_picto($langs->trans("NothingToSetup"), "setup", 'class="opacitytransp" style="padding-right: 6px"');
1030
        }
1031
1032
        if ($mode == 'commonkanban') {
1033
            // Output Kanban
1034
            print $objMod->getKanbanView($codeenabledisable, $codetoconfig);
1035
        } else {
1036
            print '<tr class="oddeven' . ($warningstring ? ' info-box-content-warning' : '') . '">' . "\n";
1037
            if (getDolGlobalString('MAIN_MODULES_SHOW_LINENUMBERS')) {
1038
                print '<td class="width50">' . $linenum . '</td>';
1039
            }
1040
1041
            // Picto + Name of module
1042
            print '  <td class="tdoverflowmax200 minwidth200imp" title="' . dol_escape_htmltag($objMod->getName()) . '">';
1043
            $alttext = '';
1044
            //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
1045
            //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
1046
            if (!empty($objMod->picto)) {
1047
                if (preg_match('/^\//i', $objMod->picto)) {
1048
                    print img_picto($alttext, $objMod->picto, 'class="valignmiddle pictomodule paddingrightonly"', 1);
1049
                } else {
1050
                    print img_object($alttext, $objMod->picto, 'class="valignmiddle pictomodule paddingrightonly"');
1051
                }
1052
            } else {
1053
                print img_object($alttext, 'generic', 'class="valignmiddle paddingrightonly"');
1054
            }
1055
            print ' <span class="valignmiddle">' . $objMod->getName() . '</span>';
1056
            print "</td>\n";
1057
1058
            // Desc
1059
            print '<td class="valignmiddle tdoverflowmax300 minwidth200imp">';
1060
            print nl2br($objMod->getDesc());
1061
            print "</td>\n";
1062
1063
            // Help
1064
            print '<td class="center nowrap" style="width: 82px;">';
1065
            //print $form->textwithpicto('', $text, 1, $imginfo, 'minheight20', 0, 2, 1);
1066
            print '<a href="javascript:document_preview(\'' . constant('BASE_URL') . '/admin/modulehelp.php?id=' . ((int)$objMod->numero) . '\',\'text/html\',\'' . dol_escape_js($langs->trans("Module")) . '\')">' . img_picto(($objMod->isCoreOrExternalModule() == 'external' ? $langs->trans("ExternalModule") . ' - ' : '') . $langs->trans("ClickToShowDescription"), $imginfo) . '</a>';
1067
            print '</td>';
1068
1069
            // Version
1070
            print '<td class="center nowrap width150" title="' . dol_escape_htmltag(dol_string_nohtmltag($versiontrans)) . '">';
1071
            if ($objMod->needUpdate) {
1072
                $versionTitle = $langs->trans('ModuleUpdateAvailable') . ' : ' . $objMod->lastVersion;
1073
                print '<span class="badge badge-warning classfortooltip" title="' . dol_escape_htmltag($versionTitle) . '">' . $versiontrans . '</span>';
1074
            } else {
1075
                print $versiontrans;
1076
            }
1077
            print "</td>\n";
1078
1079
            // Link enable/disable
1080
            print '<td class="center valignmiddle left nowraponall" width="60px">';
1081
            print $codeenabledisable;
1082
            print "</td>\n";
1083
1084
            // Link config
1085
            print '<td class="tdsetuppicto right valignmiddle" width="60px">';
1086
            print $codetoconfig;
1087
            print '</td>';
1088
1089
            print "</tr>\n";
1090
        }
1091
        if ($objMod->needUpdate) {
1092
            $foundoneexternalmodulewithupdate++;
1093
        }
1094
    }
1095
1096
    if ($action == 'checklastversion') {
1097
        if ($foundoneexternalmodulewithupdate) {
1098
            setEventMessages($langs->trans("ModuleUpdateAvailable"), null, 'mesgs');
1099
        } else {
1100
            setEventMessages($langs->trans("NoExternalModuleWithUpdate"), null, 'mesgs');
1101
        }
1102
    }
1103
1104
    if ($oldfamily) {
1105
        if ($mode == 'commonkanban') {
1106
            print '</div>';
1107
        } else {
1108
            print "</table>\n";
1109
            print '</div>';
1110
        }
1111
    }
1112
1113
    if (!$atleastonequalified) {
1114
        print '<br><span class="opacitymedium">' . $langs->trans("NoDeployedModulesFoundWithThisSearchCriteria") . '</span><br><br>';
1115
    }
1116
1117
    print dol_get_fiche_end();
1118
1119
    print '<br>';
1120
1121
    // Show warning about external users
1122
    print info_admin(showModulesExludedForExternal($modules)) . "\n";
1123
1124
    print '</form>';
1125
}
1126
1127
if ($mode == 'marketplace') {
1128
    print dol_get_fiche_head($head, $mode, '', -1);
1129
1130
    print $deschelp;
1131
1132
    print '<br>';
1133
1134
    // Marketplace
1135
    print '<div class="div-table-responsive-no-min">';
1136
    print '<table summary="list_of_modules" class="noborder centpercent">' . "\n";
1137
    print '<tr class="liste_titre">' . "\n";
1138
    print '<td class="hideonsmartphone">' . $form->textwithpicto($langs->trans("Provider"), $langs->trans("WebSiteDesc")) . '</td>';
1139
    print '<td></td>';
1140
    print '<td>' . $langs->trans("URL") . '</td>';
1141
    print '</tr>';
1142
1143
    print '<tr class="oddeven">' . "\n";
1144
    $url = 'https://www.dolistore.com';
1145
    print '<td class="hideonsmartphone"><a href="' . $url . '" target="_blank" rel="noopener noreferrer external"><img border="0" class="imgautosize imgmaxwidth180" src="' . constant('DOL_URL_ROOT') . '/theme/alxarafe_shop.png"></a></td>';
1146
    print '<td><span class="opacitymedium">' . $langs->trans("DoliStoreDesc") . '</span></td>';
1147
    print '<td><a href="' . $url . '" target="_blank" rel="noopener noreferrer external">' . $url . '</a></td>';
1148
    print '</tr>';
1149
1150
    print "</table>\n";
1151
    print '</div>';
1152
1153
    print dol_get_fiche_end();
1154
1155
    print '<br>';
1156
1157
    if (!getDolGlobalString('MAIN_DISABLE_DOLISTORE_SEARCH') && getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 1) {
1158
        // $options is array with filter criteria
1159
        //var_dump($options);
1160
        $dolistore->getRemoteCategories();
1161
        $dolistore->getRemoteProducts($options);
1162
1163
        print '<span class="opacitymedium">' . $langs->trans('DOLISTOREdescriptionLong') . '</span><br><br>';
1164
1165
        $previouslink = $dolistore->get_previous_link();
1166
        $nextlink = $dolistore->get_next_link();
1167
1168
        print '<div class="liste_titre liste_titre_bydiv centpercent"><div class="divsearchfield">';
1169
1170
        print '<form method="POST" class="centpercent" id="searchFormList" action="' . $dolistore->url . '">'; ?>
1171
        <input type="hidden" name="token" value="<?php echo newToken(); ?>">
1172
        <input type="hidden" name="mode" value="marketplace">
1173
        <div class="divsearchfield">
1174
            <input name="search_keyword" placeholder="<?php echo $langs->trans('Keyword') ?>" id="search_keyword"
1175
                   type="text" class="minwidth200" value="<?php echo dol_escape_htmltag($options['search']) ?>"><br>
1176
        </div>
1177
        <div class="divsearchfield">
1178
            <input class="button buttongen" value="<?php echo $langs->trans('Rechercher') ?>" type="submit">
1179
            <a class="buttonreset"
1180
               href="<?php echo urlencode($dolistore->url) ?>"><?php echo $langs->trans('Reset') ?></a>
1181
        </div>
1182
        <?php
1183
        print $previouslink;
1184
        print $nextlink;
1185
        print '</form>';
1186
1187
        print '</div></div>';
1188
        print '<div class="clearboth"></div>';
1189
        ?>
1190
1191
        <div id="category-tree-left">
1192
            <ul class="tree">
1193
                <?php
1194
                echo $dolistore->get_categories();  // Do not use dol_escape_htmltag here, it is already a structured content?>
1195
            </ul>
1196
        </div>
1197
        <div id="listing-content">
1198
            <table summary="list_of_modules" id="list_of_modules" class="productlist centpercent">
1199
                <tbody id="listOfModules">
1200
                <?php echo $dolistore->get_products(); ?>
1201
                </tbody>
1202
            </table>
1203
        </div>
1204
        <?php
1205
    }
1206
}
1207
1208
1209
// Install external module
1210
1211
if ($mode == 'deploy') {
1212
    print dol_get_fiche_head($head, $mode, '', -1);
1213
1214
    $fullurl = '<a href="' . $urldolibarrmodules . '" target="_blank" rel="noopener noreferrer">' . $urldolibarrmodules . '</a>';
1215
    $message = '';
1216
    if ($allowonlineinstall) {
1217
        if (!in_array('/custom', explode(',', $dolibarr_main_url_root_alt))) {
1218
            $message = info_admin($langs->trans("ConfFileMustContainCustom", DOL_DOCUMENT_ROOT . '/custom', DOL_DOCUMENT_ROOT));
1219
            $allowfromweb = -1;
1220
        } else {
1221
            if ($dirins_ok) {
1222
                if (!is_writable(dol_osencode($dirins))) {
1223
                    $langs->load("errors");
1224
                    $message = info_admin($langs->trans("ErrorFailedToWriteInDir", $dirins), 0, 0, '1', 'warning');
1225
                    $allowfromweb = 0;
1226
                }
1227
            } else {
1228
                $message = info_admin($langs->trans("NotExistsDirect", $dirins) . $langs->trans("InfDirAlt") . $langs->trans("InfDirExample"));
1229
                $allowfromweb = 0;
1230
            }
1231
        }
1232
    } else {
1233
        if (getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')) {
1234
            // Show clean message
1235
            if (!is_numeric(getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US'))) {
1236
                $message = info_admin($langs->trans(getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')), 0, 0, 'warning');
1237
            } else {
1238
                $message = info_admin($langs->trans('InstallModuleFromWebHasBeenDisabledContactUs'), 0, 0, 'warning');
1239
            }
1240
        } else {
1241
            // Show technical message
1242
            $message = info_admin($langs->trans("InstallModuleFromWebHasBeenDisabledByFile", $dolibarrdataroot . '/installmodules.lock'), 0, 0, 'warning');
1243
        }
1244
        $allowfromweb = 0;
1245
    }
1246
1247
    print $deschelp;
1248
1249
    if ($allowfromweb < 1) {
1250
        print $langs->trans("SomethingMakeInstallFromWebNotPossible");
1251
        print $message;
1252
        //print $langs->trans("SomethingMakeInstallFromWebNotPossible2");
1253
        print '<br>';
1254
    }
1255
1256
    print '<br>';
1257
1258
    // $allowfromweb = -1 if installation or setup not correct, 0 if not allowed, 1 if allowed
1259
    if ($allowfromweb >= 0) {
1260
        if ($allowfromweb == 1) {
1261
            //print $langs->trans("ThisIsProcessToFollow").'<br>';
1262
        } else {
1263
            print $langs->trans("ThisIsAlternativeProcessToFollow") . '<br>';
1264
            print '<b>' . $langs->trans("StepNb", 1) . '</b>: ';
1265
            print str_replace('{s1}', $fullurl, $langs->trans("FindPackageFromWebSite", '{s1}')) . '<br>';
1266
            print '<b>' . $langs->trans("StepNb", 2) . '</b>: ';
1267
            print str_replace('{s1}', $fullurl, $langs->trans("DownloadPackageFromWebSite", '{s1}')) . '<br>';
1268
            print '<b>' . $langs->trans("StepNb", 3) . '</b>: ';
1269
        }
1270
1271
        if ($allowfromweb == 1) {
1272
            print '<form enctype="multipart/form-data" method="POST" class="noborder" action="' . $_SERVER["PHP_SELF"] . '" name="forminstall">';
1273
            print '<input type="hidden" name="token" value="' . newToken() . '">';
1274
            print '<input type="hidden" name="action" value="install">';
1275
            print '<input type="hidden" name="mode" value="deploy">';
1276
1277
            print $langs->trans("YouCanSubmitFile") . '<br><br>';
1278
1279
            $max = getDolGlobalString('MAIN_UPLOAD_DOC'); // In Kb
1280
            $maxphp = @ini_get('upload_max_filesize'); // In unknown
1281
            if (preg_match('/k$/i', $maxphp)) {
1282
                $maxphp = preg_replace('/k$/i', '', $maxphp);
1283
                $maxphp *= 1;
1284
            }
1285
            if (preg_match('/m$/i', $maxphp)) {
1286
                $maxphp = preg_replace('/m$/i', '', $maxphp);
1287
                $maxphp *= 1024;
1288
            }
1289
            if (preg_match('/g$/i', $maxphp)) {
1290
                $maxphp = preg_replace('/g$/i', '', $maxphp);
1291
                $maxphp *= 1024 * 1024;
1292
            }
1293
            if (preg_match('/t$/i', $maxphp)) {
1294
                $maxphp = preg_replace('/t$/i', '', $maxphp);
1295
                $maxphp *= 1024 * 1024 * 1024;
1296
            }
1297
            $maxphp2 = @ini_get('post_max_size'); // In unknown
1298
            if (preg_match('/k$/i', $maxphp2)) {
1299
                $maxphp2 = preg_replace('/k$/i', '', $maxphp2);
1300
                $maxphp2 *= 1;
1301
            }
1302
            if (preg_match('/m$/i', $maxphp2)) {
1303
                $maxphp2 = preg_replace('/m$/i', '', $maxphp2);
1304
                $maxphp2 *= 1024;
1305
            }
1306
            if (preg_match('/g$/i', $maxphp2)) {
1307
                $maxphp2 = preg_replace('/g$/i', '', $maxphp2);
1308
                $maxphp2 *= 1024 * 1024;
1309
            }
1310
            if (preg_match('/t$/i', $maxphp2)) {
1311
                $maxphp2 = preg_replace('/t$/i', '', $maxphp2);
1312
                $maxphp2 *= 1024 * 1024 * 1024;
1313
            }
1314
            // Now $max and $maxphp and $maxphp2 are in Kb
1315
            $maxmin = $max;
1316
            $maxphptoshow = $maxphptoshowparam = '';
1317
            if ($maxphp > 0) {
1318
                $maxmin = min($max, $maxphp);
1319
                $maxphptoshow = $maxphp;
1320
                $maxphptoshowparam = 'upload_max_filesize';
1321
            }
1322
            if ($maxphp2 > 0) {
1323
                $maxmin = min($max, $maxphp2);
1324
                if ($maxphp2 < $maxphp) {
1325
                    $maxphptoshow = $maxphp2;
1326
                    $maxphptoshowparam = 'post_max_size';
1327
                }
1328
            }
1329
1330
            if ($maxmin > 0) {
1331
                print '<script type="text/javascript">
1332
				$(document).ready(function() {
1333
					jQuery("#fileinstall").on("change", function() {
1334
						if(this.files[0].size > ' . ($maxmin * 1024) . ') {
1335
							alert("' . dol_escape_js($langs->trans("ErrorFileSizeTooLarge")) . '");
1336
							this.value = "";
1337
						}
1338
					});
1339
				});
1340
				</script>' . "\n";
1341
                // MAX_FILE_SIZE doit précéder le champ input de type file
1342
                print '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">';
1343
            }
1344
1345
            print '<input class="flat minwidth400" type="file" name="fileinstall" id="fileinstall"> ';
1346
1347
            print '<input type="submit" name="send" value="' . dol_escape_htmltag($langs->trans("Upload")) . '" class="button small">';
1348
1349
            if (getDolGlobalString('MAIN_UPLOAD_DOC')) {
1350
                if ($user->admin) {
1351
                    $langs->load('other');
1352
                    print ' ';
1353
                    print info_admin($langs->trans("ThisLimitIsDefinedInSetup", $max, $maxphptoshow, $maxphptoshowparam), 1);
1354
                }
1355
            } else {
1356
                print ' (' . $langs->trans("UploadDisabled") . ')';
1357
            }
1358
1359
            print '</form>';
1360
1361
            print '<br>';
1362
            print '<br>';
1363
1364
            print '<div class="center"><div class="logo_setup"></div></div>';
1365
        } else {
1366
            print $langs->trans("UnpackPackageInModulesRoot", $dirins) . '<br>';
1367
            print '<b>' . $langs->trans("StepNb", 4) . '</b>: ';
1368
            print $langs->trans("SetupIsReadyForUse", constant('BASE_URL') . '/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home") . ' - ' . $langs->transnoentitiesnoconv("Setup") . ' - ' . $langs->transnoentitiesnoconv("Modules")) . '<br>';
1369
        }
1370
    }
1371
1372
    if (!empty($result['return'])) {
1373
        print '<br>';
1374
1375
        foreach ($result['return'] as $value) {
1376
            echo $value . '<br>';
1377
        }
1378
    }
1379
1380
    print dol_get_fiche_end();
1381
}
1382
1383
if ($mode == 'develop') {
1384
    print dol_get_fiche_head($head, $mode, '', -1);
1385
1386
    print $deschelp;
1387
1388
    print '<br>';
1389
1390
    // Marketplace
1391
    print '<table summary="list_of_modules" class="noborder centpercent">' . "\n";
1392
    print '<tr class="liste_titre">' . "\n";
1393
    //print '<td>'.$langs->trans("Logo").'</td>';
1394
    print '<td colspan="2">' . $langs->trans("DevelopYourModuleDesc") . '</td>';
1395
    print '<td>' . $langs->trans("URL") . '</td>';
1396
    print '</tr>';
1397
1398
    print '<tr class="oddeven" height="80">' . "\n";
1399
    print '<td class="center">';
1400
    print '<div class="imgmaxheight50 logo_setup"></div>';
1401
    print '</td>';
1402
    print '<td>' . $langs->trans("TryToUseTheModuleBuilder", $langs->transnoentitiesnoconv("ModuleBuilder")) . '</td>';
1403
    print '<td class="maxwidth300">';
1404
    if (isModEnabled('modulebuilder')) {
1405
        print $langs->trans("SeeTopRightMenu");
1406
    } else {
1407
        print '<span class="opacitymedium">' . $langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("ModuleBuilder")) . '</span>';
1408
    }
1409
    print '</td>';
1410
    print '</tr>';
1411
1412
    print '<tr class="oddeven" height="80">' . "\n";
1413
    $url = 'https://partners.dolibarr.org';
1414
    print '<td class="center">';
1415
    // TODO: Design logo for preferred partner (Gold partner, perhaps?)
1416
    print'<a href="' . $url . '" target="_blank" rel="noopener noreferrer external"><img border="0" class="imgautosize imgmaxwidth180" src="' . constant('DOL_URL_ROOT') . '/theme/alxarafe_shop.png"></a>';
1417
    print '</td>';
1418
    print '<td>' . $langs->trans("DoliPartnersDesc") . '</td>';
1419
    print '<td><a href="' . $url . '" target="_blank" rel="noopener noreferrer external">';
1420
    print img_picto('', 'url', 'class="pictofixedwidth"');
1421
    print $url . '</a></td>';
1422
    print '</tr>';
1423
1424
    print "</table>\n";
1425
1426
    print dol_get_fiche_end();
1427
}
1428
1429
// End of page
1430
ViewMain::llxFooter();
1431
$db->close();
1432