dolibarr 23.0.3
modules.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4 * Copyright (C) 2004-2024 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
6 * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2011-2023 Juanjo Menent <jmenent@2byte.es>
8 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
9 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
10 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
11 * Copyright (C) 2021-2025 Frédéric France <frederic.france@free.fr>
12 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <https://www.gnu.org/licenses/>.
26 */
27
33if (!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
34 define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET
35}
36
37// Load Dolibarr environment
38require '../main.inc.php';
49'
50@phan-var-force string $dolibarr_main_url_root_alt
51';
52require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
53require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
54require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
55require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
56require_once DOL_DOCUMENT_ROOT.'/core/class/events.class.php';
57require_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
58require_once DOL_DOCUMENT_ROOT.'/admin/remotestore/class/externalModules.class.php';
59
60
61// Load translation files required by the page
62$langs->loadLangs(array("errors", "admin", "modulebuilder"));
63
64$action = GETPOST('action', 'aZ09');
65$page = GETPOSTINT('page');
66$page_y = GETPOSTINT('page_y');
67$optioncss = GETPOST('optioncss', 'aZ09');
68$sortfield = GETPOST('sortfield', 'aZ09');
69$sortorder = GETPOST('sortorder', 'aZ09');
70
71$mode = GETPOST('mode', 'alpha');
72$value = GETPOST('value', 'alpha');
73$search_keyword = GETPOST('search_keyword', 'alpha');
74$search_status = GETPOST('search_status', 'alpha');
75$search_nature = GETPOST('search_nature', 'alpha');
76$search_version = GETPOST('search_version', 'alpha');
77
78
79// For remotestore search
80$options = array();
81$options['per_page'] = 11;
82$options['no_page'] = (GETPOSTINT('no_page') ? GETPOSTINT('no_page') : 1);
83$options['categorie'] = (GETPOSTINT('categorie') ? GETPOSTINT('categorie') : 0);
84$options['search'] = GETPOST('search_keyword', 'alpha');
85
86// If it is a new search, we reset page to 1
87if (GETPOST('buttonsubmit', 'alphanohtml', 2)) {
88 $options['no_page'] = 1;
89}
90
91// MAIN_ENABLE_EXTERNALMODULES_DOLISTORE is 1 if we enabled the dolistore modules
92$options['search_source_dolistore'] = getDolGlobalInt('MAIN_ENABLE_EXTERNALMODULES_DOLISTORE');
93// MAIN_ENABLE_EXTERNALMODULES_COMMUNITY is 1 if we enabled the community modules
94$options['search_source_github'] = getDolGlobalInt('MAIN_ENABLE_EXTERNALMODULES_COMMUNITY');
95
96if (!$user->admin) {
98}
99
100$familyinfo = array(
101 'hr' => array('position' => '001', 'label' => $langs->trans("ModuleFamilyHr")),
102 'crm' => array('position' => '006', 'label' => $langs->trans("ModuleFamilyCrm")),
103 'srm' => array('position' => '007', 'label' => $langs->trans("ModuleFamilySrm")),
104 'financial' => array('position' => '009', 'label' => $langs->trans("ModuleFamilyFinancial")),
105 'products' => array('position' => '012', 'label' => $langs->trans("ModuleFamilyProducts")),
106 'projects' => array('position' => '015', 'label' => $langs->trans("ModuleFamilyProjects")),
107 'ecm' => array('position' => '018', 'label' => $langs->trans("ModuleFamilyECM")),
108 'technic' => array('position' => '021', 'label' => $langs->trans("ModuleFamilyTechnic")),
109 'portal' => array('position' => '040', 'label' => $langs->trans("ModuleFamilyPortal")),
110 'interface' => array('position' => '050', 'label' => $langs->trans("ModuleFamilyInterface")),
111 'base' => array('position' => '060', 'label' => $langs->trans("ModuleFamilyBase")),
112 'other' => array('position' => '100', 'label' => $langs->trans("ModuleFamilyOther")),
113);
114
115$param = '';
116if (!GETPOST('buttonreset', 'alpha')) {
117 if ($search_keyword) {
118 $param .= '&search_keyword='.urlencode($search_keyword);
119 }
120 if ($search_status && $search_status != '-1') {
121 $param .= '&search_status='.urlencode($search_status);
122 }
123 if ($search_nature && $search_nature != '-1') {
124 $param .= '&search_nature='.urlencode($search_nature);
125 }
126 if ($search_version && $search_version != '-1') {
127 $param .= '&search_version='.urlencode($search_version);
128 }
129}
130
131$dirins = DOL_DOCUMENT_ROOT.'/custom';
132$urldolibarrmodules = 'https://www.dolistore.com/';
133
134// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
135$hookmanager->initHooks(array('adminmodules', 'globaladmin'));
136
137// Increase limit of time. Works only if we are not in safe mode
138$max_execution_time_for_deploy = getDolGlobalInt('MODULE_UPLOAD_MAX_EXECUTION_TIME', 300); // 5mn if not defined
139if (!empty($max_execution_time_for_deploy)) {
140 $err = error_reporting();
141 error_reporting(0); // Disable all errors
142 //error_reporting(E_ALL);
143 @set_time_limit($max_execution_time_for_deploy);
144 error_reporting($err);
145}
146// Other method - TODO is this required ?
147$max_time = @ini_get("max_execution_time");
148if ($max_time && $max_time < $max_execution_time_for_deploy) {
149 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.");
150 @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
151}
152
153
154$dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT);
155$allowonlineinstall = true;
156$allowfromweb = 1;
157if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) {
158 $allowonlineinstall = false;
159}
160
161$debug = false;
162$remotestore = new ExternalModules($debug);
163
164if ($mode == 'marketplace') {
165 // Make remote calls
166 if (GETPOSTINT('dol_resetcache')) {
167 dol_delete_file($remotestore->cache_file);
168 }
169 $remotestore->loadRemoteSources(false);
170}
171
172$object = new stdClass();
173
174$now = dol_now();
175
176
177/*
178 * Actions
179 */
180
181$formconfirm = '';
182
183$parameters = array();
184$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
185if ($reshook < 0) {
186 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
187}
188
189// if we set another view list mode, we keep it (till we change one more time)
190if (GETPOSTISSET('mode')) {
191 $mode = GETPOST('mode', 'alpha');
192 if ($mode == 'common' && !getDolGlobalString('MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT')) {
193 dolibarr_set_const($db, "MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT", $mode, 'chaine', 0, '', $conf->entity);
194 }
195} else {
196 $mode = getDolGlobalString('MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT', 'commonkanban');
197}
198
199if (GETPOST('buttonreset', 'alpha')) {
200 $search_keyword = '';
201 $search_status = '';
202 $search_nature = '';
203 $search_version = '';
204}
205
206if ($action == 'install' && $allowonlineinstall) {
207 $error = 0;
208 $modulenameval = '';
209 // $original_file should match format module_modulename-x.y[.z].zip
210 $tmpfile = $_FILES['fileinstall']['tmp_name'];
211 $original_file = basename($_FILES["fileinstall"]["name"]);
212 $original_file = preg_replace('/\s*\‍(\d+\‍)\.zip$/i', '.zip', $original_file);
213 $newfile = dol_sanitizePathName($conf->admin->dir_temp.'/'.$original_file.'/'.$original_file);
214
215 if (!$original_file) {
216 $langs->load("Error");
217 setEventMessages($langs->trans("ErrorModuleFileRequired"), null, 'warnings');
218 $error++;
219 } else {
220 if (!$error && !preg_match('/\.zip$/i', $original_file)) {
221 $langs->load("errors");
222 setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage", $original_file), null, 'errors');
223 $error++;
224 }
225 if (!$error && !preg_match('/^(module[a-zA-Z0-9]*_|theme_|).*\-([0-9][0-9\.]*)(\s\‍(\d+\‍)\s)?\.zip$/i', $original_file)) {
226 $langs->load("errors");
227 setEventMessages($langs->trans("ErrorFilenameDosNotMatchDolibarrPackageRules", $original_file, 'modulename-x[.y.z].zip'), null, 'errors');
228 $error++;
229 }
230 if (empty($tmpfile)) {
231 $langs->load("errors");
232 setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
233 $error++;
234 }
235 }
236
237 if (!$error) {
238 if ($original_file) {
239 @dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$original_file);
240 dol_mkdir($conf->admin->dir_temp.'/'.$original_file);
241 }
242
243 $tmpdir = preg_replace('/\.zip$/i', '', $original_file).'.dir';
244 if ($tmpdir) {
245 @dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$tmpdir);
246 dol_mkdir($conf->admin->dir_temp.'/'.$tmpdir);
247 }
248
249 $result = dol_move_uploaded_file($tmpfile, $newfile, 1, 0, $_FILES['fileinstall']['error']);
250 if ((int) $result > 0) {
251 $resultuncompress = dol_uncompress($newfile, $conf->admin->dir_temp.'/'.$tmpdir);
252
253 if (!empty($resultuncompress['error'])) {
254 $langs->load("errors");
255 setEventMessages($langs->trans($resultuncompress['error'], $original_file), null, 'errors');
256 $error++;
257 } else {
258 // Now we move the dir of the module
259 $modulename = preg_replace('/module_/', '', $original_file);
260 $modulename = preg_replace('/\-([0-9][0-9\.]*)\.zip$/i', '', $modulename);
261 // Search dir $modulename
262 $modulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulename; // Example ./mymodule
263
264 if (!dol_is_dir($modulenamedir)) {
265 $modulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulename; // Example ./htdocs/mymodule
266 //var_dump($modulenamedir);
267 if (!dol_is_dir($modulenamedir)) {
268 setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat").'<br>'.$langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat2", $modulename, 'htdocs/'.$modulename), null, 'errors');
269 $error++;
270 }
271 }
272
273 dol_syslog("Uncompress of module file is a success.");
274
275 // Load module into $objMod
276 /*
277 $modulesdir = array($modulenamedir.'/core/modules/');
278 foreach ($modulesdir as $dir) {
279 // Load modules attributes in arrays (name, numero, orders) from dir directory
280 //print $dir."\n<br>";
281 dol_syslog("Scan directory ".$dir." for module descriptor files (modXXX.class.php)");
282 $handle = @opendir($dir);
283 if (is_resource($handle)) {
284 while (($file = readdir($handle)) !== false) {
285 print $dir." ".$file."\n<br>";
286 if (is_readable($dir.$file) && substr($file, 0, 3) == 'mod' && substr($file, dol_strlen($file) - 10) == '.class.php') {
287 $modName = substr($file, 0, dol_strlen($file) - 10);
288 if ($modName) {
289 try {
290 $res = include_once $dir.$file; // A class already exists in a different file will send a non catchable fatal error.
291 $modName = substr($file, 0, dol_strlen($file) - 10);
292 if ($modName) {
293 if (class_exists($modName)) {
294 $objMod = new $modName($db);
295 '@phan-var-force DolibarrModules $objMod';
296
297 //var_dump($objMod);
298 }
299 }
300 } catch(Exception $e) {
301 // Nothing done
302 }
303 }
304 }
305 }
306 }
307 }
308 */
309
310 // Check if module is in the remote malware blacklist (at URL DolibarrModules::URL_FOR_BLACKLISTED_MODULES)
311 if (!$error) {
312 if (GETPOST('checkforcompliance') == 'on') {
313 try {
314 $res = include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
315 $dolibarrmodule = new DolibarrModules($db);
316 $checkRes = $dolibarrmodule->checkForcompliance($modulename);
317
318 if (!is_numeric($checkRes) && $checkRes != '') {
319 $langs->load("errors");
320 setEventMessages($modulename.' : '.$langs->trans($checkRes), null, 'errors');
321 $error++;
322 }
323 } catch (Exception $e) {
324 // Nothing done
325 }
326 }
327 }
328
329 if (!$error) {
330 // TODO Make more test ???
331 }
332
333 // We check if this is a metapackage (and wecomplete with child packages)
334 $modulenamearrays = array();
335 if (dol_is_file($modulenamedir.'/metapackage.conf')) {
336 // This is a meta package
337 $metafile = file_get_contents($modulenamedir.'/metapackage.conf');
338 $modulenamearrays = explode("\n", $metafile);
339 }
340 $modulenamearrays[$modulename] = $modulename;
341 //var_dump($modulenamearrays);exit;
342
343 // Lop on each packages (can have several if package is a metapackage)
344 if (! $error) {
345 foreach ($modulenamearrays as $modulenameval) {
346 if (strpos($modulenameval, '#') === 0) {
347 continue; // Discard comments
348 }
349 if (strpos($modulenameval, '//') === 0) {
350 continue; // Discard comments
351 }
352 if (!trim($modulenameval)) {
353 continue;
354 }
355
356 // Now we install the module
357 if (!$error) {
358 @dol_delete_dir_recursive($dirins.'/'.$modulenameval); // delete the target directory
359 $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulenameval;
360 if (!dol_is_dir($submodulenamedir)) {
361 $submodulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulenameval;
362 }
363 dol_syslog("We copy now directory ".$submodulenamedir." into target dir ".$dirins.'/'.$modulenameval);
364 $resultcopy = dolCopyDir($submodulenamedir, $dirins.'/'.$modulenameval, '0444', 1);
365 if ($resultcopy <= 0) {
366 dol_syslog('Failed to call dolCopyDir result='.$resultcopy." with param ".$submodulenamedir." and ".$dirins.'/'.$modulenameval, LOG_WARNING);
367 $langs->load("errors");
368 setEventMessages($langs->trans("ErrorFailToCopyDir", $submodulenamedir, $dirins.'/'.$modulenameval), null, 'errors');
369 $error++;
370 }
371 }
372 }
373 }
374 }
375 } else {
376 setEventMessages($langs->trans("ErrorFailToRenameFile", $tmpfile, $newfile).' - code = '.$result, null, 'errors');
377 $error++;
378 }
379 }
380
381 // Add event purge
382 $securityevent = new Events($db);
383 if ($error) {
384 $text = $langs->trans("SecurityModuleDeploymentError", dol_sanitizePathName($_FILES["fileinstall"]["name"]));
385 $securityevent->type = 'MODULE_DEPLOYMENT_ERROR';
386 } else {
387 $text = $langs->trans("SecurityModuleDeploymentSuccess", dol_sanitizePathName($_FILES["fileinstall"]["name"]));
388 $securityevent->type = 'MODULE_DEPLOYMENT_SUCCESS';
389 }
390 $securityevent->dateevent = $now;
391 $securityevent->description = $text;
392
393 $resultcreateevent = $securityevent->create($user);
394
395 if (!$error) {
396 $searchParams = array(
397 'search_keyword' => $modulenameval,
398 'search_status' => '-1',
399 'search_nature' => '-1',
400 'search_version' => '-1'
401 );
402 $queryString = http_build_query($searchParams);
403 $redirectUrl = DOL_URL_ROOT . '/admin/modules.php?' . $queryString;
404
405 $message = $langs->trans("SetupIsReadyForUse", $redirectUrl, $langs->transnoentitiesnoconv("Home").' - '.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("Modules"));
406
407 setEventMessages($message, null, 'warnings');
408 }
409} elseif ($action == 'install' && !$allowonlineinstall) {
410 httponly_accessforbidden("You try to bypass the protection to disallow deployment of an external module. Hack attempt ?");
411}
412
413if ($action == 'set' && $user->admin) {
414 // We made some check against evil eternal modules that try to low security options.
415 $checkOldValue = getDolGlobalInt('CHECKLASTVERSION_EXTERNALMODULE');
416 $csrfCheckOldValue = getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN');
417 $resarray = activateModule($value);
418 if ($checkOldValue != getDolGlobalInt('CHECKLASTVERSION_EXTERNALMODULE')) {
419 setEventMessage($langs->trans('WarningModuleHasChangedLastVersionCheckParameter', $value), 'warnings');
420 }
421 if ($csrfCheckOldValue != getDolGlobalInt('MAIN_SECURITY_CSRF_WITH_TOKEN')) {
422 setEventMessage($langs->trans('WarningModuleHasChangedSecurityCsrfParameter', $value), 'warnings');
423 }
424
425 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
426 if (!empty($resarray['errors'])) {
427 setEventMessages('', $resarray['errors'], 'errors');
428 } else {
429 //var_dump($resarray);exit;
430 if ($resarray['nbperms'] > 0) {
431 $tmpsql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."user WHERE admin <> 1";
432 $resqltmp = $db->query($tmpsql);
433 if ($resqltmp) {
434 $obj = $db->fetch_object($resqltmp);
435 //var_dump($obj->nb);exit;
436 if ($obj && $obj->nb > 1) {
437 $msg = $langs->trans('ModuleEnabledAdminMustCheckRights');
438 setEventMessages($msg, null, 'warnings');
439 }
440 } else {
441 dol_print_error($db);
442 }
443 }
444 }
445 header("Location: ".$_SERVER["PHP_SELF"]."?mode=".$mode.$param.($page_y ? '&page_y='.$page_y : ''));
446 exit;
447} elseif ($action == 'reset' && $user->admin && GETPOST('confirm') == 'yes') {
448 $result = unActivateModule($value);
449 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
450 if ($result) {
451 setEventMessages($result, null, 'errors');
452 }
453 header("Location: ".$_SERVER["PHP_SELF"]."?mode=".$mode.$param.($page_y ? '&page_y='.$page_y : ''));
454 exit;
455} elseif (getDolGlobalInt("MAIN_FEATURES_LEVEL") > 1 && $action == 'reload' && $user->admin && GETPOST('confirm') == 'yes') {
456 $result = unActivateModule($value, 0, 'newboxdefonly'); // unactivate all module features but for widget, we reload only definition and we do not change position or setup
457 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1, 'chaine', 0, '', $conf->entity);
458 if ($result) {
459 setEventMessages($result, null, 'errors');
460 header("Location: ".$_SERVER["PHP_SELF"]."?mode=".$mode.$param.($page_y ? '&page_y='.$page_y : ''));
461 }
462 $resarray = activateModule($value, 0, 1);
463 dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", (getDolGlobalInt('MAIN_IHM_PARAMS_REV') + 1), 'chaine', 0, '', $conf->entity);
464 if (!empty($resarray['errors'])) {
465 setEventMessages('', $resarray['errors'], 'errors');
466 } else {
467 if ($resarray['nbperms'] > 0) {
468 $tmpsql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."user WHERE admin <> 1";
469 $resqltmp = $db->query($tmpsql);
470 if ($resqltmp) {
471 $obj = $db->fetch_object($resqltmp);
472 if ($obj && $obj->nb > 1) {
473 $msg = $langs->trans('ModuleEnabledAdminMustCheckRights');
474 setEventMessages($msg, null, 'warnings');
475 }
476 } else {
477 dol_print_error($db);
478 }
479 }
480 }
481 header("Location: ".$_SERVER["PHP_SELF"]."?mode=".$mode.$param.($page_y ? '&page_y='.$page_y : ''));
482 exit;
483}
484
485
486/*
487 * View
488 */
489
490$form = new Form($db);
491
492$morejs = array();
493$morecss = array("/admin/remotestore/css/store.css");
494
495// Set dir where external modules are installed
496if (!dol_is_dir($dirins)) {
497 dol_mkdir($dirins);
498}
499$dirins_ok = (dol_is_dir($dirins));
500
501$help_url = 'EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones';
502llxHeader('', $langs->trans("Setup"), $help_url, '', 0, 0, $morejs, $morecss, '', 'mod-admin page-modules');
503
504
505// Search modules dirs
506$modulesdir = dolGetModulesDirs();
507
508$arrayofnatures = array(
509 'core' => array('label' => $langs->transnoentitiesnoconv("NativeModules")),
510 'external' => array('label' => $langs->transnoentitiesnoconv("External").' - ['.$langs->trans("AllPublishers").']')
511);
512$arrayofwarnings = array(); // Array of warning each module want to show when activated
513$arrayofwarningsext = array(); // Array of warning each module want to show when we activate an external module
514$filename = array();
515$modules = array();
516$orders = array();
517$categ = array();
518//$publisherlogoarray = array();
519
520$i = 0; // is a sequencer of modules found
521$j = 0; // j is module number. Automatically affected if module number not defined.
522$modNameLoaded = array();
523
524
525// Load $modules (required for the badge count)
526foreach ($modulesdir as $dir) {
527 // Load modules attributes in arrays (name, numero, orders) from dir directory
528 //print $dir."\n<br>";
529 dol_syslog("Scan directory ".$dir." for module descriptor files (modXXX.class.php)");
530 $handle = @opendir($dir);
531 if (is_resource($handle)) {
532 while (($file = readdir($handle)) !== false) {
533 //print "$i ".$file."\n<br>";
534 if (is_readable($dir.$file) && substr($file, 0, 3) == 'mod' && substr($file, dol_strlen($file) - 10) == '.class.php') {
535 $modName = substr($file, 0, dol_strlen($file) - 10);
536
537 if ($modName) {
538 if (!empty($modNameLoaded[$modName])) { // In cache of already loaded modules ?
539 $mesg = "Error: Module ".$modName." was found twice: Into ".$modNameLoaded[$modName]." and ".$dir.". You probably have an old file on your disk.<br>";
540 setEventMessages($mesg, null, 'warnings');
541 dol_syslog($mesg, LOG_ERR);
542 continue;
543 }
544
545 try {
546 $res = include_once $dir.$file; // A class already exists in a different file will send a non catchable fatal error.
547 if (class_exists($modName)) {
548 $objMod = new $modName($db);
549 '@phan-var-force DolibarrModules $objMod';
551 $modNameLoaded[$modName] = $dir;
552 if (!$objMod->numero > 0 && $modName != 'modUser') {
553 dol_syslog('The module descriptor '.$modName.' must have a numero property', LOG_ERR);
554 }
555 $j = $objMod->numero;
556
557 $modulequalified = 1;
558
559 // We discard modules according to features level (PS: if module is activated we always show it)
560 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
561 if ($objMod->version == 'development' && (!getDolGlobalString($const_name) && (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2))) {
562 $modulequalified = 0;
563 }
564 if ($objMod->version == 'experimental' && (!getDolGlobalString($const_name) && (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1))) {
565 $modulequalified = 0;
566 }
567 if (preg_match('/deprecated/', $objMod->version) && (!getDolGlobalString($const_name) && (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 0))) {
568 $modulequalified = 0;
569 }
570
571 // We discard modules according to property ->hidden
572 if (!empty($objMod->hidden)) {
573 $modulequalified = 0;
574 }
575
576 if ($modulequalified > 0) {
577 $publisher = dol_escape_htmltag($objMod->getPublisher());
578 $external = ($objMod->isCoreOrExternalModule() == 'external');
579 if ($external) {
580 if ($publisher) {
581 // Check if there is a logo forpublisher
582 /* Do not show the company logo in combo. Make combo list dirty.
583 if (!empty($objMod->editor_squarred_logo)) {
584 $publisherlogoarray['external_'.$publisher] = img_picto('', $objMod->editor_squarred_logo, 'class="publisherlogoinline"');
585 }
586 $publisherlogo = empty($publisherlogoarray['external_'.$publisher]) ? '' : $publisherlogoarray['external_'.$publisher];
587 */
588 $arrayofnatures['external_'.$publisher] = array('label' => $langs->trans("External").' - '.$publisher, 'data-html' => $langs->trans("External").' - <span class="opacitymedium inine-block valignmiddle">'.$publisher.'</span>');
589 } else {
590 $arrayofnatures['external_'] = array('label' => $langs->trans("External").' - ['.$langs->trans("UnknownPublishers").']');
591 }
592 }
593 ksort($arrayofnatures);
594
595 // Define an array $categ with categ with at least one qualified module
596 $filename[$i] = $modName;
597 $modules[$modName] = $objMod;
598
599 // Gives the possibility to the module, to provide his own family info and position of this family
600 if (is_array($objMod->familyinfo) && !empty($objMod->familyinfo)) {
601 $familyinfo = array_merge($familyinfo, $objMod->familyinfo);
602 $familykey = key($objMod->familyinfo);
603 } else {
604 $familykey = $objMod->family;
605 }
606 '@phan-var-force string $familykey'; // if not, phan considers $familykey may be null
607
608 $moduleposition = ($objMod->module_position ? $objMod->module_position : '50');
609 if ($objMod->isCoreOrExternalModule() == 'external' && $moduleposition < 100000) {
610 // an external module should never return a value lower than '80'.
611 $moduleposition = '80'; // External modules at end by default
612 }
613
614 // Add list of warnings to show into arrayofwarnings and arrayofwarningsext
615 if (!empty($objMod->warnings_activation)) {
616 $arrayofwarnings[$modName] = $objMod->warnings_activation;
617 }
618 if (!empty($objMod->warnings_activation_ext)) {
619 $arrayofwarningsext[$modName] = $objMod->warnings_activation_ext;
620 }
621
622 $familyposition = (empty($familyinfo[$familykey]['position']) ? '0' : $familyinfo[$familykey]['position']);
623 if ($external && !in_array($familykey, array_keys($familyinfo))) {
624 // 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).
625 if (is_numeric($familyposition)) {
626 $familyposition = sprintf("%03d", (int) $familyposition + 100);
627 }
628 }
629
630 $orders[$i] = $familyposition."_".$familykey."_".$moduleposition."_".$j; // Sort by family, then by module position then number
631
632 // Set categ[$i]
633 $specialstring = 'unknown';
634 if ($objMod->version == 'development' || $objMod->version == 'experimental') {
635 $specialstring = 'expdev';
636 }
637 if (isset($categ[$specialstring])) {
638 $categ[$specialstring]++; // Array of all different modules categories
639 } else {
640 $categ[$specialstring] = 1;
641 }
642 $j++;
643 $i++;
644 } else {
645 dol_syslog("Module ".get_class($objMod)." not qualified");
646 }
647 } else {
648 print info_admin("admin/modules.php Warning bad descriptor file : ".$dir.$file." (Class ".$modName." not found into file)", 0, 0, '1', 'warning');
649 }
650 } catch (Exception $e) {
651 dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR);
652 }
653 }
654 }
655 }
656 closedir($handle);
657 } else {
658 dol_syslog("htdocs/admin/modules.php: Failed to open directory ".$dir.". See permission and open_basedir option.", LOG_WARNING);
659 }
660}
661
662'@phan-var-force array<string,DolibarrModules> $modules';
665if ($action == 'reset_confirm' && $user->admin) {
666 if (!empty($modules[$value])) {
667 $objMod = $modules[$value];
668
669 if (!empty($objMod->langfiles)) {
670 $langs->loadLangs($objMod->langfiles);
671 }
672
673 $form = new Form($db);
674 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?value='.$value.'&mode='.$mode.$param, $langs->trans('ConfirmUnactivation'), $langs->trans(GETPOST('confirm_message_code')), 'reset', '', 'no', 1);
675 }
676}
677
678if ($action == 'reload_confirm' && $user->admin) {
679 if (!empty($modules[$value])) {
680 $objMod = $modules[$value];
681
682 if (!empty($objMod->langfiles)) {
683 $langs->loadLangs($objMod->langfiles);
684 }
685
686 $form = new Form($db);
687 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?value='.$value.'&mode='.$mode.$param, $langs->trans('ConfirmReload'), $langs->trans(GETPOST('confirm_message_code')), 'reload', '', 'no', 1);
688 }
689}
690
691print $formconfirm;
692
693asort($orders);
694//var_dump($orders);
695//var_dump($categ);
696//var_dump($modules);
697
698$nbofactivatedmodules = count($conf->modules);
699
700// Define $nbmodulesnotautoenabled - TODO This code is at different places
701$nbmodulesnotautoenabled = count($conf->modules);
702$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
703foreach ($listofmodulesautoenabled as $moduleautoenable) {
704 if (in_array($moduleautoenable, $conf->modules)) {
705 $nbmodulesnotautoenabled--;
706 }
707}
708
709print load_fiche_titre($langs->trans("ModulesSetup"), '', 'title_setup');
710
711// Start to show page
712$deschelp = '';
713if ($mode == 'common' || $mode == 'commonkanban') {
714 $desc = $langs->trans("ModulesDesc", '{picto}');
715 $desc .= ' '.$langs->trans("ModulesDesc2", '{picto2}');
716 $desc = str_replace('{picto}', img_picto('', 'switch_off', 'class="size15x"'), $desc);
717 $desc = str_replace('{picto2}', img_picto('', 'setup', 'class="size15x"'), $desc);
718 if (getDolGlobalInt('MAIN_SETUP_MODULES_DESC') || $nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
719 $deschelp .= '<div class="info hideonsmartphone">'.$desc."<br></div>\n";
720 }
721 if (getDolGlobalString('MAIN_SETUP_MODULES_INFO')) { // Add a custom info message. A good usage for SaaS in combination with option MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING.
722 $deschelp .= '<div class="info">'.$langs->trans(getDolGlobalString('MAIN_SETUP_MODULES_INFO'))."<br></div>\n";
723 }
724 if ($deschelp) {
725 $deschelp .= '<br>';
726 }
727}
728if ($mode == 'deploy') {
729 $deschelp = '<div class="info hideonsmartphone">'.$langs->trans("ModulesDeployDesc", $langs->transnoentitiesnoconv("AvailableModules"))."<br></div><br>\n";
730}
731if ($mode == 'develop') {
732 $deschelp = '<div class="info hideonsmartphone">'.$langs->trans("ModulesDevelopDesc")."<br></div><br>\n";
733}
734
735$head = modules_prepare_head($nbofactivatedmodules, count($modules), $nbmodulesnotautoenabled);
736
737
738if ($mode == 'common' || $mode == 'commonkanban') {
739 dol_set_focus('#search_keyword');
740
741 print '<form method="POST" id="searchFormList" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
742 print '<input type="hidden" name="token" value="'.newToken().'">';
743 if (isset($optioncss) && $optioncss != '') {
744 print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
745 }
746 if (isset($sortfield) && $sortfield != '') {
747 print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
748 }
749 if (isset($sortorder) && $sortorder != '') {
750 print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
751 }
752 if (isset($page) && $page != '') {
753 print '<input type="hidden" name="page" value="'.$page.'">';
754 }
755 print '<input type="hidden" name="mode" value="'.$mode.'">';
756
757 print dol_get_fiche_head($head, 'modules', '', -1);
758
759 print $deschelp;
760
761 $moreforfilter = '<div class="valignmiddle">';
762
763 $moreforfilter .= '<div class="floatright right pagination paddingtop --module-list"><ul><li>';
764 $moreforfilter .= dolGetButtonTitle($langs->trans('CheckForModuleUpdate'), $langs->trans('CheckForModuleUpdate').'<br><br>'.img_warning('', '', 'paddingright').$langs->trans('CheckForModuleUpdateHelp').' '.$langs->trans('CheckForModuleUpdateHelp2', DolibarrModules::URL_FOR_BLACKLISTED_MODULES).'<br>'.$langs->trans("YourIPWillBeRevealedToThisExternalProviders"), 'fa fa-sync', $_SERVER["PHP_SELF"].'?action=checklastversion&token='.newToken().'&mode='.$mode.$param, '', 1, array('morecss' => 'reposition'));
765 $moreforfilter .= dolGetButtonTitleSeparator();
766 $moreforfilter .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars imgforviewmode', $_SERVER["PHP_SELF"].'?mode=common'.$param, '', ($mode == 'common' ? 2 : 1), array('morecss' => 'reposition'));
767 $moreforfilter .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', $_SERVER["PHP_SELF"].'?mode=commonkanban'.$param, '', ($mode == 'commonkanban' ? 2 : 1), array('morecss' => 'reposition'));
768 $moreforfilter .= '</li></ul></div>';
769
770 $moreforfilter .= '<div class="divfilteralone colorbacktimesheet float valignmiddle">';
771 $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
772 $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).'" spellcheck="false" placeholder="'.dol_escape_htmltag($langs->trans('Keyword')).'">';
773 $moreforfilter .= '</div>';
774 $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
775 $moreforfilter .= $form->selectarray('search_nature', $arrayofnatures, dol_escape_htmltag($search_nature), $langs->trans('Origin'), 0, 0, '', 0, 0, 0, '', 'maxwidth250', 1);
776 $moreforfilter .= '</div>';
777
778 if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
779 $array_version = array('stable' => $langs->transnoentitiesnoconv("Stable"));
780 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 0) {
781 $array_version['deprecated'] = $langs->trans("Deprecated");
782 }
783 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
784 $array_version['experimental'] = $langs->trans("Experimental");
785 }
786 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 1) {
787 $array_version['development'] = $langs->trans("Development");
788 }
789 $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
790 $moreforfilter .= $form->selectarray('search_version', $array_version, $search_version, $langs->transnoentitiesnoconv('Version'), 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1);
791 $moreforfilter .= '</div>';
792 }
793 $array_status = array('active' => $langs->transnoentitiesnoconv("Enabled"), 'disabled' => $langs->transnoentitiesnoconv("Disabled"));
794 $moreforfilter .= '<div class="divsearchfield paddingtop paddingbottom valignmiddle inline-block">';
795 $moreforfilter .= $form->selectarray('search_status', $array_status, $search_status, $langs->transnoentitiesnoconv('Status'), 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1);
796 $moreforfilter .= '</div>';
797 $moreforfilter .= ' ';
798 $moreforfilter .= '<div class="divsearchfield valignmiddle inline-block">';
799 $moreforfilter .= '<input type="submit" name="buttonsubmit" class="button small nomarginleft" value="'.dolPrintHTMLForAttribute($langs->trans("Refresh")).'">';
800 if ($search_keyword || ($search_nature && $search_nature != '-1') || ($search_version && $search_version != '-1') || ($search_status && $search_status != '-1')) {
801 $moreforfilter .= ' ';
802 $moreforfilter .= '<input type="submit" name="buttonreset" class="buttonreset noborderall nomargintop nomarginbottom" value="'.dolPrintHTMLForAttribute($langs->trans("Reset")).'">';
803 }
804 $moreforfilter .= '</div>';
805 $moreforfilter .= '</div>';
806
807 $moreforfilter .= '</div>';
808
809 print $moreforfilter;
810 $parameters = array();
811 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
812 print $hookmanager->resPrint;
813
814 $moreforfilter = '';
815
816 print '<div class="clearboth"></div><br><br>';
817
818 $object = new stdClass();
819 $parameters = array();
820 $reshook = $hookmanager->executeHooks('insertExtraHeader', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
821 if ($reshook < 0) {
822 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
823 }
824
825 $disabled_modules = array();
826 if (!empty($_SESSION["disablemodules"])) {
827 $disabled_modules = explode(',', $_SESSION["disablemodules"]);
828 }
829
830 // Show list of modules
831 $oldfamily = '';
832 $foundoneexternalmodulewithupdate = 0;
833 $linenum = 0;
834 $atleastonequalified = 0;
835 $atleastoneforfamily = 0;
836
837 foreach ($orders as $key => $value) {
838 $linenum++;
839 $tab = explode('_', $value);
840 $familykey = $tab[1];
841 $module_position = $tab[2];
842
843 $modName = $filename[$key];
844
846 $objMod = $modules[$modName];
847
848 if (!is_object($objMod)) {
849 continue;
850 }
851
852 //print $objMod->name." - ".$key." - ".$objMod->version."<br>";
853 if ($mode == 'expdev' && $objMod->version != 'development' && $objMod->version != 'experimental') {
854 continue; // Discard if not for current tab
855 }
856
857 if (!$objMod->getName()) {
858 dol_syslog("Error for module ".$key." - Property name of module looks empty", LOG_WARNING);
859 continue;
860 }
861
862 $modulenameshort = strtolower(preg_replace('/^mod/i', '', get_class($objMod)));
863 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
864
865 // Check filters
866 $modulename = $objMod->getName();
867 $moduletechnicalname = $objMod->name;
868 $moduledesc = $objMod->getDesc();
869 $moduledesclong = $objMod->getDescLong();
870 $moduleauthor = $objMod->getPublisher();
871
872 // We discard showing according to filters
873 if ($search_keyword) {
874 $qualified = 0;
875 if (preg_match('/'.preg_quote($search_keyword, '/').'/i', $modulename)
876 || preg_match('/'.preg_quote($search_keyword, '/').'/i', $moduletechnicalname)
877 || ($moduledesc && preg_match('/'.preg_quote($search_keyword, '/').'/i', $moduledesc))
878 || ($moduledesclong && preg_match('/'.preg_quote($search_keyword, '/').'/i', $moduledesclong))
879 || ($moduleauthor && preg_match('/'.preg_quote($search_keyword, '/').'/i', $moduleauthor))
880 ) {
881 $qualified = 1;
882 }
883 if (!$qualified) {
884 continue;
885 }
886 }
887 if ($search_status) {
888 if ($search_status == 'active' && !getDolGlobalString($const_name)) {
889 continue;
890 }
891 if ($search_status == 'disabled' && getDolGlobalString($const_name)) {
892 continue;
893 }
894 }
895 if ($search_nature) {
896 if (preg_match('/^external/', $search_nature) && $objMod->isCoreOrExternalModule() != 'external') {
897 continue;
898 }
899 $reg = array();
900 if (preg_match('/^external_(.*)$/', $search_nature, $reg)) {
901 //print $reg[1].'-'.dol_escape_htmltag($objMod->getPublisher());
902 $publisher = dol_escape_htmltag($objMod->getPublisher());
903 if ($reg[1] && dol_escape_htmltag($reg[1]) != $publisher) {
904 continue;
905 }
906 if (!$reg[1] && !empty($publisher)) {
907 continue;
908 }
909 }
910 if ($search_nature == 'core' && $objMod->isCoreOrExternalModule() == 'external') {
911 continue;
912 }
913 }
914 if ($search_version) {
915 if (($objMod->version == 'development' || $objMod->version == 'experimental' || preg_match('/deprecated/', $objMod->version)) && $search_version == 'stable') {
916 continue;
917 }
918 if ($objMod->version != 'development' && ($search_version == 'development')) {
919 continue;
920 }
921 if ($objMod->version != 'experimental' && ($search_version == 'experimental')) {
922 continue;
923 }
924 if (!preg_match('/deprecated/', $objMod->version) && ($search_version == 'deprecated')) {
925 continue;
926 }
927 }
928
929 $atleastonequalified++;
930
931 // Load all language files of the qualified module
932 if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
933 foreach ($objMod->langfiles as $domain) {
934 $langs->load($domain);
935 }
936 }
937
938 // Print a separator if we change family
939 if ($familykey != $oldfamily) {
940 if ($oldfamily) {
941 print '</table></div><br>';
942 }
943
944 $familytext = empty($familyinfo[$familykey]['label']) ? $familykey : $familyinfo[$familykey]['label'];
945
946 print load_fiche_titre($familytext, '', '', 0, '', 'modulefamilygroup');
947
948 if ($mode == 'commonkanban') {
949 print '<div class="box-flex-container kanban">';
950 } else {
951 print '<div class="div-table-responsive">';
952 print '<table class="tagtable liste" summary="list_of_modules">'."\n";
953 }
954
955 $atleastoneforfamily = 0;
956 }
957
958 $atleastoneforfamily++;
959
960 if ($familykey != $oldfamily) {
961 $familytext = empty($familyinfo[$familykey]['label']) ? $familykey : $familyinfo[$familykey]['label'];
962 $oldfamily = $familykey;
963 }
964
965 // Version (with picto warning or not)
966 $version = $objMod->getVersion(0);
967 $versiontrans = '';
968 $warningstring = '';
969 if (preg_match('/development/i', $version)) {
970 $warningstring = $langs->trans("Development");
971 }
972 if (preg_match('/experimental/i', $version)) {
973 $warningstring = $langs->trans("Experimental");
974 }
975 if (preg_match('/deprecated/i', $version)) {
976 $warningstring = $langs->trans("Deprecated");
977 }
978
979 if ($objMod->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
980 $versiontrans .= $objMod->getVersion(1);
981 }
982
983 if ($objMod->isCoreOrExternalModule() == 'external' && ($action == 'checklastversion' || getDolGlobalString('CHECKLASTVERSION_EXTERNALMODULE'))) {
984 // Setting CHECKLASTVERSION_EXTERNALMODULE to on is a bad practice to activate a check on an external access during the building of the admin page.
985 // 1 external module can hang the application.
986 // Adding a cron job could be a good idea: see DolibarrModules::checkForUpdate()
987 $checkRes = $objMod->checkForUpdate();
988 if ($checkRes > 0) {
989 setEventMessages($objMod->getName().' : '.preg_replace('/[^a-z0-9_\.\-\s]/i', '', $versiontrans).' -> '.preg_replace('/[^a-z0-9_\.\-\s]/i', '', $objMod->lastVersion), null, 'warnings');
990 } elseif ($checkRes < 0) {
991 setEventMessages($objMod->getName().' '.$langs->trans('CheckVersionFail'), null, 'errors');
992 }
993 }
994
995 if ($objMod->isCoreOrExternalModule() == 'external' && $action == 'checklastversion' && !getDolGlobalString('DISABLE_CHECK_ON_MALWARE_MODULES')) {
996 $checkRes = $objMod->checkForCompliance(); // Check if module is reported as non compliant with Dolibarr rules and law
997 if (!is_numeric($checkRes) && $checkRes != '') {
998 $langs->load("errors");
999 setEventMessages($objMod->getName().' : '.$langs->trans($checkRes), null, 'errors');
1000 }
1001 }
1002
1003 // Define imginfo
1004 $imginfo = "info";
1005 if ($objMod->isCoreOrExternalModule() == 'external') {
1006 $imginfo = "info_black";
1007 }
1008
1009 $codeenabledisable = '';
1010 $codetoconfig = '';
1011
1012 // Force disable of module disabled into session (for demo for example)
1013 if (in_array($modulenameshort, $disabled_modules)) {
1014 $objMod->disabled = true;
1015 }
1016
1017 // Activate/Disable and Setup (2 columns)
1018 if (getDolGlobalString($const_name)) { // If module is already activated
1019 // Set $codeenabledisable
1020 $disableSetup = 0;
1021 if (!empty($arrayofwarnings[$modName])) {
1022 $codeenabledisable .= '<!-- This module has a warning to show when we activate it (note: your country is '.$mysoc->country_code.') -->'."\n";
1023 }
1024
1025 if (!empty($objMod->disabled)) {
1026 $codeenabledisable .= $langs->trans("Disabled");
1027 } elseif (is_object($objMod)
1028 && (!empty($objMod->always_enabled) || ((isModEnabled('multicompany') && $objMod->core_enabled) && ($user->entity || $conf->entity != 1)))) {
1029 // @phan-suppress-next-line PhanUndeclaredMethod
1030 if (method_exists($objMod, 'alreadyUsed') && $objMod->alreadyUsed()) {
1031 $codeenabledisable .= $langs->trans("Used");
1032 } else {
1033 $codeenabledisable .= img_picto($langs->trans("Required"), 'switch_on', '', 0, 0, 0, '', 'opacitymedium valignmiddle');
1034 //print $langs->trans("Required");
1035 }
1036 if (isModEnabled('multicompany') && $user->entity) {
1037 $disableSetup++;
1038 }
1039 } else {
1040 // @phan-suppress-next-line PhanUndeclaredMethod
1041 if (is_object($objMod) && !empty($objMod->warnings_unactivation[$mysoc->country_code]) && method_exists($objMod, 'alreadyUsed') && $objMod->alreadyUsed()) {
1042 $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.'">';
1043 $codeenabledisable .= img_picto($langs->trans("Activated").($warningstring ? ' '.$warningstring : ''), 'switch_on');
1044 $codeenabledisable .= '</a>';
1045 if (getDolGlobalInt("MAIN_FEATURES_LEVEL") > 1) {
1046 $codeenabledisable .= '&nbsp;';
1047 $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.'">';
1048 $codeenabledisable .= img_picto($langs->trans("Reload"), 'refresh', 'class="opacitymedium"');
1049 $codeenabledisable .= '</a>';
1050 }
1051 } else {
1052 $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.'">';
1053 $codeenabledisable .= img_picto($langs->trans("Activated").($warningstring ? ' '.$warningstring : ''), 'switch_on');
1054 $codeenabledisable .= '</a>';
1055 if (getDolGlobalInt("MAIN_FEATURES_LEVEL") > 1) {
1056 $codeenabledisable .= '&nbsp;';
1057 $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.'">';
1058 $codeenabledisable .= img_picto($langs->trans("Reload"), 'refresh', 'class="opacitymedium"');
1059 $codeenabledisable .= '</a>';
1060 }
1061 }
1062 }
1063
1064 // Set $codetoconfig
1065 if (!empty($objMod->config_page_url) && !$disableSetup) {
1066 $backtourlquery = [];
1067 if ($search_keyword != '') {
1068 $backtourlquery += ['search_keyword' => $search_keyword]; // No urlencode here, done later
1069 }
1070 if ($search_nature > -1) {
1071 $backtourlquery += ['search_nature' => $search_nature]; // No urlencode here, done later
1072 }
1073 if ($search_version > -1) {
1074 $backtourlquery += ['search_version' => $search_version]; // No urlencode here, done later
1075 }
1076 if ($search_status > -1) {
1077 $backtourlquery += ['search_status' => $search_status]; // No urlencode here, done later
1078 }
1079 $backtourl = dolBuildUrl($_SERVER["PHP_SELF"], $backtourlquery);
1080
1081 $regs = array();
1082 $query = [
1083 'save_lastsearch_values' => 1,
1084 'backtopage' => $backtourl,
1085 ];
1086 if (is_array($objMod->config_page_url)) {
1087 $i = 0;
1088 foreach ($objMod->config_page_url as $page) {
1089 $urlpage = $page;
1090 if ($i++) {
1091 $codetoconfig .= '<a href="'.$urlpage.'" title="'.$langs->trans($page).'">'.img_picto(ucfirst($page), "setup").'</a>';
1092 // print '<a href="'.$page.'">'.ucfirst($page).'</a>&nbsp;';
1093 } else {
1094 if (preg_match('/^([^@]+)@([^@]+)$/i', $urlpage, $regs)) {
1095 $urltouse = dol_buildpath('/'.$regs[2].'/admin/'.$regs[1], 1);
1096 $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"', 0, 0, 0, '', 'fa-15').'</a>';
1097 } else {
1098 $urltouse = $urlpage;
1099 $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"', 0, 0, 0, '', 'fa-15').'</a>';
1100 }
1101 }
1102 }
1103 } elseif (preg_match('/^([^@]+)@([^@]+)$/i', (string) $objMod->config_page_url, $regs)) {
1104 $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"', 0, 0, 0, '', 'fa-15').'</a>';
1105 } else {
1106 $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"', 0, 0, 0, '', 'fa-15').'</a>';
1107 }
1108 } else {
1109 $codetoconfig .= img_picto($langs->trans("NothingToSetup"), "setup", 'class="opacitytransp" style="padding-right: 6px"', 0, 0, 0, '', 'fa-15');
1110 }
1111 } else { // Module not yet activated
1112 // Set $codeenabledisable
1113 if (!empty($objMod->always_enabled)) {
1114 // A 'always_enabled' module should not never be disabled. If this happen, we keep a link to re-enable it.
1115 $codeenabledisable .= '<!-- Message to show: an always_enabled module has been disabled -->'."\n";
1116 $codeenabledisable .= '<a class="reposition" id="idalways'.$objMod->numero.'" data-alreadyclicked="0" href="'.$_SERVER["PHP_SELF"].'?id='.$objMod->numero.'&token='.newToken().'&module_position='.$module_position.'&action=set&token='.newToken().'&value='.$modName.'&mode='.$mode.$param.'"';
1117 $codeenabledisable .= '>';
1118 $codeenabledisable .= img_picto($langs->trans("Disabled"), 'switch_off');
1119 $codeenabledisable .= "</a>\n";
1120 } elseif (!empty($objMod->disabled)) {
1121 $codeenabledisable .= $langs->trans("Disabled");
1122 } else {
1123 // Module qualified for activation
1124 $warningmessage = '';
1125 $disableCancel = 0;
1126
1127 if (!empty($arrayofwarnings[$modName])) {
1128 $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";
1129 foreach ($arrayofwarnings[$modName] as $keycountry => $cursorwarningmessage) {
1130 if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^'.$mysoc->country_code.'/', $keycountry))) {
1131 if (!is_array($cursorwarningmessage)) {
1132 $cursorwarningmessage = array($cursorwarningmessage);
1133 }
1134 foreach ($cursorwarningmessage as $messagetoshow) {
1135 if (preg_match('/:1$/', $messagetoshow)) {
1136 $disableCancel = 1;
1137 }
1138 $messagetoshow = preg_replace('/:1$/', '', $messagetoshow);
1139
1140 // TODO Use a replacement instead of always adding the module name and the country code to the string message ?
1141 $warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($messagetoshow, $objMod->getName(), $mysoc->country_code);
1142 }
1143 }
1144 }
1145 }
1146 if ($objMod->isCoreOrExternalModule() == 'external' && !empty($arrayofwarningsext)) {
1147 $codeenabledisable .= '<!-- This module is an external module and it may have a warning to show (note: your country is '.$mysoc->country_code.') -->'."\n";
1148 foreach ($arrayofwarningsext as $keymodule => $arrayofwarningsextbycountry) {
1149 $keymodulelowercase = strtolower(preg_replace('/^mod/', '', $keymodule));
1150 if (preg_match('/^always/', $keymodulelowercase) || in_array($keymodulelowercase, $conf->modules)) { // If module that trigger the warning is on
1151 foreach ($arrayofwarningsextbycountry as $keycountry => $cursorwarningmessage) {
1152 if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^'.$mysoc->country_code.'/', $keycountry))) {
1153 if (!is_array($cursorwarningmessage)) {
1154 $cursorwarningmessage = array($cursorwarningmessage);
1155 }
1156 foreach ($cursorwarningmessage as $messagetoshow) {
1157 // TODO Use replacement instead of always adding param module name to enable and country code to the string message and triggering module
1158 $warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($messagetoshow, $objMod->getName(), $mysoc->country_code, $modules[$keymodule]->getName());
1159 }
1160 $warningmessage .= ($warningmessage ? "\n" : "").($warningmessage ? "\n" : "").$langs->trans("Module").' : '.$objMod->getName();
1161 if (!empty($objMod->editor_name)) {
1162 $warningmessage .= ($warningmessage ? "\n" : "").$langs->trans("Publisher").' : '.$objMod->editor_name;
1163 }
1164 if ($keymodulelowercase != 'always') {
1165 $warningmessage .= ($warningmessage ? "\n" : "").$langs->trans("ModuleTriggeringThisWarning").' : '.$modules[$keymodule]->getName();
1166 }
1167 }
1168 }
1169 }
1170 }
1171 }
1172
1173 $urltogo = $_SERVER["PHP_SELF"].'?id='.$objMod->numero.'&token='.newToken().'&module_position='.$module_position.'&action=set&token='.newToken().'&value='.$modName.'&mode='.$mode.$param;
1174 $popupWidth = 500;
1175 $popupHeight = 300;
1176 $codeenabledisable .= '<!-- Message to show: '.$warningmessage.' -->'."\n";
1177 $codeenabledisable .= '<a class="reposition" id="idqualified'.$objMod->numero.'" data-alreadyclicked="0" href="'.$urltogo.'"';
1178 if ($warningmessage) {
1179 $codeenabledisable .= ' onclick="return confirmDolibarr(\''.dol_escape_js($warningmessage).'\', \'idqualified'.$objMod->numero.'\', '.$popupWidth.', '.$popupHeight.','.$disableCancel.');"';
1180 }
1181 $codeenabledisable .= '>';
1182 $codeenabledisable .= img_picto($langs->trans("Disabled"), 'switch_off');
1183 $codeenabledisable .= "</a>\n";
1184 }
1185
1186 // Set $codetoconfig
1187 $codetoconfig .= img_picto($langs->trans("NothingToSetup"), "setup", 'class="opacitytransp" style="padding-right: 6px"');
1188 }
1189
1190 if ($mode == 'commonkanban') {
1191 // Output Kanban
1192 print $objMod->getKanbanView($codeenabledisable, $codetoconfig);
1193 } else {
1194 print '<tr class="oddeven'.($warningstring ? ' info-box-content-warning' : '').'">'."\n";
1195 if (getDolGlobalString('MAIN_MODULES_SHOW_LINENUMBERS')) {
1196 print '<td class="width50">'.$linenum.'</td>';
1197 }
1198
1199 // Picto + Name of module
1200 print ' <td class="tdoverflowmax200 minwidth200imp" title="'.dol_escape_htmltag($objMod->getName()).'">';
1201 $alttext = '';
1202 //if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
1203 //if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
1204 if (!empty($objMod->picto)) {
1205 if (preg_match('/^\//i', $objMod->picto)) {
1206 print img_picto($alttext, $objMod->picto, 'class="valignmiddle pictomodule paddingrightonly"', 1);
1207 } else {
1208 print img_object($alttext, $objMod->picto, 'class="valignmiddle pictomodule paddingrightonly"');
1209 }
1210 } else {
1211 print img_object($alttext, 'generic', 'class="valignmiddle paddingrightonly"');
1212 }
1213 print ' <span class="valignmiddle">'.$objMod->getName().'</span>';
1214 print "</td>\n";
1215
1216 // Desc
1217 print '<td class="valignmiddle tdoverflowmax300 minwidth200imp opacitylow">';
1218 print nl2br($objMod->getDesc());
1219 print "</td>\n";
1220
1221 // Help
1222 print '<td class="center nowrap" style="width: 82px;">';
1223 print '<a href="javascript:document_preview(\''.DOL_URL_ROOT.'/admin/modulehelp.php?id='.((int) $objMod->numero).'\',\'text/html\',\''.dol_escape_js($langs->trans("Module")).'\')">';
1224 print img_picto(($objMod->isCoreOrExternalModule() == 'external' ? $langs->trans("ExternalModule").' - ' : '').$langs->trans("ClickToShowDescription"), $imginfo, '', 0, 0, 0, '', 'purple');
1225 print '</a>';
1226 print '</td>';
1227
1228 // Version
1229 print '<td class="center nowrap width150" title="'.dol_escape_htmltag(dol_string_nohtmltag($versiontrans)).'">';
1230 if ($objMod->needUpdate) {
1231 $versionTitle = $langs->trans('ModuleUpdateAvailable').' : '.$objMod->lastVersion;
1232 print '<span class="badge badge-warning classfortooltip" title="'.dol_escape_htmltag($versionTitle).'">'.$versiontrans.'</span>';
1233 } else {
1234 print $versiontrans;
1235 }
1236 print "</td>\n";
1237
1238 // Link enable/disable
1239 print '<td class="center valignmiddle left nowraponall" width="60px">';
1240 print $codeenabledisable;
1241 print "</td>\n";
1242
1243 // Link config
1244 print '<td class="tdsetuppicto right valignmiddle" width="60px">';
1245 print $codetoconfig;
1246 print '</td>';
1247
1248 print "</tr>\n";
1249 }
1250 if ($objMod->needUpdate) {
1251 $foundoneexternalmodulewithupdate++;
1252 }
1253 }
1254
1255 if ($action == 'checklastversion') {
1256 if ($foundoneexternalmodulewithupdate) {
1257 setEventMessages($langs->trans("ModuleUpdateAvailable"), null, 'warnings', '', 0, 1);
1258 } else {
1259 setEventMessages($langs->trans("NoExternalModuleWithUpdate"), null, 'mesgs');
1260 }
1261 }
1262
1263 if ($oldfamily) {
1264 if ($mode == 'commonkanban') {
1265 print '</div>';
1266 } else {
1267 print "</table>\n";
1268 print '</div>';
1269 }
1270 }
1271
1272 if (!$atleastonequalified) {
1273 print '<br><span class="opacitymedium">'.$langs->trans("NoDeployedModulesFoundWithThisSearchCriteria").'</span><br><br>';
1274 }
1275
1276 print dol_get_fiche_end();
1277
1278 print '<br>';
1279
1280 // Show warning about external users
1281 print info_admin(showModulesExludedForExternal($modules))."\n";
1282
1283 print '</form>';
1284}
1285
1286if ($mode == 'marketplace') {
1287 print dol_get_fiche_head($head, $mode, '', -1);
1288
1289 print $deschelp;
1290
1291 print '<br>';
1292
1293 print '<!-- summary of sources -->';
1294
1295 // Marketplace and community modules
1296 print '<div class="div-table-responsive-no-min">';
1297 print '<table summary="list_of_modules" class="noborder centpercent">'."\n";
1298 print '<tr class="liste_titre">'."\n";
1299 print '<td colspan="2">'.$form->textwithpicto($langs->trans("ModuleProviderSites"), $langs->trans("WebSiteDesc")).'</td>';
1300 print '<td class="hideonsmartphone">';
1301 print '</td>';
1302 print '<td></td>';
1303 print '</tr>';
1304
1305 $url = 'https://www.dolistore.com';
1306
1307 // Source Marketplace DoliStore
1308 print '<tr class="oddeven nohover" height="100">'."\n";
1309 print '<td class="hideonsmartphone center width150 nopaddingleftimp nopaddingrightimp"><a href="'.$url.'" target="_blank" rel="noopener noreferrer external"><img border="0" class="imgautosize imgmaxwidth100" src="'.DOL_URL_ROOT.'/theme/dolistore_logo.svg"></a></td>';
1310 print '<td class="minwidth500imp smallonsmartphone"><span class="opacitymedium">'.$langs->trans("DoliStoreDesc").'</span><br>';
1311 print img_picto('', 'url', 'class="pictofixedwidth"').'<a href="'.$url.'" target="_blank" rel="noopener noreferrer external">'.$url.'</a></td>';
1312 print '<td>';
1313 print ajax_constantonoff('MAIN_ENABLE_EXTERNALMODULES_DOLISTORE', array(), null, 0, 0, 1);
1314 print '</td>';
1315 print '<td class="center">';
1316 if (!getDolGlobalString('MAIN_DISABLE_EXTERNALMODULES_DOLISTORE') && getDolGlobalInt('MAIN_ENABLE_EXTERNALMODULES_DOLISTORE')) {
1317 $messagetoadd = '<br><span class="small">';
1318 if ($remotestore->dolistoreApiStatus <= 0) {
1319 $messagetoadd = '<br>'.$remotestore->dolistoreApiError.'<br>Failed to get answer of remote API server<br>';
1320 }
1321
1322 $messagetoadd .= '<br>Using Shop address MAIN_MODULE_DOLISTORE_SHOP_URL = '.$remotestore->shop_url;
1323 $messagetoadd .= '<br>Using Remote API address MAIN_MODULE_DOLISTORE_API_URL = '.$remotestore->dolistore_api_url;
1324 $messagetoadd .= '<br>Using API public key MAIN_MODULE_DOLISTORE_API_KEY = '.$remotestore->dolistore_api_key;
1325 // Add basic auth if needed
1326 $basicAuthLogin = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_LOGIN');
1327 $basicAuthPassword = getDolGlobalString('MAIN_MODULE_DOLISTORE_BASIC_PASSWORD');
1328 if ($basicAuthLogin) {
1329 $messagetoadd .= '<br>Using basic auth login: base64('.$basicAuthLogin.':'.$basicAuthPassword.')';
1330 }
1331 $messagetoadd .= '</span>';
1332
1333 print $remotestore->libStatus($remotestore->dolistoreApiStatus, 2, $messagetoadd);
1334 }
1335 print '</td>';
1336 print '</tr>';
1337
1338 $url = 'https://github.com/Dolibarr/dolibarr-community-modules';
1339
1340 // Source Community github
1341 print '<tr class="oddeven nohover" height="100">'."\n";
1342 print '<td class="hideonsmartphone center width150 nopaddingleftimp nopaddingrightimp"><a href="'.$url.'" target="_blank" rel="noopener noreferrer external"><img border="0" class="imgautosize imgmaxwidth100" src="'.DOL_URL_ROOT.'/theme/dolibarr_logo.svg"></a></td>';
1343 print '<td class="minwidth500imp smallonsmartphone"><span class="opacitymedium">'.$langs->trans("CommunityModulesDesc").'</span><br>';
1344 print img_picto('', 'url', 'class="pictofixedwidth"').'<a href="'.$url.'" target="_blank" rel="noopener noreferrer external">'.$url.'</a></td>';
1345 print '<td>';
1346 print ajax_constantonoff('MAIN_ENABLE_EXTERNALMODULES_COMMUNITY', array(), null, 0, 0, 1);
1347 print '</td>';
1348 print '<td class="center">';
1349 if (!getDolGlobalString('MAIN_DISABLE_EXTERNALMODULES_COMMUNITY') && getDolGlobalInt('MAIN_ENABLE_EXTERNALMODULES_COMMUNITY')) {
1350 $messagetoadd = '<br><br><span class="small">Content of the repository index file '.$remotestore->file_source_url.' should be in the local cache file '.$remotestore->cache_file;
1351 $messagetoadd .= ' (Date: '.dol_print_date(dol_filemtime($remotestore->cache_file), 'dayhour', 'tzuserrel').')</span>';
1352 if ($remotestore->githubFileError) {
1353 $messagetoadd .= '<br><span class="error small">'.$remotestore->githubFileError.'</span>';
1354 }
1355 print $remotestore->libStatus($remotestore->githubFileStatus, 2, $messagetoadd);
1356 }
1357 print '</td>';
1358 print '</tr>';
1359
1360 print "</table>\n";
1361 print '</div>';
1362
1363 print dol_get_fiche_end();
1364
1365 print '<br>';
1366
1367 if ($remotestore->numberOfProviders > 0) {
1368 // $options is array with filter criteria
1369 $nbmaxtoshow = $options['per_page'];
1370 $options['per_page']++;
1371
1372 //$remotestore->getRemoteCategories();
1373 //$remotestore->getRemoteProducts($options);
1374
1375 //print '<span class="opacitymedium hideonsmartphone">'.$langs->trans('DOLISTOREdescriptionLong').'</span><br><br>';
1376
1377 $categories_tree = $remotestore->getCategories($options['categorie']); // Call API to get the categories
1378
1379 $products_list = $remotestore->getProducts($options); // Get list of product from all sources
1380
1381 $previouslink = $remotestore->get_previous_link();
1382
1383 $nextlink = $remotestore->get_next_link();
1384
1385
1386 print '<div class="liste_titre liste_titre_bydiv centpercent"><div class="">';
1387
1388 print '<form method="POST" class="centpercent" id="searchFormList" action="'.$remotestore->url.'">'; ?>
1389 <input type="hidden" name="token" value="<?php echo newToken(); ?>">
1390 <input type="hidden" name="mode" value="marketplace">
1391 <input type="hidden" name="page_y" value="">
1392 <div class="divsearchfield">
1393 <input name="search_keyword" placeholder="<?php echo $langs->trans('Keyword') ?>" id="search_keyword" type="text" class="minwidth200" value="<?php echo dolPrintHTMLForAttribute($options['search']) ?>" spellcheck="false">
1394 </div>
1395 <div class="divsearchfield">
1396 <input name="buttonsubmit" class="button buttongen reposition" value="<?php echo $langs->trans('Search') ?>" type="submit">
1397 <?php
1398 print $form->textwithpicto('', $langs->trans('DOLISTOREdescriptionLong'));
1399
1400 if ($search_keyword !== '') {
1401 print '<a class="buttonreset reposition" href="'.$_SERVER["PHP_SELF"].'?mode=marketplace">'.$langs->trans('Reset').'</a>';
1402 }
1403 ?>
1404 &nbsp;
1405 </div>
1406 <?php
1407 $totalnboflines = '<span class="product-count opacitymedium paddingleft">';
1408 $totalnboflines .= $langs->trans("itemFound", $remotestore->numberTotalOfProducts);
1409 $totalnboflines .= '</span>';
1410
1411 print $totalnboflines;
1412 print $remotestore->getPagination();
1413 print '</form>';
1414
1415 print '</div>';
1416 print '<div class="clearboth"></div>';
1417 print '</div>';
1418 ?>
1419 <?php if (!empty($categories_tree)) { ?>
1420 <div id="category-tree-left" class="paddingtop">
1421 <ul class="tree">
1422 <?php
1423 print $categories_tree; ?>
1424 </ul>
1425 </div>
1426 <?php } ?>
1427
1428 <div id="listing-content" class="div-table-responsive" <?php if (empty($categories_tree)) { ?>style="width:100%;"<?php } ?>>
1429 <table summary="list_of_modules" id="list_of_modules" class="productlist centpercent">
1430 <tbody id="listOfModules">
1431 <!-- $product_list is $remotestore->getProducts($options) done previously -->
1432 <?php print $products_list; ?>
1433 </tbody>
1434 </table>
1435 </div>
1436 <div style="clear: both;"></div>
1437 <div><?php print $remotestore->getPagination(); ?></div>
1438 <?php
1439 }
1440}
1441
1442
1443// Form to install an external module
1444
1445if ($mode == 'deploy') {
1446 print dol_get_fiche_head($head, $mode, '', -1);
1447
1448 $fullurl = '<a href="'.$urldolibarrmodules.'" target="_blank" rel="noopener noreferrer">'.$urldolibarrmodules.'</a>';
1449 $message = '';
1450 if ($allowonlineinstall) {
1451 if (!in_array('/custom', explode(',', $dolibarr_main_url_root_alt))) {
1452 $message = info_admin($langs->trans("ConfFileMustContainCustom", DOL_DOCUMENT_ROOT.'/custom', DOL_DOCUMENT_ROOT));
1453 $allowfromweb = -1;
1454 } else {
1455 if ($dirins_ok) {
1456 if (!is_writable(dol_osencode($dirins))) {
1457 $langs->load("errors");
1458 $message = info_admin($langs->trans("ErrorFailedToWriteInDir", $dirins), 0, 0, '1', 'warning');
1459 $allowfromweb = 0;
1460 }
1461 } else {
1462 $message = info_admin($langs->trans("NotExistsDirect", $dirins).$langs->trans("InfDirAlt").$langs->trans("InfDirExample"));
1463 $allowfromweb = 0;
1464 }
1465 }
1466 } else {
1467 if (getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')) {
1468 // Show clean message
1469 if (!is_numeric(getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US'))) {
1470 $message = info_admin($langs->trans(getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')), 0, 0, 'warning');
1471 } else {
1472 $message = info_admin($langs->trans('InstallModuleFromWebHasBeenDisabledContactUs'), 0, 0, 'warning');
1473 }
1474 } else {
1475 // Show technical message
1476 $message = info_admin($langs->trans("InstallModuleFromWebHasBeenDisabledByFile", $dolibarrdataroot.'/installmodules.lock'), 0, 0, 'warning');
1477 }
1478 $allowfromweb = 0;
1479 }
1480
1481 print $deschelp;
1482
1483 if ($allowfromweb < 1) {
1484 print $langs->trans("SomethingMakeInstallFromWebNotPossible");
1485 print $message;
1486 //print $langs->trans("SomethingMakeInstallFromWebNotPossible2");
1487 print '<br>';
1488 }
1489
1490 // $allowfromweb = -1 if installation or setup not correct, 0 if not allowed, 1 if allowed
1491 if ($allowfromweb >= 0) {
1492 if ($allowfromweb == 1) {
1493 //print $langs->trans("ThisIsProcessToFollow").'<br>';
1494 } else {
1495 print '<br>';
1496
1497 print $langs->trans("ThisIsAlternativeProcessToFollow").'<br>';
1498 print '<b>'.$langs->trans("StepNb", 1).'</b>: ';
1499 print str_replace('{s1}', $fullurl, $langs->trans("FindPackageFromWebSite", '{s1}')).'<br>';
1500 print '<b>'.$langs->trans("StepNb", 2).'</b>: ';
1501 print str_replace('{s1}', $fullurl, $langs->trans("DownloadPackageFromWebSite", '{s1}')).'<br>';
1502 print '<b>'.$langs->trans("StepNb", 3).'</b>: ';
1503 }
1504
1505 if ($allowfromweb == 1) {
1506 print '<form enctype="multipart/form-data" method="POST" class="noborder" action="'.$_SERVER["PHP_SELF"].'" name="forminstall">';
1507 print '<input type="hidden" name="token" value="'.newToken().'">';
1508 print '<input type="hidden" name="action" value="install">';
1509 print '<input type="hidden" name="mode" value="deploy">';
1510
1511 print $langs->trans("YouCanSubmitFile").'<br><br><br>';
1512
1513 print '<span class="opacitymedium"><input class="paddingright" type="checkbox" name="checkforcompliance" id="checkforcompliance"'.(getDolGlobalString('DISABLE_CHECK_ON_MALWARE_MODULES') ? ' disabled="disabled"' : 'checked="checked"').'>';
1514 print '<label for="checkforcompliance">'.$form->textwithpicto($langs->trans("CheckIfModuleIsNotBlackListed"), $langs->trans("CheckIfModuleIsNotBlackListedHelp")).'</label>';
1515 print '</span><br><br>';
1516
1517 $max = getDolGlobalString('MAIN_UPLOAD_DOC'); // In Kb
1518 $maxphp = @ini_get('upload_max_filesize'); // In unknown
1519 if (preg_match('/k$/i', $maxphp)) {
1520 $maxphp = preg_replace('/k$/i', '', $maxphp);
1521 $maxphp *= 1;
1522 }
1523 if (preg_match('/m$/i', $maxphp)) {
1524 $maxphp = preg_replace('/m$/i', '', $maxphp);
1525 $maxphp *= 1024;
1526 }
1527 if (preg_match('/g$/i', $maxphp)) {
1528 $maxphp = preg_replace('/g$/i', '', $maxphp);
1529 $maxphp *= 1024 * 1024;
1530 }
1531 if (preg_match('/t$/i', $maxphp)) {
1532 $maxphp = preg_replace('/t$/i', '', $maxphp);
1533 $maxphp *= 1024 * 1024 * 1024;
1534 }
1535 $maxphp2 = @ini_get('post_max_size'); // In unknown
1536 if (preg_match('/k$/i', $maxphp2)) {
1537 $maxphp2 = preg_replace('/k$/i', '', $maxphp2);
1538 $maxphp2 *= 1;
1539 }
1540 if (preg_match('/m$/i', $maxphp2)) {
1541 $maxphp2 = preg_replace('/m$/i', '', $maxphp2);
1542 $maxphp2 *= 1024;
1543 }
1544 if (preg_match('/g$/i', $maxphp2)) {
1545 $maxphp2 = preg_replace('/g$/i', '', $maxphp2);
1546 $maxphp2 *= 1024 * 1024;
1547 }
1548 if (preg_match('/t$/i', $maxphp2)) {
1549 $maxphp2 = preg_replace('/t$/i', '', $maxphp2);
1550 $maxphp2 *= 1024 * 1024 * 1024;
1551 }
1552 // Now $max and $maxphp and $maxphp2 are in Kb
1553 $maxmin = $max;
1554 $maxphptoshow = $maxphptoshowparam = '';
1555 if ($maxphp > 0) {
1556 $maxmin = min($max, $maxphp);
1557 $maxphptoshow = $maxphp;
1558 $maxphptoshowparam = 'upload_max_filesize';
1559 }
1560 if ($maxphp2 > 0) {
1561 $maxmin = min($max, $maxphp2);
1562 if ($maxphp2 < $maxphp) {
1563 $maxphptoshow = $maxphp2;
1564 $maxphptoshowparam = 'post_max_size';
1565 }
1566 }
1567
1568 if ($maxmin > 0) {
1569 print '<script type="text/javascript">
1570 $(document).ready(function() {
1571 jQuery("#fileinstall").on("change", function() {
1572 if(this.files[0].size > '.($maxmin * 1024).') {
1573 alert("'.dol_escape_js($langs->transnoentitiesnoconv("ErrorFileSizeTooLarge")).'");
1574 this.value = "";
1575 }
1576 });
1577 });
1578 </script>'."\n";
1579 // MAX_FILE_SIZE doit précéder le champ input de type file
1580 print '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">';
1581 }
1582
1583 print '<input class="flat minwidth400" type="file" name="fileinstall" id="fileinstall">';
1584
1585 print '<input type="submit" name="send" value="'.dol_escape_htmltag($langs->trans("Upload")).'" class="button small">';
1586
1587 if (getDolGlobalString('MAIN_UPLOAD_DOC')) {
1588 if ($user->admin) {
1589 $langs->load('other');
1590 print ' ';
1591 print info_admin($langs->trans("ThisLimitIsDefinedInSetup", $max, $maxphptoshow, $maxphptoshowparam), 1);
1592 }
1593 } else {
1594 print ' ('.$langs->trans("UploadDisabled").')';
1595 }
1596
1597 print '</form>';
1598
1599 print '<br>';
1600 print '<br>';
1601
1602 print '<div class="center"><div class="logo_setup"></div></div>';
1603 } else {
1604 print $langs->trans("UnpackPackageInModulesRoot", $dirins).'<br>';
1605 print '<b>'.$langs->trans("StepNb", 4).'</b>: ';
1606 print $langs->trans("SetupIsReadyForUse", DOL_URL_ROOT.'/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home").' - '.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("Modules")).'<br>';
1607 }
1608 }
1609
1610 print dol_get_fiche_end();
1611}
1612
1613if ($mode == 'develop') {
1614 print dol_get_fiche_head($head, $mode, '', -1);
1615
1616 print $deschelp;
1617
1618 print '<br>';
1619
1620 // Marketplace
1621 print '<div class="div-table-responsive-no-min">';
1622 print '<table summary="list_of_modules" class="noborder centpercent">'."\n";
1623 print '<tr class="liste_titre">'."\n";
1624 print '<td colspan="3">'.$langs->trans("DevelopYourModuleDesc").'</td>';
1625 print '</tr>';
1626
1627 print '<tr class="oddeven nohover" height="100">'."\n";
1628 print '<td class="center hideonsmartphone">';
1629 print '<div class="imgmaxheight50 logo_setup"></div>';
1630 print '</td>';
1631 print '<td class="minwidth500imp smallonsmartphone">'.$langs->trans("TryToUseTheModuleBuilder", $langs->transnoentitiesnoconv("ModuleBuilder")).'</td>';
1632 print '<td class="maxwidth300">';
1633 if (isModEnabled('modulebuilder')) {
1634 print $langs->trans("SeeTopRightMenu");
1635 } else {
1636 print '<span class="opacitymedium">'.$langs->trans("ModuleMustBeEnabledFirst", $langs->transnoentitiesnoconv("ModuleBuilder")).'</span>';
1637 }
1638 print '</td>';
1639 print '</tr>';
1640
1641 print '<tr class="oddeven nohover" height="100">'."\n";
1642 $url = 'https://partners.dolibarr.org';
1643 print '<td class="center hideonsmartphone">';
1644 print'<a href="'.$url.'" target="_blank" rel="noopener noreferrer external"><img border="0" class="imgautosize imgmaxwidth180" src="'.DOL_URL_ROOT.'/theme/dolibarr_preferred_partner.png"></a>';
1645 print '</td>';
1646 print '<td class="minwidth500imp smallonsmartphone">'.$langs->trans("DoliPartnersDesc").'</td>';
1647 print '<td><a href="'.$url.'" target="_blank" rel="noopener noreferrer external">';
1648 print img_picto('', 'url', 'class="pictofixedwidth"');
1649 print $url.'</a></td>';
1650 print '</tr>';
1651
1652 print "</table>\n";
1653 print '</div>';
1654
1655 print dol_get_fiche_end();
1656}
1657
1658// End of page
1659llxFooter();
1660$db->close();
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
modules_prepare_head($nbofactivatedmodules, $nboftotalmodules, $nbmodulesnotautoenabled)
Prepare array with list of tabs.
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class DolibarrModules.
Events class.
Class ExternalModules.
Class to manage generation of HTML components Only common components must be here.
global $mysoc
document_preview(file, type, title)
Function to show a document preview popup.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_uncompress($inputfile, $outputdir)
Uncompress a file.
dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists, $arrayreplacement=null, $excludesubdir=0, $excludefileext=null, $excludearchivefiles=0)
Copy a dir to another dir.
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $nohook=0, $keyforsourcefile='addedfile', $upload_dir='', $mode=0)
Check validity of a file upload from an GUI page, and move it to its final destination.
dolGetModulesDirs($subdir='')
Return list of directories that contain modules.
dol_now($mode='gmt')
Return date for now.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dolBuildUrl($url, $params=[], $addtoken=false)
Return path of url.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
setEventMessage($mesgs, $style='mesgs', $noduplicate=0, $attop=0)
Set event message in dol_events session object.
dol_sanitizePathName($str, $newstr='_', $unaccent=0, $allowdash=0)
Clean a string to use it as a path name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
treeview li table
No Email.
a disabled
httponly_accessforbidden($message='1', $http_response_code=403, $stringalreadysanitized=0)
Show a message to say access is forbidden and stop program.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.