Code

Fixed multi query editor
[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 $searches= array();
27   var $search;
28   var $category= "";
29   var $objectStorage= array();
30   var $base= "";
31   var $scope= "";
32   var $query;
33   var $value= "";
34   var $initial= false;
35   var $scopeMode= "auto";
36   var $converter= null;
37   var $pid;
40   function filter($filename)
41   {
42     global $config;
44     // Load eventually passed filename
45     if (!$this->load($filename)) {
46       die("Cannot parse $filename!");
47     }
49     $this->pid= preg_replace("/[^0-9]/", "", microtime(TRUE)); 
50   }
53   function load($filename)
54   {
55     $contents = file_get_contents($filename);
56     $this->xmlData= xml::xml2array($contents, 1);
58     if (!isset($this->xmlData['filterdef'])) {
59       return false;
60     }
62     $this->xmlData= $this->xmlData["filterdef"];
64     // Transfer category
65     if (isset($this->xmlData['definition']['category'])){
66       $this->category= $this->xmlData['definition']['category'];
67     }
69     // Load filter
70     if (isset($this->xmlData['search'])) {
72       // Array conversion
73       if (!is_array($this->xmlData['search'])) {
74         $searches= array($this->xmlData['search']);
75       } else {
76         $searches= $this->xmlData['search'];
77       }
79       // Fix problem with only one defined query.
80       if(isset($this->xmlData['search']['query'])){
81         $this->xmlData['search'] = array($this->xmlData['search']);
82       }
84       /* Store available searches */
85       foreach ($this->xmlData['search'] as $search) {
87         /* Do multi conversation */ 
88         if (!is_array($search['query'])){
89           $search['query']= array($search['query']);
90         }
92         /* Store search */
93         $search['fixed'] = TRUE;
94         $this->searches[$search['tag']]= $search;
95       }
97       $this->reloadUserFilter();
99     } else {
100       return false;
101     }
103     // Transfer scope
104     $this->scopeMode= $this->xmlData['definition']['scope'];
105     if ($this->scopeMode == "auto") {
106       $this->scope= "one";
107     } else {
108       $this->scope= $this->scopeMode;
109     }
111     // Transfer initial value
112     if (isset($this->xmlData['definition']['initial']) && $this->xmlData['definition']['initial'] == "true"){
113       $this->initial= true;
114     }
116     // Set default search mode
117     $this->setSearch($this->xmlData['definition']['default']);
119     return true;  
120   }
122   function reloadUserFilter()
123   {
124     // First remove all user filters
125     foreach($this->searches as $k => $s) {
126       if(!$s['fixed']) unset($this->searches[$k]);
127     }
128     
129     // Readd user filter.
130     foreach(userFilter::getFilter(array($this->category)) as $filter){
131       $tmp = array();
132       $tmp['tag'] = $filter['name'];
133       $tmp['label'] = $filter['description'];
134       $tmp['query'] = array();
135       foreach($filter['queries'] as $query){
136         if(isset($this->searches[$filter['parent']]['query']['attribute'])){
137           $query['attribute'] = $this->searches[$filter['parent']]['query']['attribute'];
138         }else{
139           $query['attribute'] = $this->searches[$filter['parent']]['query'][0]['attribute'];
140         }
141         $tmp['query'][] =  $query;
142       }
143       $tmp['autocomplete'] = $this->searches[$filter['parent']]['autocomplete'];
144       $tmp['fixed'] = FALSE;
145       $this->searches[$tmp['tag']] = $tmp;
146     }
147   }
150   function setSearch($method)
151   {
152     $patch= null;
154     // Move information
155     if (isset($this->searches[$method])) {
156       $this->query= array_merge($this->searches[$method]['query']);
157       if (!isset($this->query[0])) {
158         $this->query= array($this->query);
159       }
160  
161       $this->search= $method;
162     } else {
163       die ("Invalid search module!");
164     }
165   }
168   function getTextfield($tag, $value= "", $element= null)
169   {
170     $size= 30;
171     $maxlength= 30;
172     $result= "<input class='filter_textfield' id='$tag' name='$tag' type='text' size='$size' maxlength='{$maxlength}' value='".$value."'>";
173     if ($element && isset($element['autocomplete'])) {
174       $frequency= "0.5";
175       $characters= "1";
176       if (isset($element['autocomplete']['frequency'])) {
177         $frequency= $element['autocomplete']['frequency'];
178       }
179       if (isset($element['autocomplete']['characters'])) {
180         $characters= $element['autocomplete']['characters'];
181       }
182       $result.= "<div id='autocomplete$tag' class='autocomplete'></div>".
183         "<script type='text/javascript'>".
184         "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
185         "</script>";
187       $this->autocompleters[$tag]= $element;
188     }
189     return $result;
190   }
193   function getCurrentBase()
194   {
195     if (isset($this->search->base) && (string)$this->search->scope != "auto") {
196       return false;
197     }
199     return $this->base;
200   }
203   function getCurrentScope()
204   {
205     if (isset($this->search->scope) && (string)$this->search->scope != "auto") {
206       return (string)$this->search->scope;
207     }
209     return $this->scope;
210   }
213   function setConverter($hook)
214   {
215     $this->converter= $hook;
216   }
219   function setObjectStorage($storage)
220   {
221     $this->objectStorage= $storage;    
222   }
225   function setBase($base)
226   {
227     $this->base= $base;
228   }
231   function setCurrentScope($scope)
232   {
233     $this->scope= $scope;
234   }
238   function render()
239   {
241     $this->reloadUserFilter();
243     $content= "<table class='filter-wrapper'><tr><td>".$this->renderFilterMenu()."</td><td>";
244     $content.= "<div class='search-filter'>".$this->getTextfield('search_filter', $this->value, $this->searches[$this->search])."</div>".
245       "&nbsp;<button class='search-filter' type='submit' title='"._("Search")."'>".image("images/find.png")."</button></td></tr></table>";
247     // Return meta data
248     return ("<input type='hidden' name='FILTER_PID' value='".$this->pid."'>".$content);
249   }
252   function query()
253   {
254     global $class_mapping;
255     $result= array();
257     // Return empty list if initial is not set
258     if (!$this->initial) {
259       $this->initial= true;
260       return $result;
261     }
263     // Go thru all queries and merge results
264     foreach ($this->query as $query) {
265       if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
266         die("No backend specified in search config.");
267       }
269       // Is backend available?
270       $backend= "filter".$query['backend'];
271       if (!isset($class_mapping["$backend"])) {
272         die("Invalid backend specified in search config.");
273       }
275       // Load filter and attributes
276       $filter= $query['filter'];
277       $attributes= $query['attribute'];
279       // Handle converters if present
280       if ($this->converter) {
281         preg_match('/([^:]+)::(.*)$/', $this->converter, $m);
282         $filter= call_user_func(array($m[1], $m[2]), preg_replace('/\$/', $this->value, $filter));
283       }
285       // Do not replace escaped \$ - This is required to be able to search for e.g. windows machines.
286       if ($this->value == "") {
287         $filter= preg_replace("/\\$/", '*', $filter);
288       } else {
289         $filter= preg_replace("/\\$/", "*".normalizeLdap($this->value)."*", $filter);
290       }
292       $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes, $this->category, $this->objectStorage));
293     }
295     return ($result);
296   }
299   function update()
300   {
301     if (isset($_POST['FILTER_PID']) && $_POST['FILTER_PID'] == $this->pid) {
303       // Save input field
304       if (isset($_POST['search_filter'])) {
305         $this->value= validate($_POST['search_filter']);
306       }
308       // Save scope if needed
309       if ($this->scopeMode == "auto" && isset($_POST['act']) && $_POST['act'] == "toggle-subtree") {
310         $this->scope= ($this->scope == "one")?"sub":"one";
311       }
313       if (isset($_POST['act'])){
314         foreach ($this->searches as $tag => $cfg) {
315           if ($_POST['act'] == "filter-$tag") {
316             $this->setSearch($tag);
317             break;
318           }
319         }
320       }
321     }
323   }
326   function getCompletitionList($config, $value="*")
327   {
328     global $class_mapping;
329     $res= array();
331     // Load result attributes
332     $attributes= $config['autocomplete']['attribute'];
333     if (!is_array($attributes)) {
334       $attributes= array($attributes);
335     }
337     // Do the query
338     $result= array();
340     // Is backend available?
341     $queries= $config['query'];
342     if (!isset($queries[0])){
343       $queries= array($queries);
344     }
345     foreach ($queries as $query) {
346       $backend= "filter".$query['backend'];
347       if (!isset($class_mapping["$backend"])) {
348         die("Invalid backend specified in search config.");
349       }
350       $filter= preg_replace("/\\$/", "*".normalizeLdap($value)."*", $query['filter']);
352       $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
353           $this->category, $this->objectStorage));
354     }
356     foreach ($result as $entry) {
357       foreach ($attributes as $attribute) {
358         if (is_array($entry[$attribute])) {
359           for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
360             if (mb_stristr($entry[$attribute][$i], $value)) {
361               $res[]= $entry[$attribute][$i];
362             }
363           }
364         } else {
365           $res[]= $entry[$attribute];
366         }
367       }
368     }
370     return $res;
371   }
374   function processAutocomplete()
375   {
376     global $class_mapping;
377     $result= array();
379     // Introduce maximum number of entries
380     $max= 25;
382     if(isset($this->searches[$this->search]['autocomplete'])){
383       $result= $this->getCompletitionList($this->searches[$this->search], $_POST['search_filter']);
384       $result= array_unique($result);
385       asort($result);
387       echo '<ul>';
388       foreach ($result as $entry) {
389         echo '<li>'.mark($_POST['search_filter'], $entry).'</li>';
390         if ($max-- == 0) {
391           break;
392         }
393       }
395       echo '</ul>';
396     }
397   }
400   function getObjectBase($dn)
401   {
402     global $config;
403     $base= "";
405     // Try every object storage
406     $storage= $this->objectStorage;
407     if (!is_array($storage)){
408       $storage= array($storage);
409     }
410     foreach ($storage as $location) {
411       $pattern= "/^[^,]+,".preg_quote($location, '/')."/i";
412       $base= preg_replace($pattern, '', $dn);
413     }
415     /* Set to base, if we're not on a correct subtree */
416     if (!isset($config->idepartments[$base])){
417       $base= $config->current['BASE'];
418     }
420     return $base;
421   }
425   function renderFilterMenu()
426   {
427     // Load shortcut
428     $result= "<input type='hidden' name='act' id='filter' value=''><div style='display:none'><input type='submit' name='exec_filter' id='exec_filter' value=''></div>".
429       "<ul class='level1' id='filter-root'><li><a href='#'>".image("images/filter.png").image("images/lists/sort-down.png")."</a>";
431     // Build ul/li list
432     $separator= " style='border-top:1px solid #AAA' ";
433     $result.= "<ul class='level2'>";
435     // Build in filters
436     foreach ($this->searches as $tag => $config) {
438       if(!$config['fixed']) continue;  
440       if ($tag == $this->search) {
441         $result.= "<li><a href='#'>".image("images/checked.png")."&nbsp;"._($config['label'])."</a></li>";
442       } else {
443         $result.= "<li><a href='#' onClick='document.getElementById(\"filter\").value= \"filter-$tag\";mainform.submit();'>".image("images/empty.png")."&nbsp;"._($config['label'])."</a></li>";
444       }
445     }
447     // User defined filters
448     $first= true;
449     if (is_array($this->category)) {
450       $categories= $this->category;
451     } else {
452       $categories= array($this->category);
453     }
455     foreach ($this->searches as $tag => $config) {
456       if($config['fixed']) continue;  
458       if ($tag == $this->search) {
459         $result.= "<li".($first?$separator:"")."><a href='#'>".image("images/checked.png")."&nbsp;"._($config['label'])."</a></li>";
460       } else {
461         $result.= "<li".($first?$separator:"")."><a href='#' onClick='document.getElementById(\"filter\").value= \"filter-$tag\";mainform.submit();'>".image("images/empty.png")."&nbsp;"._($config['label'])."</a></li>";
462       }
464       $first= false;
465     }
467     // Render scope if set to auto
468     if ($this->scopeMode == "auto") {
469       $result.= "<li$separator><a href='#' onClick='document.getElementById(\"filter\").value= \"toggle-subtree\";mainform.submit();'>".($this->scope=="one"?image("images/empty.png"):image("images/checked.png"))."&nbsp;"._("Search in subtrees")."</a></li>";
470     }
473     // Edit filter menu
474     $result.= "<li$separator><a href='#' onClick='document.getElementById(\"filter\").value= \"config-filter\";mainform.submit();'>".image("images/configure.png")."&nbsp;"._("Edit filters")."...</a></li>";
476     $result.= "</ul>";
478     $script= '<script type="text/javascript" language="JavaScript">var menu2; menu2= new Menu("filter-root", "menu2", configMenu)</script>';
480     return "<div id='filtermenu'>".$result."</li></ul><div>$script";
481   }
484   function getFixedFilters()
485   {
486     return array_keys($this->searches);
487   }
492 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
493 ?>