1<?php
2/* Copyright (C) 2005-2009	Laurent Destailleur		<eldy@users.sourceforge.net>
3 * Copyright (C) 2007		Rodolphe Quiedeville	<rodolphe@quiedeville.org>
4 * Copyright (C) 2010-2012	Regis Houssin			<regis.houssin@inodbox.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20/**
21 *  \file       htdocs/admin/system/modules.php
22 *  \brief      File to list all Dolibarr modules
23 */
24
25require '../../main.inc.php';
26require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
27
28if (empty($user->admin)) {
29	accessforbidden();
30}
31
32// Load translation files required by the page
33$langs->loadLangs(array("install", "other", "admin"));
34
35$optioncss = GETPOST('optioncss', 'alpha');
36$contextpage		= GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'moduleoverview';
37
38$search_name		= GETPOST("search_name", 'alpha');
39$search_id = GETPOST("search_id", 'alpha');
40$search_version = GETPOST("search_version", 'alpha');
41$search_permission = GETPOST("search_permission", 'alpha');
42
43$sortfield			= GETPOST("sortfield", 'alpha');
44$sortorder			= GETPOST("sortorder", 'alpha');
45
46if (!$sortfield) {
47	$sortfield = "id";
48}
49if (!$sortorder) {
50	$sortorder = "asc";
51}
52
53// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array of hooks
54$hookmanager->initHooks(array('moduleoverview'));
55$form = new Form($db);
56$object = new stdClass();
57
58// Definition of fields for lists
59$arrayfields = array(
60	'name'=>array('label'=>$langs->trans("Modules"), 'checked'=>1, 'position'=>10),
61	'version'=>array('label'=>$langs->trans("Version"), 'checked'=>1, 'position'=>20),
62	'id'=>array('label'=>$langs->trans("IdModule"), 'checked'=>1, 'position'=>30),
63	'module_position'=>array('label'=>$langs->trans("Position"), 'checked'=>1, 'position'=>35),
64	'permission'=>array('label'=>$langs->trans("IdPermissions"), 'checked'=>1, 'position'=>40)
65);
66
67$arrayfields = dol_sort_array($arrayfields, 'position');
68$param = '';
69
70
71/*
72 * Actions
73 */
74
75$parameters = array();
76$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
77if ($reshook < 0) {
78	setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
79}
80
81if (empty($reshook)) {
82	// Selection of new fields
83	include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
84}
85
86
87// Load list of modules
88$moduleList = array();
89$modules = array();
90$modules_files = array();
91$modules_fullpath = array();
92$modulesdir = dolGetModulesDirs();
93$rights_ids = array();
94
95foreach ($modulesdir as $dir) {
96	$handle = @opendir(dol_osencode($dir));
97	if (is_resource($handle)) {
98		while (($file = readdir($handle)) !== false) {
99			if (is_readable($dir.$file) && substr($file, 0, 3) == 'mod' && substr($file, dol_strlen($file) - 10) == '.class.php') {
100				$modName = substr($file, 0, dol_strlen($file) - 10);
101
102				if ($modName) {
103					//print 'xx'.$dir.$file.'<br>';
104					if (in_array($file, $modules_files)) {
105						// File duplicate
106						print "Warning duplicate file found : ".$file." (Found ".$dir.$file.", already found ".$modules_fullpath[$file].")<br>";
107					} else {
108						// File to load
109						$res = include_once $dir.$file;
110						if (class_exists($modName)) {
111							try {
112								$objMod = new $modName($db);
113
114								$modules[$objMod->numero] = $objMod;
115								$modules_files[$objMod->numero] = $file;
116								$modules_fullpath[$file] = $dir.$file;
117							} catch (Exception $e) {
118								dol_syslog("Failed to load ".$dir.$file." ".$e->getMessage(), LOG_ERR);
119							}
120						} else {
121							print "Warning bad descriptor file : ".$dir.$file." (Class ".$modName." not found into file)<br>";
122						}
123					}
124				}
125			}
126		}
127		closedir($handle);
128	}
129}
130
131// create pre-filtered list for modules
132foreach ($modules as $key => $module) {
133	$newModule = new stdClass();
134
135	$newModule->name = $module->getName();
136	$newModule->version = $module->getVersion();
137	$newModule->id = $key;
138	$newModule->module_position = $module->getModulePosition();
139
140	$alt = $module->name.' - '.$modules_files[$key];
141
142	if (!empty($module->picto)) {
143		if (preg_match('/^\//', $module->picto)) {
144			$newModule->picto = img_picto($alt, $module->picto, 'width="14px"', 1);
145		} else {
146			$newModule->picto = img_object($alt, $module->picto, 'width="14px"');
147		}
148	} else {
149		$newModule->picto = img_object($alt, 'generic', 'width="14px"');
150	}
151
152	$permission = array();
153	if ($module->rights) {
154		foreach ($module->rights as $rights) {
155			if (empty($rights[0])) {
156				continue;
157			}
158
159			$permission[] = $rights[0];
160
161			array_push($rights_ids, $rights[0]);
162		}
163	}
164
165	$newModule->permission = $permission;
166
167	// pre-filter list
168	if (!empty($search_name) && !stristr($newModule->name, $search_name)) {
169		continue;
170	}
171	if (!empty($search_version) && !stristr($newModule->version, $search_version)) {
172		continue;
173	}
174	if (!empty($search_id) && !stristr($newModule->id, $search_id)) {
175		continue;
176	}
177
178	if (!empty($search_permission)) {
179		$found = false;
180
181		foreach ($newModule->permission as $permission) {
182			if (stristr($permission, $search_permission)) {
183				$found = true;
184				break;
185			}
186		}
187
188		if (!$found) {
189			continue;
190		}
191	}
192
193	$moduleList[] = $newModule;
194}
195
196
197
198/*
199 * View
200 */
201
202llxHeader();
203
204print '<form action="'.$_SERVER["PHP_SELF"].'" method="post" name="formulaire">';
205if ($optioncss != '') {
206	print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
207}
208print '<input type="hidden" name="token" value="'.newToken().'">';
209print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
210print '<input type="hidden" name="action" value="list">';
211print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
212print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
213print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
214
215print_barre_liste($langs->trans("AvailableModules"), empty($page) ? 0 : $page, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, '', -1, '', 'title_setup', 0, '', '', 0, 1, 1);
216
217print '<span class="opacitymedium">'.$langs->trans("ToActivateModule").'</span>';
218print '<br>';
219print '<br>';
220
221$varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage;
222$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields
223
224print '<div class="div-table-responsive-no-min">';
225print '<table class="noborder centpercent">';
226
227// Lines with input filters
228print '<tr class="liste_titre_filter">';
229
230if ($arrayfields['name']['checked']) {
231	print '<td class="liste_titre left">';
232	print '<input class="flat" type="text" name="search_name" size="8" value="'.dol_escape_htmltag($search_name).'">';
233	print '</td>';
234}
235if ($arrayfields['version']['checked']) {
236	print '<td class="liste_titre left">';
237	print '<input class="flat" type="text" name="search_version" size="6" value="'.dol_escape_htmltag($search_version).'">';
238	print '</td>';
239}
240if ($arrayfields['id']['checked']) {
241	print '<td class="liste_titre left">';
242	print '<input class="flat" type="text" name="search_id" size="6 value="'.dol_escape_htmltag($search_id).'">';
243	print '</td>';
244}
245if ($arrayfields['permission']['checked']) {
246	print '<td class="liste_titre left">';
247	print '<input class="flat" type="text" name="search_permission" size="8" value="'.dol_escape_htmltag($search_permission).'">';
248	print '</td>';
249}
250if ($arrayfields['module_position']['checked']) {
251	print '<td class="liste_titre left">';
252	print '</td>';
253}
254
255print '<td class="liste_titre center maxwidthsearch">';
256$searchpicto = $form->showFilterButtons();
257print $searchpicto;
258print '</td>';
259
260print '</tr>';
261
262print '<tr class="liste_titre">';
263
264if ($arrayfields['name']['checked']) {
265	print_liste_field_titre($arrayfields['name']['label'], $_SERVER["PHP_SELF"], "name", "", "", "", $sortfield, $sortorder);
266}
267if ($arrayfields['version']['checked']) {
268	print_liste_field_titre($arrayfields['version']['label'], $_SERVER["PHP_SELF"], "version", "", "", "", $sortfield, $sortorder);
269}
270if ($arrayfields['id']['checked']) {
271	print_liste_field_titre($arrayfields['id']['label'], $_SERVER["PHP_SELF"], "id", "", "", "", $sortfield, $sortorder, 'nowraponall ');
272}
273if ($arrayfields['permission']['checked']) {
274	print_liste_field_titre($arrayfields['permission']['label'], $_SERVER["PHP_SELF"], "permission", "", "", "", $sortfield, $sortorder);
275}
276if ($arrayfields['module_position']['checked']) {
277	print_liste_field_titre($arrayfields['module_position']['label'], $_SERVER["PHP_SELF"], "module_position", "", "", "", $sortfield, $sortorder);
278}
279
280// Fields from hook
281$parameters = array('arrayfields'=>$arrayfields, 'param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
282$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
283print $hookmanager->resPrint;
284
285print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ');
286print '</tr>';
287
288// sort list
289if ($sortfield == "name" && $sortorder == "asc") {
290	usort($moduleList, function (stdClass $a, stdClass $b) {
291		return strcasecmp($a->name, $b->name);
292	});
293} elseif ($sortfield == "name" && $sortorder == "desc") {
294	usort($moduleList, function (stdClass $a, stdClass $b) {
295		return strcasecmp($b->name, $a->name);
296	});
297} elseif ($sortfield == "version" && $sortorder == "asc") {
298	usort($moduleList, function (stdClass $a, stdClass $b) {
299		return strcasecmp($a->version, $b->version);
300	});
301} elseif ($sortfield == "version" && $sortorder == "desc") {
302	usort($moduleList, function (stdClass $a, stdClass $b) {
303		return strcasecmp($b->version, $a->version);
304	});
305} elseif ($sortfield == "id" && $sortorder == "asc") {
306	usort($moduleList, "compareIdAsc");
307} elseif ($sortfield == "id" && $sortorder == "desc") {
308	usort($moduleList, "compareIdDesc");
309} elseif ($sortfield == "permission" && $sortorder == "asc") {
310	usort($moduleList, "comparePermissionIdsAsc");
311} elseif ($sortfield == "permission" && $sortorder == "desc") {
312	usort($moduleList, "comparePermissionIdsDesc");
313} else {
314	$moduleList = dol_sort_array($moduleList, 'module_position');
315}
316
317foreach ($moduleList as $module) {
318	print '<tr class="oddeven">';
319
320	if ($arrayfields['name']['checked']) {
321		print '<td width="300" class="nowrap">';
322		print $module->picto;
323		print ' '.$module->name;
324		print "</td>";
325	}
326
327	if ($arrayfields['version']['checked']) {
328		print '<td class="nowraponall">'.$module->version.'</td>';
329	}
330
331	if ($arrayfields['id']['checked']) {
332		print '<td class="center">'.$module->id.'</td>';
333	}
334
335	if ($arrayfields['permission']['checked']) {
336		$idperms = '';
337
338		foreach ($module->permission as $permission) {
339			$idperms .= ($idperms ? ", " : "").$permission;
340			$translationKey = "Permission".$permission;
341
342			if (!empty($conf->global->MAIN_SHOW_PERMISSION)) {
343				if (empty($langs->tab_translate[$translationKey])) {
344					$tooltip = 'Missing translation (key '.$translationkey.' not found in admin.lang)';
345					$idperms .= ' <img src="../../theme/eldy/img/warning.png" alt="Warning" title="'.$tooltip.'">';
346				}
347			}
348		}
349
350		print '<td><span class="opacitymedium">'.($idperms ? $idperms : "&nbsp;").'</span></td>';
351	}
352
353	if ($arrayfields['module_position']['checked']) {
354		print '<td class="center">'.$module->module_position.'</td>';
355	}
356
357	print '<td></td>';
358	print '</tr>';
359}
360
361print '</table>';
362print '</div>';
363print '</form>';
364print '<br>';
365
366sort($rights_ids);
367$old = '';
368
369foreach ($rights_ids as $right_id) {
370	if ($old == $right_id) {
371		print "Warning duplicate id on permission : ".$right_id."<br>";
372	}
373
374	$old = $right_id;
375}
376
377// End of page
378llxFooter();
379$db->close();
380
381
382 /**
383  * Compare two modules by their ID for a ascending order
384  *
385  * @param	stdClass 	$a		First module
386  * @param	stdClass 	$b		Second module
387  * @return	int					Compare result (-1, 0, 1)
388  */
389function compareIdAsc(stdClass $a, stdClass $b)
390{
391	if ((int) $a->id == (int) $b->id) {
392		return 0;
393	}
394
395	return ((int) $a->id < (int) $b->id) ? -1 : 1;
396}
397
398 /**
399  * Compare two modules by their ID for a descending order
400  *
401  * @param	stdClass 	$a		First module
402  * @param	stdClass 	$b		Second module
403  * @return	int					Compare result (-1, 0, 1)
404  */
405function compareIdDesc(stdClass $a, stdClass $b)
406{
407	if ((int) $a->id == (int) $b->id) {
408		return 0;
409	}
410
411	return ((int) $b->id < (int) $a->id) ? -1 : 1;
412}
413
414 /**
415  * Compare two modules by their ID for a ascending order
416  *
417  * @param	stdClass 	$a		First module
418  * @param	stdClass 	$b		Second module
419  * @return	int					Compare result (-1, 0, 1)
420  */
421function comparePermissionIdsAsc(stdClass $a, stdClass $b)
422{
423	if (empty($a->permission) && empty($b->permission)) {
424		return compareIdAsc($a, $b);
425	}
426
427	if (empty($a->permission)) {
428		return 1;
429	}
430	if (empty($b->permission)) {
431		return -1;
432	}
433
434	if ($a->permission[0] == $b->permission[0]) {
435		return 0;
436	}
437
438	return $a->permission[0] < $b->permission[0] ? -1 : 1;
439}
440
441 /**
442  * Compare two modules by their permissions for a descending order
443  *
444  * @param	stdClass 	$a		First module
445  * @param	stdClass 	$b		Second module
446  * @return	int					Compare result (-1, 0, 1)
447  */
448function comparePermissionIdsDesc(stdClass $a, stdClass $b)
449{
450	if (empty($a->permission) && empty($b->permission)) {
451		return compareIdDesc($a, $b);
452	}
453
454	if (empty($a->permission)) {
455		return -1;
456	}
457	if (empty($b->permission)) {
458		return 1;
459	}
460
461	if ($a->permission[0] == $b->permission[0]) {
462		return 0;
463	}
464
465	return $b->permission[0] < $a->permission[0] ? -1 : 1;
466}
467