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