Code

0e0ee1641badbaa4c705fd9a30bfd7d932b2ba5f
[gosa.git] / gosa-core / include / class_listing.inc
1 <?php
3 class listing {
5   var $xmlData;
6   var $entries;
7   var $departmentBrowser= false;
8   var $departmentRootVisible= false;
9   var $multiSelect= false;
10   var $template;
11   var $headline;
12   var $module;
13   var $header= array();
14   var $colprops= array();
15   var $filters= array();
16   var $pid;
17   var $objectTypes;
20   function listing($filename)
21   {
22     if (!$this->load($filename)) {
23       die("Cannot parse $filename!");
24     }
26     // Register build in filters
27     $this->registerElementFilter("objectType", "listing::filterObjectType");
28     $this->registerElementFilter("link", "listing::filterLink");
29     $this->registerElementFilter("actions", "listing::filterActions");
31     // Initialize pid
32     $this->pid= preg_replace("/[^0-9]/", "", microtime(TRUE));
33   }
36   function registerElementFilter($name, $call)
37   {
38     if (!isset($this->filters[$name])) {
39       $this->filters[$name]= $call;
40       return true;
41     }
43     return false;
44   }
47   function load($filename)
48   {
49     $contents = file_get_contents($filename);
50     $this->xmlData= xml::xml2array($contents, 1);
52     if (!isset($this->xmlData['list'])) {
53       return false;
54     }
56     $this->xmlData= $this->xmlData["list"];
58     // Load some definition values
59     foreach (array("departmentBrowser", "departmentRootVisible", "multiSelect") as $token) {
60       if (isset($this->xmlData['definition'][$token]) &&
61           $this->xmlData['definition'][$token] == "true"){
62         $this->$token= true;
63       }
64     }
66     // Fill objectTypes
67     if (isset($this->xmlData['definition']['objectType'])) {
68       foreach ($this->xmlData['definition']['objectType'] as $index => $otype) {
69         $this->objectTypes[$otype['objectClass']]= $this->xmlData['definition']['objectType'][$index];
70       }
71     }
73     // Parse layout per column
74     $this->colprops= $this->parseLayout($this->xmlData['table']['layout']);
76     // Prepare table headers
77     $this->header= array();
78     if (isset($this->xmlData['table']['column'])){
79       foreach ($this->xmlData['table']['column'] as $index => $config) {
80         if (isset($config['label'])) {
81           $this->header[$index]= "<td class='listheader' ".$this->colprops[$index].">"._($config['label'])."</td>";
82         } else {
83           $this->header[$index]= "<td class='listheader' ".$this->colprops[$index].">&nbsp;</td>";
84         }
85       }
86     }
88     // Assign headline/module
89     $this->headline= _($this->xmlData['definition']['label']);
90     $this->module= $this->xmlData['definition']['module'];
92     return true;  
93   }
96   function render()
97   {
98 echo "filter for images, action menu, sorting, department browsing, filter base handling, bottom list info, copy'n paste handler, snapshot handler<br>";
100     // Initialize list
101     $result= "<input type='hidden' value='$this->pid' name='PID'>";
102     $result.= "<div class='contentboxb' id='listing_container' style='border-top:1px solid #B0B0B0;'>";
103     $result.= "<table summary='$this->headline' style='width:600px;height:450px;' cellspacing='0' id='t_scrolltable'>
104 <tr><td class='scrollhead'><table summary='' style='width:100%;' cellspacing='0' id='t_scrollhead'>";
105     $num_cols= count($this->colprops) + ($this->multiSelect?1:0);
107     // Build list header
108     $result.= "<tr>";
109     if ($this->multiSelect) {
110       $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>";
111     }
112     foreach ($this->header as $header) {
113       $result.= $header;
114     }
116     // Add 13px for scroller
117     $result.= "<td class='listheader' style='width:13px;border-right:0px;'>&nbsp;</td></table></td></tr>";
119     // New table for the real list contents
120     $result.= "<tr><td colspan='$num_cols' 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'>";
122     // No results? Just take an empty colspanned row
123     if (count($this->entries) == 0) {
124       $result.= "<tr class='rowxp0'><td class='list1nohighlight' colspan='$num_cols' style='height:100%;border-right:0px;width:100%;'>&nbsp;</td></tr>";
125     }
127     // Fill with contents
128     foreach ($this->entries as $row => $entry){
129       $result.="<tr class='rowxp".($row&1)."'>";
131       // Render multi select if needed
132       if ($this->multiSelect) {
133         $result.="<td style='text-align:center;width:20px;' class='list0'><input type='checkbox' id='listing_selected_$row' name='listing_selected_$row'></td>";
134       }
136       foreach ($this->xmlData['table']['column'] as $index => $config) {
137         $result.="<td ".$this->colprops[$index]." class='list0'>".$this->renderCell($config['value'], $entry, $row)."</td>";
138       }
139       $result.="</tr>";
140     }
142     // Need to fill the list if it's not full (nobody knows why this is 22 ;-))
143     if (count($this->entries) < 22) {
144       $result.= "<tr>";
145       for ($i= 0; $i<$num_cols; $i++) {
146         if ($i == 0) {
147           $result.= "<td class='list1nohighlight' style='height:100%;'>&nbsp;</td>";
148           continue;
149         }
150         if ($i != $num_cols-1) {
151           $result.= "<td class='list1nohighlight''>&nbsp;</td>";
152         } else {
153           $result.= "<td class='list1nohighlight' style='border-right:0px'>&nbsp;</td>";
154         }
155       }
156       $result.= "</tr>";
157     }
159     $result.= "</table></div></td></tr></table></div>";
161     $smarty= get_smarty();
162     $smarty->assign("FILTER", $this->filter->render());
163     $smarty->assign("LIST", $result);
165     // Assign navigation elements
166     $nav= $this->renderNavigation();
167     foreach ($nav as $key => $html) {
168       $smarty->assign($key, $html);
169     }
171     // Assign action menu
172     $smarty->assign("ACTIONS", $this->renderActionMenu());
174     // Assign separator
175     $smarty->assign("SEPARATOR", "<img src='images/lists/seperator.png' alt='-' align='middle' height='16' width='1' class='center'>");
177     // Assign summary
178     $smarty->assign("HEADLINE", $this->headline);
180     return ($smarty->fetch(get_template_path($this->xmlData['definition']['template'], true)));
181   }
184   function setFilter($filter)
185   {
186     $this->filter= &$filter;
187     $this->entries= $this->filter->query();
188   }
191   function update()
192   {
193     // Do not do anything if this is not our PID
194     # DISABLED because the object is not in the session
195     #if(isset($_REQUEST['PID']) && $_REQUEST['PID'] != $this->pid) {
196     #  return;
197     #}
199     $this->entries= $this->filter->query();
200   }
203   function parseLayout($layout)
204   {
205     $result= array();
206     $layout= preg_replace("/^\|/", "", $layout);
207     $layout= preg_replace("/\|$/", "", $layout);
208     $cols= split("\|", $layout);
209     foreach ($cols as $index => $config) {
210       if ($config != "") {
211         $components= split(';', $config);
212         $config= "";
213         foreach ($components as $part) {
214           if (preg_match("/^r$/", $part)) {
215             $config.= "text-align:right;";
216             continue;
217           }
218           if (preg_match("/^l$/", $part)) {
219             $config.= "text-align:left;";
220             continue;
221           }
222           if (preg_match("/^c$/", $part)) {
223             $config.= "text-align:center;";
224             continue;
225           }
226           if (preg_match("/^[0-9]+(|px|%)$/", $part)) {
227             $config.= "width:$part;";
228             continue;
229           }
230         }
232         $result[$index]= " style='$config' ";
233       } else {
234         $result[$index]= null;
235       }
236     }
238     return $result;
239   }
242   function renderCell($data, $config, $row)
243   {
244     // Replace flat attributes in data string
245     for ($i= 0; $i<$config['count']; $i++) {
246       $attr= $config[$i];
247       $value= "";
248       if (is_array($config[$attr])) {
249         $value= $config[$attr][0];
250       } else {
251         $value= $config[$attr];
252       }
253       $data= preg_replace("/%\{$attr\}/", $value, $data);
254     }
256     // Watch out for filters and prepare to execute them
257     $data= $this->processElementFilter($data, $config, $row);
259     return $data;
260   }
263   function processElementFilter($data, $config, $row)
264   {
265     preg_match_all("/%\{filter:([^(]+)\((.*)\)\}/", $data, $matches, PREG_SET_ORDER);
267     foreach ($matches as $match) {
268       if (!isset($this->filters[$match[1]])) {
269         continue;
270       }
271       $cl= preg_replace('/::.*$/', '', $this->filters[$match[1]]);
272       $method= preg_replace('/^.*::/', '', $this->filters[$match[1]]);
274       // Prepare params for function call
275       $params= array();
276       preg_match_all('/"[^"]+"|[^,]+/', $match[2], $parts);
277       foreach ($parts[0] as $param) {
279         // Row is replaced by the row number
280         if ($param == "row") {
281           $params[]= $row;
282         }
284         // pid is replaced by the current PID
285         if ($param == "pid") {
286           $params[]= $this->pid;
287         }
289         // Fixie with "" is passed directly
290         if (preg_match('/^".*"$/', $param)){
291           $params[]= preg_replace('/"/', '', $param);
292         }
294         // LDAP variables get replaced by their objects
295         for ($i= 0; $i<$config['count']; $i++) {
296           if ($param == $config[$i]) {
297             $values= $config[$config[$i]];
298             if (is_array($values)){
299               unset($values['count']);
300             }
301             $params[]= $values;
302           }
303         }
305         // Move dn if needed
306         if ($param == "dn") {
307           $params[]= LDAP::fix($config["dn"]);
308         }
309       }
311       // Replace information
312       if ($cl == "listing") {
313         // Non static call - seems to result in errors
314         $data= @preg_replace('/'.preg_quote($match[0]).'/', call_user_func_array(array($this, "$method"), $params), $data);
315       } else {
316         // Static call
317         $data= preg_replace('/'.preg_quote($match[0]).'/', call_user_func_array(array($cl, $method), $params), $data);
318       }
319     }
321     return $data;
322   }
325   function filterObjectType($dn, $classes)
326   {
327     // Walk thru classes and return on first match
328     $result= "&nbsp;";
329     $prio= 99;
330     foreach ($classes as $objectClass) {
331       if (isset($this->objectTypes[$objectClass])){
332         if (!isset($this->objectTypes[$objectClass]["priority"])){
333           $result= "<img class='center' title='".LDAP::fix($dn)."' src='".$this->objectTypes[$objectClass]["image"]."'>";
334           return $result; 
335         }
337         if ($this->objectTypes[$objectClass]["priority"] < $prio){
338           $prio= $this->objectTypes[$objectClass]["priority"];
339           $result= "<img class='center' title='".LDAP::fix($dn)."' src='".$this->objectTypes[$objectClass]["image"]."'>";
340         }
341       }
342     }
344     return $result;
345   }
348   function filterActions($row, $dn)
349   {
350     return "TBD";
351   }
354   function filterLink()
355   {
356     $result= "&nbsp;";
358     $row= func_get_arg(0);
359     $pid= func_get_arg(1);
360     $dn= LDAP::fix(func_get_arg(2));
361     $params= array(func_get_arg(3));
363     // Collect sprintf params
364     for ($i = 4;$i < func_num_args();$i++) {
365       $val= func_get_arg($i);
366       if (is_array($val)){
367         $params[]= $val[0];
368         continue;
369       }
370       $params[]= $val;
371     }
373     $result= "&nbsp;";
374     $trans= call_user_func_array("sprintf", $params);
375     if ($trans != "") {
376       return("<a href='?plug=".$_GET['plug']."&amp;PID=$pid&amp;act=listing_edit_$row' title='$dn'>$trans</a>");
377     }
379     return $result;
380   }
383   function renderNavigation()
384   {
385     $result= array();
386     $enableBack = true;
387     $enableRoot = true;
388     $enableHome = true;
390     $ui = get_userinfo();
392     /* Check if base = first available base */
393     $deps = $ui->get_module_departments($this->module);
395     if(!count($deps) || $deps[0] == $this->filter->base){
396       $enableBack = false;
397       $enableRoot = false;
398     }
400     $listhead ="";
402     /* Check if we are in users home  department */
403     if(!count($deps) ||$this->filter->base == get_base_from_people($ui->dn)){
404       $enableHome = false;
405     }
407     /* Draw root button */
408     if($enableRoot){
409       $result["ROOT"]= "<input class='center' type='image' src='images/lists/root.png' align='middle' ".
410                        "title='"._("Go to root department")."' name='dep_root' alt='"._("Root")."'>";
411     }else{
412       $result["ROOT"]= "<img src='images/lists/root_grey.png' class='center' alt='"._("Root")."'>";
413     }
415     /* Draw back button */
416     if($enableBack){
417       $result["BACK"]= "<input class='center' type='image' align='middle' src='images/lists/back.png' ".
418                        "title='"._("Go up one department")."' alt='"._("Up")."' name='dep_back'>";
419     }else{
420       $result["BACK"]= "<img src='images/lists/back_grey.png' class='center' alt='"._("Up")."'>";
421     }
423     /* Draw home button */
424     if($enableHome){
425       $result["HOME"]= "<input class='center' type='image' align='middle' src='images/lists/home.png' ".
426                        "title='"._("Go to users department")."' alt='"._("Home")."' name='dep_home'>";
427     }else{
428       $result["HOME"]= "<img src='images/lists/home_grey.png' class='center' alt='"._("Home")."'>";
429     }
431     /* Draw reload button, this button is enabled everytime */
432     $result["RELOAD"]= "<input class='center' type='image' src='images/lists/reload.png' align='middle' ".
433                        "title='"._("Reload list")."' name='submit_department' alt='"._("Submit")."'>";
435     return ($result);
436   }
439   function getAction()
440   {
441     // Do not do anything if this is not our PID
442     # DISABLED because the object is not in the session
443     #if(isset($_REQUEST['PID']) && $_REQUEST['PID'] != $this->pid) {
444     #  return;
445     #}
447     $result= array("targets" => array(), "action" => "");
449     // Filter GET with "act" attributes
450     if (isset($_GET['act'])) {
451       $key= validate($_GET['act']);
452       $target= preg_replace('/^listing_[a-zA-Z_]+_([0-9]+)$/', '$1', $key);
453       if (isset($this->entries[$target]['dn'])) {
454         $result['action']= preg_replace('/^listing_([a-zA-Z_]+)_[0-9]+$/', '$1', $key);
455         $result['targets'][]= $this->entries[$target]['dn'];
456       }
457       return $result;
458     }
460     // Filter POST with "listing_" attributes
461     foreach ($_POST as $key => $prop) {
463       // Capture selections
464       if (preg_match('/^listing_selected_[0-9]+$/', $key)) {
465         $target= preg_replace('/^listing_selected_([0-9]+)$/', '$1', $key);
466         if (isset($this->entries[$target]['dn'])) {
467           $result['targets'][]= $this->entries[$target]['dn'];
468         }
469         continue;
470       }
472       // Capture action with target - this is a one shot
473       if (preg_match('/^listing_[a-zA-Z_]+_[0-9]+(|_x)$/', $key)) {
474         $target= preg_replace('/^listing_[a-zA-Z_]+_([0-9]+)(|_x)$/', '$1', $key);
475         if (isset($this->entries[$target]['dn'])) {
476           $result['action']= preg_replace('/^listing_([a-zA-Z_]+)_[0-9]+(|_x)$/', '$1', $key);
477           $result['targets']= array($this->entries[$target]['dn']);
478         }
479         break;
480       }
482       // Capture action without target
483       if (preg_match('/^listing_[a-zA-Z_]+(|_x)$/', $key)) {
484         $result['action']= preg_replace('/^listing_([a-zA-Z_]+)(|_x)$/', '$1', $key);
485         continue;
486       }
487     }
489     // Filter POST with "act" attributes -> posted from action menu
490     if (isset($_POST['act']) && $_POST['act'] != '') {
491       $result['action']= validate($_POST['act']);
492     }
494     return $result;
495   }
498   function renderActionMenu()
499   {
500     // Don't send anything if the menu is not defined
501     if (!isset($this->xmlData['actionmenu']['action'])){
502       return "";
503     }
505     // Load shortcut
506     $actions= &$this->xmlData['actionmenu']['action'];
507     $result= "<input type='hidden' name='act' id='actionmenu' value=''>".
508              "<ul class='level1' id='root'><li><a href='#'>Aktionen&nbsp;<img ".
509              "border=0 src='images/lists/sort-down.png'></a>";
511     // Build ul/li list
512     $result.= $this->recurseActions($actions);
514     return "<div id='pulldown'>".$result."</li></ul><div>";
515   }
518   function recurseActions($actions)
519   {
520     static $level= 2;
521     $result= "<ul class='level$level'>";
522     $separator= "";
524     foreach ($actions as $action) {
526       // Skip the entry completely if there's no permission to execute it
527       if (!$this->hasActionPermission($action, $this->filter->base)) {
528         continue;
529       }
531       // Fill image if set
532       $img= "";
533       if (isset($action['image'])){
534         $img= "<img border=0 src='".$action['image']."'>&nbsp;";
535       }
537       if ($action['type'] == "separator"){
538         $separator= " style='border-top:1px solid #AAA' ";
539         continue;
540       }
542       // Dive into subs
543       if ($action['type'] == "sub" && isset($action['action'])) {
544         $level++;
545         if (isset($action['label'])){
546           $result.= "<li$separator><a href='#'>$img"._($action['label'])."&nbsp;<img border='0' src='images/forward-arrow.png'></a>";
547         }
548         $result.= $this->recurseActions($action['action'])."</li>";
549         $level--;
550         $separator= "";
551         continue;
552       }
554       // Render entry elseways
555       if (isset($action['label'])){
556         $result.= "<li$separator><a href='#' onClick='document.getElementById(\"actionmenu\").value= \"".$action['name']."\";mainform.submit();'>$img"._($action['label'])."</a></li>";
557       }
559       // Check for special types
560       switch ($action['type']) {
561         case 'copypaste':
562           echo "actionmenu: copypaste missing<br>";
563           break;
565         case 'snapshot':
566           echo "actionmenu: snapshot missing<br>";
567           break;
569         case 'daemon':
570           echo "actionmenu: daemon missing<br>";
571           break;
572       }
574       $separator= "";
575     }
577     $result.= "</ul>";
578     return $result;
579   }
582   function hasActionPermission($action, $dn)
583   {
584     $ui= get_userinfo();
586     if (isset($action['acl'])) {
587       $acls= $action['acl'];
588       if (!is_array($acls)) {
589         $acls= array($acls);
590       }
592       // Every ACL has to pass
593       foreach ($acls as $acl) {
594         $module= $this->module;
595         $acllist= array();
597         // Split for category and plugins if needed
598         // match for "[rw]" style entries
599         if (preg_match('/^\[([rwcdm]+)\]$/', $acl, $match)){
600           $aclList= array($match[1]);
601         }
603         // match for "users[rw]" style entries
604         if (preg_match('/^([a-zA-Z0-9]+)\[([rwcdm]+)\]$/', $acl, $match)){
605           $module= $match[1];
606           $aclList= array($match[2]);
607         }
609         // match for "users/user[rw]" style entries
610         if (preg_match('/^([a-zA-Z0-9]+\/[a-zA-Z0-9]+)\[([rwcdm]+)\]$/', $acl, $match)){
611           $module= $match[1];
612           $aclList= array($match[2]);
613         }
615         // match "users/user[userPassword:rw(,...)*]" style entries
616         if (preg_match('/^([a-zA-Z0-9]+\/[a-zA-Z0-9]+)\[([a-zA-Z0-9]+:[rwcdm]+(,[a-zA-Z0-9]+:[rwcdm]+)*)\]$/', $acl, $match)){
617           $module= $match[1];
618           $aclList= split(',', $match[2]);
619         }
621         // Walk thru prepared ACL by using $module
622         foreach($aclList as $sAcl) {
623           $checkAcl= "";
625           // Category or detailed permission?
626           if (strpos('/', $module) === false) {
627             if (preg_match('/([a-zA-Z0-9]+):([rwcdm]+)/', $sAcl, $m) ) {
628               $checkAcl= $ui->get_permissions($dn, $module, $m[1]);
629               $sAcl= $m[2];
630             } else {
631               $checkAcl= $ui->get_permissions($dn, $module, '0');
632             }
633           } else {
634             $checkAcl= $ui->get_category_permissions($dn, $module);
635           }
637           // Split up remaining part of the acl and check if it we're
638           // allowed to do something...
639           $parts= str_split($sAcl);
640           foreach ($parts as $part) {
641             if (strpos($checkAcl, $part) === false){
642               return false;
643             }
644           }
646         }
647       }
648     }
650     return true;
651   }
654 ?>