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 /* Store available searches */
75 foreach ($this->xmlData['search'] as $search) {
77 /* Do multi conversation */
78 if (!is_array($search['query'])){
79 $search['query']= array($search['query']);
80 }
82 /* Store search */
83 $this->searches[$search['label']]= $search;
85 }
86 } else {
87 return false;
88 }
90 // Transfer scope
91 $this->scopeMode= $this->xmlData['definition']['scope'];
92 if ($this->scopeMode == "auto") {
93 $this->scope= "one";
94 } else {
95 $this->scope= $this->scopeMode;
96 }
98 // Transfer initial value
99 if (isset($this->xmlData['definition']['initial']) && $this->xmlData['definition']['initial'] == "true"){
100 $this->initial= true;
101 }
103 // Transfer category
104 if (isset($this->xmlData['definition']['category'])){
105 $this->category= $this->xmlData['definition']['category'];
106 }
108 // Set default search mode
109 $this->setSearch($this->xmlData['definition']['default']);
111 return true;
112 }
115 function setSearch($method)
116 {
117 // Move information
118 if (isset($this->searches[$method])) {
119 $this->query= $this->searches[$method]['query'];
120 if (!isset($this->query[0])) {
121 $this->query= array($this->query);
122 }
123 $this->search= $method;
124 } else {
125 die ("Invalid search module!");
126 }
127 }
130 function getTextfield($element)
131 {
132 $tag= $element['tag'];
133 $size= 30;
134 if (isset($element['size'])){
135 $size= $element['size'];
136 }
137 $maxlength= 30;
138 if (isset($element['maxlength'])){
139 $maxlength= $element['maxlength'];
140 }
141 $result= "<input class='filter_textfield' id='$tag' name='$tag' type='text' size='$size' maxlength='{$maxlength}' value='".$this->elementValues[$tag]."'>";
142 if (isset($element['autocomplete'])) {
143 $frequency= "0.5";
144 $characters= "1";
145 if (isset($element['autocomplete']['frequency'])) {
146 $frequency= $element['autocomplete']['frequency'];
147 }
148 if (isset($element['autocomplete']['characters'])) {
149 $characters= $element['autocomplete']['characters'];
150 }
151 $result.= "<div id='autocomplete$tag' class='autocomplete'></div>".
152 "<script type='text/javascript'>".
153 "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
154 "</script>";
156 $this->autocompleters[$tag]= $element['autocomplete'];
157 }
158 return $result;
159 }
162 function getCurrentBase()
163 {
164 if (isset($this->search->base) && (string)$this->search->scope != "auto") {
165 return false;
166 }
168 return $this->base;
169 }
172 function getCurrentScope()
173 {
174 if (isset($this->search->scope) && (string)$this->search->scope != "auto") {
175 return (string)$this->search->scope;
176 }
178 return $this->scope;
179 }
182 function setConverter($hook)
183 {
184 $this->converter= $hook;
185 }
188 function setObjectStorage($storage)
189 {
190 $this->objectStorage= $storage;
191 }
194 function setBase($base)
195 {
196 $this->base= $base;
197 }
200 function setCurrentScope($scope)
201 {
202 $this->scope= $scope;
203 }
206 function renderApply()
207 {
208 return ("<input type='submit' name='apply' value='"._("Apply filter")."'>");
209 }
212 function renderScope()
213 {
214 $checked= $this->scope == "sub"?" checked":"";
215 return "<input type='checkbox' id='SCOPE' name='SCOPE' value='1' onClick='document.mainform.submit();'$checked> <LABEL for='SCOPE'>"._("Search in subtrees")."</LABEL>";
216 }
219 function render()
220 {
221 $content= "<table class='filter-wrapper'><tr><td>".$this->renderFilterMenu()."</td><td>";
222 $content.= "<div class='search-filter'><input type='text' name='search_filter' value='".$this->value."'></div>".
223 " <button class='search-filter' type='submit' title='"._("Search")."'>".image("images/find.png")."</button></td></tr></table>";
225 // Return meta data
226 return ("<input type='hidden' name='FILTER_PID' value='".$this->pid."'>".$content);
227 }
230 function query()
231 {
232 global $class_mapping;
233 $result= array();
235 // Return empty list if initial is not set
236 if (!$this->initial) {
237 $this->initial= true;
238 return $result;
239 }
241 // Go thru all queries and merge results
242 foreach ($this->query as $query) {
243 if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
244 die("No backend specified in search config.");
245 }
247 // Is backend available?
248 $backend= "filter".$query['backend'];
249 if (!isset($class_mapping["$backend"])) {
250 die("Invalid backend specified in search config.");
251 }
253 // Load filter and attributes
254 $filter= $query['filter'];
255 $attributes= $query['attribute'];
257 // Handle converters if present
258 if ($this->converter) {
259 preg_match('/([^:]+)::(.*)$/', $this->converter, $m);
260 $filter= call_user_func(array($m[1], $m[2]), preg_replace('/\$/', $this->value, $filter));
261 }
263 // Do not replace escaped \$ - This is required to be able to search for e.g. windows machines.
264 if ($this->value == "") {
265 $filter= preg_replace("/\\$/", '*', $filter);
266 } else {
267 $filter= preg_replace("/\\$/", "*".normalizeLdap($this->value)."*", $filter);
268 }
270 $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes, $this->category, $this->objectStorage));
271 }
273 return ($result);
274 }
277 function update()
278 {
279 if (isset($_POST['FILTER_PID']) && $_POST['FILTER_PID'] == $this->pid) {
281 // Save input field
282 if (isset($_POST['search_filter'])) {
283 $this->value= validate($_POST['search_filter']);
284 }
286 // Save scope if needed
287 if ($this->scopeMode == "auto") {
288 $this->scope= isset($_POST['SCOPE'])?"sub":"one";
289 }
290 }
292 }
295 function getCompletitionList($config, $value="*")
296 {
297 global $class_mapping;
298 $res= array();
300 // Is backend available?
301 $backend= "filter".$config['backend'];
302 if (!isset($class_mapping["$backend"])) {
303 die("Invalid backend specified in search config.");
304 }
306 // Load filter and attributes
307 $filter= $config['filter'];
308 $attributes= $config['attribute'];
309 if (!is_array($attributes)) {
310 $attributes= array($attributes);
311 }
313 // Make filter
314 $filter= preg_replace("/\\$/", normalizeLdap($value), $filter);
315 if (isset($config['base']) && isset($config['scope']) && isset($config['category'])) {
316 $result= call_user_func(array($backend, 'query'), $config['base'], $config['scope'], $filter, $attributes,
317 $config["category"], $config["objectStorage"]);
318 } else {
319 $result= call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
320 $this->category, $this->objectStorage);
321 }
323 foreach ($result as $entry) {
324 foreach ($attributes as $attribute) {
325 if (is_array($entry[$attribute])) {
326 for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
327 if (mb_stristr($entry[$attribute][$i], $value)) {
328 $res[]= $entry[$attribute][$i];
329 }
330 }
331 } else {
332 $res[]= $entry[$attribute];
333 }
334 }
335 }
337 return $res;
338 }
341 function processAutocomplete()
342 {
343 global $class_mapping;
344 $result= array();
346 // Introduce maximum number of entries
347 $max= 25;
349 if(isset($this->searches[$this->search]['autocomplete'])){
350 $result= $this->getCompletitionList($this->searches[$this->search]['autocomplete'], $_POST['search_filter']);
351 $result= array_unique($result);
352 asort($result);
354 echo '<ul>';
355 foreach ($result as $entry) {
356 echo '<li>'.mark($_POST['search_filter'], $entry).'</li>';
357 if ($max-- == 0) {
358 break;
359 }
360 }
362 echo '</ul>';
363 }
364 }
367 function getObjectBase($dn)
368 {
369 global $config;
370 $base= "";
372 // Try every object storage
373 $storage= $this->objectStorage;
374 if (!is_array($storage)){
375 $storage= array($storage);
376 }
377 foreach ($storage as $location) {
378 $pattern= "/^[^,]+,".preg_quote($location, '/')."/i";
379 $base= preg_replace($pattern, '', $dn);
380 }
382 /* Set to base, if we're not on a correct subtree */
383 if (!isset($config->idepartments[$base])){
384 $base= $config->current['BASE'];
385 }
387 return $base;
388 }
392 function renderFilterMenu()
393 {
394 // Load shortcut
395 $result= "<input type='hidden' name='act' id='filter' value=''><div style='display:none'><input type='submit' name='exec_filter' id='exec_filter' value=''></div>".
396 "<ul class='level1' id='filter-root'><li><a href='#'>".image("images/filter.png")._("...")." ".image("images/lists/sort-down.png")."</a>";
398 // Build ul/li list
399 $separator= " style='border-top:1px solid #AAA' ";
400 $result.= "<ul class='level2'>";
401 $result.= "<li><a href='#'>Dummy placeholder</a></li>";
402 $result.= "<li$separator><a href='#' onClick='document.getElementById(\"filter\").value= \"config-filter\";mainform.submit();'>".image("images/configure.png")." "._("Edit filters")."...</a></li>";
403 #$result.= "<li$separator><a href='#' onClick='document.getElementById(\"actionmenu\").value= \"".$action['name']."\";mainform.submit();'>$img"._($action['label'])."</a></li>";
405 # AUTOCOMPLETER and getTextbox
407 $result.= "</ul>";
409 $script= '<script type="text/javascript" language="JavaScript">var menu2; menu2= new Menu("filter-root", "menu2", configMenu)</script>';
411 return "<div id='filtermenu'>".$result."</li></ul><div>$script";
412 }
414 }
417 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
418 ?>