1 <?php
3 class filter {
5 var $xmlData;
6 var $elements= array();
7 var $elementValues= array();
8 var $alphabetElements= array();
9 var $autocompleter= array();
10 var $category= "";
11 var $objectStorage= array();
12 var $objectBase= "";
13 var $base= "";
14 var $bases= array();
15 var $scope= "";
16 var $query;
17 var $initial= false;
18 var $baseMode= false;
19 var $scopeMode= "auto";
20 var $alphabet= null;
23 function filter($filename)
24 {
25 if (!$this->load($filename)) {
26 die("Cannot parse $filename!");
27 }
28 }
31 function load($filename)
32 {
33 $contents = file_get_contents($filename);
34 $this->xmlData= xml::xml2array($contents, 1);
36 if (!isset($this->xmlData['filter'])) {
37 return false;
38 }
40 $this->xmlData= $this->xmlData["filter"];
42 // Load filter
43 if (isset($this->xmlData['search'])) {
44 if (!isset($this->xmlData['search']['query'][0])){
45 $this->xmlData['search']['query']= array($this->xmlData['search']['query']);
46 }
48 // Move information
49 $entry= $this->xmlData['search'];
50 $this->baseMode= $entry['base'];
51 $this->scopeMode= $entry['scope'];
52 if ($entry['scope'] == "auto") {
53 $this->scope= "one";
54 } else {
55 $this->scope= $entry['scope'];
56 }
57 $this->query= $entry['query'];
58 } else {
59 return false;
60 }
62 // Transfer initial value
63 if (isset($this->xmlData['definition']['initial']) && $this->xmlData['definition']['initial'] == "true"){
64 $this->initial= true;
65 }
67 // Transfer category
68 if (isset($this->xmlData['definition']['category'])){
69 $this->category= $this->xmlData['definition']['category'];
70 }
72 // Generate formular data
73 if (isset($this->xmlData['element'])) {
74 if (!isset($this->xmlData['element'][0])){
75 $this->xmlData['element']= array($this->xmlData['element']);
76 }
77 foreach ($this->xmlData['element'] as $element) {
79 // Ignore elements without type
80 if (!isset($element['type']) || !isset($element['tag'])) {
81 next;
82 }
84 $tag= $element['tag'];
86 // Fix arrays
87 if (isset($element['value']) && !isset($element['value'][0])) {
88 $element['value']= array($element['value']);
89 }
91 // Store element for quick access
92 $this->elements[$tag] = $element;
94 // Preset elementValues with default values if exist
95 if (isset($element['default']) && !is_array($element['default'])) {
96 $this->elementValues[$tag] = $element['default'];
97 } else {
98 $this->elementValues[$tag] = "";
99 }
101 // Does this element react on alphabet links?
102 if (isset($element['alphabet']) && $element['alphabet'] == "true") {
103 $this->alphabetElements[]= $tag;
104 }
105 }
106 }
108 return true;
109 }
112 function getTextfield($element)
113 {
114 $tag= $element['tag'];
115 $result= "<input class='filter_textfield' id='$tag' name='$tag' type='text' size='30' maxlength='30' value='".$this->elementValues[$tag]."'>";
116 if (isset($element['autocomplete'])) {
117 $frequency= "0.5";
118 $characters= "1";
119 if (isset($element['autocomplete']['frequency'])) {
120 $frequency= $element['autocomplete']['frequency'];
121 }
122 if (isset($element['autocomplete']['characters'])) {
123 $characters= $element['autocomplete']['characters'];
124 }
125 $result.= "<div id='autocomplete$tag' class='autocomplete'></div>".
126 "<script type='text/javascript'>".
127 "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
128 "</script>";
130 $this->autocompleters[$tag]= $element['autocomplete'];
131 }
132 return $result;
133 }
136 function getCheckbox($element)
137 {
138 $tag= $element['tag'];
139 $checked= "";
140 if ($this->elementValues[$tag] == "true") {
141 $checked= " checked";
142 }
144 $result= "<input class='filter_checkbox' name='$tag' type='checkbox' onClick='document.mainform.submit();' value='true'$checked>";
145 return $result;
146 }
149 function getCombobox($element)
150 {
151 $result= "<select name='".$element['tag']."' size='1' onClick='document.mainform.submit();'>";
153 // Fill with presets
154 foreach ($element['value'] as $value) {
155 $selected= "";
156 if ($this->elementValues[$element['tag']] == $value['key']) {
157 $selected= " selected";
158 }
160 // Handle translations
161 $result.= "<option value='".$value['key']."'$selected>"._($value['label'])."</option>";
162 }
164 // Use autocompleter for additional data
165 if (isset($element['autocomplete'])) {
166 $list= $this->getCompletitionList($element['autocomplete'], $element['tag']);
167 foreach ($list as $value) {
168 $selected= "";
169 if ($this->elementValues[$element['tag']] == $value) {
170 $selected= " selected";
171 }
172 $result.= "<option value='$value'$selected>$value</option>";
173 }
174 }
176 $result.= "</select>";
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 setBases($bases) {
203 $this->bases= $bases;
204 }
207 function setObjectStorage($storage) {
208 $this->objectStorage= $storage;
209 }
212 function setObjectBase($base) {
213 $this->objectBase= $base;
214 }
217 function setCurrentBase($base) {
218 $this->base= $base;
219 }
222 function setCurrentScope($scope) {
223 $this->scope= $scope;
224 }
227 function renderBase()
228 {
229 $result= "<select name='base' onChange='mainform.submit()' size='1'>";
231 foreach ($this->bases as $key=>$value) {
232 $selected= "";
233 if ($key == $this->base) {
234 $selected= " selected";
235 }
236 $result.= "<option value='".$key."'$selected>".$value."</option>";
237 }
238 $result.= "</select>";
240 return $result;
241 }
244 function renderAlphabet($columns= 10)
245 {
246 // Return pre-rendered alphabet if available
247 if ($this->alphabet) {
248 return ($this->alphabet);
249 }
251 $characters= _("*ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
252 $alphabet= "";
253 $c= 0;
255 /* Fill cells with charaters */
256 for ($i= 0, $l= mb_strlen($characters, 'UTF8'); $i<$l; $i++){
257 if ($c == 0){
258 $alphabet.= "<tr>";
259 }
261 $ch = mb_substr($characters, $i, 1, "UTF8");
262 $alphabet.= "<td><a class=\"alphaselect\" href=\"main.php?plug=".
263 validate($_GET['plug'])."&filter=".$ch."\"> ".$ch." </a></td>";
265 if ($c++ == $columns){
266 $alphabet.= "</tr>";
267 $c= 0;
268 }
269 }
271 /* Fill remaining cells */
272 while ($c++ <= $columns){
273 $alphabet.= "<td> </td>";
274 }
276 /* Save alphabet */
277 $this->alphabet= "<table width='100%'>$alphabet</table>";
279 return ($this->alphabet);
280 }
283 function renderApply()
284 {
285 return ("<input type='submit' name='apply' value='"._("Apply filter")."'>");
286 }
289 function renderScope()
290 {
291 $checked= $this->scope == "sub"?" checked":"";
292 return "<input type='checkbox' name='SCOPE' value='1' onClick='document.mainform.submit();'$checked> "._("Search in subtrees");
293 }
296 function render()
297 {
298 $smarty= get_smarty();
299 $smarty->assign("ALPHABET", $this->renderAlphabet());
300 $smarty->assign("APPLY", $this->renderApply());
301 $smarty->assign("SCOPE", $this->renderScope());
302 $smarty->assign("BASE", $this->renderBase());
304 // Load template and replace elementsHtml[]
305 foreach ($this->elements as $tag => $element) {
306 $htmlCode= "";
307 switch ($element['type']) {
308 case "textfield":
309 $htmlCode = $this->getTextfield($element);
310 break;
312 case "checkbox":
313 $htmlCode = $this->getCheckbox($element);
314 break;
316 case "combobox":
317 $htmlCode = $this->getCombobox($element);
318 break;
320 default:
321 die ("Unknown element type specified!");
322 }
323 $smarty->assign("$tag", $htmlCode);
324 }
326 // Load template
327 return ("<input type='hidden' name='FILTER_LOADED' value='1'>".$smarty->fetch(get_template_path($this->xmlData['definition']['template'], true)));
328 }
331 function query()
332 {
333 global $class_mapping;
334 $result= array();
336 // Go thru all queries and merge results
337 foreach ($this->query as $query) {
338 if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
339 die("No backend specified in search config.");
340 }
342 // Is backend available?
343 $backend= "filter".$query['backend'];
344 if (!isset($class_mapping["$backend"])) {
345 die("Invalid backend specified in search config.");
346 }
348 // Load filter and attributes
349 $filter= $query['filter'];
350 $attributes= $query['attribute'];
352 // Generate final filter
353 foreach ($this->elements as $tag => $element) {
354 if (!isset($element['set']) || !isset($element['unset'])) {
355 continue;
356 }
357 $e_set= is_array($element['set'])?"":$element['set'];
358 $e_unset= is_array($element['unset'])?"":$element['unset'];
360 if ($this->elementValues[$tag] == "") {
361 $e_unset= preg_replace('/\$/', normalizeLdap($this->elementValues[$tag]), $e_unset);
362 $filter= preg_replace("/\\$$tag/", $e_unset, $filter);
363 } else {
364 $e_set= preg_replace('/\$/', normalizeLdap($this->elementValues[$tag]), $e_set);
365 $filter= preg_replace("/\\$$tag/", $e_set, $filter);
366 }
367 }
369 $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
370 $this->category, $this->objectStorage, $this->objectBase));
371 }
374 return ($result);
375 }
378 function isValid()
379 {
380 foreach ($this->elements as $tag => $element) {
381 if (isset($element->regex)){
382 if (!preg_match('/'.(string)$element->regex.'/', $this->elementValues[$tag])){
383 return false;
384 }
385 }
386 }
387 return true;
388 }
391 function update()
392 {
394 /* React on alphabet links if needed */
395 if (isset($_GET['filter'])){
396 $s= mb_substr(validate($_GET['filter']), 0, 1, "UTF8")."*";
397 if ($s == "**"){
398 $s= "*";
399 }
400 foreach ($this->alphabetElements as $tag) {
401 $this->elementValues[$tag]= $s;
402 }
403 }
405 if (isset($_POST['FILTER_LOADED'])) {
406 // Load post values and adapt filter, base and scope accordingly - but
407 // only if we didn't get a _GET
408 foreach ($this->elements as $tag => $element) {
409 if (isset($_POST[$tag])){
410 $this->elementValues[$tag]= validate($_POST[$tag]);
411 } else {
412 $this->elementValues[$tag]= "";
413 }
414 }
415 }
417 // Save base
418 if (isset($_POST['BASE']) && $this->baseMode == "true") {
419 $base= validate($_POST['BASE']);
420 if (isset($this->bases[$base])) {
421 $this->base= $base;
422 }
423 }
425 // Save scope if needed
426 if ($this->scopeMode == "auto") {
427 $this->scope= isset($_POST['SCOPE'])?"sub":"one";
428 }
429 }
432 function getCompletitionList($config, $tag, $value="*")
433 {
434 global $class_mapping;
435 $res= array();
437 // Is backend available?
438 $backend= "filter".$config['backend'];
439 if (!isset($class_mapping["$backend"])) {
440 die("Invalid backend specified in search config.");
441 }
443 // Load filter and attributes
444 $filter= $config['filter'];
445 $attributes= $config['attribute'];
446 if (!is_array($attributes)) {
447 $attributes= array($attributes);
448 }
450 // Make filter
451 $filter= preg_replace("/\\$$tag/", normalizeLDAP($value), $filter);
452 if (isset($config['base']) && isset($config['scope'])
453 && isset($config['category']) && isset($config['objectbase']) ) {
454 $result= call_user_func(array($backend, 'query'), $config['base'], $config['scope'], $filter, $attributes,
455 $config["category"], $config["objectbase"]);
456 } else {
457 $result= call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
458 $this->category, $this->objectStorage, $this->objectBase);
459 }
461 foreach ($result as $entry) {
462 foreach ($attributes as $attribute) {
463 if (is_array($entry[$attribute])) {
464 for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
465 $res[]= $entry[$attribute][$i];
466 }
467 } else {
468 $res[]= $entry[$attribute];
469 }
470 }
471 }
473 return $res;
474 }
477 function processAutocomplete()
478 {
479 global $class_mapping;
480 $result= array();
482 foreach ($this->autocompleters as $tag => $config) {
483 if(isset($_POST[$tag])){
484 $result= $this->getCompletitionList($config, $tag, $_POST[$tag]);
486 echo '<ul>';
487 foreach ($result as $entry) {
488 echo '<li>'.$entry.'</li>';
489 }
491 echo '</ul>';
492 }
494 }
495 }
497 }
499 ?>