Code

Fixed entry loading
[gosa.git] / gosa-core / include / class_filter.inc
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 filter {
25   var $xmlData;
26   var $elements= array();
27   var $elementValues= array();
28   var $alphabetElements= array();
29   var $autocompleter= array();
30   var $category= "";
31   var $objectStorage= array();
32   var $base= "";
33   var $scope= "";
34   var $query;
35   var $initial= false;
36   var $scopeMode= "auto";
37   var $alphabet= null;
40   function filter($filename)
41   {
42     global $config;
44     // Load eventually passed filename
45     if (!$this->load($filename)) {
46       die("Cannot parse $filename!");
47     }
48   }
51   function load($filename)
52   {
53     $contents = file_get_contents($filename);
54     $this->xmlData= xml::xml2array($contents, 1);
56     if (!isset($this->xmlData['filter'])) {
57       return false;
58     }
60     $this->xmlData= $this->xmlData["filter"];
62     // Load filter
63     if (isset($this->xmlData['search'])) {
64       if (!isset($this->xmlData['search']['query'][0])){
65         $this->xmlData['search']['query']= array($this->xmlData['search']['query']);
66       }
68       // Move information
69       $entry= $this->xmlData['search'];
70       $this->scopeMode= $entry['scope'];
71       if ($entry['scope'] == "auto") {
72         $this->scope= "one";
73       } else {
74         $this->scope= $entry['scope'];
75       }
76       $this->query= $entry['query'];
77     } else {
78       return false;
79     }
81     // Transfer initial value
82     if (isset($this->xmlData['definition']['initial']) && $this->xmlData['definition']['initial'] == "true"){
83       $this->initial= true;
84     }
86     // Transfer category
87     if (isset($this->xmlData['definition']['category'])){
88       $this->category= $this->xmlData['definition']['category'];
89     }
91     // Generate formular data
92     if (isset($this->xmlData['element'])) {
93       if (!isset($this->xmlData['element'][0])){
94         $this->xmlData['element']= array($this->xmlData['element']);
95       }
96       foreach ($this->xmlData['element'] as $element) {
98         // Ignore elements without type
99         if (!isset($element['type']) || !isset($element['tag'])) {
100           next;
101         }
103         $tag= $element['tag'];
105         // Fix arrays
106         if (isset($element['value']) && !isset($element['value'][0])) {
107           $element['value']= array($element['value']);
108         }
110         // Store element for quick access
111         $this->elements[$tag] = $element;
113         // Preset elementValues with default values if exist
114         if (isset($element['default']) && !is_array($element['default'])) {
115           $this->elementValues[$tag] = $element['default'];
116         } else {
117           $this->elementValues[$tag] = "";
118         }
120         // Does this element react on alphabet links?
121         if (isset($element['alphabet']) && $element['alphabet'] == "true") {
122           $this->alphabetElements[]= $tag;
123         }
124       }
126       // Sort elements for element length to allow proper replacing later on
127       function strlenSort($a, $b) {
128         if (strlen($a['tag']) == strlen($b['tag'])) {
129           return 0;
130         }
131        return (strlen($a['tag']) < strlen($b['tag']) ? -1 : 1);
132       } 
133       uasort($this->elements, 'strlenSort');
134       $this->elements= array_reverse($this->elements);
136     }
138     return true;  
139   }
142   function getTextfield($element)
143   {
144     $tag= $element['tag'];
145     $size= 30;
146     if (isset($element['size'])){
147       $size= $element['size'];
148     }
149     $maxlength= 30;
150     if (isset($element['maxlength'])){
151       $maxlength= $element['maxlength'];
152     }
153     $result= "<input class='filter_textfield' id='$tag' name='$tag' type='text' size='$size' maxlength='maxlength' value='".$this->elementValues[$tag]."'>";
154     if (isset($element['autocomplete'])) {
155       $frequency= "0.5";
156       $characters= "1";
157       if (isset($element['autocomplete']['frequency'])) {
158         $frequency= $element['autocomplete']['frequency'];
159       }
160       if (isset($element['autocomplete']['characters'])) {
161         $characters= $element['autocomplete']['characters'];
162       }
163       $result.= "<div id='autocomplete$tag' class='autocomplete'></div>".
164                 "<script type='text/javascript'>".
165                 "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
166                 "</script>";
168        $this->autocompleters[$tag]= $element['autocomplete'];
169     }
170     return $result;
171   }
174   function getCheckbox($element)
175   {
176     $tag= $element['tag'];
177     $checked= "";
178     if ($this->elementValues[$tag] == "true") {
179       $checked= " checked";
180     }
182     $result= "<input class='filter_checkbox' name='$tag' type='checkbox' onClick='document.mainform.submit();' value='true'$checked>";
183     return $result;
184   }
187   function getCombobox($element)
188   {
189     $result= "<select name='".$element['tag']."' size='1' onClick='document.mainform.submit();'>";
191     // Fill with presets
192     foreach ($element['value'] as $value) {
193       $selected= "";
194       if ($this->elementValues[$element['tag']] == $value['key']) {
195         $selected= " selected";
196       }
198       // Handle translations
199       $result.= "<option value='".$value['key']."'$selected>"._($value['label'])."</option>";
200     }
202     // Use autocompleter for additional data
203     if (isset($element['autocomplete'])) {
204       $list= $this->getCompletitionList($element['autocomplete'], $element['tag']);
205       foreach ($list as $value) {
206         $selected= "";
207         if ($this->elementValues[$element['tag']] == $value) {
208           $selected= " selected";
209         }
210         $result.= "<option value='$value'$selected>$value</option>";
211       }
212     }
214     $result.= "</select>";
216     return $result;
217   }
220   function getCurrentBase()
221   {
222     if (isset($this->search->base) && (string)$this->search->scope != "auto") {
223       return false;
224     }
226     return $this->base;
227   }
230   function getCurrentScope()
231   {
232     if (isset($this->search->scope) && (string)$this->search->scope != "auto") {
233       return (string)$this->search->scope;
234     }
236     return $this->scope;
237   }
240   function setObjectStorage($storage) {
241     $this->objectStorage= $storage;    
242   }
245   function setBase($base) {
246     $this->base= $base;
247   }
250   function setCurrentScope($scope) {
251     $this->scope= $scope;
252   }
255   function renderAlphabet($columns= 10)
256   {
257     // Return pre-rendered alphabet if available
258     if ($this->alphabet) {
259       return ($this->alphabet);
260     }
262     $characters= _("*ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
263     $alphabet= "";
264     $c= 0;
266     /* Fill cells with charaters */
267     for ($i= 0, $l= mb_strlen($characters, 'UTF8'); $i<$l; $i++){
268       if ($c == 0){
269         $alphabet.= "<tr>";
270       }
272       $ch = mb_substr($characters, $i, 1, "UTF8");
273       $alphabet.= "<td><a class=\"alphaselect\" href=\"main.php?plug=".
274         validate($_GET['plug'])."&amp;filter=".$ch."\">&nbsp;".$ch."&nbsp;</a></td>";
276       if ($c++ == $columns){
277         $alphabet.= "</tr>";
278         $c= 0;
279       }
280     }
282     /* Fill remaining cells */
283     while ($c++ <= $columns){
284       $alphabet.= "<td>&nbsp;</td>";
285     }
287     /* Save alphabet */
288     $this->alphabet= "<table width='100%'>$alphabet</table>";
290     return ($this->alphabet);
291   }
294   function renderApply()
295   {
296     return ("<input type='submit' name='apply' value='"._("Apply filter")."'>");
297   }
300   function renderScope()
301   {
302     $checked= $this->scope == "sub"?" checked":"";
303     return "<input type='checkbox' name='SCOPE' value='1' onClick='document.mainform.submit();'$checked>&nbsp;"._("Search in subtrees");
304   }
307   function render()
308   {
309     $smarty= get_smarty();
310     $smarty->assign("ALPHABET", $this->renderAlphabet());
311     $smarty->assign("APPLY", $this->renderApply());
312     $smarty->assign("SCOPE", $this->renderScope());
314     // Load template and replace elementsHtml[]
315     foreach ($this->elements as $tag => $element) {
316       $htmlCode= "";
317       switch ($element['type']) {
318         case "textfield":
319           $htmlCode = $this->getTextfield($element);
320           break;
322         case "checkbox":
323           $htmlCode = $this->getCheckbox($element);
324           break;
326         case "combobox":
327           $htmlCode = $this->getCombobox($element);
328           break;
330         default:
331           die ("Unknown element type specified!");
332       }
333       $smarty->assign("$tag", $htmlCode);
334     }
336     // Load template
337     return ("<input type='hidden' name='FILTER_LOADED' value='1'>".$smarty->fetch(get_template_path($this->xmlData['definition']['template'], true)));
338   }
341   function query()
342   {
343     global $class_mapping;
344     $result= array();
346     // Go thru all queries and merge results
347     foreach ($this->query as $query) {
348       if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
349         die("No backend specified in search config.");
350       }
352       // Is backend available?
353       $backend= "filter".$query['backend'];
354       if (!isset($class_mapping["$backend"])) {
355         die("Invalid backend specified in search config.");
356       }
358       // Load filter and attributes
359       $filter= $query['filter'];
360       $attributes= $query['attribute'];
362       // Generate final filter
363       foreach ($this->elements as $tag => $element) {
364         if (!isset($element['set']) || !isset($element['unset'])) {
365           continue;
366         }
367         $e_set= is_array($element['set'])?"":$element['set'];
368         $e_unset= is_array($element['unset'])?"":$element['unset'];
370         if ($this->elementValues[$tag] == "") {
371           $e_unset= preg_replace('/\$/', normalizeLdap($this->elementValues[$tag]), $e_unset);
372           $filter= preg_replace("/\\$$tag/", $e_unset, $filter);
373         } else {
374           $e_set= preg_replace('/\$/', normalizeLdap($this->elementValues[$tag]), $e_set);
375           $filter= preg_replace("/\\$$tag/", $e_set, $filter);
376         }
377       }
379       $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
380                                $this->category, $this->objectStorage));
381     }
382     
384     return ($result);
385   }
388   function isValid()
389   {
390     foreach ($this->elements as $tag => $element) {
391       if (isset($element->regex)){
392         if (!preg_match('/'.(string)$element->regex.'/', $this->elementValues[$tag])){
393           return false;
394         }
395       }
396     }
397     return true;
398   }
401   function update()
402   {
404     /* React on alphabet links if needed */
405     if (isset($_GET['filter'])){
406       $s= mb_substr(validate($_GET['filter']), 0, 1, "UTF8")."*";
407       if ($s == "**"){
408         $s= "*";
409       }
410       foreach ($this->alphabetElements as $tag) {
411         $this->elementValues[$tag]= $s;
412       }
413     }
415     if (isset($_POST['FILTER_LOADED'])) {
416       // Load post values and adapt filter, base and scope accordingly - but
417       // only if we didn't get a _GET
418       foreach ($this->elements as $tag => $element) {
419         if (isset($_POST[$tag])){
420           $this->elementValues[$tag]= validate($_POST[$tag]);
421         } else {
422           $this->elementValues[$tag]= "";
423         }
424       }
426       // Save scope if needed
427       if ($this->scopeMode == "auto") {
428         $this->scope= isset($_POST['SCOPE'])?"sub":"one";
429       }
430     }
432   }
435   function getCompletitionList($config, $tag, $value="*")
436   {
437     global $class_mapping;
438     $res= array();
440     // Is backend available?
441     $backend= "filter".$config['backend'];
442     if (!isset($class_mapping["$backend"])) {
443       die("Invalid backend specified in search config.");
444     }
446     // Load filter and attributes
447     $filter= $config['filter'];
448     $attributes= $config['attribute'];
449     if (!is_array($attributes)) {
450       $attributes= array($attributes);
451     }
453     // Make filter
454     $filter= preg_replace("/\\$$tag/", normalizeLDAP($value), $filter);
455     if (isset($config['base']) && isset($config['scope']) && isset($config['category'])) {
456       $result= call_user_func(array($backend, 'query'), $config['base'], $config['scope'], $filter, $attributes,
457                            $config["category"], $config["objectStorage"]);
458     } else {
459       $result= call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
460                            $this->category, $this->objectStorage);
461     }
463     foreach ($result as $entry) {
464       foreach ($attributes as $attribute) {
465         if (is_array($entry[$attribute])) {
466           for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
467             $res[]= $entry[$attribute][$i];
468           }
469         } else {
470           $res[]= $entry[$attribute];
471         }
472       }
473     }
475     return $res;
476   }
479   function processAutocomplete()
480   {
481     global $class_mapping;
482     $result= array();
484     // Introduce maximum number of entries
485     $max= 25;
487     foreach ($this->autocompleters as $tag => $config) {
488       if(isset($_POST[$tag])){
489         $result= $this->getCompletitionList($config, $tag, $_POST[$tag]);
490         $result= array_unique($result);
491         asort($result);
493         echo '<ul>';
494         foreach ($result as $entry) {
495           echo '<li>'.$entry.'</li>';
496           if ($max-- == 0) {
497             break;
498           }
499         }
501         echo '</ul>';
502       }
503     }
504   }
509 ?>