f28f400c209e2b2f78dae8ea5ed98a05143eaff8
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 }
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= $this->searches[$method]['query'];
157 if (!isset($this->query[0])) {
158 $this->query= array($this->query);
159 }
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 " <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")." "._($config['label'])."</a></li>";
442 } else {
443 $result.= "<li><a href='#' onClick='document.getElementById(\"filter\").value= \"filter-$tag\";mainform.submit();'>".image("images/empty.png")." "._($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")." "._($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")." "._($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"))." "._("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")." "._("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 }
489 }
492 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
493 ?>