FusionDirectory
class_pluglist.inc
Go to the documentation of this file.
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4  Copyright (C) 2003-2010 Cajus Pollmeier
5  Copyright (C) 2011-2019 FusionDirectory
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 
33 class pluglist
34 {
35  var $menu = '';
36  protected $iconmenu = '';
37 
42  var $info = [];
43 
47  var $dnForeignRefs = [];
48 
52  var $dirlist = [];
53 
57  protected $allowed_plugins = [];
58  protected $silly_cache = [];
59 
63  function __construct ()
64  {
65  global $class_mapping;
66 
67  /* Fill info part of pluglist */
68  $classes = get_declared_classes();
69  /* To avoid plugins changing index when reloading */
70  sort($classes);
71 
72  $index = 0;
73  $depends_infos = [];
74  $conflicts_infos = [];
75  $foreign_refs = [];
76  foreach ($classes as $cname) {
77  $cmethods = get_class_methods($cname);
78  if (in_array_ics('plInfo', $cmethods)) {
79  $infos = call_user_func([$cname, 'plInfo']);
80  if (empty($infos)) {
81  continue;
82  }
83  if (is_subclass_of($cname, 'simpleService')) {
84  $infos['plSelfModify'] = FALSE;
85  /* services are not part of any objectType */
86  unset($infos['plObjectType']);
87  $infos['plCategory'] = ['server'];
88  } else {
89  if (!isset($infos['plSelfModify'])) {
90  $infos['plSelfModify'] = FALSE;
91  }
92  }
93  if (isset($class_mapping[$cname])) {
94  $infos['PATH'] = dirname($class_mapping[$cname]);
95  }
96  if (isset($infos['plDepends'])) {
97  $depends_infos[] = $cname;
98  }
99  if (isset($infos['plConflicts'])) {
100  $conflicts_infos[] = $cname;
101  }
102  if (isset($infos['plForeignKeys'])) {
103  foreach ($infos['plForeignKeys'] as $ofield => &$pfks) {
104  if (!is_array($pfks)) {
105  $pfks = [$pfks];
106  }
107  if (!is_array($pfks[0])) {
108  $pfks = [$pfks];
109  }
110  foreach ($pfks as &$pfk) {
111  $class = $pfk[0];
112  if (isset($pfk[1])) {
113  $field = $pfk[1];
114  } else {
115  $field = 'dn';
116  $pfk[1] = $field;
117  }
118  $filter = NULL;
119  if (isset($pfk[2])) {
120  $filter = $pfk[2];
121  }
122  if ($filter === NULL) {
123  $filter = "$ofield=%oldvalue%";
124  }
125  $pfk[2] = $filter;
126  if (!isset($foreign_refs[$class])) {
127  $foreign_refs[$class] = [];
128  }
129  if (!isset($foreign_refs[$class][$field])) {
130  $foreign_refs[$class][$field] = [];
131  }
132  $foreign_refs[$class][$field][] = [$cname, $ofield, $filter];
133  if ($field == 'dn') {
134  $this->dnForeignRefs[] = [$cname, $ofield, $filter, (isset($pfk[3]) ? $pfk[3] : "$ofield=*%oldvalue%")];
135  }
136  }
137  unset($pfk);
138  }
139  unset($pfks);
140  } else {
141  $infos['plForeignKeys'] = [];
142  }
143  if (!isset($infos['plProvidedAcls'])) {
144  $infos['plProvidedAcls'] = [];
145  }
146  if (!isset($infos['plCategory'])) {
147  $infos['plCategory'] = [];
148  }
149  if (!isset($infos['plTitle']) && isset($infos['plShortName'])) {
150  $infos['plTitle'] = $infos['plShortName'];
151  }
152  if (!empty($infos['plObjectClass']) && !isset($infos['plFilter'])) {
153  if (count($infos['plObjectClass']) == 1) {
154  $infos['plFilter'] = '(objectClass='.$infos['plObjectClass'][0].')';
155  } else {
156  $infos['plFilter'] = '(&(objectClass='.implode(')(objectClass=', $infos['plObjectClass']).'))';
157  }
158  }
159  if (isset($infos['plFilter'])) {
160  $infos['plFilterObject'] = ldapFilter::parse($infos['plFilter']);
161  }
162  if (isset($infos['plObjectType']) && !isset($infos['plPriority']) && !is_numeric(key($infos['plObjectType']))) {
163  /* Set main tab priority to 0 */
164  $infos['plPriority'] = 0;
165  }
166  $infos['plForeignRefs'] = [];
167  $infos['INDEX'] = $index;
168  $this->info[$cname] = $infos;
169  $this->dirlist[$index++] = $cname;
170  }
171  }
172 
173  foreach ($depends_infos as $cname) {
174  foreach ($this->info[$cname]['plDepends'] as $depend) {
175  if (isset($this->info[$depend])) {
176  if (isset($this->info[$depend]['plDepending'])) {
177  $this->info[$depend]['plDepending'][] = $cname;
178  } else {
179  $this->info[$depend]['plDepending'] = [$cname];
180  }
181  } else {
182  trigger_error("$cname depends of the inexisting plugin $depend");
183  }
184  }
185  }
186  foreach ($conflicts_infos as $cname) {
187  foreach ($this->info[$cname]['plConflicts'] as $conflict) {
188  if (isset($this->info[$conflict])) {
189  if (isset($this->info[$conflict]['plConflicts'])) {
190  if (!in_array($cname, $this->info[$conflict]['plConflicts'])) {
191  $this->info[$conflict]['plConflicts'][] = $cname;
192  }
193  } else {
194  $this->info[$conflict]['plConflicts'] = [$cname];
195  }
196  }
197  }
198  }
199  foreach ($foreign_refs as $cname => $refs) {
200  if (isset($this->info[$cname])) {
201  $this->info[$cname]['plForeignRefs'] = $refs;
202  }
203  }
204 
205  /* Provide field for 'all' */
206  $this->info['all'] = [];
207 
208  $this->info['all']['plProvidedAcls'] = [];
209  $this->info['all']['plDescription'] = _("All objects in this category");
210  $this->info['all']['plSelfModify'] = FALSE;
211 
212  uasort($this->info,
213  function ($a, $b)
214  {
215  if (isset($a['plPriority']) && isset($b['plPriority'])) {
216  if ($a['plPriority'] == $b['plPriority']) {
217  return 0;
218  } elseif ($a['plPriority'] < $b['plPriority']) {
219  return -1;
220  } else {
221  return 1;
222  }
223  } elseif (isset($a['plPriority'])) {
224  return -1;
225  } elseif (isset($b['plPriority'])) {
226  return 1;
227  } else {
228  return 0;
229  }
230  }
231  );
232  }
233 
243  function check_access ($infos)
244  {
245  global $ui;
246 
247  if (isset($infos['CLASS']) && $ui->isBlacklisted($infos['CLASS'])) {
248  return FALSE;
249  }
250 
251  if (!isset($infos['ACL'])) {
252  return TRUE;
253  }
254 
255  $aclname = $infos['ACL'];
256 
257  if (isset($this->silly_cache[$aclname])) {
258  return $this->silly_cache[$aclname];
259  }
260 
261  /* Split given acl string into an array.
262  e.g. "user,systems" => array("user","systems");
263  */
264  $acls_to_check = [];
265  if (preg_match("/,/", $aclname)) {
266  $acls_to_check = explode(",", $aclname);
267  } else {
268  $acls_to_check = [$aclname];
269  }
270 
271  foreach ($acls_to_check as $acl_to_check) {
272  $acl_to_check = trim($acl_to_check);
273 
274  /* Check if the given acl tag is only valid for self acl entries
275  <plugin acl="user/user:self" class="user"... */
276  if (preg_match("/:self$/", $acl_to_check)) {
277  $acl_to_check = preg_replace("/:self$/", "", $acl_to_check);
278  if (strpos($acl_to_check, '/')) {
279  if ($ui->get_permissions($ui->dn, $acl_to_check, "") != "") {
280  $this->silly_cache[$aclname] = TRUE;
281  return TRUE;
282  }
283  } else {
284  if ($ui->get_category_permissions($ui->dn, $acl_to_check) != '') {
285  $this->silly_cache[$aclname] = TRUE;
286  return TRUE;
287  }
288  }
289  } else {
290 
291  /* No self acls. Check if we have any acls for the given ACL type */
292  $deps = $ui->get_module_departments($acl_to_check, TRUE);
293  if (count($deps)) {
294  $this->silly_cache[$aclname] = TRUE;
295  return TRUE;
296  }
297  }
298  }
299 
300  $this->silly_cache[$aclname] = FALSE;
301  return FALSE;
302  }
303 
307  function get_infos ($cname)
308  {
309  $plHeadline = FALSE;
310  $plIcon = FALSE;
311  $plDescription = FALSE;
312  $index = $this->get_index($cname);
313  $href = "main.php?plug=$index&amp;reset=1";
314  if (isset($this->info[$cname])) {
315  if (isset($this->info[$cname]['plShortName'])) {
316  $plHeadline = $this->info[$cname]['plShortName'];
317  }
318  if (isset($this->info[$cname]['plIcon'])) {
319  $plIcon = $this->info[$cname]['plIcon'];
320  }
321  if (isset($this->info[$cname]['plDescription'])) {
322  $plDescription = $this->info[$cname]['plDescription'];
323  }
324  if ($plHeadline && $plIcon && $plDescription) {
325  return [$plHeadline,$plDescription,$href,$plIcon];
326  }
327  }
328  $vars = get_class_vars($cname);
329  if ($vars) {
330  if (!$plHeadline && isset($vars['plHeadline'])) {
331  $plHeadline = _($vars['plHeadline']);
332  }
333  if (!$plDescription && isset($vars['plDescription'])) {
334  $plDescription = _($vars['plDescription']);
335  }
336  if (!$plIcon && isset($vars['plIcon'])) {
337  $plIcon = $vars['plIcon'];
338  }
339  } else {
340  die('Unknown class '.$cname);
341  }
342  if (!$plIcon) {
343  $plIcon = "icon.png";
344  }
345  return [$plHeadline,$plDescription,$href,$plIcon];
346  }
347 
351  function gen_menu ()
352  {
353  global $config;
354  if ($this->menu == "") {
355  $this->menu = '<ul class="menu">'."\n";
356  /* Parse headlines */
357  foreach ($config->data['SECTIONS'] as $section => $section_infos) {
358  $entries = '';
359 
360  /* Parse sub-plugins */
361  foreach ($config->data['MENU'][$section] as $info) {
362  if (!$this->check_access($info)) {
363  continue;
364  }
365  if (isset($info['CLASS']) && plugin_available($info['CLASS'])) {
366  $index = $this->get_index($info['CLASS']);
367  $this->allowed_plugins[$index] = $index;
368  list ($plHeadline, $plDescription, $href, ) = $this->get_infos($info['CLASS']);
369  $id = $info['CLASS'];
370  } elseif (!isset($info['CLASS'])) {
371  $plHeadline = $info['TITLE'];
372  $plDescription = $info['DESCRIPTION'];
373  $href = $info['LINK'];
374  $id = $info['NAME'];
375  } else {
376  continue;
377  }
378 
379  $entries .= '<li class="menuitem" id="menuitem_'.$id.'">';
380  $entries .= '<a href="'.$href.'" title="'.htmlescape($plDescription).'">'.htmlescape($plHeadline).'</a></li>'."\n";
381  }
382 
383  /* Append to menu */
384  if ($entries != "") {
385  $this->menu .= '<li><a>'.htmlescape($section_infos['NAME'])."</a>\n<ul>\n".$entries."\n</ul></li>\n";
386  }
387  }
388  $this->menu .= '</ul>'."\n";
389  }
390 
391  /* Add the menucurrent class to current plugin */
392  if (isset($_GET['plug'])) {
393  $plug = $_GET['plug'];
394  } else {
395  $plug = "NOTHING";
396  }
397  $lines = preg_split("/\n/", $this->menu);
398  foreach ($lines as &$line) {
399  if (preg_match('/'.preg_quote("main.php?plug=$plug&amp;reset=1", '/').'/', $line)) {
400  $line = preg_replace('/class="menuitem"/', 'class="menuitem menucurrent"', $line);
401  } elseif (preg_match('/class="menuitem menucurrent"/', $line)) {
402  $line = preg_replace('/class="menuitem menucurrent"/', 'class="menuitem"', $line);
403  }
404  }
405  unset($line);
406 
407  /* Write menu output */
408  $this->menu = join("\n", $lines);
409  }
410 
414  function show_iconmenu ()
415  {
416  global $class_mapping, $config;
417  if ($this->iconmenu == "") {
418 
419  /* Parse headlines */
420  foreach ($config->data['SECTIONS'] as $section => $section_infos) {
421  $entries = '';
422  $sectionMenu = '<div class="iconmenu-section"><h1 class="menuheader">';
423  $sectionMenu .= htmlescape($section_infos['NAME'])."</h1>\n";
424 
425  foreach ($config->data['MENU'][$section] as $info) {
426  if (!$this->check_access($info)) {
427  continue;
428  }
429  if (isset($info['CLASS']) && plugin_available($info['CLASS'])) {
430  /* Read information from class variable */
431  list ($plHeadline, $plDescription, $href, $plIcon) = $this->get_infos($info['CLASS']);
432  $id = $info['CLASS'];
433  } elseif (!isset($info['CLASS'])) {
434  $plHeadline = $info['TITLE'];
435  $plDescription = $info['DESCRIPTION'];
436  $href = $info['LINK'];
437  $plIcon = $info['ICONPATH'];
438  $id = $info['NAME'];
439  } else {
440  continue;
441  }
442 
443  /* Load icon */
444  if (isset($info['CLASS']) && !preg_match("/\//", $plIcon) && !preg_match("/^geticon/", $plIcon)) {
445  $image = get_template_path('plugins/'.preg_replace('%^.*/([^/]+)/[^/]+$%', '\1', $class_mapping[$info['CLASS']]).'/images/'.$plIcon);
446  } else {
447  $image = $plIcon;
448  }
449 
450  $entries .= '<div class="iconmenu" id="menuitem_icon_'.$id.'" onClick=\'location.href="'.$href.'"\' title="'.htmlescape($plDescription).'">';
451  $item = '<div class="imgcontainer"><img src="'.htmlescape($image).'" alt=""/></div><span>&nbsp;'.htmlescape($plHeadline).'</span>';
452  $entries .= $item."</div>\n";
453  }
454 
455  /* Append to menu */
456  if ($entries != "") {
457  $this->iconmenu .= $sectionMenu.$entries."</div>\n";
458  }
459  }
460  }
461 
462  /* Write menu output */
463  return $this->iconmenu;
464  }
465 
466  /*
467  * \brief Get the path of the index
468  *
469  * \param string $index The index which we want the path
470  */
471  function get_path ($index)
472  {
473  if (!isset($this->dirlist[$index])) {
474  return "";
475  }
476  return "../".$this->info[$this->dirlist[$index]]['PATH'];
477  }
478 
479  /*
480  * \brief Search for plugin index (id), identify entry by path and class
481  *
482  * \param string $class The name of the class
483  */
484  function get_index ($class)
485  {
486  /* Search for plugin index (id), identify entry by class */
487  if (isset($this->info[$class])) {
488  return $this->info[$class]['INDEX'];
489  }
490 
491  /* Nothing */
492  return 0;
493  }
494 
502  function plugin_access_allowed ($plug_id)
503  {
504  return isset($this->allowed_plugins[$plug_id]);
505  }
506 
512  public function resetCache ()
513  {
514  $this->menu = '';
515  $this->iconmenu = '';
516  $this->silly_cache = [];
517  $this->allowed_plugins = [];
518  }
519 
520  static function pluginInfos ($cname)
521  {
522  $plist = session::get('plist');
523  if ($plist) {
524  if (!isset($plist->info[$cname])) {
525  throw new UnknownClassException($cname);
526  }
527  return $plist->info[$cname];
528  } else {
529  trigger_error('plist not loaded yet');
530  }
531  }
532 
536  static function load ()
537  {
538  global $config, $plist;
539  if (!session::is_set('plist')) {
540  /* Initially load all classes */
541  load_all_classes();
542 
543  $plist = new pluglist();
544  session::set('plist', $plist);
545  $config->loadPlist($plist);
546  $config->resetDepartmentCache();
547  } else {
548  $plist = session::get('plist');
549  }
550 
551  return $plist;
552  }
553 
554  static function runMainInc ($index, $forceCleanup = FALSE)
555  {
556  global $BASE_DIR, $config, $plist, $ui, $smarty, $display, $remove_lock, $cleanup, $plug;
557 
558  if ($index == 'welcome') {
559  $plugin_dir = "$BASE_DIR/plugins/generic/welcome";
560  $plugin = $index;
561  } else {
562  $plugin_dir = $plist->get_path($index);
563  $plugin = $plist->dirlist[$index];
564  }
565  /* Used by get_template_path */
566  session::set('plugin_dir', $plugin_dir);
567 
568  try {
569  if ($forceCleanup) {
570  $cleanup = $remove_lock = TRUE;
571  }
572  if (is_file("$plugin_dir/main.inc")) {
573  $display = '';
574  require("$plugin_dir/main.inc");
575  } elseif (is_callable([$plugin, 'mainInc'])) {
576  $plugin::mainInc();
577  } else {
578  throw new FatalError(
579  htmlescape(sprintf(
580  _('Fatal error: Cannot find any plugin definitions for plugin "%s" ("%s" is not a file)!'),
581  $plugin,
582  "$plugin_dir/main.inc"
583  ))
584  );
585  }
586  if ($forceCleanup) {
587  $cleanup = $remove_lock = FALSE;
588  }
589  } catch (Exception $e) {
590  $smarty->assign('headline', _('Fatal error!'));
591  $smarty->assign('headline_image', 'geticon.php?context=status&icon=dialog-error&size=32');
592  $display = '<h1>'.htmlescape(_('An unrecoverable error occurred. Please contact your administator.')).'</h1><p>';
593  if (ini_get('display_errors') == 1) {
594  $display .= nl2br(htmlescape((string)$e));
595  } else {
596  $display .= 'Error detail display is turned off.';
597  }
598  $display .= '</p>'."\n";
599  }
600  }
601 }
resetCache()
Resets menu and ACL cache.
htmlescape(string $str)
Escape string for HTML output.
Definition: php_setup.inc:32
show_iconmenu()
Show the menu icon.
in_array_ics($value, array $items)
Check if a value exists in an array (case-insensitive)
Definition: functions.inc:814
static load()
Loads plist and load it in config object.
get_template_path($filename='', $plugin=FALSE, $path='')
Return themed path for specified base file.
Definition: functions.inc:174
static get($name)
Accessor of a session var.
This class contains all the function needed to make list of plugin and manage them.
static set($name, $value)
Set a value in a session.
check_access($infos)
Check whether we are allowed to modify the given acl or not.
$dnForeignRefs
Foreign references on DNs.
$info
The plInfo result for all plugin, using class as key. Contains the plugin index in &#39;INDEX&#39; and the pa...
Fatal error class. Does not extend FusionDirectoryError.
$allowed_plugins
List plugin indexes of all plugin that the user have acl for.
__construct()
List the plugins.
plugin_access_allowed($plug_id)
This function checks if we are allowed to view the plugin with the given id.
$dirlist
Using the plugin index as a key, the class of the plugin.
plugin_available($plugin)
Check if plugin is available.
Definition: functions.inc:108
static is_set($name)
Check if the name of the session is set.
get_infos($cname)
Get headline, description and icon of a plugin.
gen_menu()
Generate menu.