Code

Replaced config->search with get_cfg_value
[gosa.git] / gosa-core / include / class_filter.inc
index a829942939f92646e73e802514e31ecad23926ad..04de2dae2c5cb13fce48ef21f3638fe6ce823937 100644 (file)
 <?php
+/*
+ * This code is part of GOsa (http://www.gosa-project.org)
+ * Copyright (C) 2003-2008 GONICUS GmbH
+ *
+ * ID: $$Id$$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
 
 class filter {
 
-  var $xmlData;
-  var $elements= array();
-  var $elementValues= array();
-  var $alphabetElements= array();
-  var $autocompleter= array();
+  var $searches= array();
+  var $xmlSearches= array();
+  var $attributes= array();
+  var $search;
+  var $defaultSearch;
   var $category= "";
   var $objectStorage= array();
-  var $objectBase= "";
   var $base= "";
-  var $bases= array();
   var $scope= "";
   var $query;
-  var $baseMode= false;
+  var $value= "";
+  var $initial= false;
   var $scopeMode= "auto";
-  var $alphabet= null;
+  var $converter= null;
+  var $pid;
 
 
   function filter($filename)
   {
+    global $config;
+
+    // Load eventually passed filename
     if (!$this->load($filename)) {
       die("Cannot parse $filename!");
     }
+
+    $this->pid= preg_replace("/[^0-9]/", "", microtime(TRUE)); 
   }
 
 
   function load($filename)
   {
     $contents = file_get_contents($filename);
-    $this->xmlData= xml::xml2array($contents, 1);
+    $xmlData= xml::xml2array($contents, 1);
 
-    if (!isset($this->xmlData['filter'])) {
+    if (!isset($xmlData['filterdef'])) {
       return false;
     }
 
-    $this->xmlData= $this->xmlData["filter"];
+    $xmlData= $xmlData["filterdef"];
 
     // Load filter
-    if (isset($this->xmlData['search'])) {
-      if (!isset($this->xmlData['search']['query'][0])){
-        $this->xmlData['search']['query']= array($this->xmlData['search']['query']);
+    if (isset($xmlData['search'])) {
+
+      // Array conversion
+      if (!isset($xmlData['search'][0])) {
+        $searches= array($xmlData['search']);
+      } else {
+        $searches= $xmlData['search'];
       }
 
-      // Move information
-      $entry= $this->xmlData['search'];
-      $this->baseMode= $entry['base'];
-      $this->scopeMode= $entry['scope'];
-      $this->query= $entry['query'];
+      /* Store available searches */
+      foreach ($searches as $search) {
+
+        /* Do multi conversation */ 
+        if (!isset($search['query'][0])){
+          $search['query']= array($search['query']);
+        }
+
+        /* Store search */
+        $this->xmlSearches[$search['tag']]= $search;
+
+      }
     } else {
       return false;
     }
 
-    // Generate formular data
-    if (isset($this->xmlData['element'])) {
-      if (!isset($this->xmlData['element'][0])){
-        $this->xmlData['element']= array($this->xmlData['element']);
-      }
-      foreach ($this->xmlData['element'] as $element) {
+    // Transfer scope
+    $this->scopeMode= $xmlData['definition']['scope'];
+    if ($this->scopeMode == "auto") {
+      $this->scope= "one";
+    } else {
+      $this->scope= $this->scopeMode;
+    }
 
-        // Ignore elements without type
-        if (!isset($element['type']) || !isset($element['tag'])) {
-          next;
-        }
+    // Transfer attributes
+    $this->attributes= $xmlData['definition']['attribute'];
+    if (!is_array($this->attributes)) {
+      $this->attributes= array($this->attributes);
+    }
 
-        $tag= $element['tag'];
+    // Transfer initial value
+    if (isset($xmlData['definition']['initial']) && $xmlData['definition']['initial'] == "true"){
+      $this->initial= true;
+    }
 
-        // Fix arrays
-        if (isset($element['value']) && !isset($element['value'][0])) {
-          $element['value']= array($element['value']);
-        }
+    // Transfer category
+    if (isset($xmlData['definition']['category'])){
+      $this->category= $xmlData['definition']['category'];
+    }
+    if (!is_array($this->category)) {
+      $this->category= array($this->category);
+    }
 
-        // Store element for quick access
-        $this->elements[$tag] = $element;
+    // Initialize searches and default search mode
+    $this->defaultSearch= $xmlData['definition']['default'];
+    $this->reloadFilters();
+    $this->setSearch($this->defaultSearch);
 
-        // Preset elementValues with default values if exist
-        if (isset($element['default']) && !is_array($element['default'])) {
-          $this->elementValues[$tag] = $element['default'];
-        } else {
-          $this->elementValues[$tag] = "";
-        }
+    return true;  
+  }
 
-        // Does this element react on alphabet links?
-        if (isset($element['alphabet']) && $element['alphabet'] == "true") {
-          $this->alphabetElements[]= $tag;
-        }
-      }
+
+  function reloadFilters()
+  {
+    $this->searches= array_merge($this->xmlSearches, userFilter::getFilter($this->category));
+    $this->setSearch($this->search);
+  }
+
+
+  function setSearch($method= null)
+  {
+    $patch= null;
+
+    // Maybe our search method has gone?
+    if (!isset($this->searches[$method])) {
+      $method= $this->defaultSearch;
     }
 
-    return true;  
+    // Try to use it, but bail out if there's no help...
+    if (isset($this->searches[$method])) {
+      $this->query= $this->searches[$method]['query'];
+      $this->search= $method;
+    } else {
+      die ("Invalid search module!");
+    }
   }
 
 
-  function getTextfield($element)
+  function getTextfield($tag, $value= "", $element= null)
   {
-    $tag= $element['tag'];
-    $result= "<input class='filter_textfield' id='$tag' name='$tag' type='text' size='30' maxlength='30' value='".$this->elementValues[$tag]."'>";
-    if (isset($element['autocomplete'])) {
+    $size= 30;
+    $maxlength= 30;
+    $result= "<input class='filter_textfield' placeholder='"._("Search")."...' id='$tag' name='$tag' type='text' size='$size' maxlength='{$maxlength}' value='".$value."'>";
+    if ($element && isset($element['autocomplete'])) {
       $frequency= "0.5";
       $characters= "1";
       if (isset($element['autocomplete']['frequency'])) {
@@ -107,41 +166,12 @@ class filter {
         $characters= $element['autocomplete']['characters'];
       }
       $result.= "<div id='autocomplete$tag' class='autocomplete'></div>".
-                "<script type='text/javascript'>".
-                "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
-                "</script>";
-
-       $this->autocompleters[$tag]= $element['autocomplete'];
-    }
-    return $result;
-  }
-
+        "<script type='text/javascript'>".
+        "new Ajax.Autocompleter('$tag', 'autocomplete$tag', 'autocomplete.php', { minChars: $characters, frequency: $frequency });".
+        "</script>";
 
-  function getCheckbox($element)
-  {
-    $tag= $element['tag'];
-    $checked= "";
-    if ($this->elementValues[$tag] == "true") {
-      $checked= " checked";
+      $this->autocompleters[$tag]= $element;
     }
-
-    $result= "<input class='filter_checkbox' name='$tag' type='checkbox' onClick='document.mainform.submit();' value='true'$checked>";
-    return $result;
-  }
-
-
-  function getCombobox($element)
-  {
-    $result= "<select name='".$element['tag']."' size='1' onClick='document.mainform.submit();'>";
-    foreach ($element['value'] as $value) {
-      $selected= "";
-      if ($this->elementValues[$element['tag']] == $value['key']) {
-        $selected= " selected";
-      }
-      $result.= "<option value='".$value['key']."'$selected>{t}".$value['set']."{/t}</option>";
-    }
-    $result.= "</select>";
-
     return $result;
   }
 
@@ -166,286 +196,273 @@ class filter {
   }
 
 
-  function setBases($bases) {
-    $this->bases= $bases;    
+  function setConverter($hook)
+  {
+    $this->converter= $hook;
   }
 
 
-  function setObjectStorage($storage) {
+  function setObjectStorage($storage)
+  {
     $this->objectStorage= $storage;    
   }
 
 
-  function setObjectBase($base) {
-    $this->objectBase= $base;    
-  }
-
-
-  function setCategory($category) {
-    $this->category= $category;    
-  }
-
-
-  function setCurrentBase($base) {
+  function setBase($base)
+  {
     $this->base= $base;
   }
 
 
-  function setCurrentScope($scope) {
+  function setCurrentScope($scope)
+  {
     $this->scope= $scope;
   }
 
 
-  function renderBase()
-  {
-    $result= "<select name='currentMainBase' onChange='mainform.submit()' size='1'>";
 
-    foreach ($this->bases as $key=>$value) {
-      $selected= "";
-      if ($key == $this->base) {
-        $selected= " selected";
-      }
-      $result.= "<option value='".$key."'$selected>".$value."</option>";
-    }
-    $result.= "</select>";
+  function render()
+  {
+    $content= "<table class='filter-wrapper' summary='"._("Search filter")."'><tr><td>".$this->renderFilterMenu()."</td><td>";
+    $content.= "<div class='search-filter'>".$this->getTextfield('search_filter', $this->value, $this->searches[$this->search])."</div>".
+      "&nbsp;<button class='search-filter' type='submit' title='"._("Search")."'>".image("images/find.png")."</button></td></tr></table>";
 
-    return $result;
+    // Return meta data
+    return ("<input type='hidden' name='FILTER_PID' value='".$this->pid."'>".$content);
   }
 
 
-  function renderAlphabet($columns= 10)
+  function query()
   {
-    // Return pre-rendered alphabet if available
-    if ($this->alphabet) {
-      return ($this->alphabet);
-    }
-
-    $characters= _("*ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
-    $alphabet= "";
-    $c= 0;
+    global $class_mapping;
+    $result= array();
 
-    /* Fill cells with charaters */
-    for ($i= 0, $l= mb_strlen($characters, 'UTF8'); $i<$l; $i++){
-      if ($c == 0){
-        $alphabet.= "<tr>";
+    // Return empty list if initial is not set
+    if (!$this->initial) {
+      $this->initial= true;
+      return $result;
+    }
+    // Go thru all queries and merge results
+    foreach ($this->query as $query) {
+      if (!isset($query['backend']) || !isset($query['filter'])) {
+        die("No backend specified in search config.");
       }
 
-      $ch = mb_substr($characters, $i, 1, "UTF8");
-      $alphabet.= "<td><a class=\"alphaselect\" href=\"main.php?plug=".
-        validate($_GET['plug'])."&amp;filter=".$ch."\">&nbsp;".$ch."&nbsp;</a></td>";
-
-      if ($c++ == $columns){
-        $alphabet.= "</tr>";
-        $c= 0;
+      // Is backend available?
+      $backend= "filter".$query['backend'];
+      if (!isset($class_mapping["$backend"])) {
+        die("Invalid backend specified in search config.");
       }
-    }
-
-    /* Fill remaining cells */
-    while ($c++ <= $columns){
-      $alphabet.= "<td>&nbsp;</td>";
-    }
 
-    /* Save alphabet */
-    $this->alphabet= "<table width='100%'>$alphabet</table>";
-
-    return ($this->alphabet);
-  }
+      // Load filter and attributes
+      $filter= $query['filter'];
 
+      // Handle converters if present
+      if ($this->converter) {
+        preg_match('/([^:]+)::(.*)$/', $this->converter, $m);
+        if ($this->value == "") {
+          $filter= call_user_func(array($m[1], $m[2]), preg_replace('/\$/', "*", $filter));
+        } else {
+          $filter= call_user_func(array($m[1], $m[2]), preg_replace('/\$/', $this->value, $filter));
+        }
+      }
 
-  function renderApply()
-  {
-    return ("<input type='submit' name='apply' value='"._("Apply filter")."'>");
-  }
+      // Do not replace escaped \$ - This is required to be able to search for e.g. windows machines.
+      if ($this->value == "") {
+        $filter= preg_replace("/\\$/", '*', $filter);
+      } else {
+        $filter= preg_replace("/\\$/", "*".normalizeLdap($this->value)."*", $filter);
+      }
 
+      $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $this->attributes, $this->category, $this->objectStorage));
+    }
 
-  function renderScope()
-  {
-    $checked= $this->scope == "sub"?" checked":"";
-    return "<input type='checkbox' name='SCOPE' value='1' onClick='document.mainform.submit();'$checked>&nbsp;"._("Search in subtrees");
+    return ($result);
   }
 
 
-  function renderFilter()
+  function update()
   {
-    $smarty= get_smarty();
-    $smarty->assign("ALPHABET", $this->renderAlphabet());
-    $smarty->assign("APPLY", $this->renderApply());
-    $smarty->assign("SCOPE", $this->renderScope());
-    $smarty->assign("BASE", $this->renderBase());
-
-    // Load template and replace elementsHtml[]
-    foreach ($this->elements as $tag => $element) {
-      $htmlCode= "";
-      switch ($element['type']) {
-        case "textfield":
-          $htmlCode = $this->getTextfield($element);
-          break;
-
-        case "checkbox":
-          $htmlCode = $this->getCheckbox($element);
-          break;
+    if (isset($_POST['FILTER_PID']) && $_POST['FILTER_PID'] == $this->pid) {
+      // Save input field
+      if (isset($_POST['search_filter'])) {
+        $this->value= validate($_POST['search_filter']);
+      }
 
-        case "combobox":
-          $htmlCode = $this->getCombobox($element);
-          break;
+      // Save scope if needed
+      if ($this->scopeMode == "auto" && isset($_POST['act']) && $_POST['act'] == "toggle-subtree") {
+        $this->scope= ($this->scope == "one")?"sub":"one";
+      }
 
-        default:
-          die ("Unknown element type specified!");
+      // Switch filter?
+      if (isset($_POST['act'])) {
+        foreach ($this->searches as $tag => $cfg) {
+          if ($_POST['act'] == "filter-$tag") {
+            $this->setSearch($tag);
+            break;
+          }
+        }
       }
-      $smarty->assign("$tag", $htmlCode);
     }
 
-    // Load template
-    return ("<input type='hidden' name='FILTER_LOADED' value='1'>".$smarty->fetch(get_template_path("filter/".$this->xmlData['definition']['template'])));
   }
 
 
-  function query()
+  function getCompletitionList($config, $value="*")
   {
     global $class_mapping;
-    $result= array();
+    $res= array();
 
-    // Go thru all queries and merge results
-    foreach ($this->query as $query) {
-      if (!isset($query['backend']) || !isset($query['filter']) || !isset($query['attribute'])) {
-        die("No backend specified in search config.");
-      }
+    // Load result attributes
+    $attributes= $config['autocomplete']['attribute'];
+    if (!is_array($attributes)) {
+      $attributes= array($attributes);
+    }
 
-      // Is backend available?
+    // Do the query
+    $result= array();
+
+    // Is backend available?
+    # FIXME
+    $queries= $config['query'];
+    if (!isset($queries[0])){
+      $queries= array($queries);
+    }
+    foreach ($queries as $query) {
       $backend= "filter".$query['backend'];
       if (!isset($class_mapping["$backend"])) {
         die("Invalid backend specified in search config.");
       }
+      $filter= preg_replace("/\\$/", "*".normalizeLdap($value)."*", $query['filter']);
 
-      // Load filter and attributes
-      $filter= $query['filter'];
-      $attributes= $query['attribute'];
-
-      // Generate final filter
-      foreach ($this->elements as $tag => $element) {
-        if (!isset($element['set']) || !isset($element['unset'])) {
-          continue;
-        }
-        $e_set= is_array($element['set'])?"":$element['set'];
-        $e_unset= is_array($element['unset'])?"":$element['unset'];
+      $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
+          $this->category, $this->objectStorage));
+    }
 
-        if ($this->elementValues[$tag] == "") {
-          $e_unset= preg_replace('/\$/', normalizeLdap($this->elementValues[$tag]), $e_unset);
-          $filter= preg_replace("/\\$$tag/", $e_unset, $filter);
+    foreach ($result as $entry) {
+      foreach ($attributes as $attribute) {
+        if (is_array($entry[$attribute])) {
+          for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
+            if (mb_stristr($entry[$attribute][$i], $value)) {
+              $res[]= $entry[$attribute][$i];
+            }
+          }
         } else {
-          $e_set= preg_replace('/\$/', normalizeLdap($this->elementValues[$tag]), $e_set);
-          $filter= preg_replace("/\\$$tag/", $e_set, $filter);
+          $res[]= $entry[$attribute];
         }
       }
-
-      $result= array_merge($result, call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
-                               $this->category, $this->objectStorage, $this->objectBase));
     }
-    
 
-    return ($result);
+    return $res;
   }
 
 
-  function isValid()
+  function processAutocomplete()
   {
-    foreach ($this->elements as $tag => $element) {
-      if (isset($element->regex)){
-        if (!preg_match('/'.(string)$element->regex.'/', $this->elementValues[$tag])){
-          return false;
+    global $class_mapping;
+    $result= array();
+
+    // Introduce maximum number of entries
+    $max= 25;
+
+    if(isset($this->searches[$this->search]['autocomplete'])){
+      $result= $this->getCompletitionList($this->searches[$this->search], $_POST['search_filter']);
+      $result= array_unique($result);
+      asort($result);
+
+      echo '<ul>';
+      foreach ($result as $entry) {
+        echo '<li>'.mark($_POST['search_filter'], $entry).'</li>';
+        if ($max-- == 0) {
+          break;
         }
       }
+
+      echo '</ul>';
     }
-    return true;
   }
 
 
-  function update()
+  function getObjectBase($dn)
   {
+    global $config;
+    $base= "";
 
-    /* React on alphabet links if needed */
-    if (isset($_GET['filter'])){
-      $s= mb_substr(validate($_GET['filter']), 0, 1, "UTF8")."*";
-      if ($s == "**"){
-        $s= "*";
-      }
-      foreach ($this->alphabetElements as $tag) {
-        $this->elementValues[$tag]= $s;
-      }
+    // Try every object storage
+    $storage= $this->objectStorage;
+    if (!is_array($storage)){
+      $storage= array($storage);
+    }
+    foreach ($storage as $location) {
+      $pattern= "/^[^,]+,".preg_quote($location, '/')."/i";
+      $base= preg_replace($pattern, '', $dn);
     }
 
-    if (isset($_POST['FILTER_LOADED'])) {
-      // Load post values and adapt filter, base and scope accordingly - but
-      // only if we didn't get a _GET
-      foreach ($this->elements as $tag => $element) {
-        if (isset($_POST[$tag])){
-          $this->elementValues[$tag]= validate($_POST[$tag]);
-        } else {
-          $this->elementValues[$tag]= "";
-        }
+    /* Set to base, if we're not on a correct subtree */
+    if (!isset($config->idepartments[$base])){
+      $base= $config->current['BASE'];
+    }
+
+    return $base;
+  }
+
+
+
+  function renderFilterMenu()
+  {
+    // Load shortcut
+    $result= "<ul class='level1' id='filter-root'><li><a href='#'>".image("images/filter.png").image("images/lists/sort-down.png")."</a>";
+
+    // Build ul/li list
+    $separator= " style='border-top:1px solid #AAA' ";
+    $result.= "<ul class='level2'>";
+
+    // Build in filters
+    foreach ($this->xmlSearches as $tag => $config) {
+      $label = htmlentities(_($config['label']),ENT_COMPAT,'UTF-8');
+      if ($tag == $this->search) {
+        $result.= "<li><a href='#'>".image("images/checked.png")."&nbsp;".$label."</a></li>";
+      } else {
+        $result.= "<li><a href='#' onClick='\$(\"act\").value= \"filter-$tag\";\$(\"exec_act\").click();'>".image("images/empty.png")."&nbsp;".$label."</a></li>";
       }
     }
 
-    // Save base
-    if (isset($_POST['BASE']) && $this->baseMode == "true") {
-      $base= validate($_POST['BASE']);
-      if (isset($this->bases[$base])) {
-        $this->base= $base;
+    // User defined filters
+    $first= true;
+    foreach (userFilter::getFilter($this->category) as $tag => $config) {
+      if ($tag == $this->search) {
+        $result.= "<li".($first?$separator:"")."><a href='#'>".image("images/checked.png")."&nbsp;"._($config['description'])."</a></li>";
+      } else {
+        $result.= "<li".($first?$separator:"")."><a href='#' onClick='\$(\"act\").value= \"filter-$tag\";\$(\"exec_act\").click();'>".image("images/empty.png")."&nbsp;"._($config['description'])."</a></li>";
       }
+
+      $first= false;
     }
 
-    // Save scope if needed
+    // Render scope if set to auto
     if ($this->scopeMode == "auto") {
-      $this->scope= isset($_POST['SCOPE'])?"sub":"one";
+      $result.= "<li$separator><a href='#' onClick='\$(\"act\").value= \"toggle-subtree\";\$(\"exec_act\").click();'>".($this->scope=="one"?image("images/empty.png"):image("images/checked.png"))."&nbsp;"._("Search in subtrees")."</a></li>";
     }
-  }
 
 
-  function processAutocomplete()
-  {
-    global $class_mapping;
-    $result= array();
+    // Edit filter menu
+    $result.= "<li$separator><a href='#' onClick='\$(\"act\").value= \"config-filter\";\$(\"exec_act\").click();'>".image("images/configure.png")."&nbsp;"._("Edit filters")."...</a></li>";
 
-    foreach ($this->autocompleters as $tag => $config) {
-      if(isset($_POST[$tag])){
-      
-        // Is backend available?
-        $backend= "filter".$config['backend'];
-        if (!isset($class_mapping["$backend"])) {
-          die("Invalid backend specified in search config.");
-        }
+    $result.= "</ul>";
 
-        // Load filter and attributes
-        $filter= $config['filter'];
-        $attributes= $config['attribute'];
-        if (!is_array($attributes)) {
-          $attributes= array($attributes);
-        }
+    $script= '<script type="text/javascript" language="JavaScript">var menu2; menu2= new Menu("filter-root", "menu2", configMenu)</script>';
 
-        // Make filter
-        $filter= preg_replace("/\\$$tag/", normalizeLDAP($_POST[$tag]), $filter);
-        $result= call_user_func(array($backend, 'query'), $this->base, $this->scope, $filter, $attributes,
-                             $this->category, $this->objectStorage, $this->objectBase);
-
-        echo '<ul>';
-        foreach ($result as $entry) {
-          foreach ($attributes as $attribute) {
-            if (is_array($entry[$attribute])) {
-              for ($i= 0; $i<$entry[$attribute]['count']; $i++) {
-                echo '<li>'.$entry[$attribute][$i].'</li>';
-              }
-            } else {
-              echo '<li>'.$entry[$entry][$attribute].'</li>';
-            }
-          }
-        }
+    return "<div id='filtermenu'>".$result."</li></ul></div>$script";
+  }
 
-        echo '</ul>';
-      }
 
-    }
+  function getFixedFilters()
+  {
+    return array_keys($this->searches);
   }
 
 }
 
+
+// vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
 ?>