FusionDirectory
 All Data Structures Files Functions Variables
class_listing.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-2016 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 
31 class listing
32 {
33  var $xmlData;
34  var $entries;
35  var $departments = array();
36  var $departmentBrowser = FALSE;
37  var $departmentRootVisible = FALSE;
38  var $multiSelect = FALSE;
39  var $template;
40  var $headline;
41  var $base;
42  var $sortDirection = NULL;
43  var $sortColumn = NULL;
44  var $sortAttribute;
45  var $sortType;
46  var $numColumns;
47  var $baseMode = FALSE;
48  var $bases = array();
49  var $header = array();
50  var $colprops = array();
51  var $filters = array();
52  var $filter = NULL;
53  var $pid;
54  protected $departmentTypes = array();
55  var $objectTypes = array();
56  var $objectTypeCount = array();
57  protected $objectDnMapping = array();
58  protected $copyPasteHandler = NULL;
59  protected $snapshotHandler = NULL;
60  var $exporter = array();
61  var $exportColumns = array();
62  var $height = 0;
63  var $scrollPosition = 0;
64  var $baseSelector;
65  protected $filterCache = array();
66 
72  function __construct($data)
73  {
74  global $config;
75  global $class_mapping;
76 
77  // Initialize pid
78  $this->pid = preg_replace("/[^0-9]/", "", microtime(TRUE));
79 
80  if (!$this->load($data)) {
81  if (is_array($data)) {
82  die("Cannot parse data : ".print_r($data, TRUE));
83  } else {
84  die("Cannot parse $data!");
85  }
86  }
87 
88  // Set base for filter
89  if ($this->baseMode) {
90  $this->base = session::global_get("CurrentMainBase");
91  if ($this->base == NULL) {
92  $this->base = $config->current['BASE'];
93  }
94  $this->refreshBasesList();
95  } else {
96  $this->base = $config->current['BASE'];
97  }
98 
99  // Move footer information
100  $this->showFooter = ($config->get_cfg_value("listSummary") == "TRUE");
101 
102  // Register build in filters
103  $this->registerElementFilter("departmentType", "listing::filterDepartmentType");
104  $this->registerElementFilter("departmentLink", "listing::filterDepartmentLink");
105  $this->registerElementFilter("objectType", "listing::filterObjectType");
106  $this->registerElementFilter("link", "listing::filterLink");
107  $this->registerElementFilter("nameLink", "listing::filterNameLink");
108  $this->registerElementFilter("actions", "listing::filterActions");
109 
110  // Load exporters
111  foreach (array_keys($class_mapping) as $class) {
112  if (preg_match('/Exporter$/', $class)) {
113  $info = call_user_func(array($class, "getInfo"));
114  if ($info != NULL) {
115  $this->exporter = array_merge($this->exporter, $info);
116  }
117  }
118  }
119 
120  // Instanciate base selector
121  $this->baseSelector = new baseSelector($this->bases, $this->base);
122  }
123 
131  function setCopyPasteHandler($handler)
132  {
133  $this->copyPasteHandler = $handler;
134  }
135 
141  function setHeight($height)
142  {
143  $this->height = $height;
144  }
145 
153  function setSnapshotHandler($handler)
154  {
155  if ($handler->enabled()) {
156  $this->snapshotHandler = $handler;
157  } else {
158  $this->snapshotHandler = NULL;
159  }
160  }
161 
169  function setFilter($filter)
170  {
171  $this->filter = $filter;
172  $filter->headpage = $this;
173  if ($this->departmentBrowser) {
174  $this->departments = $this->getDepartments();
175  }
176  $this->filter->setBase($this->base);
177  }
178 
186  function registerElementFilter($name, $call)
187  {
188  if (!isset($this->filters[$name])) {
189  $this->filters[$name] = $call;
190  return TRUE;
191  }
192 
193  return FALSE;
194  }
195 
201  function load($data)
202  {
203  if (is_array($data)) {
204  $this->xmlData = $data;
205  } else {
206  $contents = file_get_contents($data);
207  $this->xmlData = xml::xml2array($contents, 1);
208  }
209 
210  $this->filterCache = array();
211 
212  if (!isset($this->xmlData['list'])) {
213  return FALSE;
214  }
215 
216  $this->xmlData = $this->xmlData["list"];
217 
218  // Load some definition values
219  foreach (array("departmentBrowser", "departmentRootVisible", "multiSelect", "baseMode") as $token) {
220  if (isset($this->xmlData['definition'][$token]) &&
221  $this->xmlData['definition'][$token] == "true") {
222  $this->$token = TRUE;
223  }
224  }
225 
226  // Fill objectTypes from departments and xml definition
227  $types = departmentManagement::getDepartmentTypes();
228  foreach ($types as $type) {
229  $i = objects::infos($type);
230  $this->departmentTypes[strtoupper($type)] = array(
231  'label' => $i['name'],
232  'image' => $i['icon'],
233  'category' => $i['aclCategory'],
234  'class' => $i['mainTab'],
235  'filter' => objects::getFilterObject($type),
236  'nameAttr' => $i['nameAttr'],
237  );
238  }
239  $this->categories = array();
240  if (isset($this->xmlData['definition']['objectType'])) {
241  if (isset($this->xmlData['definition']['objectType']['label'])) {
242  $this->xmlData['definition']['objectType'] = array($this->xmlData['definition']['objectType']);
243  }
244  foreach ($this->xmlData['definition']['objectType'] as $index => $otype) {
245  $this->objectTypes[$otype['objectClass']] = $otype;
246  if (isset($otype['category'])) {
247  $this->categories[] = $otype['category'];
248  }
249  }
250  }
251  $this->categories = array_unique($this->categories);
252 
253  // Parse layout per column
254  $this->colprops = $this->parseLayout($this->xmlData['table']['layout']);
255 
256  // Prepare table headers
257  $this->renderHeader();
258 
259  // Assign headline/Categories
260  $this->headline = _($this->xmlData['definition']['label']);
261  if (!is_array($this->categories)) {
262  $this->categories = array($this->categories);
263  }
264 
265  // Evaluate columns to be exported
266  if (isset($this->xmlData['table']['column'])) {
267  foreach ($this->xmlData['table']['column'] as $index => $cfg) {
268  if (isset($cfg['export']) && $cfg['export'] == "true") {
269  $this->exportColumns[] = $index;
270  }
271  }
272  }
273 
274  if (isset($this->xmlData['actiontriggers']['action']['type'])) {
275  $this->xmlData['actiontriggers']['action'] = array($this->xmlData['actiontriggers']['action']);
276  }
277 
278  return TRUE;
279  }
280 
281 
282  function renderHeader()
283  {
284  $this->header = array();
285  $this->plainHeader = array();
286 
287  // Initialize sort?
288  $sortInit = FALSE;
289  if (!$this->sortDirection) {
290  $this->sortColumn = 0;
291  if (isset($this->xmlData['definition']['defaultSortColumn'])) {
292  $this->sortColumn = $this->xmlData['definition']['defaultSortColumn'];
293  } else {
294  $this->sortAttribute = "";
295  }
296  $this->sortDirection = array();
297  $sortInit = TRUE;
298  }
299 
300  if (isset($this->xmlData['table']['column'])) {
301  foreach ($this->xmlData['table']['column'] as $index => $cfg) {
302  // Initialize everything to one direction
303  if ($sortInit) {
304  $this->sortDirection[$index] = FALSE;
305  }
306 
307  $sorter = "";
308  if ($index == $this->sortColumn && isset($cfg['sortAttribute']) &&
309  isset($cfg['sortType'])) {
310  $this->sortAttribute = $cfg['sortAttribute'];
311  $this->sortType = $cfg['sortType'];
312  $sorter = "&nbsp;<img class='center' title='".($this->sortDirection[$index]?_("Up"):_("Down"))."' src='geticon.php?context=actions&amp;size=16&amp;icon=view-sort-".($this->sortDirection[$index]?"descending":"ascending")."' alt='".($this->sortDirection[$index]?_('Sort up'):_('Sort down'))."'>";
313  }
314  $sortable = (isset($cfg['sortAttribute']));
315 
316  $link = "href='?plug=".$_GET['plug']."&amp;PID=".$this->pid."&amp;act=SORT_$index'";
317  if (isset($cfg['label'])) {
318  if ($sortable) {
319  $this->header[$index] = "<th ".$this->colprops[$index]."><a $link>"._($cfg['label'])."$sorter</a></th>";
320  } else {
321  $this->header[$index] = "<th ".$this->colprops[$index].">"._($cfg['label'])."</th>";
322  }
323  $this->plainHeader[] = _($cfg['label']);
324  } else {
325  if ($sortable) {
326  $this->header[$index] = "<th ".$this->colprops[$index]."><a $link>&nbsp;$sorter</a></th>";
327  } else {
328  $this->header[$index] = "<th ".$this->colprops[$index].">&nbsp;</th>";
329  }
330  $this->plainHeader[] = "";
331  }
332  }
333  }
334  }
335 
339  function render()
340  {
341  // Check for exeeded sizelimit
342  if (($message = check_sizelimit()) != '') {
343  return $message;
344  }
345 
346  // Initialize list
347  $result = '<input type="hidden" value="'.$this->pid.'" name="PID"/>'."\n";
348  $result .= '<input type="hidden" name="position_'.$this->pid.'" id="position_'.$this->pid.'"/>'."\n";
349  $height = 450;
350  if ($this->height != 0) {
351  $result .= '<input type="hidden" value="'.$this->height.'" id="d_height"/>'."\n";
352  $height = $this->height;
353  }
354 
355  $result .= '<div id="d_scrollbody" style="width:100%;">'."\n";
356  $result .= '<table style="width:100%;" id="t_scrolltable" class="listingTable">'."\n";
357 
358  // Build list header
359  $result .= '<thead><tr>'."\n";
360  if ($this->multiSelect) {
361  $width = '24px';
362  $result .= '<th style="text-align:center;padding:0;width:'.$width.';"><input type="checkbox" id="select_all" name="select_all" title="'._('Select all').'" onClick=\'toggle_all_("listing_selected_[0-9]*$","select_all");\' /></th>'."\n";
363  }
364  foreach ($this->header as $header) {
365  $result .= $header;
366  }
367  $result .= '</tr></thead>'."\n";
368 
369  // Build list body
370  $result .= '<tbody id="t_nscrollbody">'."\n";
371 
372  // No results? Just take an empty colspanned row
373  if (count($this->entries) + count($this->departments) == 0) {
374  $result .= '<tr><td colspan="'.($this->numColumns + ($this->multiSelect ? 1 : 0)).'" style="height:100%;width:100%;">&nbsp;</td></tr>'."\n";
375  }
376 
377  // Draw department browser if configured and we're not in sub mode
378  if ($this->departmentBrowser && ($this->filter->scope != 'sub')) {
379  // Fill with department browser if configured this way
380  $departmentIterator = new departmentSortIterator($this->departments, $this->sortDirection[$this->sortColumn]);
381  foreach ($departmentIterator as $row => $entry) {
382  $result .= '<tr>';
383 
384  // Render multi select if needed
385  if ($this->multiSelect) {
386  $result .= '<td>&nbsp;</td>';
387  }
388 
389  // Render defined department columns, fill the rest with some stuff
390  $rest = $this->numColumns;
391  foreach ($this->xmlData['table']['department'] as $index => $cfg) {
392  $colspan = 1;
393  if (isset($cfg['span'])) {
394  $colspan = $cfg['span'];
395  }
396  $result .= '<td colspan="'.$colspan.'" '.$this->colprops[$index].'>'.$this->renderCell('department', $index, $cfg['value'], $entry, $row).'</td>';
397  $rest -= $colspan;
398  }
399 
400  // Fill remaining cols with nothing
401  for ($i = $this->numColumns - $rest; $i < $this->numColumns; $i++) {
402  $result .= '<td '.$this->colprops[$i].'>&nbsp;</td>';
403  }
404  $result .= '</tr>';
405  }
406  }
407 
408  // Fill with contents, sort as configured
409  foreach ($this->entries as $row => $entry) {
410  $trow = '';
411 
412  // Render multi select if needed
413  if ($this->multiSelect) {
414  $trow .= '<td style="text-align:center;width:20px;"><input type="checkbox" id="listing_selected_'.$row.'" name="listing_selected_'.$row.'"/></td>'."\n";
415  }
416 
417  foreach ($this->xmlData['table']['column'] as $index => $cfg) {
418  $renderedCell = $this->renderCell('column', $index, $cfg['value'], $entry, $row);
419  $trow .= '<td '.$this->colprops[$index].'>'.$renderedCell.'</td>'."\n";
420 
421  // Save rendered column
422  $sort = preg_replace('/.*>([^<]+)<.*$/', '$1', $renderedCell);
423  $sort = str_replace('&nbsp;', '', $sort);
424  if (strpos($sort, '<') !== FALSE) {
425  $sort = '';
426  }
427  $this->entries[$row]['_sort'.$index] = $sort;
428  }
429 
430  // Save rendered entry
431  $this->entries[$row]['_rendered'] = $trow;
432  }
433 
434  // Complete list by sorting entries for _sort$index and appending them to the output
435  $entryIterator = new listingSortIterator($this->entries, $this->sortDirection[$this->sortColumn], "_sort".$this->sortColumn, $this->sortType);
436  foreach ($entryIterator as $row => $entry) {
437  // Apply custom class to row?
438  if (preg_match("/<rowClass:([a-z0-9_-]*)\/>/i", $entry['_rendered'], $matches)) {
439  $result .= "<tr class='".$matches[1]."'>\n";
440  $result .= preg_replace("/<rowClass[^>]+>/", '', $entry['_rendered']);
441  } else {
442  $result .= "<tr>\n";
443  $result .= $entry['_rendered'];
444  }
445 
446  $result .= "</tr>\n";
447  }
448 
449  // Close list body
450  $result .= "</tbody></table></div>";
451 
452  // Add the footer if requested
453  if ($this->showFooter) {
454  $result .= '<div class="nlistFooter">';
455 
456  if ($this->departmentBrowser && ($this->filter->scope != 'sub')) {
457  foreach ($this->departmentTypes as $objectType) {
458  if (isset($this->objectTypeCount[$objectType['label']])) {
459  $result .= '<img class="center" src="'.htmlentities($objectType['image'], ENT_COMPAT, 'UTF-8').'" title="'.$objectType['label'].'" alt="'.$objectType['label'].'"/>&nbsp;'.$this->objectTypeCount[$objectType['label']]."&nbsp;&nbsp;&nbsp;&nbsp;";
460  }
461  }
462  }
463 
464  foreach ($this->objectTypes as $objectType) {
465  if (isset($this->objectTypeCount[$objectType['label']])) {
466  $result .= '<img class="center" src="'.htmlentities($objectType['image'], ENT_COMPAT, 'UTF-8').'" title="'.$objectType['label'].'" alt="'.$objectType['label'].'"/>&nbsp;'.$this->objectTypeCount[$objectType['label']]."&nbsp;&nbsp;&nbsp;&nbsp;";
467  }
468  }
469 
470  $result .= '</div>';
471  }
472 
473  // Close list
474  $result .= '';
475 
476  // Add scroll positioner
477  $result .= '<script type="text/javascript">';
478  $result .= '$("t_nscrollbody").scrollTop= '.$this->scrollPosition.';';
479  $result .= 'var box = $("t_nscrollbody").onscroll= function() {$("position_'.$this->pid.'").value= this.scrollTop;}';
480  $result .= '</script>';
481 
482  $smarty = get_smarty();
483  $smarty->assign("usePrototype", "true");
484  $smarty->assign("FILTER", $this->filter->render());
485  $smarty->assign("SIZELIMIT", print_sizelimit_warning());
486  $smarty->assign("LIST", $result);
487  $smarty->assign("MULTISELECT", $this->multiSelect);
488 
489  // Assign navigation elements
490  $nav = $this->renderNavigation();
491  foreach ($nav as $key => $html) {
492  $smarty->assign($key, $html);
493  }
494 
495  // Assign action menu / base
496  $smarty->assign("ACTIONS", $this->renderActionMenu());
497  $smarty->assign("BASE", $this->renderBase());
498 
499  // Assign separator
500  $smarty->assign("SEPARATOR", "<img src='images/lists/seperator.png' alt='-' height='16' width='1' class='center'>");
501 
502  // Assign summary
503  $smarty->assign("HEADLINE", $this->headline);
504 
505  // Try to load template from plugin the folder first...
506  $file = get_template_path($this->xmlData['definition']['template'], TRUE);
507 
508  // ... if this fails, try to load the file from the theme folder.
509  if (!file_exists($file)) {
510  $file = get_template_path($this->xmlData['definition']['template']);
511  }
512 
513  return $smarty->fetch($file);
514  }
515 
519  function update()
520  {
521  $ui = get_userinfo();
522 
523  // Take care of base selector
524  if ($this->baseMode) {
525  $this->baseSelector->update();
526  // Check if a wrong base was supplied
527  if (!$this->baseSelector->checkLastBaseUpdate()) {
528  msg_dialog::display(_("Error"), msgPool::check_base(), ERROR_DIALOG);
529  }
530  }
531 
532  // Save base
533  $refresh = FALSE;
534  if ($this->baseMode) {
535  $this->base = $this->baseSelector->getBase();
536  session::global_set("CurrentMainBase", $this->base);
537  $refresh = TRUE;
538  }
539 
540  // Reset object counter / DN mapping
541  $this->objectTypeCount = array();
542  $this->objectDnMapping = array();
543 
544  // Do not do anything if this is not our PID
545  if ($refresh || !(isset($_REQUEST['PID']) && $_REQUEST['PID'] != $this->pid)) {
546 
547  // Save position if set
548  if (isset($_POST['position_'.$this->pid]) && is_numeric($_POST['position_'.$this->pid])) {
549  $this->scrollPosition = $_POST['position_'.$this->pid];
550  }
551 
552  // Override the base if we got a message from the browser navigation
553  if ($this->departmentBrowser && isset($_GET['act'])) {
554  if (preg_match('/^department_([0-9]+)$/', validate($_GET['act']), $match)) {
555  if (isset($this->departments[$match[1]])) {
556  $this->base = $this->departments[$match[1]]['dn'];
557  if ($this->baseMode) {
558  $this->baseSelector->setBase($this->base);
559  }
560  session::global_set("CurrentMainBase", $this->base);
561  }
562  }
563  }
564 
565  // Filter POST with "act" attributes -> posted from action menu
566  if (isset($_POST['exec_act']) && $_POST['act'] != '') {
567  if (preg_match('/^export.*$/', $_POST['act']) && isset($this->exporter[$_POST['act']])) {
568  $exporter = $this->exporter[$_POST['act']];
569  $userinfo = ", "._("created by")." ".$ui->cn." - ".strftime('%A, %d. %B %Y, %H:%M:%S');
570  $entryIterator = new listingSortIterator($this->entries, $this->sortDirection[$this->sortColumn], "_sort".$this->sortColumn, $this->sortType);
571  $sortedEntries = array();
572  foreach ($entryIterator as $entry) {
573  $sortedEntries[] = $entry;
574  }
575  $instance = new $exporter['class']($this->headline.$userinfo, $this->plainHeader, $sortedEntries, $this->exportColumns);
576  $type = call_user_func(array($exporter['class'], "getInfo"));
577  $type = $type[$_POST['act']];
578  send_binary_content($instance->query(), $type['filename'], $type = $type['mime']);
579  }
580  }
581 
582  // Filter GET with "act" attributes
583  if (isset($_GET['act'])) {
584  $key = validate($_GET['act']);
585  if (preg_match('/^SORT_([0-9]+)$/', $key, $match)) {
586  // Switch to new column or invert search order?
587  $column = $match[1];
588  if ($this->sortColumn != $column) {
589  $this->sortColumn = $column;
590  } else {
591  $this->sortDirection[$column] = !$this->sortDirection[$column];
592  }
593 
594  // Allow header to update itself according to the new sort settings
595  $this->renderHeader();
596  }
597  }
598 
599  if ($this->baseMode) {
600  // Override base if we got signals from the navigation elements
601  $action = '';
602  foreach (array_keys($_POST) as $key) {
603  if (preg_match('/^(ROOT|BACK|HOME)_x$/', $key, $match)) {
604  $action = $match[1];
605  break;
606  }
607  }
608 
609  // Navigation handling
610  if ($action == 'ROOT') {
611  $deps = $ui->get_module_departments($this->categories);
612  $this->setBase($deps[0]);
613  session::global_set('CurrentMainBase', $this->base);
614  } elseif ($action == 'BACK') {
615  $base = preg_replace('/^[^,]+,/', '', $this->base);
616  $this->tryAndSetBase($base);
617  } elseif ($action == 'HOME') {
618  $this->tryAndSetBase(get_base_from_people($ui->dn));
619  }
620  }
621  }
622 
623  // Reload departments
624  if ($this->departmentBrowser) {
625  $this->departments = $this->getDepartments();
626  }
627 
628  // Update filter and refresh entries
629  $this->filter->setBase($this->base);
630  $this->entries = $this->filter->query();
631 
632  // Fix filter if querie returns NULL
633  if ($this->entries == NULL) {
634  $this->entries = array();
635  }
636 
637  $this->dnToRow = array();
638  foreach ($this->entries as $row => $entry) {
639  $this->dnToRow[$entry['dn']] = $row;
640  }
641 
642  // Init snapshot list for renderSnapshotActions
643  if (is_object($this->snapshotHandler)) {
644  $this->snapshotHandler->initSnapshotCache($this->base);
645  }
646  }
647 
653  function setBase($base)
654  {
655  $this->base = $base;
656  if ($this->baseMode) {
657  $this->baseSelector->setBase($this->base);
658  }
659  }
660 
661  function tryAndSetBase($base)
662  {
663  $ui = get_userinfo();
664  $deps = $ui->get_module_departments($this->categories);
665  if (in_array_ics($base, $deps)) {
666  $this->setBase($base);
667  session::global_set("CurrentMainBase", $this->base);
668  }
669  }
670 
676  function getBase()
677  {
678  return $this->base;
679  }
680 
686  function parseLayout($layout)
687  {
688  $result = array();
689  $layout = preg_replace("/^\|/", "", $layout);
690  $layout = preg_replace("/\|$/", "", $layout);
691  $cols = explode("|", $layout);
692 
693  foreach ($cols as $index => $cfg) {
694  if ($cfg != "") {
695  $res = "";
696  $classes = "";
697  $components = explode(';', $cfg);
698  foreach ($components as $part) {
699  switch ($part) {
700  case 'r':
701  $res .= 'text-align:right;';
702  break;
703  case 'l':
704  $res .= 'text-align:left;';
705  break;
706  case 'c':
707  $res .= 'text-align:center;';
708  break;
709  case 'o':
710  $classes .= 'optional ';
711  break;
712  default:
713  if (preg_match('/^[0-9]+(|px|%)(-d)?$/', $part)) {
714  if (!preg_match('/-d$/', $part)) {
715  /* d suffix means dynamic, ie no fixed width */
716  $res .= "width:$part;";
717  } else {
718  /* Remove the -d suffix */
719  $part = preg_replace('/-d$/', '', $part);
720  }
721  $res .= "min-width:$part;";
722  }
723  }
724  }
725 
726  $result[$index] = " style='$res'";
727  if ($classes != "") {
728  $result[$index] .= " class='$classes'";
729  }
730  } else {
731  $result[$index] = "";
732  }
733  }
734 
735  // Save number of columns for later use
736  $this->numColumns = count($cols);
737 
738  return $result;
739  }
740 
741 
742  function renderCell($table, $index, $data, $cfg, $row)
743  {
744  // Replace flat attributes in data string
745  $offset = 0;
746  while (preg_match('/%{([^}:]+)}/', $data, $m, PREG_OFFSET_CAPTURE, $offset)) {
747  if (isset($cfg[$m[1][0]])) {
748  $replace = $cfg[$m[1][0]];
749  if (is_array($replace)) {
750  $replace = $replace[0];
751  }
752  $replace = htmlentities($replace, ENT_COMPAT, 'UTF-8');
753  } else {
754  $replace = '&nbsp;';
755  }
756  $data = substr_replace($data, $replace, $m[0][1], strlen($m[0][0]));
757  $offset = $m[0][1] + strlen($replace);
758  }
759 
760  // Watch out for filters and prepare to execute them
761  $data = $this->processElementFilter($table, $index, $data, $cfg, $row);
762 
763  return $data;
764  }
765 
766 
767  function renderBase()
768  {
769  if (!$this->baseMode) {
770  return;
771  }
772 
773  return $this->baseSelector->render();
774  }
775 
776 
777  function processElementFilter($type, $index, $data, $cfg, $row)
778  {
779  if (isset($this->filterCache[$type.$index])) {
780  $filters = $this->filterCache[$type.$index];
781  } else {
782  preg_match_all('/%{filter:([^(]+)\((.*)\)}/', $data, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
783 
784  $filters = array();
785  foreach ($matches as $match) {
786  $cl = '';
787  $method = '';
788  if (!preg_match('/^(.*)::(.*)$/', $match[1][0], $m)) {
789  if (!isset($this->filters[$match[1][0]]) || !preg_match('/^(.*)::(.*)$/', $this->filters[$match[1][0]], $m)) {
790  trigger_error('Unknown filter '.$match[1][0]);
791  continue;
792  }
793  }
794  $cl = $m[1];
795  $method = $m[2];
796 
797  // Prepare params for function call
798  preg_match_all('/"[^"]+"|[^,]+/', $match[2][0], $parts);
799 
800  $filters[$match[0][0]] = array($cl, $method, $parts[0], $match[0][1]);
801  }
802 
803  $this->filterCache[$type.$index] = $filters;
804  }
805 
806  foreach ($filters as $filterstring => $filter) {
807  list ($cl, $method, $parts, $offset) = $filter;
808  $params = array();
809  foreach ($parts as $param) {
810  switch ($param) {
811  case 'row':
812  $params[] = $row;
813  break;
814  case 'pid':
815  $params[] = $this->pid;
816  break;
817  case 'base':
818  $params[] = $this->getBase();
819  break;
820  case 'entry':
821  $params[] = $cfg;
822  break;
823  case 'objectType':
824  $params[] = $this->getObjectType($cfg['dn'], $cfg);
825  break;
826  case 'dn':
827  $params[] = $cfg['dn'];
828  break;
829  default:
830  if (preg_match('/^"(.*)"$/', $param, $m)) {
831  // Fixie with "" is passed directly
832  $params[] = $m[1];
833  } elseif (isset($cfg[$param])) {
834  // LDAP variables get replaced by their objects
835  $values = $cfg[$param];
836  if (is_array($values)) {
837  unset($values['count']);
838  }
839  $params[] = $values;
840  } else {
841  $params[] = '';
842  }
843  break;
844  }
845  }
846 
847  // Replace information
848  if ($cl == 'listing') {
849  // Non static call
850  $data = substr_replace($data, call_user_func_array(array($this, $method), $params), $offset, strlen($filterstring));
851  } else {
852  // Static call
853  $data = substr_replace($data, call_user_func_array(array($cl, $method), $params), $offset, strlen($filterstring));
854  }
855  }
856 
857  return $data;
858  }
859 
865  function getObjectType($dn, $attrs)
866  {
867  return $this->genericGetType($dn, $attrs, $this->objectTypes);
868  }
869 
873  function getDepartmentType($dn, $attrs)
874  {
875  return $this->genericGetType($dn, $attrs, $this->departmentTypes);
876  }
877 
881  protected function genericGetType($dn, $attrs, $types)
882  {
883  if (isset($this->objectDnMapping[$dn])) {
884  return $this->objectDnMapping[$dn];
885  }
886  $classes = $attrs['objectClass'];
887  // Walk thru types and see if there's something matching
888  foreach ($types as $type => $objectType) {
889  if (isset($objectType['filter'])) {
890  if ($objectType['filter']($attrs)) {
891  $this->objectDnMapping[$dn] = $type;
892  return $this->objectDnMapping[$dn];
893  } else {
894  continue;
895  }
896  }
897  $ocs = $objectType['objectClass'];
898  if (!is_array($ocs)) {
899  $ocs = array($ocs);
900  }
901 
902  $found = TRUE;
903  foreach ($ocs as $oc) {
904  if (preg_match('/^!(.*)$/', $oc, $match)) {
905  $oc = $match[1];
906  if (in_array($oc, $classes)) {
907  $found = FALSE;
908  }
909  } else {
910  if (!in_array($oc, $classes)) {
911  $found = FALSE;
912  }
913  }
914  }
915 
916  if ($found) {
917  $this->objectDnMapping[$dn] = $type;
918  return $this->objectDnMapping[$dn];
919  }
920  }
921 
922  return NULL;
923  }
924 
925  function getObjectTypeInfos($dn, $attrs)
926  {
927  $type = $this->getObjectType($dn, $attrs);
928  if ($type === NULL) {
929  return NULL;
930  }
931  return $this->objectTypes[$type];
932  }
933 
941  function filterObjectType($row, $dn)
942  {
943  return $this->filterGenericType($dn, $this->objectTypes, $this->getObjectType($dn, $this->entries[$row]));
944  }
945 
949  protected function filterGenericType($dn, $types, $type)
950  {
951  $result = "&nbsp;";
952 
953  if ($type) {
954  $result = '<img class="center" title="'.$dn.'" src="'.htmlentities($types[$type]['image'], ENT_COMPAT, 'UTF-8').'" alt="'.$type.'"/>';
955  if (!isset($this->objectTypeCount[$types[$type]['label']])) {
956  $this->objectTypeCount[$types[$type]['label']] = 0;
957  }
958  $this->objectTypeCount[$types[$type]['label']]++;
959  }
960 
961  return $result;
962  }
963 
971  function filterDepartmentType($row, $dn)
972  {
973  return $this->filterGenericType($dn, $this->departmentTypes, $this->getDepartmentType($dn, $this->departments[$row]));
974  }
975 
985  function filterActions($dn, $row, $classes)
986  {
987  // Do nothing if there's no menu defined
988  if (!isset($this->xmlData['actiontriggers']['action'])) {
989  return '&nbsp;';
990  }
991 
992  // Go thru all actions
993  $result = '';
994  $emptyimg = '<img src="images/empty.png" alt=" " class="center optional"/>';
995  foreach ($this->xmlData['actiontriggers']['action'] as $action) {
996  // If there's an objectclass definition and we don't have it
997  // add an empty picture here.
998  if (isset($action['objectclass'])) {
999  $objectclass = $action['objectclass'];
1000  $skip = FALSE;
1001  if (preg_match('/^!(.*)$/', $objectclass, $m)) {
1002  $objectclass = $m[1];
1003  if (in_array($objectclass, $classes)) {
1004  $skip = TRUE;
1005  }
1006  } elseif (is_string($objectclass)) {
1007  if (!in_array($objectclass, $classes)) {
1008  $skip = TRUE;
1009  }
1010  } elseif (is_array($objectclass)) {
1011  if (count(array_intersect($objectclass, $classes)) != count($objectclass)) {
1012  $skip = TRUE;
1013  }
1014  }
1015  if ($skip) {
1016  $result .= $emptyimg;
1017  if ($action['type'] == 'snapshot') {
1018  $result .= $emptyimg;
1019  }
1020  continue;
1021  }
1022  }
1023 
1024  // Skip the entry completely if there's no permission to execute it
1025  if (!$this->hasActionPermission($action, $dn, $row)) {
1026  $result .= $emptyimg;
1027  continue;
1028  }
1029 
1030  // Skip entry if the pseudo filter does not fit
1031  if (isset($action['filter']) && preg_match('/^[a-z0-9_]+!?=[a-z0-9_]+$/i', $action['filter'])) {
1032  list($fa, $fv) = explode('=', $action['filter']);
1033  if (preg_match('/^(.*)!$/', $fa, $m)) {
1034  $fa = $m[1];
1035  if (isset($this->entries[$row][$fa]) && $this->entries[$row][$fa][0] == $fv) {
1036  $result .= $emptyimg;
1037  continue;
1038  }
1039  } else {
1040  if (!isset($this->entries[$row][$fa]) && !$this->entries[$row][$fa][0] == $fv) {
1041  $result .= $emptyimg;
1042  continue;
1043  }
1044  }
1045  }
1046 
1047  if ($action['type'] == 'entry') {
1048  // Render normal entries as usual
1049  $label = $this->processElementFilter('label', $action['name'], $action['label'], $this->entries[$row], $row);
1050  $image = $this->processElementFilter('image', $action['name'], $action['image'], $this->entries[$row], $row);
1051  $result .= '<input class="center" type="image" src="'.htmlentities($image, ENT_COMPAT, 'UTF-8').'" title="'.$label.'" alt="'.$label.'" '.
1052  'name="listing_'.$action['name'].'_'.$row.'"/>';
1053  } elseif (($action['type'] == 'copypaste') || ($action['type'] == 'snapshot')) {
1054  // Handle special types
1055  $objectType = $this->getObjectTypeInfos($dn, $this->entries[$row]);
1056  $category = $class = NULL;
1057  if ($objectType) {
1058  $category = $objectType['category'];
1059  $class = $objectType['class'];
1060  }
1061 
1062  if ($action['type'] == 'copypaste') {
1063  $copy = (!isset($action['copy']) || ($action['copy'] == 'true'));
1064  $cut = (!isset($action['cut']) || ($action['cut'] == 'true'));
1065  $result .= $this->renderCopyPasteActions($row, $this->entries[$row]['dn'], $category, $class, $copy, $cut);
1066  } else {
1067  $result .= $this->renderSnapshotActions($row, $this->entries[$row]['dn'], $category);
1068  }
1069  }
1070  }
1071 
1072  return $result;
1073  }
1074 
1084  function filterDepartmentLink($row, $dn, $description)
1085  {
1086  $attr = $this->departments[$row]['sort-attribute'];
1087  $name = $this->departments[$row][$attr];
1088  if (is_array($name)) {
1089  $name = $name[0];
1090  }
1091  $result = htmlentities(sprintf("%s [%s]", $name, $description[0]), ENT_COMPAT, 'UTF-8');
1092  return "<a href='?plug=".$_GET['plug']."&amp;PID=$this->pid&amp;act=department_$row' title='$dn'>$result</a>";
1093  }
1094 
1098  function filterNameLink($row, $dn)
1099  {
1100  $infos = $this->getObjectTypeInfos($dn, $this->entries[$row]);
1101  $value = $this->entries[$row][$infos['nameAttr']];
1102  if (is_array($value)) {
1103  unset($value['count']);
1104  }
1105  return $this->filterLink($row, $dn, "%s", $value);
1106  }
1107 
1111  function filterLink()
1112  {
1113  $row = func_get_arg(0);
1114  $pid = $this->pid;
1115  $dn = func_get_arg(1);
1116  $params = array(func_get_arg(2));
1117 
1118  // Collect sprintf params
1119  for ($i = 3;$i < func_num_args();$i++) {
1120  $val = func_get_arg($i);
1121  if (empty($val)) {
1122  continue;
1123  }
1124  if (!is_array($val)) {
1125  $val = array($val);
1126  }
1127  $val = array_map(
1128  function ($v)
1129  {
1130  return htmlentities($v, ENT_COMPAT, 'UTF-8');
1131  },
1132  $val
1133  );
1134  $params[] = implode("<br/>\n", $val);
1135  }
1136 
1137  $result = '&nbsp;';
1138  if (count($params) > 1) {
1139  $trans = call_user_func_array('sprintf', $params);
1140  if ($trans != '') {
1141  return '<a href="?plug='.$_GET['plug'].'&amp;PID='.$pid.'&amp;act=listing_edit_'.$row.'" title="'.$dn.'">'.$trans.'</a>';
1142  }
1143  }
1144 
1145  return $result;
1146  }
1147 
1148 
1149  function renderNavigation()
1150  {
1151  $result = array();
1152  $enableBack = TRUE;
1153  $enableRoot = TRUE;
1154  $enableHome = TRUE;
1155 
1156  $ui = get_userinfo();
1157 
1158  /* Check if base = first available base */
1159  $deps = $ui->get_module_departments($this->categories);
1160 
1161  if (!count($deps) || $deps[0] == $this->filter->base) {
1162  $enableBack = FALSE;
1163  $enableRoot = FALSE;
1164  }
1165 
1166  /* Check if we are in users home department */
1167  if (!count($deps) || ($this->filter->base == get_base_from_people($ui->dn)) || !in_array_ics(get_base_from_people($ui->dn), $deps)) {
1168  $enableHome = FALSE;
1169  }
1170 
1171  /* Draw root button */
1172  if ($enableRoot) {
1173  $result["ROOT"] = "<input class='center' type='image' src='geticon.php?context=actions&amp;icon=go-first&amp;size=16' ".
1174  "title='"._("Go to root department")."' name='ROOT' alt='"._("Root")."'>";
1175  } else {
1176  $result["ROOT"] = "<img src='geticon.php?context=actions&amp;icon=go-first&amp;size=16&amp;disabled=1' class='center' alt='"._("Root")."'>";
1177  }
1178 
1179  /* Draw back button */
1180  if ($enableBack) {
1181  $result["BACK"] = "<input class='center' type='image' src='geticon.php?context=actions&amp;icon=go-up&amp;size=16' ".
1182  "title='"._("Go up one department")."' alt='"._("Up")."' name='BACK'>";
1183  } else {
1184  $result["BACK"] = "<img src='geticon.php?context=actions&amp;icon=go-up&amp;size=16&amp;disabled=1' class='center' alt='"._("Up")."'>";
1185  }
1186 
1187  /* Draw home button */
1188  if ($enableHome) {
1189  $result["HOME"] = '<input class="center" type="image" src="geticon.php?context=actions&amp;icon=go-home&amp;size=16"'.
1190  ' title="'._("Go to user's department").'" alt="'._('Home').'" name="HOME"/>';
1191  } else {
1192  $result["HOME"] = "<img src='geticon.php?context=actions&amp;icon=go-home&amp;size=16&amp;disabled=1' class='center' alt='"._("Home")."'>";
1193  }
1194 
1195  /* Draw reload button, this button is enabled everytime */
1196  $result["RELOAD"] = "<input class='center optional' type='image' src='geticon.php?context=actions&amp;icon=view-refresh&amp;size=16' ".
1197  "title='"._("Reload list")."' name='REFRESH' alt='"._("Submit")."'>";
1198 
1199  return $result;
1200  }
1201 
1205  function getAction()
1206  {
1207  global $config;
1208 
1209  // Do not do anything if this is not our PID, or there's even no PID available...
1210  if (!isset($_REQUEST['dn']) && (!isset($_REQUEST['PID']) || $_REQUEST['PID'] != $this->pid)) {
1211  return;
1212  }
1213 
1214  // Save position if set
1215  if (isset($_POST['position_'.$this->pid]) && is_numeric($_POST['position_'.$this->pid])) {
1216  $this->scrollPosition = $_POST['position_'.$this->pid];
1217  }
1218 
1219  $result = array("targets" => array(), "action" => "");
1220 
1221  // Filter GET with "act" attributes
1222  if (isset($_GET['act'])) {
1223  $key = validate($_GET['act']);
1224  if (preg_match('/^listing_([a-zA-Z_]+)_([0-9]+)$/', $key, $m)) {
1225  $action = $m[1];
1226  $target = $m[2];
1227  if (isset($this->entries[$target]['dn'])) {
1228  $result['action'] = $action;
1229  $result['targets'][] = $this->entries[$target]['dn'];
1230  }
1231  } elseif (isset($_REQUEST['dn']) && preg_match('/^listing_([a-zA-Z_]+)$/', $key, $m)) {
1232  /* Pre-render list to init things if a dn is gonna be opened on first load */
1233  $this->setBase($config->current['BASE']);
1234  $this->filter->setCurrentScope('sub');
1235  $this->update();
1236  $this->render();
1237  $dn = urldecode($_REQUEST['dn']);
1238  $result['action'] = $m[1];
1239  $result['targets'][] = $dn;
1240  // Make sure no other management class intercept the same dn
1241  unset($_REQUEST['dn']);
1242  }
1243 
1244  // Drop targets if empty
1245  if (count($result['targets']) == 0) {
1246  unset($result['targets']);
1247  }
1248  if (preg_match('/^(edit)_([a-zA-Z_]+)$/', $result['action'], $m)) {
1249  $result['action'] = $m[1];
1250  $result['subaction'] = $m[2];
1251  }
1252  return $result;
1253  }
1254 
1255  // Filter POST with "listing_" attributes
1256  foreach (array_keys($_POST) as $key) {
1257 
1258  // Capture selections
1259  if (preg_match('/^listing_selected_[0-9]+$/', $key)) {
1260  $target = preg_replace('/^listing_selected_([0-9]+)$/', '$1', $key);
1261  if (isset($this->entries[$target]['dn'])) {
1262  $result['targets'][] = $this->entries[$target]['dn'];
1263  }
1264  continue;
1265  }
1266 
1267  // Capture action with target - this is a one shot
1268  if (preg_match('/^listing_[a-zA-Z_]+_[0-9]+(|_x)$/', $key)) {
1269  $target = preg_replace('/^listing_[a-zA-Z_]+_([0-9]+)(|_x)$/', '$1', $key);
1270  if (isset($this->entries[$target]['dn'])) {
1271  $result['action'] = preg_replace('/^listing_([a-zA-Z_]+)_[0-9]+(|_x)$/', '$1', $key);
1272  $result['targets'] = array($this->entries[$target]['dn']);
1273  }
1274  break;
1275  }
1276 
1277  // Capture action without target
1278  if (preg_match('/^listing_[a-zA-Z_]+(|_x)$/', $key)) {
1279  $result['action'] = preg_replace('/^listing_([a-zA-Z_]+)(|_x)$/', '$1', $key);
1280  continue;
1281  }
1282  }
1283 
1284  // Filter POST with "act" attributes -> posted from action menu
1285  if (isset($_POST['act']) && $_POST['act'] != '') {
1286  if (!preg_match('/^export.*$/', $_POST['act'])) {
1287  $result['action'] = validate($_POST['act']);
1288  }
1289  }
1290 
1291  // Drop targets if empty
1292  if (count($result['targets']) == 0) {
1293  unset($result['targets']);
1294  }
1295  if (preg_match('/^(edit)_([a-zA-Z_]+)/', $result['action'], $m)) {
1296  $result['action'] = $m[1];
1297  $result['subaction'] = $m[2];
1298  }
1299  return $result;
1300  }
1301 
1302 
1303  function renderActionMenu()
1304  {
1305  // Don't send anything if the menu is not defined
1306  if (!isset($this->xmlData['actionmenu']['action'])) {
1307  return "";
1308  }
1309 
1310  // Make sure we got an array of actions
1311  if (isset($this->xmlData['actionmenu']['action']['type'])) {
1312  $this->xmlData['actionmenu']['action'] = array($this->xmlData['actionmenu']['action']);
1313  }
1314 
1315  // Load shortcut
1316  $result = '<input type="hidden" name="act" id="actionmenu" value="">'.
1317  '<div style="display:none"><input type="submit" name="exec_act" id="exec_act" value=""/></div>'.
1318  '<ul class="level1" id="root"><li><a href="#">'._('Actions').
1319  '&nbsp;<img class="center optional" src="images/down-arrow.png" alt="down arrow"/></a>';
1320 
1321  // Build ul/li list
1322  $result .= $this->recurseActions($this->xmlData['actionmenu']['action']);
1323 
1324  return '<div id="pulldown">'.$result.'</li></ul></div>';
1325  }
1326 
1327  function renderActionMenuActionLink($separator, $action, $name, $icon)
1328  {
1329  return '<li'.$separator.' id="actionmenu_'.$action.'">'
1330  .'<a href="#" onClick="'
1331  ."document.getElementById('actionmenu').value='$action';document.getElementById('exec_act').click();"
1332  .'">'
1333  .'<img src="'.htmlentities($icon, ENT_COMPAT, 'UTF-8').'" alt="'.$action.'" class="center">&nbsp;'.$name.'</a>'
1334  .'</li>';
1335  }
1336 
1337  function recurseActions(&$actions)
1338  {
1339  global $class_mapping;
1340  static $level = 2;
1341  $result = "<ul class='level$level'>";
1342  $separator = "";
1343 
1344  foreach ($actions as &$action) {
1345 
1346  // Skip the entry completely if there's no permission to execute it
1347  if (!$this->hasActionPermission($action, $this->filter->base)) {
1348  continue;
1349  }
1350 
1351  // Skip entry if there're missing dependencies
1352  if (isset($action['depends'])) {
1353  $deps = is_array($action['depends'])?$action['depends']:array($action['depends']);
1354  foreach ($deps as $clazz) {
1355  if (!isset($class_mapping[$clazz])) {
1356  continue 2;
1357  }
1358  }
1359  }
1360 
1361  if ($action['type'] == "separator") {
1362  $separator = " style='border-top:1px solid #AAA' ";
1363  continue;
1364  }
1365 
1366  // Dive into subs
1367  if ($action['type'] == "sub" && isset($action['action'])) {
1368  $level++;
1369  if (isset($action['label'])) {
1370  $img = "";
1371  if (isset($action['image'])) {
1372  $img = "<img class='center' src='".htmlentities($action['image'], ENT_COMPAT, 'UTF-8')."' alt='".$action['label']."'/>&nbsp;";
1373  }
1374  $result .= "<li id='actionmenu_".strtolower($action['label'])."'$separator><a href='#'>$img"._($action['label'])."&nbsp;<img src='images/forward-arrow.png' alt='forward arrow'/></a>";
1375  }
1376 
1377  // Ensure we've an array of actions, this enables sub menus with only one action.
1378  if (isset($action['action']['type'])) {
1379  $action['action'] = array($action['action']);
1380  }
1381 
1382  $result .= $this->recurseActions($action['action'])."</li>";
1383  $level--;
1384  $separator = "";
1385  continue;
1386  }
1387 
1388  // Render entry elseways
1389  if (isset($action['label'])) {
1390  $result .= $this->renderActionMenuActionLink($separator, $action['name'], _($action['label']), $action['image']);
1391  }
1392 
1393  // Check for special types
1394  switch ($action['type']) {
1395  case 'copypaste':
1396  $cut = !isset($action['cut']) || $action['cut'] != "false";
1397  $copy = !isset($action['copy']) || $action['copy'] != "false";
1398  $result .= $this->renderCopyPasteMenu($separator, $copy, $cut);
1399  break;
1400 
1401  case 'snapshot':
1402  $result .= $this->renderSnapshotMenu($separator);
1403  break;
1404 
1405  case 'exporter':
1406  $result .= $this->renderExporterMenu($separator);
1407  break;
1408 
1409  case 'daemon':
1410  $result .= $this->renderDaemonMenu($separator);
1411  break;
1412  }
1413 
1414  $separator = "";
1415  }
1416  unset($action);
1417 
1418  $result .= "</ul>";
1419  return $result;
1420  }
1421 
1432  function hasActionPermission(&$action, $dn, $row = NULL)
1433  {
1434  global $ui;
1435 
1436  if (isset($action['acl'])) {
1437  if (isset ($action['aclInfos'])) {
1438  $aclInfos = $action['aclInfos'];
1439  } else {
1440  /* First time we check permission for this action */
1441  if ($row !== NULL) {
1442  $otype = $this->getObjectTypeInfos($dn, $this->entries[$row]);
1443  } else {
1444  $otype = FALSE;
1445  }
1446  $acls = $action['acl'];
1447  if (!is_array($acls)) {
1448  $acls = array($acls);
1449  }
1450  $aclInfos = array();
1451 
1452  // Every ACL has to pass
1453  foreach ($acls as $acl) {
1454  $module = $this->categories;
1455  $aclList = array();
1456 
1457  // Replace %acl if available
1458  if ($otype) {
1459  $acl = str_replace('%acl', $otype['category'].'/'.$otype['class'], $acl);
1460  }
1461 
1462  // Split for category and plugins if needed
1463  if (preg_match('/^\[([rwcdm]+)\]$/', $acl, $match)) {
1464  // match for "[rw]" style entries
1465  $aclList = array($match[1]);
1466  } elseif (preg_match('/^([a-zA-Z0-9]+\/?[a-zA-Z0-9]+)\[([rwcdm]+)\]$/', $acl, $match)) {
1467  // match for "user[rw]" style entries
1468  // match for "user/user[rw]" style entries
1469  $module = $match[1];
1470  $aclList = array($match[2]);
1471  } elseif (preg_match('/^([a-zA-Z0-9]+\/[a-zA-Z0-9]+)\[([a-zA-Z0-9]+:[rwcdm]+(,[a-zA-Z0-9]+:[rwcdm]+)*)\]$/', $acl, $match)) {
1472  // match "user/user[userPassword:rw(,...)*]" style entries
1473  $module = $match[1];
1474  $aclList = explode(',', $match[2]);
1475  }
1476 
1477  $modules = $module;
1478  if (!is_array($modules)) {
1479  $modules = array($modules);
1480  }
1481 
1482  $aclInfos[] = array($aclList, $modules);
1483  }
1484  $action['aclInfos'] = $aclInfos;
1485  }
1486 
1487  foreach ($aclInfos as $aclInfo) {
1488  list ($aclList, $modules) = $aclInfo;
1489  // Walk thru prepared ACL by using $module
1490  foreach ($aclList as $sAcl) {
1491  $checkAcl = '';
1492 
1493  // Category or detailed permission?
1494  foreach ($modules as $module) {
1495  if (strpos($module, '/') !== FALSE) {
1496  if (preg_match('/([a-zA-Z0-9]+):([rwcdm]+)/', $sAcl, $m)) {
1497  $checkAcl .= $ui->get_permissions($dn, $module, $m[1]);
1498  $sAcl = $m[2];
1499  } else {
1500  $checkAcl .= $ui->get_permissions($dn, $module, '0');
1501  }
1502  } else {
1503  $checkAcl .= $ui->get_category_permissions($dn, $module);
1504  }
1505  }
1506 
1507  // Split up remaining part of the acl and check if it we're
1508  // allowed to do something...
1509  $parts = str_split($sAcl);
1510  foreach ($parts as $part) {
1511  if (strpos($checkAcl, $part) === FALSE) {
1512  return FALSE;
1513  }
1514  }
1515  }
1516  }
1517  }
1518 
1519  return TRUE;
1520  }
1521 
1525  function refreshBasesList()
1526  {
1527  global $config;
1528  $ui = get_userinfo();
1529 
1530  // Fill internal bases list
1531  $this->bases = array();
1532  $deps = $ui->get_module_departments($this->categories);
1533  foreach ($config->idepartments as $key => $dep) {
1534  if (in_array_ics($key, $deps)) {
1535  $this->bases[$key] = $dep;
1536  }
1537  }
1538 
1539  if (!empty($this->bases) && !isset($this->bases[$this->base])) {
1540  $this->base = key($this->bases);
1541  }
1542 
1543  // Populate base selector if already present
1544  if ($this->baseSelector && $this->baseMode) {
1545  $this->baseSelector->setBases($this->bases);
1546  $this->baseSelector->setBase($this->base);
1547  $this->baseSelector->update(TRUE);
1548  }
1549  }
1550 
1552  function getDepartments()
1553  {
1554  $departments = array();
1555  $ui = get_userinfo();
1556 
1557  // Get list of supported department types
1558  $types = departmentManagement::getDepartmentTypes();
1559 
1560  // Load departments allowed by ACL
1561  $validDepartments = $ui->get_module_departments($this->categories);
1562 
1563  /* Fetch departments andtheir informations */
1564  foreach ($types as $type) {
1565  $i = objects::infos($type);
1566  $deps = objects::ls(
1567  $type,
1568  array(
1569  'dn' => 'raw',
1570  'objectClass' => 'raw',
1571  'description' => 'raw',
1572  $i['mainAttr'] => 'raw'
1573  ),
1574  $this->base,
1575  '',
1576  FALSE,
1577  'one'
1578  );
1579 
1580  // Analyze list of departments
1581  foreach ($deps as $department) {
1582  if (!in_array($department['dn'], $validDepartments)) {
1583  continue;
1584  }
1585 
1586  /* php-ldap like indexes are needed for renderCell */
1587  $count = 0;
1588  foreach ($department as $key => $values) {
1589  if ($key != 'dn') {
1590  $department[$count++] = $key;
1591  }
1592  }
1593  $department['count'] = $count;
1594 
1595  // Add the attribute where we use for sorting
1596  $department['sort-attribute'] = $i['mainAttr'];
1597 
1598  // Move to the result list
1599  $departments[] = $department;
1600  }
1601  }
1602 
1603  return $departments;
1604  }
1605 
1606 
1607  function renderCopyPasteMenu($separator, $copy = TRUE, $cut = TRUE)
1608  {
1609  // We can only provide information if we've got a copypaste handler
1610  // instance
1611  if (!is_object($this->copyPasteHandler)) {
1612  return '';
1613  }
1614 
1615  // Presets
1616  $result = "";
1617  $read = FALSE;
1618  $paste = FALSE;
1619  $ui = get_userinfo();
1620 
1621  // Switch flags to on if there's at least one category which allows read/paste
1622  foreach ($this->categories as $category) {
1623  $read = $read || (strpos($ui->get_category_permissions($this->base, $category), 'r') !== FALSE);
1624  $paste = $paste || ($ui->is_pasteable($this->base, $category) == 1);
1625  }
1626 
1627  // Draw entries that allow copy and cut
1628  if ($read) {
1629  // Copy entry
1630  if ($copy) {
1631  $result .= $this->renderActionMenuActionLink($separator, 'copy', _('Copy'), 'geticon.php?context=actions&icon=edit-copy&size=16');
1632  $separator = '';
1633  }
1634 
1635  // Cut entry
1636  if ($cut) {
1637  $result .= $this->renderActionMenuActionLink($separator, 'cut', _('Cut'), 'geticon.php?context=actions&icon=edit-cut&size=16');
1638  $separator = '';
1639  }
1640  }
1641 
1642  // Draw entries that allow pasting entries
1643  if ($paste) {
1644  if ($this->copyPasteHandler->entries_queued()) {
1645  $result .= $this->renderActionMenuActionLink($separator, 'paste', _('Paste'), 'geticon.php?context=actions&icon=edit-paste&size=16');
1646  } else {
1647  $result .= "<li$separator>".'<a href="#"><img src="geticon.php?context=actions&amp;icon=edit-paste&amp;size=16&amp;disabled=1" alt="paste" class="center">&nbsp;'._('Paste').'</a></li>';
1648  }
1649  }
1650 
1651  return $result;
1652  }
1653 
1654 
1655  function renderCopyPasteActions($row, $dn, $category, $class, $copy = TRUE, $cut = TRUE)
1656  {
1657  // We can only provide information if we've got a copypaste handler
1658  // instance
1659  if (!is_object($this->copyPasteHandler)) {
1660  return '';
1661  }
1662 
1663  // Presets
1664  $ui = get_userinfo();
1665  $result = "";
1666 
1667  // Render cut entries
1668  if ($cut) {
1669  if ($ui->is_cutable($dn, $category, $class)) {
1670  $result .= '<input class="center" type="image"'.
1671  ' src="geticon.php?context=actions&amp;icon=edit-cut&amp;size=16"'.
1672  ' alt="'._('Cut').'" name="listing_cut_'.$row.'" title="'._('Cut this entry').'"'.
1673  '/>';
1674  } else {
1675  $result .= '<img src="images/empty.png" alt=" " class="center optional"/>';
1676  }
1677  }
1678 
1679  // Render copy entries
1680  if ($copy) {
1681  if ($ui->is_copyable($dn, $category)) {
1682  $result .= '<input class="center" type="image"'.
1683  ' src="geticon.php?context=actions&amp;icon=edit-copy&amp;size=16"'.
1684  ' alt="'._('Copy').'" name="listing_copy_'.$row.'" title="'._('Copy this entry').'"'.
1685  '/>';
1686  } else {
1687  $result .= '<img src="images/empty.png" alt=" " class="center optional"/>';
1688  }
1689  }
1690 
1691  return $result;
1692  }
1693 
1694 
1695  function renderSnapshotMenu($separator)
1696  {
1697  // We can only provide information if we've got a snapshot handler instance
1698  if (!is_object($this->snapshotHandler)) {
1699  return '';
1700  }
1701 
1702  // Presets
1703  $result = "";
1704  $ui = get_userinfo();
1705 
1706  if ($ui->allow_snapshot_restore($this->base, $this->categories)) {
1707  // Draw icons according to the restore flag
1708  if ($this->snapshotHandler->hasDeletedSnapshots()) {
1709  $result .= $this->renderActionMenuActionLink($separator, 'restore', _('Restore snapshots'), 'geticon.php?context=actions&icon=document-restore&size=16');
1710  } else {
1711  $result .= "<li$separator><a href='#'><img src='geticon.php?context=actions&amp;icon=document-restore&amp;size=16&amp;disabled=1' alt='restore' class='center'>&nbsp;"._("Restore snapshots")."</a></li>";
1712  }
1713  }
1714 
1715  return $result;
1716  }
1717 
1718 
1719  function renderExporterMenu($separator)
1720  {
1721  // Presets
1722  $result = "";
1723 
1724  // Draw entries
1725  $result .= "<li$separator id='actionmenu_exportList'><a href='#'><img class='center' src='geticon.php?context=actions&amp;icon=document-export&amp;size=16' alt='export'>&nbsp;"._("Export list")."&nbsp;<img src='images/forward-arrow.png' alt='arrow'></a><ul class='level3'>";
1726 
1727  // Export CVS as build in exporter
1728  foreach ($this->exporter as $action => $exporter) {
1729  $result .= $this->renderActionMenuActionLink('', $action, $exporter['label'], $exporter['image']);
1730  }
1731 
1732  // Finalize list
1733  $result .= "</ul></li>";
1734 
1735  return $result;
1736  }
1737 
1738 
1739  function renderSnapshotActions($row, $dn, $category)
1740  {
1741  /* We can only provide information if we've got a snapshot handler instance */
1742  if (!is_object($this->snapshotHandler)) {
1743  return '';
1744  }
1745 
1746  // Presets
1747  $result = '';
1748  $ui = get_userinfo();
1749 
1750  if ($ui->allow_snapshot_restore($dn, $category)) {
1751  /* Draw restore button */
1752 
1753  if ($this->snapshotHandler->hasSnapshots($dn)) {
1754  /* We have snapshots for this dn */
1755  $result .= '<input class="center" type="image"'.
1756  ' src="geticon.php?context=actions&amp;icon=document-restore&amp;size=16"'.
1757  ' alt="'._('Restore snapshot').'" name="listing_restore_'.$row.'"'.
1758  ' title="'._('Restore snapshot').'"/>';
1759  } else {
1760  $result .= '<img class="center"'.
1761  ' src="geticon.php?context=actions&amp;icon=document-restore&amp;size=16&amp;disabled=1"'.
1762  ' alt="restore"/>';
1763  }
1764  }
1765 
1766  if ($ui->allow_snapshot_create($dn, $category)) {
1767  /* Draw snapshot button */
1768  $result .= '<input class="center" type="image"'.
1769  ' src="geticon.php?context=actions&amp;icon=snapshot&amp;size=16"'.
1770  ' alt="'._('Create snapshot').'" name="listing_snapshot_'.$row.'"'.
1771  ' title="'._('Create a new snapshot from this object').'"/>';
1772  } else {
1773  $result .= '<img src="images/empty.png" alt=" " class="center optional"/>';
1774  }
1775 
1776  return $result;
1777  }
1778 
1779 
1780  function renderDaemonMenu($separator)
1781  {
1782  $result = "";
1783 
1784  // If there is a daemon registered, draw the menu entries
1785  if (class_available("DaemonEvent")) {
1786  $events = DaemonEvent::get_event_types_by_category($this->categories);
1787  if (isset($events['BY_CLASS']) && count($events['BY_CLASS'])) {
1788  foreach ($events['BY_CLASS'] as $name => $event) {
1789  $result .= "<li$separator><a href='#' onClick='document.getElementById(\"actionmenu\").value=\"$name\";document.getElementById(\"exec_act\").click();'>".$event['MenuImage']."&nbsp;".$event['s_Menu_Name']."</a></li>";
1790  $separator = "";
1791  }
1792  }
1793  }
1794 
1795  return $result;
1796  }
1797 
1798 
1799  function getEntry($dn)
1800  {
1801  if (isset($this->dnToRow[$dn])) {
1802  return $this->entries[$this->dnToRow[$dn]];
1803  }
1804  return NULL;
1805  }
1806 
1810  function getEntries()
1811  {
1812  return $this->entries;
1813  }
1814 
1820  function getType($dn)
1821  {
1822  if (isset($this->objectDnMapping[$dn])) {
1823  return $this->objectDnMapping[$dn];
1824  }
1825  return NULL;
1826  }
1827 
1828 }
1829 
1830 ?>
render()
Accessor of the member tree.
filterGenericType($dn, $types, $type)
Generic method for department and objects once type is known.
filterDepartmentLink($row, $dn, $description)
Filter the department link.
This class contains all the function needed to sort department.
in_array_ics($value, array $items)
Check if a value exists in an array (case-insensitive)
Definition: functions.inc:1657
setBase($base)
Set a new base valor.
check_sizelimit()
Show sizelimit configuration dialog.
Definition: functions.inc:846
registerElementFilter($name, $call)
Save element from a filter.
setHeight($height)
Set the height.
load($data)
Load a file.
filterObjectType($row, $dn)
Icon of the object type.
update()
Update a listing.
getDepartments()
Get the departments.
update($force=FALSE)
Update the base.
send_binary_content($data, $name, $type="application/octet-stream")
Initialize a file download with given content, name and data type.
Definition: functions.inc:2341
setBase($base)
Set a base.
parseLayout($layout)
Parse a layout.
setBase($base)
Set a new value of the member base.
__construct($data)
Create a listing.
filterNameLink($row, $dn)
Filter link with object name.
genericGetType($dn, $attrs, $types)
Get the object or department type.
& get_userinfo()
Return the current userinfo object.
Definition: functions.inc:922
hasActionPermission(&$action, $dn, $row=NULL)
Check if user have action permission.
This class contains all the function needed to manage filter.
filterActions($dn, $row, $classes)
Filter actions.
get_template_path($filename= '', $plugin=FALSE, $path= '')
Return themed path for specified base file.
Definition: functions.inc:296
Class Base Selector.
get_base_from_people($dn)
Return a base from a given user DN.
Definition: functions.inc:1111
getBase()
Accessor of the base.
This class contains all the function needed to sort list go up, go down , back , next. etc...
checkLastBaseUpdate()
Check the last base value updated.
getAction()
Get action.
& get_smarty()
Get global smarty object.
Definition: functions.inc:934
setCopyPasteHandler($handler)
Set a CopyPasteHandler.
static global_set($name, $value)
Set a value in a session.
filterLink()
Filter link.
render()
Render.
static check_base()
Display error when checking the base.
getBase()
Accessor of the base.
query()
Query.
setSnapshotHandler($handler)
Set a SnapshotHandler.
getDepartmentType($dn, $attrs)
Get the department type.
validate($string)
Removes malicious characters from a (POST) string.
Definition: functions.inc:1702
getEntries()
Get listing entries.
getObjectType($dn, $attrs)
Get the object type.
static global_get($name)
Accessor of a session var.
static ls($types, $attrs=NULL, $ou=NULL, $filter= '', $checkAcl=FALSE, $scope= 'subtree')
Get list of object of objectTypes from $types in $ou.
setCurrentScope($scope)
Set the current scope.
render()
Render.
filterDepartmentType($row, $dn)
Icon of the department type.
static display($s_title, $s_message, $i_type=INFO_DIALOG)
Display a message dialog.
This class contains all the function needed to make the list that show the objects inside FusionDirec...
getType($dn)
Get type.
setBases($bases)
Set new bases.
setFilter($filter)
Set a filter.
refreshBasesList()
Refresh the bases list.
static xml2array($contents, $get_attributes=1, $priority= 'tag')
Transform a xml document to an array.
Definition: class_xml.inc:41
print_sizelimit_warning()
Print a sizelimit warning.
Definition: functions.inc:871
class_available($name)
Checks if a class is available.
Definition: functions.inc:130