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= "Search comes here...";
223 // Return meta data
224 return ("<input type='hidden' name='FILTER_PID' value='".$this->pid."'>".$content);
225 }
228 function query()
229 {
230 global $class_mapping;
231 $result= array();
233 // Return empty list if initial is not set
234 if (!$this->initial) {
235 $this->initial= true;
236 return $result;
237 }
239 // Go thru all queries and merge results
240 foreach ($this->query as $query) {
241 if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
242 die("No backend specified in search config.");
243 }
245 // Is backend available?
246 $backend= "filter".$query['backend'];
247 if (!isset($class_mapping["$backend"])) {
248 die("Invalid backend specified in search config.");
249 }
251 // Load filter and attributes
252 $filter= $query['filter'];
253 $attributes= $query['attribute'];
255 // Handle converters if present
256 if ($this->converter) {
257 preg_match('/([^:]+)::(.*)$/', $this->converter, $m);
258 $filter= call_user_func(array($m[1], $m[2]), preg_replace('/\$/', $this->value, $filter));
259 }
261 // Do not replace escaped \$ - This is required to be able to search for e.g. windows machines.
262 if ($this->value == "") {
263 $filter= preg_replace("/\\$/", '*', $filter);
264 } else {
265 $filter= preg_replace("/\\$/", normalizeLdap($this->value), $filter);
266 }
268 $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes, $this->category, $this->objectStorage));
269 }
271 return ($result);
272 }
275 function update()
276 {
277 if (isset($_POST['FILTER_PID']) && $_POST['FILTER_PID'] == $this->pid) {
279 // Save input field
280 if (isset($_POST['search_filter'])) {
281 $this->value= validate($_POST['search_filter']);
282 }
284 // Save scope if needed
285 if ($this->scopeMode == "auto") {
286 $this->scope= isset($_POST['SCOPE'])?"sub":"one";
287 }
288 }
290 }
293 function getCompletitionList($config, $value="*")
294 {
295 global $class_mapping;
296 $res= array();
298 // Is backend available?
299 $backend= "filter".$config['backend'];
300 if (!isset($class_mapping["$backend"])) {
301 die("Invalid backend specified in search config.");
302 }
304 // Load filter and attributes
305 $filter= $config['filter'];
306 $attributes= $config['attribute'];
307 if (!is_array($attributes)) {
308 $attributes= array($attributes);
309 }
311 // Make filter
312 $filter= preg_replace("/\\$/", normalizeLdap($value), $filter);
313 if (isset($config['base']) && isset($config['scope']) && isset($config['category'])) {
314 $result= call_user_func(array($backend, 'query'), $config['base'], $config['scope'], $filter, $attributes,
315 $config["category"], $config["objectStorage"]);
316 } else {
317 $result= call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
318 $this->category, $this->objectStorage);
319 }
321 foreach ($result as $entry) {
322 foreach ($attributes as $attribute) {
323 if (is_array($entry[$attribute])) {
324 for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
325 if (mb_stristr($entry[$attribute][$i], $value)) {
326 $res[]= $entry[$attribute][$i];
327 }
328 }
329 } else {
330 $res[]= $entry[$attribute];
331 }
332 }
333 }
335 return $res;
336 }
339 function processAutocomplete()
340 {
341 global $class_mapping;
342 $result= array();
344 // Introduce maximum number of entries
345 $max= 25;
347 if(isset($this->searches[$this->search]['autocomplete'])){
348 $result= $this->getCompletitionList($this->searches[$this->search]['autocomplete'], $_POST['search_filter']);
349 $result= array_unique($result);
350 asort($result);
352 echo '<ul>';
353 foreach ($result as $entry) {
354 echo '<li>'.mark($_POST['search_filter'], $entry).'</li>';
355 if ($max-- == 0) {
356 break;
357 }
358 }
360 echo '</ul>';
361 }
362 }
365 function getObjectBase($dn)
366 {
367 global $config;
368 $base= "";
370 // Try every object storage
371 $storage= $this->objectStorage;
372 if (!is_array($storage)){
373 $storage= array($storage);
374 }
375 foreach ($storage as $location) {
376 $pattern= "/^[^,]+,".preg_quote($location, '/')."/i";
377 $base= preg_replace($pattern, '', $dn);
378 }
380 /* Set to base, if we're not on a correct subtree */
381 if (!isset($config->idepartments[$base])){
382 $base= $config->current['BASE'];
383 }
385 return $base;
386 }
387 }
390 ?>