Code

Updated filter to support single query entries
[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     // Load filter
65     if (isset($this->xmlData['search'])) {
67       // Array conversion
68       if (!is_array($this->xmlData['search'])) {
69         $searches= array($this->xmlData['search']);
70       } else {
71         $searches= $this->xmlData['search'];
72       }
74       // Fix problem with only one defined query.
75       if(isset($this->xmlData['search']['query'])){
76         $this->xmlData['search'] = array($this->xmlData['search']);
77       }
79       /* Store available searches */
80       foreach ($this->xmlData['search'] as $search) {
82         /* Do multi conversation */ 
83         if (!is_array($search['query'])){
84           $search['query']= array($search['query']);
85         }
87         /* Store search */
88         $this->searches[$search['tag']]= $search;
90       }
91     } else {
92       return false;
93     }
95     // Transfer scope
96     $this->scopeMode= $this->xmlData['definition']['scope'];
97     if ($this->scopeMode == "auto") {
98       $this->scope= "one";
99     } else {
100       $this->scope= $this->scopeMode;
101     }
103     // Transfer initial value
104     if (isset($this->xmlData['definition']['initial']) && $this->xmlData['definition']['initial'] == "true"){
105       $this->initial= true;
106     }
108     // Transfer category
109     if (isset($this->xmlData['definition']['category'])){
110       $this->category= $this->xmlData['definition']['category'];
111     }
113     // Set default search mode
114     $this->setSearch($this->xmlData['definition']['default']);
116     return true;  
117   }
120   function setSearch($method)
121   {
122     $patch= null;
123     if (is_array($this->category)) {
124       $categories= $this->category;
125     } else {
126       $categories= array($this->category);
127     }
128     $userfilters= userFilter::getFilter($categories);
130     // User filter selected?
131     if (isset($userfilters[$method])){
132       $usermethod= $method;
133       $patch= $userfilters[$method]['filter'];
134       $method= $userfilters[$method]['parent'];
135     }
137     // Move information
138     if (isset($this->searches[$method])) {
139       $this->query= array_merge($this->searches[$method]['query']);
140       if (!isset($this->query[0])) {
141         $this->query= array($this->query);
142       }
143  
144       // Patch first filter?
145       if ($patch) {
146         $this->query[0]['filter']= $patch;
147         $method= $usermethod;
148       }
150       $this->search= $method;
151     } else {
152       die ("Invalid search module!");
153     }
154   }
157   function getTextfield($tag, $value= "", $element= null)
158   {
159     $size= 30;
160     $maxlength= 30;
161     $result= "<input class='filter_textfield' id='$tag' name='$tag' type='text' size='$size' maxlength='{$maxlength}' value='".$value."'>";
162     if ($element && isset($element['autocomplete'])) {
163       $frequency= "0.5";
164       $characters= "1";
165       if (isset($element['autocomplete']['frequency'])) {
166         $frequency= $element['autocomplete']['frequency'];
167       }
168       if (isset($element['autocomplete']['characters'])) {
169         $characters= $element['autocomplete']['characters'];
170       }
171       $result.= "<div id='autocomplete$tag' class='autocomplete'></div>".
172         "<script type='text/javascript'>".
173         "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
174         "</script>";
176       $this->autocompleters[$tag]= $element;
177     }
178     return $result;
179   }
182   function getCurrentBase()
183   {
184     if (isset($this->search->base) && (string)$this->search->scope != "auto") {
185       return false;
186     }
188     return $this->base;
189   }
192   function getCurrentScope()
193   {
194     if (isset($this->search->scope) && (string)$this->search->scope != "auto") {
195       return (string)$this->search->scope;
196     }
198     return $this->scope;
199   }
202   function setConverter($hook)
203   {
204     $this->converter= $hook;
205   }
208   function setObjectStorage($storage)
209   {
210     $this->objectStorage= $storage;    
211   }
214   function setBase($base)
215   {
216     $this->base= $base;
217   }
220   function setCurrentScope($scope)
221   {
222     $this->scope= $scope;
223   }
227   function render()
228   {
229     $content= "<table class='filter-wrapper'><tr><td>".$this->renderFilterMenu()."</td><td>";
230     $content.= "<div class='search-filter'>".$this->getTextfield('search_filter', $this->value, $this->searches[$this->search])."</div>".
231       "&nbsp;<button class='search-filter' type='submit' title='"._("Search")."'>".image("images/find.png")."</button></td></tr></table>";
233     // Return meta data
234     return ("<input type='hidden' name='FILTER_PID' value='".$this->pid."'>".$content);
235   }
238   function query()
239   {
240     global $class_mapping;
241     $result= array();
243     // Return empty list if initial is not set
244     if (!$this->initial) {
245       $this->initial= true;
246       return $result;
247     }
249     // Go thru all queries and merge results
250     foreach ($this->query as $query) {
251       if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
252         die("No backend specified in search config.");
253       }
255       // Is backend available?
256       $backend= "filter".$query['backend'];
257       if (!isset($class_mapping["$backend"])) {
258         die("Invalid backend specified in search config.");
259       }
261       // Load filter and attributes
262       $filter= $query['filter'];
263       $attributes= $query['attribute'];
265       // Handle converters if present
266       if ($this->converter) {
267         preg_match('/([^:]+)::(.*)$/', $this->converter, $m);
268         $filter= call_user_func(array($m[1], $m[2]), preg_replace('/\$/', $this->value, $filter));
269       }
271       // Do not replace escaped \$ - This is required to be able to search for e.g. windows machines.
272       if ($this->value == "") {
273         $filter= preg_replace("/\\$/", '*', $filter);
274       } else {
275         $filter= preg_replace("/\\$/", "*".normalizeLdap($this->value)."*", $filter);
276       }
278       $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes, $this->category, $this->objectStorage));
279     }
281     return ($result);
282   }
285   function update()
286   {
287     if (isset($_POST['FILTER_PID']) && $_POST['FILTER_PID'] == $this->pid) {
289       // Save input field
290       if (isset($_POST['search_filter'])) {
291         $this->value= validate($_POST['search_filter']);
292       }
294       // Save scope if needed
295       if ($this->scopeMode == "auto" && isset($_POST['act']) && $_POST['act'] == "toggle-subtree") {
296         $this->scope= ($this->scope == "one")?"sub":"one";
297       }
299       // Switch filter?
300       if (is_array($this->category)) {
301         $categories= $this->category;
302       } else {
303         $categories= array($this->category);
304       }
306       if (isset($_POST['act'])) {
307         foreach (array_merge($this->searches, userFilter::getFilter($categories)) as $tag => $cfg) {
308           if ($_POST['act'] == "filter-$tag") {
309             $this->setSearch($tag);
310             break;
311           }
312         }
313       }
314     }
316   }
319   function getCompletitionList($config, $value="*")
320   {
321     global $class_mapping;
322     $res= array();
324     // Load result attributes
325     $attributes= $config['autocomplete']['attribute'];
326     if (!is_array($attributes)) {
327       $attributes= array($attributes);
328     }
330     // Do the query
331     $result= array();
333     // Is backend available?
334     $queries= $config['query'];
335     if (!isset($queries[0])){
336       $queries= array($queries);
337     }
338     foreach ($queries as $query) {
339       $backend= "filter".$query['backend'];
340       if (!isset($class_mapping["$backend"])) {
341         die("Invalid backend specified in search config.");
342       }
343       $filter= preg_replace("/\\$/", "*".normalizeLdap($value)."*", $query['filter']);
345       $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
346           $this->category, $this->objectStorage));
347     }
349     foreach ($result as $entry) {
350       foreach ($attributes as $attribute) {
351         if (is_array($entry[$attribute])) {
352           for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
353             if (mb_stristr($entry[$attribute][$i], $value)) {
354               $res[]= $entry[$attribute][$i];
355             }
356           }
357         } else {
358           $res[]= $entry[$attribute];
359         }
360       }
361     }
363     return $res;
364   }
367   function processAutocomplete()
368   {
369     global $class_mapping;
370     $result= array();
372     // Introduce maximum number of entries
373     $max= 25;
375     if(isset($this->searches[$this->search]['autocomplete'])){
376       $result= $this->getCompletitionList($this->searches[$this->search], $_POST['search_filter']);
377       $result= array_unique($result);
378       asort($result);
380       echo '<ul>';
381       foreach ($result as $entry) {
382         echo '<li>'.mark($_POST['search_filter'], $entry).'</li>';
383         if ($max-- == 0) {
384           break;
385         }
386       }
388       echo '</ul>';
389     }
390   }
393   function getObjectBase($dn)
394   {
395     global $config;
396     $base= "";
398     // Try every object storage
399     $storage= $this->objectStorage;
400     if (!is_array($storage)){
401       $storage= array($storage);
402     }
403     foreach ($storage as $location) {
404       $pattern= "/^[^,]+,".preg_quote($location, '/')."/i";
405       $base= preg_replace($pattern, '', $dn);
406     }
408     /* Set to base, if we're not on a correct subtree */
409     if (!isset($config->idepartments[$base])){
410       $base= $config->current['BASE'];
411     }
413     return $base;
414   }
418   function renderFilterMenu()
419   {
420     // Load shortcut
421     $result= "<input type='hidden' name='act' id='filter' value=''><div style='display:none'><input type='submit' name='exec_filter' id='exec_filter' value=''></div>".
422       "<ul class='level1' id='filter-root'><li><a href='#'>".image("images/filter.png").image("images/lists/sort-down.png")."</a>";
424     // Build ul/li list
425     $separator= " style='border-top:1px solid #AAA' ";
426     $result.= "<ul class='level2'>";
428     // Build in filters
429     foreach ($this->searches as $tag => $config) {
430       if ($tag == $this->search) {
431         $result.= "<li><a href='#'>".image("images/checked.png")."&nbsp;"._($config['label'])."</a></li>";
432       } else {
433         $result.= "<li><a href='#' onClick='document.getElementById(\"filter\").value= \"filter-$tag\";mainform.submit();'>".image("images/empty.png")."&nbsp;"._($config['label'])."</a></li>";
434       }
435     }
437     // User defined filters
438     $first= true;
439     if (is_array($this->category)) {
440       $categories= $this->category;
441     } else {
442       $categories= array($this->category);
443     }
444     foreach (userFilter::getFilter($categories) as $tag => $config) {
445       if ($tag == $this->search) {
446         $result.= "<li".($first?$separator:"")."><a href='#'>".image("images/checked.png")."&nbsp;"._($config['description'])."</a></li>";
447       } else {
448         $result.= "<li".($first?$separator:"")."><a href='#' onClick='document.getElementById(\"filter\").value= \"filter-$tag\";mainform.submit();'>".image("images/empty.png")."&nbsp;"._($config['description'])."</a></li>";
449       }
451       $first= false;
452     }
454     // Render scope if set to auto
455     if ($this->scopeMode == "auto") {
456       $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>";
457     }
460     // Edit filter menu
461     $result.= "<li$separator><a href='#' onClick='document.getElementById(\"filter\").value= \"config-filter\";mainform.submit();'>".image("images/configure.png")."&nbsp;"._("Edit filters")."...</a></li>";
463     $result.= "</ul>";
465     $script= '<script type="text/javascript" language="JavaScript">var menu2; menu2= new Menu("filter-root", "menu2", configMenu)</script>';
467     return "<div id='filtermenu'>".$result."</li></ul><div>$script";
468   }
471   function getFixedFilters()
472   {
473     return array_keys($this->searches);
474   }
479 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
480 ?>