1 <?php
2 /*
3 * This code is part of GOsa (http://www.gosa-project.org)
4 * Copyright (C) 2003-2008 GONICUS GmbH
5 *
6 * ID: $$Id$$
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
23 class listing {
25 var $xmlData;
26 var $entries;
27 var $departments= array();
28 var $departmentBrowser= false;
29 var $departmentRootVisible= false;
30 var $multiSelect= false;
31 var $template;
32 var $headline;
33 var $module;
34 var $base;
35 var $sortDirection= null;
36 var $sortColumn= null;
37 var $sortAttribute;
38 var $sortType;
39 var $numColumns;
40 var $baseMode= false;
41 var $bases= array();
42 var $header= array();
43 var $colprops= array();
44 var $filters= array();
45 var $pid;
46 var $objectTypes= array();
47 var $objectTypeCount= array();
50 function listing($filename)
51 {
52 global $config;
54 // Initialize pid
55 $this->pid= preg_replace("/[^0-9]/", "", microtime(TRUE));
57 if (!$this->load($filename)) {
58 die("Cannot parse $filename!");
59 }
61 // Set base for filter
62 $this->base= session::global_get("CurrentMainBase");
63 if ($this->base == null) {
64 $this->base= $config->current['BASE'];
65 }
66 $this->refreshBasesList();
68 // Move footer information
69 $this->showFooter= ($config->get_cfg_value("listSummary") == "true");
71 // Register build in filters
72 $this->registerElementFilter("objectType", "listing::filterObjectType");
73 $this->registerElementFilter("departmentLink", "listing::filterDepartmentLink");
74 $this->registerElementFilter("link", "listing::filterLink");
75 $this->registerElementFilter("actions", "listing::filterActions");
76 }
79 function registerElementFilter($name, $call)
80 {
81 if (!isset($this->filters[$name])) {
82 $this->filters[$name]= $call;
83 return true;
84 }
86 return false;
87 }
90 function load($filename)
91 {
92 $contents = file_get_contents($filename);
93 $this->xmlData= xml::xml2array($contents, 1);
95 if (!isset($this->xmlData['list'])) {
96 return false;
97 }
99 $this->xmlData= $this->xmlData["list"];
101 // Load some definition values
102 foreach (array("departmentBrowser", "departmentRootVisible", "multiSelect", "baseMode") as $token) {
103 if (isset($this->xmlData['definition'][$token]) &&
104 $this->xmlData['definition'][$token] == "true"){
105 $this->$token= true;
106 }
107 }
109 // Fill objectTypes from departments and xml definition
110 $types = departmentManagement::get_support_departments();
111 foreach ($types as $class => $data) {
112 $this->objectTypes[]= array("label" => $data['TITLE'],
113 "objectClass" => $data['OC'],
114 "image" => $data['IMG']);
115 }
116 if (isset($this->xmlData['definition']['objectType'])) {
117 foreach ($this->xmlData['definition']['objectType'] as $index => $otype) {
118 $this->objectTypes[]= $this->xmlData['definition']['objectType'][$index];
119 }
120 }
122 // Parse layout per column
123 $this->colprops= $this->parseLayout($this->xmlData['table']['layout']);
125 // Prepare table headers
126 $this->renderHeader();
128 // Assign headline/module
129 $this->headline= _($this->xmlData['definition']['label']);
130 $this->module= $this->xmlData['definition']['module'];
132 return true;
133 }
136 function renderHeader()
137 {
138 $this->header= array();
140 // Initialize sort?
141 $sortInit= false;
142 if (!$this->sortDirection) {
143 $this->sortColumn= 0;
144 if (isset($this->xmlData['definition']['defaultSortColumn'])){
145 $this->sortColumn= $this->xmlData['definition']['defaultSortColumn'];
146 } else {
147 $this->sortAttribute= "";
148 }
149 $this->sortDirection= array();
150 $sortInit= true;
151 }
153 if (isset($this->xmlData['table']['column'])){
154 foreach ($this->xmlData['table']['column'] as $index => $config) {
155 // Initialize everything to one direction
156 if ($sortInit) {
157 $this->sortDirection[$index]= false;
158 }
160 $sorter= "";
161 if ($index == $this->sortColumn && isset($config['sortAttribute']) &&
162 isset($config['sortType'])) {
163 $this->sortAttribute= $config['sortAttribute'];
164 $this->sortType= $config['sortType'];
165 $sorter= " <img border='0' title='".($this->sortDirection[$index]?_("Up"):_("Down"))."' src='images/lists/sort-".($this->sortDirection[$index]?"up":"down").".png' align='top'>";
166 }
167 $sortable= (isset($config['sortAttribute']));
169 $link= "href='?plug=".$_GET['plug']."&PID=".$this->pid."&act=SORT_$index'";
170 if (isset($config['label'])) {
171 if ($sortable) {
172 $this->header[$index]= "<td class='listheader' ".$this->colprops[$index]."><a $link>"._($config['label'])."$sorter</a></td>";
173 } else {
174 $this->header[$index]= "<td class='listheader' ".$this->colprops[$index].">"._($config['label'])."</td>";
175 }
176 } else {
177 if ($sortable) {
178 $this->header[$index]= "<td class='listheader' ".$this->colprops[$index]."><a $link> $sorter</a></td>";
179 } else {
180 $this->header[$index]= "<td class='listheader' ".$this->colprops[$index]."> </td>";
181 }
182 }
183 }
184 }
185 }
187 function render()
188 {
189 echo "sizelimit, copypaste handler, snapshot handler, daemon handler<br>";
191 // Initialize list
192 $result= "<input type='hidden' value='$this->pid' name='PID'>";
193 $result.= "<div class='contentboxb' id='listing_container' style='border-top:1px solid #B0B0B0;'>";
194 $result.= "<table summary='$this->headline' style='width:600px;height:450px;' cellspacing='0' id='t_scrolltable'>
195 <tr><td class='scrollhead'><table summary='' style='width:100%;' cellspacing='0' id='t_scrollhead'>";
196 $this->numColumns= count($this->colprops) + ($this->multiSelect?1:0);
198 // Build list header
199 $result.= "<tr>";
200 if ($this->multiSelect) {
201 $result.= "<td class='listheader' style='width:20px;'><input type='checkbox' id='select_all' name='select_all' title='"._("Select all")."' onClick='toggle_all_(\"listing_selected_[0-9]*$\",\"select_all\");' ></td>";
202 }
203 foreach ($this->header as $header) {
204 $result.= $header;
205 }
207 // Add 13px for scroller
208 $result.= "<td class='listheader' style='width:13px;border-right:0px;'> </td></table></td></tr>";
210 // New table for the real list contents
211 $result.= "<tr><td colspan='$this->numColumns' class='scrollbody'><div style='width:600px;height:430px;' id='d_scrollbody' class='scrollbody'><table summary='' style='height:100%;width:581px;' cellspacing='0' id='t_scrollbody'>";
213 // No results? Just take an empty colspanned row
214 if (count($this->entries) + count($this->departments) == 0) {
215 $result.= "<tr class='rowxp0'><td class='list1nohighlight' colspan='$this->numColumns' style='height:100%;border-right:0px;width:100%;'> </td></tr>";
216 }
218 // Line color alternation
219 $alt= 0;
220 $deps= 0;
222 // Draw department browser if configured and we're not in sub mode
223 if ($this->departmentBrowser && $this->filter->scope != "sub") {
224 // Fill with department browser if configured this way
225 $departmentIterator= new departmentSortIterator($this->departments, $this->sortDirection[$this->sortColumn]);
226 foreach ($departmentIterator as $row => $entry){
227 $result.="<tr class='rowxp".($alt&1)."'>";
229 // Render multi select if needed
230 if ($this->multiSelect) {
231 $result.="<td style='text-align:center;width:20px;' class='list1'> </td>";
232 }
234 // Render defined department columns, fill the rest with some stuff
235 foreach ($this->xmlData['table']['department'] as $index => $config) {
236 $result.="<td ".$this->colprops[$index]." class='list1'>".$this->renderCell($config['value'], $entry, $row)."</td>";
237 }
238 $last= count($this->xmlData['table']['department']) + 1;
239 $rest= $this->numColumns - $last;
240 for ($i= 0; $i<$rest; $i++){
241 $result.= "<td ".$this->colprops[$last+$i-1]." class='list1'> </td>";
242 }
243 $result.="</tr>";
245 $alt++;
246 }
247 $deps= $alt;
248 }
250 // Fill with contents, sort as configured
251 $entryIterator= new listingSortIterator($this->entries, $this->sortDirection[$this->sortColumn], $this->sortAttribute, $this->sortType);
252 foreach ($entryIterator as $row => $entry){
253 $result.="<tr class='rowxp".($alt&1)."'>";
255 // Render multi select if needed
256 if ($this->multiSelect) {
257 $result.="<td style='text-align:center;width:20px;' class='list0'><input type='checkbox' id='listing_selected_$row' name='listing_selected_$row'></td>";
258 }
260 foreach ($this->xmlData['table']['column'] as $index => $config) {
261 $result.="<td ".$this->colprops[$index]." class='list0'>".$this->renderCell($config['value'], $entry, $row)."</td>";
262 }
263 $result.="</tr>";
265 $alt++;
266 }
268 // Need to fill the list if it's not full (nobody knows why this is 22 ;-))
269 $emptyListStyle= (count($this->entries) + count($deps) == 0)?"border:0;":"";
270 if (count($this->entries) + count($deps) < 22) {
271 $result.= "<tr>";
272 for ($i= 0; $i<$this->numColumns; $i++) {
273 if ($i == 0) {
274 $result.= "<td class='list1nohighlight' style='$emptyListStyle height:100%;'> </td>";
275 continue;
276 }
277 if ($i != $this->numColumns-1) {
278 $result.= "<td class='list1nohighlight' style='$emptyListStyle'> </td>";
279 } else {
280 $result.= "<td class='list1nohighlight' style='border-right:1px solid #AAA;$emptyListStyle'> </td>";
281 }
282 }
283 $result.= "</tr>";
284 }
286 $result.= "</table></div></td></tr>";
288 // Add the footer if requested
289 if ($this->showFooter) {
290 $result.= "<tr><td class='scrollhead'><table summary='' style='width:100%' cellspacing='0' id='t_scrollfoot'><tr><td class='listfooter' style='border-bottom:0px;'>";
292 foreach ($this->objectTypes as $objectType) {
293 if (isset($this->objectTypeCount[$objectType['label']])) {
294 $label= _($objectType['label']);
295 $result.= "<img class='center' src='".$objectType['image']."' title='$label' alt='$label'> ".$this->objectTypeCount[$objectType['label']]." ";
296 }
297 }
299 $result.= "<td class='listfooter' style='width:13px;border-right:0px;'> </td></table></td></tr>";
300 }
302 $result.= "</table></div>";
304 $smarty= get_smarty();
305 $smarty->assign("FILTER", $this->filter->render());
306 $smarty->assign("LIST", $result);
308 // Assign navigation elements
309 $nav= $this->renderNavigation();
310 foreach ($nav as $key => $html) {
311 $smarty->assign($key, $html);
312 }
314 // Assign action menu / base
315 $smarty->assign("ACTIONS", $this->renderActionMenu());
316 $smarty->assign("BASE", $this->renderBase());
318 // Assign separator
319 $smarty->assign("SEPARATOR", "<img src='images/lists/seperator.png' alt='-' align='middle' height='16' width='1' class='center'>");
321 // Assign summary
322 $smarty->assign("HEADLINE", $this->headline);
324 return ($smarty->fetch(get_template_path($this->xmlData['definition']['template'], true)));
325 }
328 function setFilter($filter)
329 {
330 $this->filter= &$filter;
331 if ($this->departmentBrowser){
332 $this->departments= $this->getDepartments();
333 }
334 $this->filter->setBase($this->base);
335 $this->entries= $this->filter->query();
336 }
339 function update()
340 {
341 global $config;
342 $ui= get_userinfo();
344 // Do not do anything if this is not our PID
345 if(isset($_REQUEST['PID']) && $_REQUEST['PID'] != $this->pid) {
346 return;
347 }
349 // Save base
350 if (isset($_POST['BASE']) && $this->baseMode == true) {
351 $base= validate($_POST['BASE']);
352 if (isset($this->bases[$base])) {
353 $this->base= $base;
354 }
355 }
357 // Override the base if we got a message from the browser navigation
358 if ($this->departmentBrowser && isset($_GET['act'])) {
359 if (preg_match('/^department_([0-9]+)$/', validate($_GET['act']), $match)){
360 if (isset($this->departments[$match[1]])){
361 $this->base= $this->departments[$match[1]]['dn'];
362 }
363 }
364 }
366 // Filter GET with "act" attributes
367 if (isset($_GET['act'])) {
368 $key= validate($_GET['act']);
369 if (preg_match('/^SORT_([0-9]+)$/', $key, $match)) {
370 // Switch to new column or invert search order?
371 $column= $match[1];
372 if ($this->sortColumn != $column) {
373 $this->sortColumn= $column;
374 } else {
375 $this->sortDirection[$column]= !$this->sortDirection[$column];
376 }
378 // Allow header to update itself according to the new sort settings
379 $this->renderHeader();
380 }
381 }
383 // Override base if we got signals from the navigation elements
384 $action= "";
385 foreach ($_POST as $key => $value) {
386 if (preg_match('/^(ROOT|BACK|HOME)_x$/', $key, $match)) {
387 $action= $match[1];
388 break;
389 }
390 }
392 // Navigation handling
393 if ($action == 'ROOT') {
394 $deps= $ui->get_module_departments($this->module);
395 $this->base= $deps[0];
396 }
397 if ($action == 'BACK') {
398 $deps= $ui->get_module_departments($this->module);
399 $base= preg_replace("/^[^,]+,/", "", $this->base);
400 if(in_array_ics($base, $deps)){
401 $this->base= $base;
402 }
403 }
404 if ($action == 'HOME') {
405 $ui= get_userinfo();
406 $this->base= get_base_from_people($ui->dn);
407 }
409 // Reload departments
410 if ($this->departmentBrowser){
411 $this->departments= $this->getDepartments();
412 }
414 // Update filter and refresh entries
415 $this->filter->setBase($this->base);
416 $this->entries= $this->filter->query();
417 }
420 function parseLayout($layout)
421 {
422 $result= array();
423 $layout= preg_replace("/^\|/", "", $layout);
424 $layout= preg_replace("/\|$/", "", $layout);
425 $cols= split("\|", $layout);
426 foreach ($cols as $index => $config) {
427 if ($config != "") {
428 $components= split(';', $config);
429 $config= "";
430 foreach ($components as $part) {
431 if (preg_match("/^r$/", $part)) {
432 $config.= "text-align:right;";
433 continue;
434 }
435 if (preg_match("/^l$/", $part)) {
436 $config.= "text-align:left;";
437 continue;
438 }
439 if (preg_match("/^c$/", $part)) {
440 $config.= "text-align:center;";
441 continue;
442 }
443 if (preg_match("/^[0-9]+(|px|%)$/", $part)) {
444 $config.= "width:$part;";
445 continue;
446 }
447 }
449 $result[$index]= " style='$config' ";
450 } else {
451 $result[$index]= null;
452 }
453 }
455 // Save number of columns for later use
456 $this->numColumns= count($cols);
458 return $result;
459 }
462 function renderCell($data, $config, $row)
463 {
464 // Replace flat attributes in data string
465 for ($i= 0; $i<$config['count']; $i++) {
466 $attr= $config[$i];
467 $value= "";
468 if (is_array($config[$attr])) {
469 $value= $config[$attr][0];
470 } else {
471 $value= $config[$attr];
472 }
473 $data= preg_replace("/%\{$attr\}/", $value, $data);
474 }
476 // Watch out for filters and prepare to execute them
477 $data= $this->processElementFilter($data, $config, $row);
479 return $data;
480 }
483 function renderBase()
484 {
485 $result= "<select name='BASE' onChange='mainform.submit()' size='1'>";
486 $firstDN= null;
487 $found= false;
489 foreach ($this->bases as $key=>$value) {
490 // Keep first entry to fall back eventually
491 if(!$firstDN) {
492 $firstDN= $key;
493 }
495 // Prepare to render entry
496 $selected= "";
497 if ($key == $this->base) {
498 $selected= " selected";
499 $found= true;
500 }
501 $result.= "<option value='".$key."'$selected>".$value."</option>";
502 }
503 $result.= "</select>";
505 // Reset the currently used base to the first DN we found if there
506 // was no match.
507 if(!$found){
508 $this->base = $firstDN;
509 }
511 return $result;
512 }
515 function processElementFilter($data, $config, $row)
516 {
517 preg_match_all("/%\{filter:([^(]+)\((.*)\)\}/", $data, $matches, PREG_SET_ORDER);
519 foreach ($matches as $match) {
520 if (!isset($this->filters[$match[1]])) {
521 continue;
522 }
523 $cl= preg_replace('/::.*$/', '', $this->filters[$match[1]]);
524 $method= preg_replace('/^.*::/', '', $this->filters[$match[1]]);
526 // Prepare params for function call
527 $params= array();
528 preg_match_all('/"[^"]+"|[^,]+/', $match[2], $parts);
529 foreach ($parts[0] as $param) {
531 // Row is replaced by the row number
532 if ($param == "row") {
533 $params[]= $row;
534 }
536 // pid is replaced by the current PID
537 if ($param == "pid") {
538 $params[]= $this->pid;
539 }
541 // Fixie with "" is passed directly
542 if (preg_match('/^".*"$/', $param)){
543 $params[]= preg_replace('/"/', '', $param);
544 }
546 // LDAP variables get replaced by their objects
547 for ($i= 0; $i<$config['count']; $i++) {
548 if ($param == $config[$i]) {
549 $values= $config[$config[$i]];
550 if (is_array($values)){
551 unset($values['count']);
552 }
553 $params[]= $values;
554 }
555 }
557 // Move dn if needed
558 if ($param == "dn") {
559 $params[]= LDAP::fix($config["dn"]);
560 }
561 }
563 // Replace information
564 if ($cl == "listing") {
565 // Non static call - seems to result in errors
566 $data= @preg_replace('/'.preg_quote($match[0]).'/', call_user_func_array(array($this, "$method"), $params), $data);
567 } else {
568 // Static call
569 $data= preg_replace('/'.preg_quote($match[0]).'/', call_user_func_array(array($cl, $method), $params), $data);
570 }
571 }
573 return $data;
574 }
577 function getObjectType($types, $classes)
578 {
579 // Walk thru types and see if there's something matching
580 foreach ($types as $objectType) {
581 $ocs= $objectType['objectClass'];
582 if (!is_array($ocs)){
583 $ocs= array($ocs);
584 }
586 $found= true;
587 foreach ($ocs as $oc){
588 if (preg_match('/^!(.*)$/', $oc, $match)) {
589 $oc= $match[1];
590 if (in_array($oc, $classes)) {
591 $found= false;
592 }
593 } else {
594 if (!in_array($oc, $classes)) {
595 $found= false;
596 }
597 }
598 }
600 if ($found) {
601 return $objectType;
602 }
603 }
605 return null;
606 }
609 function filterObjectType($dn, $classes)
610 {
611 // Walk thru classes and return on first match
612 $result= " ";
614 $objectType= $this->getObjectType($this->objectTypes, $classes);
615 if ($objectType) {
616 $result= "<img class='center' title='".LDAP::fix($dn)."' src='".$objectType["image"]."'>";
617 if (!isset($this->objectTypeCount[$objectType['label']])) {
618 $this->objectTypeCount[$objectType['label']]= 0;
619 }
620 $this->objectTypeCount[$objectType['label']]++;
621 }
622 return $result;
623 }
626 function filterActions($dn, $row, $classes)
627 {
628 // Do nothing if there's no menu defined
629 if (!isset($this->xmlData['actiontriggers']['action'])) {
630 return " ";
631 }
633 // Go thru all actions
634 $result= "";
635 $actions= $this->xmlData['actiontriggers']['action'];
636 foreach($actions as $action) {
637 // Skip the entry completely if there's no permission to execute it
638 if (!$this->hasActionPermission($action, $dn)) {
639 continue;
640 }
642 // If there's an objectclass definition and we don't have it
643 // add an empty picture here.
644 if (isset($action['objectclass'])){
645 $objectclass= $action['objectclass'];
646 if (preg_match('/^!(.*)$/', $objectclass, $m)){
647 $objectclass= $m[1];
648 if(in_array($objectclass, $classes)) {
649 $result.= "<img src='images/empty.png' alt=' ' class='center' style='padding:1px'>";
650 continue;
651 }
652 } else {
653 if(!in_array($objectclass, $classes)) {
654 $result.= "<img src='images/empty.png' alt=' ' class='center' style='padding:1px'>";
655 continue;
656 }
657 }
658 }
660 // Render normal entries as usual
661 if ($action['type'] == "entry") {
662 $label= $this->processElementFilter($action['label'], $this->entries[$row], $row);
663 $image= $this->processElementFilter($action['image'], $this->entries[$row], $row);
664 $result.="<input class='center' type='image' src='$image' title='$label' ".
665 "name='listing_".$action['name']."_$row' style='padding:1px'>";
666 }
668 // Handle special types
669 if ($action['type'] == "snapshot") {
670 #TODO
671 #echo "actiontriggers: snapshot missing<br>";
672 }
673 if ($action['type'] == "copypaste") {
674 #TODO
675 #echo "actiontriggers: copypaste missing<br>";
676 }
677 if ($action['type'] == "daemon") {
678 #TODO
679 #echo "actiontriggers: daemon missing<br>";
680 }
682 }
684 return $result;
685 }
688 function filterDepartmentLink($row, $dn, $description)
689 {
690 $attr= $this->departments[$row]['sort-attribute'];
691 $name= $this->departments[$row][$attr];
692 if (is_array($name)){
693 $name= $name[0];
694 }
695 $result= sprintf("%s [%s]", $name, $description[0]);
696 return("<a href='?plug=".$_GET['plug']."&PID=$this->pid&act=department_$row' title='$dn'>$result</a>");
697 }
700 function filterLink()
701 {
702 $result= " ";
704 $row= func_get_arg(0);
705 $pid= $this->pid;
706 $dn= LDAP::fix(func_get_arg(1));
707 $params= array(func_get_arg(2));
709 // Collect sprintf params
710 for ($i = 3;$i < func_num_args();$i++) {
711 $val= func_get_arg($i);
712 if (is_array($val)){
713 $params[]= $val[0];
714 continue;
715 }
716 $params[]= $val;
717 }
719 $result= " ";
720 $trans= call_user_func_array("sprintf", $params);
721 if ($trans != "") {
722 return("<a href='?plug=".$_GET['plug']."&PID=$pid&act=listing_edit_$row' title='$dn'>$trans</a>");
723 }
725 return $result;
726 }
729 function renderNavigation()
730 {
731 $result= array();
732 $enableBack = true;
733 $enableRoot = true;
734 $enableHome = true;
736 $ui = get_userinfo();
738 /* Check if base = first available base */
739 $deps = $ui->get_module_departments($this->module);
741 if(!count($deps) || $deps[0] == $this->filter->base){
742 $enableBack = false;
743 $enableRoot = false;
744 }
746 $listhead ="";
748 /* Check if we are in users home department */
749 if(!count($deps) ||$this->filter->base == get_base_from_people($ui->dn)){
750 $enableHome = false;
751 }
753 /* Draw root button */
754 if($enableRoot){
755 $result["ROOT"]= "<input class='center' type='image' src='images/lists/root.png' align='middle' ".
756 "title='"._("Go to root department")."' name='ROOT' alt='"._("Root")."'>";
757 }else{
758 $result["ROOT"]= "<img src='images/lists/root_grey.png' class='center' alt='"._("Root")."'>";
759 }
761 /* Draw back button */
762 if($enableBack){
763 $result["BACK"]= "<input class='center' type='image' align='middle' src='images/lists/back.png' ".
764 "title='"._("Go up one department")."' alt='"._("Up")."' name='BACK'>";
765 }else{
766 $result["BACK"]= "<img src='images/lists/back_grey.png' class='center' alt='"._("Up")."'>";
767 }
769 /* Draw home button */
770 if($enableHome){
771 $result["HOME"]= "<input class='center' type='image' align='middle' src='images/lists/home.png' ".
772 "title='"._("Go to users department")."' alt='"._("Home")."' name='HOME'>";
773 }else{
774 $result["HOME"]= "<img src='images/lists/home_grey.png' class='center' alt='"._("Home")."'>";
775 }
777 /* Draw reload button, this button is enabled everytime */
778 $result["RELOAD"]= "<input class='center' type='image' src='images/lists/reload.png' align='middle' ".
779 "title='"._("Reload list")."' name='REFRESH' alt='"._("Submit")."'>";
781 return ($result);
782 }
785 function getAction()
786 {
787 // Do not do anything if this is not our PID
788 if(isset($_REQUEST['PID']) && $_REQUEST['PID'] != $this->pid) {
789 return;
790 }
792 $result= array("targets" => array(), "action" => "");
794 // Filter GET with "act" attributes
795 if (isset($_GET['act'])) {
796 $key= validate($_GET['act']);
797 $target= preg_replace('/^listing_[a-zA-Z_]+_([0-9]+)$/', '$1', $key);
798 if (isset($this->entries[$target]['dn'])) {
799 $result['action']= preg_replace('/^listing_([a-zA-Z_]+)_[0-9]+$/', '$1', $key);
800 $result['targets'][]= $this->entries[$target]['dn'];
801 }
803 // Drop targets if empty
804 if (count($result['targets']) == 0) {
805 unset($result['targets']);
806 }
807 return $result;
808 }
810 // Filter POST with "listing_" attributes
811 foreach ($_POST as $key => $prop) {
813 // Capture selections
814 if (preg_match('/^listing_selected_[0-9]+$/', $key)) {
815 $target= preg_replace('/^listing_selected_([0-9]+)$/', '$1', $key);
816 if (isset($this->entries[$target]['dn'])) {
817 $result['targets'][]= $this->entries[$target]['dn'];
818 }
819 continue;
820 }
822 // Capture action with target - this is a one shot
823 if (preg_match('/^listing_[a-zA-Z_]+_[0-9]+(|_x)$/', $key)) {
824 $target= preg_replace('/^listing_[a-zA-Z_]+_([0-9]+)(|_x)$/', '$1', $key);
825 if (isset($this->entries[$target]['dn'])) {
826 $result['action']= preg_replace('/^listing_([a-zA-Z_]+)_[0-9]+(|_x)$/', '$1', $key);
827 $result['targets']= array($this->entries[$target]['dn']);
828 }
829 break;
830 }
832 // Capture action without target
833 if (preg_match('/^listing_[a-zA-Z_]+(|_x)$/', $key)) {
834 $result['action']= preg_replace('/^listing_([a-zA-Z_]+)(|_x)$/', '$1', $key);
835 continue;
836 }
837 }
839 // Filter POST with "act" attributes -> posted from action menu
840 if (isset($_POST['act']) && $_POST['act'] != '') {
841 $result['action']= validate($_POST['act']);
842 }
844 // Drop targets if empty
845 if (count($result['targets']) == 0) {
846 unset($result['targets']);
847 }
848 return $result;
849 }
852 function renderActionMenu()
853 {
854 // Don't send anything if the menu is not defined
855 if (!isset($this->xmlData['actionmenu']['action'])){
856 return "";
857 }
859 // Load shortcut
860 $actions= &$this->xmlData['actionmenu']['action'];
861 $result= "<input type='hidden' name='act' id='actionmenu' value=''>".
862 "<ul class='level1' id='root'><li><a href='#'>Aktionen <img ".
863 "border=0 src='images/lists/sort-down.png'></a>";
865 // Build ul/li list
866 $result.= $this->recurseActions($actions);
868 return "<div id='pulldown'>".$result."</li></ul><div>";
869 }
872 function recurseActions($actions)
873 {
874 static $level= 2;
875 $result= "<ul class='level$level'>";
876 $separator= "";
878 foreach ($actions as $action) {
880 // Skip the entry completely if there's no permission to execute it
881 if (!$this->hasActionPermission($action, $this->filter->base)) {
882 continue;
883 }
885 // Fill image if set
886 $img= "";
887 if (isset($action['image'])){
888 $img= "<img border=0 src='".$action['image']."'> ";
889 }
891 if ($action['type'] == "separator"){
892 $separator= " style='border-top:1px solid #AAA' ";
893 continue;
894 }
896 // Dive into subs
897 if ($action['type'] == "sub" && isset($action['action'])) {
898 $level++;
899 if (isset($action['label'])){
900 $result.= "<li$separator><a href='#'>$img"._($action['label'])." <img border='0' src='images/forward-arrow.png'></a>";
901 }
902 $result.= $this->recurseActions($action['action'])."</li>";
903 $level--;
904 $separator= "";
905 continue;
906 }
908 // Render entry elseways
909 if (isset($action['label'])){
910 $result.= "<li$separator><a href='#' onClick='document.getElementById(\"actionmenu\").value= \"".$action['name']."\";mainform.submit();'>$img"._($action['label'])."</a></li>";
911 }
913 // Check for special types
914 switch ($action['type']) {
915 case 'copypaste':
916 #TODO
917 #echo "actionmenu: copypaste missing<br>";
918 break;
920 case 'snapshot':
921 #TODO
922 #echo "actionmenu: snapshot missing<br>";
923 break;
925 case 'daemon':
926 #TODO
927 #echo "actionmenu: daemon missing<br>";
928 break;
929 }
931 $separator= "";
932 }
934 $result.= "</ul>";
935 return $result;
936 }
939 function hasActionPermission($action, $dn)
940 {
941 $ui= get_userinfo();
943 if (isset($action['acl'])) {
944 $acls= $action['acl'];
945 if (!is_array($acls)) {
946 $acls= array($acls);
947 }
949 // Every ACL has to pass
950 foreach ($acls as $acl) {
951 $module= $this->module;
952 $acllist= array();
954 // Split for category and plugins if needed
955 // match for "[rw]" style entries
956 if (preg_match('/^\[([rwcdm]+)\]$/', $acl, $match)){
957 $aclList= array($match[1]);
958 }
960 // match for "users[rw]" style entries
961 if (preg_match('/^([a-zA-Z0-9]+)\[([rwcdm]+)\]$/', $acl, $match)){
962 $module= $match[1];
963 $aclList= array($match[2]);
964 }
966 // match for "users/user[rw]" style entries
967 if (preg_match('/^([a-zA-Z0-9]+\/[a-zA-Z0-9]+)\[([rwcdm]+)\]$/', $acl, $match)){
968 $module= $match[1];
969 $aclList= array($match[2]);
970 }
972 // match "users/user[userPassword:rw(,...)*]" style entries
973 if (preg_match('/^([a-zA-Z0-9]+\/[a-zA-Z0-9]+)\[([a-zA-Z0-9]+:[rwcdm]+(,[a-zA-Z0-9]+:[rwcdm]+)*)\]$/', $acl, $match)){
974 $module= $match[1];
975 $aclList= split(',', $match[2]);
976 }
978 // Walk thru prepared ACL by using $module
979 foreach($aclList as $sAcl) {
980 $checkAcl= "";
982 // Category or detailed permission?
983 if (strpos('/', $module) === false) {
984 if (preg_match('/([a-zA-Z0-9]+):([rwcdm]+)/', $sAcl, $m) ) {
985 $checkAcl= $ui->get_permissions($dn, $module, $m[1]);
986 $sAcl= $m[2];
987 } else {
988 $checkAcl= $ui->get_permissions($dn, $module, '0');
989 }
990 } else {
991 $checkAcl= $ui->get_category_permissions($dn, $module);
992 }
994 // Split up remaining part of the acl and check if it we're
995 // allowed to do something...
996 $parts= str_split($sAcl);
997 foreach ($parts as $part) {
998 if (strpos($checkAcl, $part) === false){
999 return false;
1000 }
1001 }
1003 }
1004 }
1005 }
1007 return true;
1008 }
1011 function refreshBasesList()
1012 {
1013 global $config;
1014 $ui= get_userinfo();
1016 // Do some array munching to get it user friendly
1017 $ids= $config->idepartments;
1018 $d= $ui->get_module_departments($this->module);
1019 $k_ids= array_keys($ids);
1020 $deps= array_intersect($d,$k_ids);
1022 // Fill internal bases list
1023 $this->bases= array();
1024 foreach($k_ids as $department){
1025 $this->bases[$department] = $ids[$department];
1026 }
1027 }
1030 function getDepartments()
1031 {
1032 $departments= array();
1033 $ui= get_userinfo();
1035 // Get list of supported department types
1036 $types = departmentManagement::get_support_departments();
1038 // Load departments allowed by ACL
1039 $validDepartments = $ui->get_module_departments($this->module);
1041 // Build filter and look in the LDAP for possible sub departments
1042 // of current base
1043 $filter= "(&(objectClass=gosaDepartment)(|";
1044 $attrs= array("description", "objectClass");
1045 foreach($types as $name => $data){
1046 $filter.= "(objectClass=".$data['OC'].")";
1047 $attrs[]= $data['ATTR'];
1048 }
1049 $filter.= "))";
1050 $res= get_list($filter, $this->module, $this->base, $attrs, GL_NONE | GL_SIZELIMIT);
1052 // Analyze list of departments
1053 foreach ($res as $department) {
1054 if (!in_array($department['dn'], $validDepartments)) {
1055 continue;
1056 }
1058 // Add the attribute where we use for sorting
1059 $oc= null;
1060 foreach(array_keys($types) as $type) {
1061 if (in_array($type, $department['objectClass'])) {
1062 $oc= $type;
1063 break;
1064 }
1065 }
1066 $department['sort-attribute']= $types[$oc]['ATTR'];
1068 // Move to the result list
1069 $departments[]= $department;
1070 }
1072 return $departments;
1073 }
1075 }
1077 ?>