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-2017	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		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 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26/**
27 *  \file       htdocs/admin/modules.php
28 *  \brief      Page to activate/disable all modules
29 */
30
31if (!defined('CSRFCHECK_WITH_TOKEN') && (empty($_GET['action']) || $_GET['action'] != 'reset')) {	// We do not force security to disable modules so we can do it if problem
32	define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET
33}
34
35require '../main.inc.php';
36require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
37require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
38require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
39require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
40require_once DOL_DOCUMENT_ROOT.'/admin/dolistore/class/dolistore.class.php';
41
42// Load translation files required by the page
43$langs->loadLangs(array("errors", "admin", "modulebuilder"));
44
45$mode = GETPOSTISSET('mode') ? GETPOST('mode', 'alpha') : (empty($conf->global->MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT) ? 'commonkanban' : 'common');
46if (empty($mode)) {
47	$mode = 'common';
48}
49$action = GETPOST('action', 'aZ09');
50//var_dump($_POST);exit;
51$value = GETPOST('value', 'alpha');
52$page_y = GETPOST('page_y', 'int');
53$search_keyword = GETPOST('search_keyword', 'alpha');
54$search_status = GETPOST('search_status', 'alpha');
55$search_nature = GETPOST('search_nature', 'alpha');
56$search_version = GETPOST('search_version', 'alpha');
57
58
59// For dolistore search
60$options              = array();
61$options['per_page']  = 20;
62$options['categorie'] = ((GETPOST('categorie', 'int') ?GETPOST('categorie', 'int') : 0) + 0);
63$options['start']     = ((GETPOST('start', 'int') ?GETPOST('start', 'int') : 0) + 0);
64$options['end']       = ((GETPOST('end', 'int') ?GETPOST('end', 'int') : 0) + 0);
65$options['search']    = GETPOST('search_keyword', 'alpha');
66$dolistore            = new Dolistore(false);
67
68
69if (!$user->admin) {
70	accessforbidden();
71}
72
73$familyinfo = array(
74	'hr'=>array('position'=>'001', 'label'=>$langs->trans("ModuleFamilyHr")),
75	'crm'=>array('position'=>'006', 'label'=>$langs->trans("ModuleFamilyCrm")),
76	'srm'=>array('position'=>'007', 'label'=>$langs->trans("ModuleFamilySrm")),
77	'financial'=>array('position'=>'009', 'label'=>$langs->trans("ModuleFamilyFinancial")),
78	'products'=>array('position'=>'012', 'label'=>$langs->trans("ModuleFamilyProducts")),
79	'projects'=>array('position'=>'015', 'label'=>$langs->trans("ModuleFamilyProjects")),
80	'ecm'=>array('position'=>'018', 'label'=>$langs->trans("ModuleFamilyECM")),
81	'technic'=>array('position'=>'021', 'label'=>$langs->trans("ModuleFamilyTechnic")),
82	'portal'=>array('position'=>'040', 'label'=>$langs->trans("ModuleFamilyPortal")),
83	'interface'=>array('position'=>'050', 'label'=>$langs->trans("ModuleFamilyInterface")),
84	'base'=>array('position'=>'060', 'label'=>$langs->trans("ModuleFamilyBase")),
85	'other'=>array('position'=>'100', 'label'=>$langs->trans("ModuleFamilyOther")),
86);
87
88$param = '';
89if (!GETPOST('buttonreset', 'alpha')) {
90	if ($search_keyword) {
91		$param .= '&search_keyword='.urlencode($search_keyword);
92	}
93	if ($search_status && $search_status != '-1') {
94		$param .= '&search_status='.urlencode($search_status);
95	}
96	if ($search_nature && $search_nature != '-1') {
97		$param .= '&search_nature='.urlencode($search_nature);
98	}
99	if ($search_version && $search_version != '-1') {
100		$param .= '&search_version='.urlencode($search_version);
101	}
102}
103
104$dirins = DOL_DOCUMENT_ROOT.'/custom';
105$urldolibarrmodules = 'https://www.dolistore.com/';
106
107// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
108$hookmanager->initHooks(array('adminmodules', 'globaladmin'));
109
110
111/*
112 * Actions
113 */
114
115$formconfirm = '';
116
117$parameters = array();
118$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
119if ($reshook < 0) {
120	setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
121}
122
123if (GETPOST('buttonreset', 'alpha')) {
124	$search_keyword = '';
125	$search_status = '';
126	$search_nature = '';
127	$search_version = '';
128}
129
130if ($action == 'install') {
131	$error = 0;
132
133	// $original_file should match format module_modulename-x.y[.z].zip
134	$original_file = basename($_FILES["fileinstall"]["name"]);
135	$original_file = preg_replace('/\(\d+\)\.zip$/i', '.zip', $original_file);
136	$newfile = $conf->admin->dir_temp.'/'.$original_file.'/'.$original_file;
137
138	if (!$original_file) {
139		$langs->load("Error");
140		setEventMessages($langs->trans("ErrorModuleFileRequired"), null, 'warnings');
141		$error++;
142	} else {
143		if (!$error && !preg_match('/\.zip$/i', $original_file)) {
144			$langs->load("errors");
145			setEventMessages($langs->trans("ErrorFileMustBeADolibarrPackage", $original_file), null, 'errors');
146			$error++;
147		}
148		if (!$error && !preg_match('/^(module[a-zA-Z0-9]*|theme)_.*\-([0-9][0-9\.]*)\.zip$/i', $original_file)) {
149			$langs->load("errors");
150			setEventMessages($langs->trans("ErrorFilenameDosNotMatchDolibarrPackageRules", $original_file, 'module_*-x.y*.zip'), null, 'errors');
151			$error++;
152		}
153		if (empty($_FILES['fileinstall']['tmp_name'])) {
154			$langs->load("errors");
155			setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
156			$error++;
157		}
158	}
159
160	if (!$error) {
161		if ($original_file) {
162			@dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$original_file);
163			dol_mkdir($conf->admin->dir_temp.'/'.$original_file);
164		}
165
166		$tmpdir = preg_replace('/\.zip$/i', '', $original_file).'.dir';
167		if ($tmpdir) {
168			@dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$tmpdir);
169			dol_mkdir($conf->admin->dir_temp.'/'.$tmpdir);
170		}
171
172		$result = dol_move_uploaded_file($_FILES['fileinstall']['tmp_name'], $newfile, 1, 0, $_FILES['fileinstall']['error']);
173		if ($result > 0) {
174			$result = dol_uncompress($newfile, $conf->admin->dir_temp.'/'.$tmpdir);
175
176			if (!empty($result['error'])) {
177				$langs->load("errors");
178				setEventMessages($langs->trans($result['error'], $original_file), null, 'errors');
179				$error++;
180			} else {
181				// Now we move the dir of the module
182				$modulename = preg_replace('/module_/', '', $original_file);
183				$modulename = preg_replace('/\-([0-9][0-9\.]*)\.zip$/i', '', $modulename);
184				// Search dir $modulename
185				$modulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulename; // Example ./mymodule
186
187				if (!dol_is_dir($modulenamedir)) {
188					$modulenamedir = $conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulename; // Example ./htdocs/mymodule
189					//var_dump($modulenamedir);
190					if (!dol_is_dir($modulenamedir)) {
191						setEventMessages($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat").'<br>'.$langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat2", $modulename, 'htdocs/'.$modulename), null, 'errors');
192						$error++;
193					}
194				}
195
196				if (!$error) {
197					// TODO Make more test
198				}
199
200				dol_syslog("Uncompress of module file is a success.");
201
202				$modulenamearrays = array();
203				if (dol_is_file($modulenamedir.'/metapackage.conf')) {
204					// This is a meta package
205					$metafile = file_get_contents($modulenamedir.'/metapackage.conf');
206					$modulenamearrays = explode("\n", $metafile);
207				}
208				$modulenamearrays[$modulename] = $modulename;
209				//var_dump($modulenamearrays);exit;
210
211				foreach ($modulenamearrays as $modulenameval) {
212					if (strpos($modulenameval, '#') === 0) {
213						continue; // Discard comments
214					}
215					if (strpos($modulenameval, '//') === 0) {
216						continue; // Discard comments
217					}
218					if (!trim($modulenameval)) {
219						continue;
220					}
221
222					// Now we install the module
223					if (!$error) {
224						@dol_delete_dir_recursive($dirins.'/'.$modulenameval); // delete the zip file
225						dol_syslog("We copy now directory ".$conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulenameval." into target dir ".$dirins.'/'.$modulenameval);
226						$result = dolCopyDir($modulenamedir, $dirins.'/'.$modulenameval, '0444', 1);
227						if ($result <= 0) {
228							dol_syslog('Failed to call dolCopyDir result='.$result." with param ".$modulenamedir." and ".$dirins.'/'.$modulenameval, LOG_WARNING);
229							$langs->load("errors");
230							setEventMessages($langs->trans("ErrorFailToCopyDir", $modulenamedir, $dirins.'/'.$modulenameval), null, 'errors');
231							$error++;
232						}
233					}
234				}
235			}
236		} else {
237			setEventMessages($langs->trans("ErrorFailToRenameFile", $_FILES['fileinstall']['tmp_name'], $newfile), null, 'errors');
238			$error++;
239		}
240	}
241
242	if (!$error) {
243		setEventMessages($langs->trans("SetupIsReadyForUse", DOL_URL_ROOT.'/admin/modules.php?mainmenu=home', $langs->transnoentitiesnoconv("Home").' - '.$langs->transnoentitiesnoconv("Setup").' - '.$langs->transnoentitiesnoconv("Modules")), null, 'warnings');
244	}
245}
246
247if ($action == 'set' && $user->admin) {
248	$resarray = activateModule($value);
249	if (!empty($resarray['errors'])) {
250		setEventMessages('', $resarray['errors'], 'errors');
251	} else {
252		//var_dump($resarray);exit;
253		if ($resarray['nbperms'] > 0) {
254			$tmpsql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX."user WHERE admin <> 1";
255			$resqltmp = $db->query($tmpsql);
256			if ($resqltmp) {
257				$obj = $db->fetch_object($resqltmp);
258				//var_dump($obj->nb);exit;
259				if ($obj && $obj->nb > 1) {
260					$msg = $langs->trans('ModuleEnabledAdminMustCheckRights');
261					setEventMessages($msg, null, 'warnings');
262				}
263			} else {
264				dol_print_error($db);
265			}
266		}
267	}
268	header("Location: ".$_SERVER["PHP_SELF"]."?mode=".$mode.$param.($page_y ? '&page_y='.$page_y : ''));
269	exit;
270} elseif ($action == 'reset' && $user->admin && GETPOST('confirm') == 'yes') {
271	$result = unActivateModule($value);
272	if ($result) {
273		setEventMessages($result, null, 'errors');
274	}
275	header("Location: ".$_SERVER["PHP_SELF"]."?mode=".$mode.$param.($page_y ? '&page_y='.$page_y : ''));
276	exit;
277}
278
279
280
281/*
282 * View
283 */
284
285$form = new Form($db);
286
287$morejs = array();
288$morecss = array("/admin/dolistore/css/dolistore.css");
289
290// Set dir where external modules are installed
291if (!dol_is_dir($dirins)) {
292	dol_mkdir($dirins);
293}
294$dirins_ok = (dol_is_dir($dirins));
295
296$help_url = 'EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones';
297llxHeader('', $langs->trans("Setup"), $help_url, '', '', '', $morejs, $morecss, 0, 0);
298
299
300// Search modules dirs
301$modulesdir = dolGetModulesDirs();
302
303$arrayofnatures = array('core'=>$langs->transnoentitiesnoconv("Core"), 'external'=>$langs->transnoentitiesnoconv("External").' - ['.$langs->trans("AllPublishers").']');
304$arrayofwarnings = array(); // Array of warning each module want to show when activated
305$arrayofwarningsext = array(); // Array of warning each module want to show when we activate an external module
306$filename = array();
307$modules = array();
308$orders = array();
309$categ = array();
310
311$i = 0; // is a sequencer of modules found
312$j = 0; // j is module number. Automatically affected if module number not defined.
313$modNameLoaded = array();
314
315foreach ($modulesdir as $dir) {
316	// Load modules attributes in arrays (name, numero, orders) from dir directory
317	//print $dir."\n<br>";
318	dol_syslog("Scan directory ".$dir." for module descriptor files (modXXX.class.php)");
319	$handle = @opendir($dir);
320	if (is_resource($handle)) {
321		while (($file = readdir($handle)) !== false) {
322			//print "$i ".$file."\n<br>";
323			if (is_readable($dir.$file) && substr($file, 0, 3) == 'mod' && substr($file, dol_strlen($file) - 10) == '.class.php') {
324				$modName = substr($file, 0, dol_strlen($file) - 10);
325
326				if ($modName) {
327					if (!empty($modNameLoaded[$modName])) {   // In cache of already loaded modules ?
328						$mesg = "Error: Module ".$modName." was found twice: Into ".$modNameLoaded[$modName]." and ".$dir.". You probably have an old file on your disk.<br>";
329						setEventMessages($mesg, null, 'warnings');
330						dol_syslog($mesg, LOG_ERR);
331						continue;
332					}
333
334					try {
335						$res = include_once $dir.$file; // A class already exists in a different file will send a non catchable fatal error.
336						if (class_exists($modName)) {
337							try {
338								$objMod = new $modName($db);
339								$modNameLoaded[$modName] = $dir;
340								if (!$objMod->numero > 0 && $modName != 'modUser') {
341									dol_syslog('The module descriptor '.$modName.' must have a numero property', LOG_ERR);
342								}
343								$j = $objMod->numero;
344
345								$modulequalified = 1;
346
347								// We discard modules according to features level (PS: if module is activated we always show it)
348								$const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
349								if ($objMod->version == 'development' && (empty($conf->global->$const_name) && ($conf->global->MAIN_FEATURES_LEVEL < 2))) {
350									$modulequalified = 0;
351								}
352								if ($objMod->version == 'experimental' && (empty($conf->global->$const_name) && ($conf->global->MAIN_FEATURES_LEVEL < 1))) {
353									$modulequalified = 0;
354								}
355								if (preg_match('/deprecated/', $objMod->version) && (empty($conf->global->$const_name) && ($conf->global->MAIN_FEATURES_LEVEL >= 0))) {
356									$modulequalified = 0;
357								}
358
359								// We discard modules according to property ->hidden
360								if (!empty($objMod->hidden)) {
361									$modulequalified = 0;
362								}
363
364								if ($modulequalified > 0) {
365									$publisher = dol_escape_htmltag($objMod->getPublisher());
366									$external = ($objMod->isCoreOrExternalModule() == 'external');
367									if ($external) {
368										if ($publisher) {
369											$arrayofnatures['external_'.$publisher] = $langs->trans("External").' - '.$publisher;
370										} else {
371											$arrayofnatures['external_'] = $langs->trans("External").' - '.$langs->trans("UnknownPublishers");
372										}
373									}
374									ksort($arrayofnatures);
375
376									// Define array $categ with categ with at least one qualified module
377									$filename[$i] = $modName;
378									$modules[$modName] = $objMod;
379
380									// Gives the possibility to the module, to provide his own family info and position of this family
381									if (is_array($objMod->familyinfo) && !empty($objMod->familyinfo)) {
382										$familyinfo = array_merge($familyinfo, $objMod->familyinfo);
383										$familykey = key($objMod->familyinfo);
384									} else {
385										$familykey = $objMod->family;
386									}
387
388									$moduleposition = ($objMod->module_position ? $objMod->module_position : '50');
389									if ($moduleposition == '50' && ($objMod->isCoreOrExternalModule() == 'external')) {
390										$moduleposition = '80'; // External modules at end by default
391									}
392
393									// Add list of warnings to show into arrayofwarnings and arrayofwarningsext
394									if (!empty($objMod->warnings_activation)) {
395										$arrayofwarnings[$modName] = $objMod->warnings_activation;
396									}
397									if (!empty($objMod->warnings_activation_ext)) {
398										$arrayofwarningsext[$modName] = $objMod->warnings_activation_ext;
399									}
400
401									$familyposition = (empty($familyinfo[$familykey]['position']) ? 0 : $familyinfo[$familykey]['position']);
402									$listOfOfficialModuleGroups = array('hr', 'technic', 'interface', 'technic', 'portal', 'financial', 'crm', 'base', 'products', 'srm', 'ecm', 'projects', 'other');
403									if ($external && !in_array($familykey, $listOfOfficialModuleGroups)) {
404										// 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).
405										if (is_numeric($familyposition)) {
406											$familyposition = sprintf("%03d", (int) $familyposition + 100);
407										}
408									}
409
410									$orders[$i] = $familyposition."_".$familykey."_".$moduleposition."_".$j; // Sort by family, then by module position then number
411
412									// Set categ[$i]
413									$specialstring = 'unknown';
414									if ($objMod->version == 'development' || $objMod->version == 'experimental') {
415										$specialstring = 'expdev';
416									}
417									if (isset($categ[$specialstring])) {
418										$categ[$specialstring]++; // Array of all different modules categories
419									} else {
420										$categ[$specialstring] = 1;
421									}
422									$j++;
423									$i++;
424								} else {
425									dol_syslog("Module ".get_class($objMod)." not qualified");
426								}
427							} catch (Exception $e) {
428								 dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR);
429							}
430						} else {
431							print "Warning bad descriptor file : ".$dir.$file." (Class ".$modName." not found into file)<br>";
432						}
433					} catch (Exception $e) {
434						 dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR);
435					}
436				}
437			}
438		}
439		closedir($handle);
440	} else {
441		dol_syslog("htdocs/admin/modules.php: Failed to open directory ".$dir.". See permission and open_basedir option.", LOG_WARNING);
442	}
443}
444
445if ($action == 'reset_confirm' && $user->admin) {
446	if (!empty($modules[$value])) {
447		$objMod = $modules[$value];
448
449		if (!empty($objMod->langfiles)) {
450			$langs->loadLangs($objMod->langfiles);
451		}
452
453		$form = new Form($db);
454		$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?value='.$value.'&mode='.$mode.$param, $langs->trans('ConfirmUnactivation'), $langs->trans(GETPOST('confirm_message_code')), 'reset', '', 'no', 1);
455	}
456}
457
458print $formconfirm;
459
460asort($orders);
461//var_dump($orders);
462//var_dump($categ);
463//var_dump($modules);
464
465$nbofactivatedmodules = count($conf->modules);
466$moreinfo = $langs->trans("TitleNumberOfActivatedModules");
467$moreinfo2 = ($nbofactivatedmodules - 1)." / ".count($modules);
468if ($nbofactivatedmodules <= 1) {
469	$moreinfo2 .= ' '.img_warning($langs->trans("YouMustEnableOneModule"));
470}
471
472print load_fiche_titre($langs->trans("ModulesSetup"), '', 'title_setup');
473
474// Start to show page
475if ($mode == 'common' || $mode == 'commonkanban') {
476	$desc = $langs->trans("ModulesDesc", '{picto}');
477	$desc = str_replace('{picto}', img_picto('', 'switch_off'), $desc);
478	print '<span class="opacitymedium hideonsmartphone">'.$desc."<br><br></span>\n";
479}
480if ($mode == 'marketplace') {
481	print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("ModulesMarketPlaceDesc")."<br><br></span>\n";
482}
483if ($mode == 'deploy') {
484	print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("ModulesDeployDesc", $langs->transnoentitiesnoconv("AvailableModules"))."<br><br></span>\n";
485}
486if ($mode == 'develop') {
487	print '<span class="opacitymedium hideonsmartphone">'.$langs->trans("ModulesDevelopDesc")."<br><br></span>\n";
488}
489
490
491$head = modules_prepare_head();
492
493
494if ($mode == 'common' || $mode == 'commonkanban') {
495	dol_set_focus('#search_keyword');
496
497	print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
498	if ($optioncss != '') {
499		print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
500	}
501	print '<input type="hidden" name="token" value="'.newToken().'">';
502	print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
503	print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
504	print '<input type="hidden" name="page" value="'.$page.'">';
505	print '<input type="hidden" name="mode" value="'.$mode.'">';
506
507	print dol_get_fiche_head($head, 'modules', '', -1);
508
509	$moreforfilter = '<div class="valignmiddle">';
510
511	$moreforfilter .= '<div class="floatright right pagination"><ul><li>';
512	$moreforfilter .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', $_SERVER["PHP_SELF"].'?mode=commonkanban'.$param, '', 1, array('morecss'=>'reposition'.($mode == 'common' ? '' : ' btnTitleSelected')));
513	$moreforfilter .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-list-alt imgforviewmode', $_SERVER["PHP_SELF"].'?mode=common'.$param, '', 1, array('morecss'=>'reposition'.($mode == 'commonkanban' ? '' : ' btnTitleSelected')));
514	$moreforfilter .= '</li></ul></div>';
515
516	$moreforfilter .= '<div class="floatright center marginrightonly hideonsmartphone" style="padding-top: 3px"><span class="">'.$moreinfo.'</span><br><b class="largenumber">'.$moreinfo2.'</b></div>';
517
518	$moreforfilter .= '<div class="colorbacktimesheet float valignmiddle">';
519	$moreforfilter .= '<div class="divsearchfield paddingtop">';
520	$moreforfilter .= $langs->trans('Keyword').': <input type="text" id="search_keyword" name="search_keyword" class="maxwidth100" value="'.dol_escape_htmltag($search_keyword).'">';
521	$moreforfilter .= '</div>';
522	$moreforfilter .= '<div class="divsearchfield paddingtop">';
523	$moreforfilter .= $langs->trans('Origin').': '.$form->selectarray('search_nature', $arrayofnatures, dol_escape_htmltag($search_nature), 1, 0, 0, '', 0, 0, 0, '', 'maxwidth200', 1);
524	$moreforfilter .= '</div>';
525	if (!empty($conf->global->MAIN_FEATURES_LEVEL)) {
526		$array_version = array('stable'=>$langs->transnoentitiesnoconv("Stable"));
527		if ($conf->global->MAIN_FEATURES_LEVEL < 0) {
528			$array_version['deprecated'] = $langs->trans("Deprecated");
529		}
530		if ($conf->global->MAIN_FEATURES_LEVEL > 0) {
531			$array_version['experimental'] = $langs->trans("Experimental");
532		}
533		if ($conf->global->MAIN_FEATURES_LEVEL > 1) {
534			$array_version['development'] = $langs->trans("Development");
535		}
536		$moreforfilter .= '<div class="divsearchfield paddingtop">';
537		$moreforfilter .= $langs->trans('Version').': '.$form->selectarray('search_version', $array_version, $search_version, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1);
538		$moreforfilter .= '</div>';
539	}
540	$moreforfilter .= '<div class="divsearchfield paddingtop">';
541	$moreforfilter .= $langs->trans('Status').': '.$form->selectarray('search_status', array('active'=>$langs->transnoentitiesnoconv("Enabled"), 'disabled'=>$langs->transnoentitiesnoconv("Disabled")), $search_status, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth150', 1);
542	$moreforfilter .= '</div>';
543	$moreforfilter .= ' ';
544	$moreforfilter .= '<div class="divsearchfield">';
545	$moreforfilter .= '<input type="submit" name="buttonsubmit" class="button" value="'.dol_escape_htmltag($langs->trans("Refresh")).'">';
546	$moreforfilter .= ' ';
547	$moreforfilter .= '<input type="submit" name="buttonreset" class="butActionDelete noborderbottom" value="'.dol_escape_htmltag($langs->trans("Reset")).'">';
548	$moreforfilter .= '</div>';
549	$moreforfilter .= '</div>';
550
551	$moreforfilter .= '</div>';
552
553	if (!empty($moreforfilter)) {
554		print $moreforfilter;
555		$parameters = array();
556		$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
557		print $hookmanager->resPrint;
558	}
559
560	$moreforfilter = '';
561
562	print '<div class="clearboth"></div><br>';
563
564	$object = new stdClass();
565	$parameters = array();
566	$reshook = $hookmanager->executeHooks('insertExtraHeader', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
567	if ($reshook < 0) {
568		setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
569	}
570
571	// Show list of modules
572	$oldfamily = '';
573	$linenum = 0;
574
575	foreach ($orders as $key => $value) {
576		$linenum++;
577		$tab = explode('_', $value);
578		$familykey = $tab[1];
579		$module_position = $tab[2];
580
581		$modName = $filename[$key];
582		$objMod = $modules[$modName];
583
584		//print $objMod->name." - ".$key." - ".$objMod->version."<br>";
585		if ($mode == 'expdev' && $objMod->version != 'development' && $objMod->version != 'experimental') {
586			continue; // Discard if not for current tab
587		}
588
589		if (!$objMod->getName()) {
590			dol_syslog("Error for module ".$key." - Property name of module looks empty", LOG_WARNING);
591			continue;
592		}
593
594		$const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
595
596		// Check filters
597		$modulename = $objMod->getName();
598		$moduletechnicalname = $objMod->name;
599		$moduledesc = $objMod->getDesc();
600		$moduledesclong = $objMod->getDescLong();
601		$moduleauthor = $objMod->getPublisher();
602
603		// We discard showing according to filters
604		if ($search_keyword) {
605			$qualified = 0;
606			if (preg_match('/'.preg_quote($search_keyword).'/i', $modulename)
607				|| preg_match('/'.preg_quote($search_keyword).'/i', $moduletechnicalname)
608				|| preg_match('/'.preg_quote($search_keyword).'/i', $moduledesc)
609				|| preg_match('/'.preg_quote($search_keyword).'/i', $moduledesclong)
610				|| preg_match('/'.preg_quote($search_keyword).'/i', $moduleauthor)
611				) {
612				$qualified = 1;
613			}
614			if (!$qualified) {
615				continue;
616			}
617		}
618		if ($search_status) {
619			if ($search_status == 'active' && empty($conf->global->$const_name)) {
620				continue;
621			}
622			if ($search_status == 'disabled' && !empty($conf->global->$const_name)) {
623				continue;
624			}
625		}
626		if ($search_nature) {
627			if (preg_match('/^external/', $search_nature) && $objMod->isCoreOrExternalModule() != 'external') {
628				continue;
629			}
630			$reg = array();
631			if (preg_match('/^external_(.*)$/', $search_nature, $reg)) {
632				//print $reg[1].'-'.dol_escape_htmltag($objMod->getPublisher());
633				$publisher = dol_escape_htmltag($objMod->getPublisher());
634				if ($reg[1] && dol_escape_htmltag($reg[1]) != $publisher) {
635					continue;
636				}
637				if (!$reg[1] && !empty($publisher)) {
638					continue;
639				}
640			}
641			if ($search_nature == 'core' && $objMod->isCoreOrExternalModule() == 'external') {
642				continue;
643			}
644		}
645		if ($search_version) {
646			if (($objMod->version == 'development' || $objMod->version == 'experimental' || preg_match('/deprecated/', $objMod->version)) && $search_version == 'stable') {
647				continue;
648			}
649			if ($objMod->version != 'development' && ($search_version == 'development')) {
650				continue;
651			}
652			if ($objMod->version != 'experimental' && ($search_version == 'experimental')) {
653				continue;
654			}
655			if (!preg_match('/deprecated/', $objMod->version) && ($search_version == 'deprecated')) {
656				continue;
657			}
658		}
659
660		// Load all lang files of module
661		if (isset($objMod->langfiles) && is_array($objMod->langfiles)) {
662			foreach ($objMod->langfiles as $domain) {
663				$langs->load($domain);
664			}
665		}
666
667		// Print a separator if we change family
668		if ($familykey != $oldfamily) {
669			if ($oldfamily) {
670				print '</table></div><br>';
671			}
672
673			$familytext = empty($familyinfo[$familykey]['label']) ? $familykey : $familyinfo[$familykey]['label'];
674
675			print load_fiche_titre($familytext, '', '', 0, '', 'modulefamilygroup');
676
677			if ($mode == 'commonkanban') {
678				print '<div class="box-flex-container">';
679			} else {
680				print '<div class="div-table-responsive">';
681				print '<table class="tagtable liste" summary="list_of_modules">'."\n";
682			}
683
684			$atleastoneforfamily = 0;
685		}
686
687		$atleastoneforfamily++;
688
689		if ($familykey != $oldfamily) {
690			$familytext = empty($familyinfo[$familykey]['label']) ? $familykey : $familyinfo[$familykey]['label'];
691			$oldfamily = $familykey;
692		}
693
694		// Version (with picto warning or not)
695		$version = $objMod->getVersion(0);
696		$versiontrans = '';
697		if (preg_match('/development/i', $version)) {
698			$versiontrans .= img_warning($langs->trans("Development"), '', 'floatleft paddingright');
699		}
700		if (preg_match('/experimental/i', $version)) {
701			$versiontrans .= img_warning($langs->trans("Experimental"), '', 'floatleft paddingright');
702		}
703		if (preg_match('/deprecated/i', $version)) {
704			$versiontrans .= img_warning($langs->trans("Deprecated"), '', 'floatleft paddingright');
705		}
706		if ($objMod->isCoreOrExternalModule() == 'external' || preg_match('/development|experimental|deprecated/i', $version)) {
707			$versiontrans .= $objMod->getVersion(1);
708		}
709
710		// Define imginfo
711		$imginfo = "info";
712		if ($objMod->isCoreOrExternalModule() == 'external') {
713			$imginfo = "info_black";
714		}
715
716		$codeenabledisable = '';
717		$codetoconfig = '';
718
719		// Activate/Disable and Setup (2 columns)
720		if (!empty($conf->global->$const_name)) {	// If module is already activated
721			// Set $codeenabledisable
722			$disableSetup = 0;
723			if (!empty($arrayofwarnings[$modName])) {
724				$codeenabledisable .= '<!-- This module has a warning to show when we activate it (note: your country is '.$mysoc->country_code.') -->'."\n";
725			}
726			if (!empty($objMod->disabled)) {
727				$codeenabledisable .= $langs->trans("Disabled");
728			} elseif (!empty($objMod->always_enabled) || ((!empty($conf->multicompany->enabled) && $objMod->core_enabled) && ($user->entity || $conf->entity != 1))) {
729				if (method_exists($objMod, 'alreadyUsed') && $objMod->alreadyUsed()) {
730					$codeenabledisable .= $langs->trans("Used");
731				} else {
732					$codeenabledisable .= img_picto($langs->trans("Required"), 'switch_on', '', false, 0, 0, '', 'opacitymedium valignmiddle');
733					//print $langs->trans("Required");
734				}
735				if (!empty($conf->multicompany->enabled) && $user->entity) {
736					$disableSetup++;
737				}
738			} else {
739				if (!empty($objMod->warnings_unactivation[$mysoc->country_code]) && method_exists($objMod, 'alreadyUsed') && $objMod->alreadyUsed()) {
740					$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='.$objMod->warnings_unactivation[$mysoc->country_code].'&amp;value='.$modName.'&amp;mode='.$mode.$param.'">';
741					$codeenabledisable .= img_picto($langs->trans("Activated"), 'switch_on');
742					$codeenabledisable .= '</a>';
743				} else {
744					$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.'">';
745					$codeenabledisable .= img_picto($langs->trans("Activated"), 'switch_on');
746					$codeenabledisable .= '</a>';
747				}
748			}
749
750			// Set $codetoconfig
751			if (!empty($objMod->config_page_url) && !$disableSetup) {
752				$backtourlparam = '';
753				if ($search_keyword != '') {
754					$backtourlparam .= ($backtourlparam ? '&' : '?').'search_keyword='.$search_keyword; // No urlencode here, done later
755				}
756				if ($search_nature > -1) {
757					$backtourlparam .= ($backtourlparam ? '&' : '?').'search_nature='.$search_nature;
758				}
759				if ($search_version > -1) {
760					$backtourlparam .= ($backtourlparam ? '&' : '?').'search_version='.$search_version;
761				}
762				if ($search_status > -1) {
763					$backtourlparam .= ($backtourlparam ? '&' : '?').'search_status='.$search_status;
764				}
765				$backtourl = $_SERVER["PHP_SELF"].$backtourlparam;
766
767				$regs = array();
768				if (is_array($objMod->config_page_url)) {
769					$i = 0;
770					foreach ($objMod->config_page_url as $page) {
771						$urlpage = $page;
772						if ($i++) {
773							$codetoconfig .= '<a href="'.$urlpage.'" title="'.$langs->trans($page).'">'.img_picto(ucfirst($page), "setup").'</a>';
774							//    print '<a href="'.$page.'">'.ucfirst($page).'</a>&nbsp;';
775						} else {
776							if (preg_match('/^([^@]+)@([^@]+)$/i', $urlpage, $regs)) {
777								$urltouse = dol_buildpath('/'.$regs[2].'/admin/'.$regs[1], 1);
778								$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>';
779							} else {
780								$urltouse = $urlpage;
781								$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>';
782							}
783						}
784					}
785				} elseif (preg_match('/^([^@]+)@([^@]+)$/i', $objMod->config_page_url, $regs)) {
786					$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>';
787				} else {
788					$codetoconfig .= '<a class="valignmiddle" href="'.$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>';
789				}
790			} else {
791				$codetoconfig .= img_picto($langs->trans("NothingToSetup"), "setup", 'class="opacitytransp" style="padding-right: 6px"', false, 0, 0, '', 'fa-15');
792			}
793		} else // Module not yet activated
794		{
795			// Set $codeenabledisable
796			if (!empty($objMod->always_enabled)) {
797				// Should never happened
798			} elseif (!empty($objMod->disabled)) {
799				$codeenabledisable .= $langs->trans("Disabled");
800			} else {
801				// Module qualified for activation
802				$warningmessage = '';
803				if (!empty($arrayofwarnings[$modName])) {
804					$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";
805					foreach ($arrayofwarnings[$modName] as $keycountry => $cursorwarningmessage) {
806						if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^'.$mysoc->country_code.'/', $keycountry))) {
807							$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($cursorwarningmessage, $objMod->getName(), $mysoc->country_code);
808						}
809					}
810				}
811				if ($objMod->isCoreOrExternalModule() == 'external' && !empty($arrayofwarningsext)) {
812					$codeenabledisable .= '<!-- This module is an external module and it may have a warning to show (note: your country is '.$mysoc->country_code.') -->'."\n";
813					foreach ($arrayofwarningsext as $keymodule => $arrayofwarningsextbycountry) {
814						$keymodulelowercase = strtolower(preg_replace('/^mod/', '', $keymodule));
815						if (in_array($keymodulelowercase, $conf->modules)) {    // If module that request warning is on
816							foreach ($arrayofwarningsextbycountry as $keycountry => $cursorwarningmessage) {
817								if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^'.$mysoc->country_code.'/', $keycountry))) {
818									$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($cursorwarningmessage, $objMod->getName(), $mysoc->country_code, $modules[$keymodule]->getName());
819									$warningmessage .= ($warningmessage ? "\n" : "").($warningmessage ? "\n" : "").$langs->trans("Module").' : '.$objMod->getName();
820									if (!empty($objMod->editor_name)) {
821										$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans("Publisher").' : '.$objMod->editor_name;
822									}
823									if (!empty($objMod->editor_name)) {
824										$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans("ModuleTriggeringThisWarning").' : '.$modules[$keymodule]->getName();
825									}
826								}
827							}
828						}
829					}
830				}
831				$codeenabledisable .= '<!-- Message to show: '.$warningmessage.' -->'."\n";
832				$codeenabledisable .= '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$objMod->numero.'&amp;token='.newToken().'&amp;module_position='.$module_position.'&amp;action=set&amp;value='.$modName.'&amp;mode='.$mode.$param.'"';
833				if ($warningmessage) {
834					$codeenabledisable .= ' onclick="return confirm(\''.dol_escape_js($warningmessage).'\');"';
835				}
836				$codeenabledisable .= '>';
837				$codeenabledisable .= img_picto($langs->trans("Disabled"), 'switch_off');
838				$codeenabledisable .= "</a>\n";
839			}
840
841			// Set $codetoconfig
842			$codetoconfig .= img_picto($langs->trans("NothingToSetup"), "setup", 'class="opacitytransp" style="padding-right: 6px"');
843		}
844
845		if ($mode == 'commonkanban') {
846			// Output Kanban
847			print $objMod->getKanbanView($codeenabledisable, $codetoconfig);
848		} else {
849			print '<tr class="oddeven">'."\n";
850			if (!empty($conf->global->MAIN_MODULES_SHOW_LINENUMBERS)) {
851				print '<td width="20px">'.$linenum.'</td>';
852			}
853
854			// Picto + Name of module
855			print '  <td width="200px">';
856			$alttext = '';
857			//if (is_array($objMod->need_dolibarr_version)) $alttext.=($alttext?' - ':'').'Dolibarr >= '.join('.',$objMod->need_dolibarr_version);
858			//if (is_array($objMod->phpmin)) $alttext.=($alttext?' - ':'').'PHP >= '.join('.',$objMod->phpmin);
859			if (!empty($objMod->picto)) {
860				if (preg_match('/^\//i', $objMod->picto)) {
861					print img_picto($alttext, $objMod->picto, 'class="valignmiddle pictomodule paddingrightonly"', 1);
862				} else {
863					print img_object($alttext, $objMod->picto, 'class="valignmiddle pictomodule paddingrightonly"');
864				}
865			} else {
866				print img_object($alttext, 'generic', 'class="valignmiddle paddingrightonly"');
867			}
868			print ' <span class="valignmiddle">'.$objMod->getName().'</span>';
869			print "</td>\n";
870
871			// Desc
872			print '<td class="valignmiddle tdoverflowmax300">';
873			print nl2br($objMod->getDesc());
874			print "</td>\n";
875
876			// Help
877			print '<td class="center nowrap" style="width: 82px;">';
878			//print $form->textwithpicto('', $text, 1, $imginfo, 'minheight20', 0, 2, 1);
879			print '<a href="javascript:document_preview(\''.DOL_URL_ROOT.'/admin/modulehelp.php?id='.$objMod->numero.'\',\'text/html\',\''.dol_escape_js($langs->trans("Module")).'\')">'.img_picto(($objMod->isCoreOrExternalModule() == 'external' ? $langs->trans("ExternalModule").' - ' : '').$langs->trans("ClickToShowDescription"), $imginfo).'</a>';
880			print '</td>';
881
882			// Version
883			print '<td class="center nowrap" width="120px">';
884			print $versiontrans;
885			if (!empty($conf->global->CHECKLASTVERSION_EXTERNALMODULE)) {	// This is a bad practice to activate a synch external access during building of a page. 1 external module can hang the application.
886				require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
887				if (!empty($objMod->url_last_version)) {
888					$newversion = getURLContent($objMod->url_last_version);
889					if (isset($newversion['content'])) {
890						if (version_compare($newversion['content'], $versiontrans) > 0) {
891							print "&nbsp;<span class='butAction' title='".$langs->trans('LastStableVersion')."'>".$newversion['content']."</span>";
892						}
893					}
894				}
895			}
896			print "</td>\n";
897
898			// Link enable/disable
899			print '<td class="center valignmiddle" width="60px">';
900			print $codeenabledisable;
901			print "</td>\n";
902
903			// Link config
904			print '<td class="tdsetuppicto right valignmiddle" width="60px">';
905			print $codetoconfig;
906			print '</td>';
907
908			print "</tr>\n";
909		}
910	}
911
912	if ($oldfamily) {
913		if ($mode == 'commonkanban') {
914			print '</div>';
915		} else {
916			print "</table>\n";
917			print '</div>';
918		}
919	}
920
921	print dol_get_fiche_end();
922
923	print '<br>';
924
925	// Show warning about external users
926	print info_admin(showModulesExludedForExternal($modules))."\n";
927
928	print '</form>';
929}
930
931if ($mode == 'marketplace') {
932	print dol_get_fiche_head($head, $mode, '', -1);
933
934	// Marketplace
935	print '<div class="div-table-responsive-no-min">';
936	print '<table summary="list_of_modules" class="noborder centpercent">'."\n";
937	print '<tr class="liste_titre">'."\n";
938	print '<td class="hideonsmartphone">'.$form->textwithpicto($langs->trans("Provider"), $langs->trans("WebSiteDesc")).'</td>';
939	print '<td></td>';
940	print '<td>'.$langs->trans("URL").'</td>';
941	print '</tr>';
942
943	print '<tr class="oddeven">'."\n";
944	$url = 'https://www.dolistore.com';
945	print '<td class="hideonsmartphone"><a href="'.$url.'" target="_blank" rel="external"><img border="0" class="imgautosize imgmaxwidth180" src="'.DOL_URL_ROOT.'/theme/dolistore_logo.png"></a></td>';
946	print '<td>'.$langs->trans("DoliStoreDesc").'</td>';
947	print '<td><a href="'.$url.'" target="_blank" rel="external">'.$url.'</a></td>';
948	print '</tr>';
949
950	print "</table>\n";
951	print '</div>';
952
953	print dol_get_fiche_end();
954
955	print '<br>';
956
957	if (empty($conf->global->MAIN_DISABLE_DOLISTORE_SEARCH) && $conf->global->MAIN_FEATURES_LEVEL >= 1) {
958		// $options is array with filter criterias
959		//var_dump($options);
960		$dolistore->getRemoteCategories();
961		$dolistore->getRemoteProducts($options);
962
963		print '<span class="opacitymedium">'.$langs->trans('DOLISTOREdescriptionLong').'</span><br><br>';
964
965		$previouslink = $dolistore->get_previous_link();
966		$nextlink = $dolistore->get_next_link();
967
968		print '<div class="liste_titre liste_titre_bydiv centpercent"><div class="divsearchfield">';
969
970		print '<form method="POST" class="centpercent" id="searchFormList" action="'.$dolistore->url.'">';
971		?>
972					<input type="hidden" name="token" value="<?php echo newToken(); ?>">
973					<input type="hidden" name="mode" value="marketplace">
974					<div class="divsearchfield"><?php echo $langs->trans('Keyword') ?>:
975						<input name="search_keyword" placeholder="<?php echo $langs->trans('Chercher un module') ?>" id="search_keyword" type="text" size="50" value="<?php echo $options['search'] ?>"><br>
976					</div>
977					<div class="divsearchfield">
978						<input class="button buttongen" value="<?php echo $langs->trans('Rechercher') ?>" type="submit">
979						<a class="buttonreset" href="<?php echo $dolistore->url ?>"><?php echo $langs->trans('Reset') ?></a>
980
981						&nbsp;
982					</div>
983		<?php
984		print $previouslink;
985		print $nextlink;
986		print '</form>';
987
988
989		print '</div></div>';
990		print '<div class="clearboth"></div>';
991
992		?>
993
994			<div id="category-tree-left">
995				<ul class="tree">
996					<?php echo $dolistore->get_categories(); ?>
997				</ul>
998			</div>
999			<div id="listing-content">
1000				<table summary="list_of_modules" id="list_of_modules" class="productlist centpercent">
1001					<tbody id="listOfModules">
1002						<?php echo $dolistore->get_products($categorie); ?>
1003					</tbody>
1004				</table>
1005			</div>
1006
1007		<?php
1008	}
1009}
1010
1011
1012// Install external module
1013
1014if ($mode == 'deploy') {
1015	print dol_get_fiche_head($head, $mode, '', -1);
1016
1017	$dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT);
1018	$allowonlineinstall = true;
1019	$allowfromweb = 1;
1020	if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) {
1021		$allowonlineinstall = false;
1022	}
1023
1024	$fullurl = '<a href="'.$urldolibarrmodules.'" target="_blank">'.$urldolibarrmodules.'</a>';
1025	$message = '';
1026	if (!empty($allowonlineinstall)) {
1027		if (!in_array('/custom', explode(',', $dolibarr_main_url_root_alt))) {
1028			$message = info_admin($langs->trans("ConfFileMustContainCustom", DOL_DOCUMENT_ROOT.'/custom', DOL_DOCUMENT_ROOT));
1029			$allowfromweb = -1;
1030		} else {
1031			if ($dirins_ok) {
1032				if (!is_writable(dol_osencode($dirins))) {
1033					$langs->load("errors");
1034					$message = info_admin($langs->trans("ErrorFailedToWriteInDir", $dirins), 0, 0, '1', 'warning');
1035					$allowfromweb = 0;
1036				}
1037			} else {
1038				$message = info_admin($langs->trans("NotExistsDirect", $dirins).$langs->trans("InfDirAlt").$langs->trans("InfDirExample"));
1039				$allowfromweb = 0;
1040			}
1041		}
1042	} else {
1043		$message = info_admin($langs->trans("InstallModuleFromWebHasBeenDisabledByFile", $dolibarrdataroot.'/installmodules.lock'));
1044		$allowfromweb = 0;
1045	}
1046
1047	if ($allowfromweb < 1) {
1048		print $langs->trans("SomethingMakeInstallFromWebNotPossible");
1049		print $message;
1050		//print $langs->trans("SomethingMakeInstallFromWebNotPossible2");
1051		print '<br>';
1052	}
1053
1054	print '<br>';
1055
1056	if ($allowfromweb >= 0) {
1057		if ($allowfromweb == 1) {
1058			//print $langs->trans("ThisIsProcessToFollow").'<br>';
1059		} else {
1060			print $langs->trans("ThisIsAlternativeProcessToFollow").'<br>';
1061			print '<b>'.$langs->trans("StepNb", 1).'</b>: ';
1062			print str_replace('{s1}', $fullurl, $langs->trans("FindPackageFromWebSite", '{s1}')).'<br>';
1063			print '<b>'.$langs->trans("StepNb", 2).'</b>: ';
1064			print str_replace('{s1}', $fullurl, $langs->trans("DownloadPackageFromWebSite", '{s1}')).'<br>';
1065			print '<b>'.$langs->trans("StepNb", 3).'</b>: ';
1066		}
1067
1068		if ($allowfromweb == 1) {
1069			print $langs->trans("UnpackPackageInModulesRoot", $dirins).'<br>';
1070
1071			print '<br>';
1072
1073			print '<form enctype="multipart/form-data" method="POST" class="noborder" action="'.$_SERVER["PHP_SELF"].'" name="forminstall">';
1074			print '<input type="hidden" name="token" value="'.newToken().'">';
1075			print '<input type="hidden" name="action" value="install">';
1076			print '<input type="hidden" name="mode" value="deploy">';
1077
1078			print $langs->trans("YouCanSubmitFile");
1079
1080			$max = $conf->global->MAIN_UPLOAD_DOC; // In Kb
1081			$maxphp = @ini_get('upload_max_filesize'); // In unknown
1082			if (preg_match('/k$/i', $maxphp)) {
1083				$maxphp = $maxphp * 1;
1084			}
1085			if (preg_match('/m$/i', $maxphp)) {
1086				$maxphp = $maxphp * 1024;
1087			}
1088			if (preg_match('/g$/i', $maxphp)) {
1089				$maxphp = $maxphp * 1024 * 1024;
1090			}
1091			if (preg_match('/t$/i', $maxphp)) {
1092				$maxphp = $maxphp * 1024 * 1024 * 1024;
1093			}
1094			$maxphp2 = @ini_get('post_max_size'); // In unknown
1095			if (preg_match('/k$/i', $maxphp2)) {
1096				$maxphp2 = $maxphp2 * 1;
1097			}
1098			if (preg_match('/m$/i', $maxphp2)) {
1099				$maxphp2 = $maxphp2 * 1024;
1100			}
1101			if (preg_match('/g$/i', $maxphp2)) {
1102				$maxphp2 = $maxphp2 * 1024 * 1024;
1103			}
1104			if (preg_match('/t$/i', $maxphp2)) {
1105				$maxphp2 = $maxphp2 * 1024 * 1024 * 1024;
1106			}
1107			// Now $max and $maxphp and $maxphp2 are in Kb
1108			$maxmin = $max;
1109			$maxphptoshow = $maxphptoshowparam = '';
1110			if ($maxphp > 0) {
1111				$maxmin = min($max, $maxphp);
1112				$maxphptoshow = $maxphp;
1113				$maxphptoshowparam = 'upload_max_filesize';
1114			}
1115			if ($maxphp2 > 0) {
1116				$maxmin = min($max, $maxphp2);
1117				if ($maxphp2 < $maxphp) {
1118					$maxphptoshow = $maxphp2;
1119					$maxphptoshowparam = 'post_max_size';
1120				}
1121			}
1122
1123			if ($maxmin > 0) {
1124				print '<script type="text/javascript">
1125				$(document).ready(function() {
1126					jQuery("#fileinstall").on("change", function() {
1127						if(this.files[0].size > '.($maxmin * 1024).'){
1128							alert("'.dol_escape_js($langs->trans("ErrorFileSizeTooLarge")).'");
1129							this.value = "";
1130						};
1131					});
1132				});
1133				</script>'."\n";
1134				// MAX_FILE_SIZE doit précéder le champ input de type file
1135				print '<input type="hidden" name="max_file_size" value="'.($maxmin * 1024).'">';
1136			}
1137
1138			print '<input class="flat minwidth400" type="file" name="fileinstall" id="fileinstall"> ';
1139
1140			print '<input type="submit" name="send" value="'.dol_escape_htmltag($langs->trans("Upload")).'" class="button">';
1141
1142			if (!empty($conf->global->MAIN_UPLOAD_DOC)) {
1143				if ($user->admin) {
1144					$langs->load('other');
1145					print ' ';
1146					print info_admin($langs->trans("ThisLimitIsDefinedInSetup", $max, $maxphptoshow, $maxphptoshowparam), 1);
1147				}
1148			} else {
1149				print ' ('.$langs->trans("UploadDisabled").')';
1150			}
1151
1152			print '</form>';
1153
1154			print '<br>';
1155			print '<br>';
1156
1157			print '<div class="center"><div class="logo_setup"></div></div>';
1158		} else {
1159			print $langs->trans("UnpackPackageInModulesRoot", $dirins).'<br>';
1160			print '<b>'.$langs->trans("StepNb", 4).'</b>: ';
1161			print $langs->trans("SetupIsReadyForUse").'<br>';
1162		}
1163	}
1164
1165	if (!empty($result['return'])) {
1166		print '<br>';
1167
1168		foreach ($result['return'] as $value) {
1169			echo $value.'<br>';
1170		}
1171	}
1172
1173	print dol_get_fiche_end();
1174}
1175
1176if ($mode == 'develop') {
1177	print dol_get_fiche_head($head, $mode, '', -1);
1178
1179	// Marketplace
1180	print "<table summary=\"list_of_modules\" class=\"noborder\" width=\"100%\">\n";
1181	print "<tr class=\"liste_titre\">\n";
1182	//print '<td>'.$langs->trans("Logo").'</td>';
1183	print '<td colspan="2">'.$langs->trans("DevelopYourModuleDesc").'</td>';
1184	print '<td>'.$langs->trans("URL").'</td>';
1185	print '</tr>';
1186
1187	print '<tr class="oddeven" height="80">'."\n";
1188	print '<td class="left">';
1189	print '<div class="imgmaxheight50 logo_setup"></div>';
1190	print '</td>';
1191	print '<td>'.$langs->trans("TryToUseTheModuleBuilder", $langs->transnoentitiesnoconv("ModuleBuilder")).'</td>';
1192	print '<td>'.$langs->trans("SeeTopRightMenu").'</td>';
1193	print '</tr>';
1194
1195	print '<tr class="oddeven" height="80">'."\n";
1196	$url = 'https://partners.dolibarr.org';
1197	print '<td class="left">';
1198	print'<a href="'.$url.'" target="_blank" rel="external"><img border="0" class="imgautosize imgmaxwidth180" src="'.DOL_URL_ROOT.'/theme/dolibarr_preferred_partner.png"></a>';
1199	print '</td>';
1200	print '<td>'.$langs->trans("DoliPartnersDesc").'</td>';
1201	print '<td><a href="'.$url.'" target="_blank" rel="external">'.$url.'</a></td>';
1202	print '</tr>';
1203
1204	print "</table>\n";
1205
1206	print dol_get_fiche_end();
1207}
1208
1209// End of page
1210llxFooter();
1211$db->close();
1212