LSsearch : Added initial check of predefinedFilters validity/syntax before use.
[ldapsaisie.git] / public_html / includes / class / class.LSsearch.php
1 <?php
2 /*******************************************************************************
3  * Copyright (C) 2007 Easter-eggs
4  * http://ldapsaisie.labs.libre-entreprise.org
5  *
6  * Author: See AUTHORS file in top-level directory.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 ******************************************************************************/
22
23 /**
24  * Object LSsearch
25  *
26  * @author Benjamin Renard <brenard@easter-eggs.com>
27  */
28 class LSsearch { 
29   
30   // The LdapObject type of search
31   private $LSobject=NULL;
32   
33   // The configuration of search
34   private $config;
35   
36   // The context of search
37   private $context;
38   
39   // The parameters of the search
40   private $params=array (
41     // Search params
42     'filter' => NULL,
43     'pattern' => NULL,
44     'predefinedFilter' => false,
45     'basedn' => NULL,
46     'subDn' => NULL,
47     'scope' => NULL,
48     'sizelimit' => 0,
49     'attronly' => false,    // If true, only attribute names are returned
50     'approx' => false,
51     'recursive' => false,
52     'attributes' => array(),
53     // Display params
54     'sortDirection' => NULL,
55     'sortBy' => NULL,
56     'sortlimit' => 0,
57     'displaySubDn' => NULL,
58     'displayFormat' => NULL,
59     'nbObjectsByPage' => NB_LSOBJECT_LIST,
60     'nbPageLinkByPage' => 10,
61     'customInfos' => array(),
62     'withoutCache' => false
63   );
64   
65   // The cache of search parameters
66   private $_searchParams = NULL;
67   
68   // The cache of the hash of the search parameters
69   private $_hash = NULL;
70   
71   // The result of the search
72   private $result=NULL;
73   
74   // Caches
75   private $_canCopy=NULL;
76   
77   /**
78    * Constructor
79    * 
80    * @param[in] $LSobject string The LdapObject type of search
81    * @param[in] $context string Context of search (LSrelation / LSldapObject/ ...)
82    * @param[in] $params array Parameters of search
83    * @param[in] $purgeParams boolean If params in session have to be purged
84    * 
85    **/
86   function LSsearch($LSobject,$context,$params=null,$purgeParams=false) {
87     if (!LSsession :: loadLSobject($LSobject)) {
88       return;
89     }
90     $this -> LSobject = $LSobject;
91     
92     $this -> loadConfig();
93     
94     if (isset($_REQUEST['LSsearchPurgeSession'])) {
95       $this -> purgeSession();
96     }
97     
98     $this -> context = $context;
99     
100     if (!$purgeParams) {
101       if (! $this -> loadParamsFromSession()) {
102         LSdebug('LSsearch : load default parameters');
103         $this -> loadDefaultParameters();
104       }
105     }
106     else {
107       $this -> purgeParams();
108       $this -> loadDefaultParameters();
109     }
110     
111     if (is_array($params)) {
112       $this -> setParams($params);
113     }
114     
115   }
116
117   /**
118    * Load configuration from LSconfig
119    * 
120    * @retval void
121    */
122   private function loadConfig() {
123     $this -> config = LSconfig::get("LSobjects.".$this -> LSobject.".LSsearch");
124     if (is_array($this -> config['predefinedFilters'])) {
125       foreach(array_keys($this -> config['predefinedFilters']) as $key) {
126         if(!LSldap::isValidFilter($key)) {
127           unset($this -> config['predefinedFilters'][$key]);
128         }
129       }
130     }
131   }
132   
133   /**
134    * Load default search parameters from configuration
135    * 
136    * @retval boolean True on success or False
137    */
138   private function loadDefaultParameters() {
139     if (is_array($this -> config['params'])) {
140       return $this -> setParams($this -> config['params']);
141     }
142     return true;
143   }
144   
145   /**
146    * Load search parameters from session
147    * 
148    * @retval boolean True if params has been loaded from session or False
149    */
150   private function loadParamsFromSession() {
151     LSdebug('LSsearch : load context params session '.$this -> context);
152     if (is_array($_SESSION['LSsession']['LSsearch'][$this -> LSobject]['params'][$this -> context])) {
153       $params = $_SESSION['LSsession']['LSsearch'][$this -> LSobject]['params'][$this -> context];
154       
155       if ($params['filter']) {
156         $params['filter'] = Net_LDAP2_Filter::parse($params['filter']);
157       }
158       
159       $this -> params = $params;
160       return true;
161     }
162     return;
163   }
164
165   /**
166    * Save search parameters in session
167    * 
168    * @retval void
169    */
170   private function saveParamsInSession() {
171     LSdebug('LSsearch : save context params session '.$this -> context);
172     $params = $this -> params;
173     if ($params['filter'] instanceof Net_LDAP2_Filter) {
174       $params['filter'] = $params['filter'] -> asString();
175     }
176     
177     foreach ($params as $param => $value) {
178       if ($_SESSION['LSsession']['LSsearch'][$this -> LSobject]['params'][$this -> context][$param]!=$value) {
179         LSdebug("S: $param => $value");
180         $_SESSION['LSsession']['LSsearch'][$this -> LSobject]['params'][$this -> context][$param]=$value;
181       }
182     }
183   }
184   
185   /**
186    * Purge parameters in session
187    * 
188    * @param[in] $LSobject string The LSobject type
189    * 
190    * @retval void
191    */
192   public function purgeParams($LSobject=NULL) {
193     if (is_null($LSobject)) {
194       $LSobject = $this -> LSobject;
195     }
196     unset($_SESSION['LSsession']['LSsearch'][$LSobject]['params']);
197   }
198   
199   /**
200    * Purge cache
201    * 
202    * @retval void
203    */
204   public function purgeCache($LSobject=NULL) {
205     if (is_null($LSobject))
206       $LSobject = $this -> LSobject;
207     unset($_SESSION['LSsession']['LSsearch'][$LSobject]);
208   }
209   
210   /**
211    * Purge session
212    * 
213    * @retval void
214    */
215   private function purgeSession() {
216     unset($_SESSION['LSsession']['LSsearch']);
217   }
218   
219   /**
220    * Define one search parameter
221    * 
222    * @param[in] $param string The parameter name
223    * @param[in] $value mixed The parameter value
224    * 
225    * @retval boolean True on success or False
226    */
227   public function setParam($param,$value) {
228     return $this -> setParams(array($param => $value));
229   }
230   
231   /**
232    * Define search parameters
233    * 
234    * @param[in] $params array Parameters of search
235    * 
236    * @retval boolean True on success or False
237    */
238   public function setParams($params) {
239     $OK=true;
240     
241     // Filter
242     if (is_string($params['filter'])) {
243       $filter = Net_LDAP2_Filter::parse($params['filter']);
244       if (!LSerror::isLdapError($filter)) {
245         $this -> params['filter'] = $filter;
246       }
247       else {
248         LSerror :: addErrorCode('LSsearch_01',$params['filter']);
249         $OK=false;
250       }
251     }
252     elseif($params['filter'] instanceof Net_LDAP2_Filter) {
253       $this -> params['filter'] =& $params['filter'];
254     }
255
256     // Approx
257     if (isset($params['approx'])) {
258       if (is_bool($params['approx']) || $params['approx']==0 || $params['approx']==1) {
259         $this -> params['approx'] = (bool)$params['approx'];
260       }
261       else {
262         LSerror :: addErrorCode('LSsearch_05','approx');
263         $OK=false;
264       }
265     }
266     
267     // Without Cache
268     if (isset($params['withoutCache'])) {
269       if (is_bool($params['withoutCache']) || $params['withoutCache']==0 || $params['withoutCache']==1) {
270         $this -> params['withoutCache'] = (bool)$params['withoutCache'];
271       }
272       else {
273         LSerror :: addErrorCode('LSsearch_05','withoutCache');
274         $OK=false;
275       }
276     }
277     
278     // Patterm
279     if (isset($params['pattern']) && $params['pattern']=="") {
280       $this -> params['pattern'] = NULL;
281       $this -> params['filter'] = NULL;
282     }
283     elseif (self :: isValidPattern($params['pattern'])) {
284       $this -> params['pattern'] = $params['pattern'];
285       if (!is_string($params['filter'])) {
286         $this -> params['filter']=NULL;
287       }
288     }
289     
290     
291     // BaseDN
292     if (is_string($params['basedn'])) {
293       if (isCompatibleDNs(LSsession :: getRootDn(),$params['basedn'])) {
294         $this -> params['basedn'] = $params['basedn'];
295       }
296       else {
297         LSerror :: addErrorCode('LSsearch_02',$params['basedn']);
298         $OK=false;
299       }
300     }
301     
302     // subDn
303     if (is_string($params['subDn'])) {
304       if (LSsession :: validSubDnLdapServer($params['subDn'])) {
305         $this -> params['subDn'] = $params['subDn'];
306       }
307       else {
308         LSerror :: addErrorCode('LSsearch_03','subDn');
309         $OK=false;
310       }
311     }
312     
313     // Scope
314     if (is_string($params['scope'])) {
315       if (in_array($params['scope'],array('sub','one','base'))) {
316         $this -> params['scope'] = $params['scope'];
317       }
318       else {
319         LSerror :: addErrorCode('LSsearch_03','scope');
320         $OK=false;
321       }
322     }
323     
324     // nbObjectsByPage
325     if (isset($params['nbObjectsByPage'])) {
326       if (((int)$params['nbObjectsByPage'])>1 ) {
327         $this -> params['nbObjectsByPage'] = (int)$params['nbObjectsByPage'];
328       }
329       else {
330         LSerror :: addErrorCode('LSsearch_03','nbObjectsByPage');
331         $OK=false;
332       }
333     }
334     
335     // Sort Limit
336     if (isset($params['sortlimit'])) {
337       if (is_int($params['sortlimit']) && $params['sortlimit']>=0 ) {
338         $this -> params['sortlimit'] = $params['sortlimit'];
339       }
340       elseif ((int)$params['sortlimit'] > 0) {
341         $this -> params['sortlimit'] = (int)$params['sortlimit'];
342       }
343       else {
344         LSerror :: addErrorCode('LSsearch_03','sortlimit');
345         $OK=false;
346       }
347     }
348     
349     // Sort Direction
350     if (is_string($params['sortDirection'])) {
351       if (in_array($params['sortDirection'],array('ASC','DESC'))) {
352         $this -> params['sortDirection'] = $params['sortDirection'];
353       }
354       else {
355         LSerror :: addErrorCode('LSsearch_03','sortDirection');
356         $OK=false;
357       }
358     }
359     
360     // Sort By
361     if (is_string($params['sortBy'])) {
362       if (in_array($params['sortBy'],array('displayName','subDn'))) {
363         if ($this -> params['sortBy'] == $params['sortBy']) {
364           $this -> toggleSortDirection();
365         }
366         else {
367           $this -> params['sortBy'] = $params['sortBy'];
368           if (!is_string($params['sortDirection'])) {
369             $this -> params['sortDirection']='ASC';
370           }
371         }
372       }
373       else {
374         LSerror :: addErrorCode('LSsearch_03','sortBy');
375         $OK=false;
376       }
377     }
378     
379     // Size Limit
380     if (isset($params['sizelimit'])) {
381       if (((int)$params['sizelimit']) >= 0) {
382         $this -> params['sizelimit'] = $params['sizelimit'];
383       }
384       else {
385         LSerror :: addErrorCode('LSsearch_04');
386         $OK=false;
387       }
388     }
389     
390     // Attronly
391     if (isset($params['attronly'])) {
392       if (is_bool($params['attronly']) || $params['attronly']==0 || $params['attronly']==1) {
393         $this -> params['attronly'] = (bool)$params['attronly'];
394       }
395       else {
396         LSerror :: addErrorCode('LSsearch_05','attronly');
397         $OK=false;
398       }
399     }
400     
401     // Recursive
402     if (isset($params['recursive'])) {
403       if (is_bool($params['recursive']) || $params['recursive']==0 || $params['recursive']==1) {
404         $this -> params['recursive'] = (bool)$params['recursive'];
405       }
406       else {
407         LSerror :: addErrorCode('LSsearch_05','recursive');
408         $OK=false;
409       }
410     }
411     
412     // displaySubDn
413     if (isset($params['displaySubDn'])) {
414       if (! LSsession :: isSubDnLSobject($this -> LSobject) ) {
415         if (is_bool($params['displaySubDn']) || $params['displaySubDn']==0 || $params['displaySubDn']==1) {
416           $this -> params['displaySubDn'] = (bool)$params['displaySubDn'];
417         }
418         else {
419           LSerror :: addErrorCode('LSsearch_05','displaySubDn');
420           $OK=false;
421         }
422       }
423     }
424     
425     // Attributes
426     if (isset($params['attributes'])) {
427       if (is_string($params['attributes'])) {
428         $this -> params['attributes'] = array($params['attributes']);
429       }
430       elseif (is_array($params['attributes'])) {
431         $this -> params['attributes']=array();
432         foreach ($this -> params['attributes'] as $attr) {
433           if (is_string($attr)) {
434             if (LSconfig::get("LSobjects.".$this -> LSobject.".attrs.$attr")) {;
435               $this -> params['attributes'][] = $attr;
436             }
437             else {
438               LSerror :: addErrorCode('LSsearch_11',$attr);
439             }
440           }
441         }
442       }
443       else {
444         LSerror :: addErrorCode('LSsearch_06');
445         $OK=false;
446       }
447     }
448
449     // predefinedFilter
450     if (isset($params['predefinedFilter'])) {
451       if (is_string($params['predefinedFilter'])) {
452         if (empty($params['predefinedFilter'])) {
453           $this->params['predefinedFilter']=false;
454         }
455         elseif(is_array($this -> config['predefinedFilters'])) {
456           if(isset($this->config['predefinedFilters'][$params['predefinedFilter']])) {
457             $this -> params['predefinedFilter'] = $params['predefinedFilter'];
458           }
459           else {
460             LSerror :: addErrorCode('LSsearch_03','predefinedFilter');
461             $OK=false;
462           }
463         }
464       }
465       else {
466         LSerror :: addErrorCode('LSsearch_03','predefinedFilter');
467         $OK=false;
468       }
469     }
470     
471     // Display Format
472     if (is_string($params['displayFormat'])) {
473       $this -> params['displayFormat'] = $params['displayFormat'];
474     }
475     
476     // Custom Infos
477     if (is_array($params['customInfos'])) {
478       foreach($params['customInfos'] as $name => $data) {
479         if(is_array($data['function']) && is_string($data['function'][0])) {
480           LSsession::loadLSclass($data['function'][0]);
481         }
482         if (is_callable($data['function'])) {
483           $this -> params['customInfos'][$name] = array (
484             'function' => &$data['function'],
485             'args' => $data['args']
486           );
487         }
488         else {
489           LSerror :: addErrorCode('LSsearch_14',$name);
490         }
491       }
492     }
493
494     $this -> saveParamsInSession();
495     return $OK;
496   }
497
498   /**
499    * Define search parameters by reading Post Data ($_REQUEST)
500    * 
501    * @retval void
502    */
503   public function setParamsFormPostData() {
504     $data = $_REQUEST;
505     
506     if (isset($data['LSsearch_submit'])) {
507       // Recursive 
508       if (is_null($data['recursive'])) {
509         $data['recursive']=false;
510       }
511       else {
512         $data['recursive']=true;
513       }
514       
515       // Approx 
516       if (is_null($data['approx'])) {
517         $data['approx']=false;
518       }
519       else {
520         $data['approx']=true;
521       }
522       
523       if (isset($data['ajax']) && !isset($data['pattern'])) {
524         $data['pattern']="";
525       }
526     }
527     
528     $this -> setParams($data);
529   }
530   
531   /**
532    * Toggle the sort direction
533    * 
534    * @retval void
535    **/
536   private function toggleSortDirection() {
537     if ($this -> params['sortDirection']=="ASC") {
538       $this -> params['sortDirection'] = "DESC";
539     }
540     else {
541       $this -> params['sortDirection'] = "ASC";
542     }
543   }
544   
545   /**
546    * Make a filter object with a pattern of search
547    *
548    * @param[in] $pattern The pattern of search. If is null, the pattern in params will be used.
549    * 
550    * @retval mixed Net_LDAP2_Filter on success or False
551    */ 
552   function getFilterFromPattern($pattern=NULL) {
553     if ($pattern==NULL) {
554       $pattern=$this -> params['pattern'];
555     }
556     if (self :: isValidPattern($pattern)) {
557       $operator=( ($params['approx'])?'approx':'contains' );
558       $attrsList=LSconfig::get("LSobjects.".$this -> LSobject.".LSsearch.attrs");
559       if (!is_array($attrsList)) {
560         $attrsList = array_keys(LSconfig::get("LSobjects.".$this -> LSobject.".attrs"));
561       }
562       
563       if (empty($attrsList)) {
564         LSerror :: addErrorCode('LSsearch_07');
565         return;
566       }
567       
568       $filters=array();
569       foreach ($attrsList as $attr) {
570         $filter=Net_LDAP2_Filter::create($attr,$operator,$pattern);
571         if (!Net_LDAP2::isError($filter)) {
572           $filters[]=$filter;
573         }
574         else {
575           LSerror :: addErrorCode('LSsearch_08',array('attr' => $attr,'pattern' => $pattern));
576           return;
577         }
578       }
579       if(!empty($filters)) {
580         $filter=LSldap::combineFilters('or',$filters);
581         if ($filter) {
582           return $filter;
583         }
584         else {
585           LSerror :: addErrorCode('LSsearch_09');
586         }
587       }
588     }
589     else {
590       LSerror :: addErrorCode('LSsearch_10');
591     }
592     return;
593   }
594   
595   /**
596    * Check if search pattern is valid
597    * 
598    * @param[in] $pattern string The pattern
599    * 
600    * @retval boolean True if pattern is valid or False
601    **/
602   static function isValidPattern($pattern) {
603     return (is_string($pattern) && $pattern!= "" && $pattern!="*");
604   }
605   
606   /**
607    * Check if cache is enabled
608    * 
609    * @retval boolean True if cache is enabled or False
610    **/
611   public function cacheIsEnabled() {
612     if (isset($this -> config['cache'])) {
613       $conf=$this -> config['cache'];
614       if (is_bool($conf) || $conf==0 || $conf==1) {
615         return (bool)$conf;
616       }
617       else {
618         LSerror :: addErrorCode('LSsearch_03','cache');
619       }
620     }
621     return LSsession :: cacheSearch();
622   }
623   
624   /**
625    * Methode for parameters value access
626    * 
627    * @param[in] $key string The parameter name
628    * 
629    * @retval mixed The parameter value or NULL
630    **/
631   public function getParam($key) {
632     if(in_array($key,array_keys($this -> params))) {
633       return $this -> params[$key];
634     }
635     return NULL;
636   }
637   
638   /**
639    * Return hidden fileds to add in search form
640    * 
641    * @retval array The hield fields whith their values
642    **/
643   public function getHiddenFieldForm() {
644     return array (
645       'LSobject' => $this -> LSobject
646     );
647   }
648   
649   /**
650    * Generate an array with search parameters, only parameters whitch have to be
651    * passed to Net_LDAP2 for the LDAP search. This array will be store in 
652    * $this -> _searchParams private variable.
653    * 
654    * @retval void
655    **/
656   private function generateSearchParams() {
657     // Purge the cache of the hash
658     $this -> _hash = NULL;
659     
660     // Base
661     $retval = array(
662       'filter' => $this -> params['filter'],
663       'basedn' => $this -> params['basedn'],
664       'scope' => $this -> params['scope'],
665       'sizelimit' => $this -> params['sizelimit'],
666       'attronly' => $this -> params['attronly'],
667       'attributes' => $this -> params['attributes']
668     );
669     
670     // Pattern
671     if (!is_null($this -> params['pattern'])) {
672       $filter=$this ->getFilterFromPattern();
673       if (is_null($retval['filter'])) {
674         $retval['filter']=$filter;
675       }
676       else {
677         $retval['filter']=LSldap::combineFilters('and',array($retval['filter'],$filter));
678       }
679     }
680     
681     // predefinedFilter
682     if (is_string($this -> params['predefinedFilter'])) {
683       if (!is_null($retval['filter'])) {
684         $filter=LSldap::combineFilters('and',array($this -> params['predefinedFilter'],$retval['filter']));
685         if ($filter) {
686           $retval['filter']=$filter;
687         }
688       }
689       else {
690         $retval['filter']=$this -> params['predefinedFilter'];
691       }
692     }
693     
694     // Filter
695     $objFilter=LSldapObject::getObjectFilter($this -> LSobject);
696     if ($objFilter) {
697       if (!is_null($retval['filter'])) {
698         $filter=LSldap::combineFilters('and',array($objFilter,$retval['filter']));
699         if ($filter) {
700           $retval['filter']=$filter;
701         }
702       }
703       else {
704         $retval['filter']=$objFilter;
705       }
706     }
707     
708     // Recursive
709     if (is_null($retval['basedn'])) {
710       if (!is_null($this -> params['subDn'])) {
711         if ($this -> params['recursive']) {
712           $retval['basedn'] = $this -> params['subDn'];
713         }
714         else {
715           $retval['basedn'] = LSconfig::get("LSobjects.".$this -> LSobject.".container_dn").','.$this -> params['subDn'];
716         }
717       }
718       else {
719         if ($this -> params['recursive']) {
720           $retval['basedn'] = LSsession :: getTopDn();
721         }
722         else {
723           $retval['basedn'] = LSconfig::get("LSobjects.".$this -> LSobject.".container_dn").','.LSsession :: getTopDn();
724         }
725       }
726     }
727     if ($this -> params['recursive'] || !isset($retval['scope'])) {
728       $retval['scope'] = 'sub';
729     }
730     
731     if (is_null($this -> params['displayFormat'])) {
732       $this -> params['displayFormat']=LSconfig::get("LSobjects.".$this -> LSobject.".display_name_format");
733     }
734     
735     // Display Format
736     $attrs=getFieldInFormat($this -> params['displayFormat']);
737     if(is_array($retval['attributes'])) {
738       $retval['attributes']=array_merge($attrs,$retval['attributes']);
739     }
740     else {
741       $retval['attributes']=$attrs;
742     }
743     
744     $this -> _searchParams = $retval;
745   }
746   
747   /**
748    * Run the search
749    *
750    * @param[in] $cache boolean Define if the cache can be used
751    * 
752    * @retval boolean True on success or False
753    */ 
754   public function run($cache=true) {
755     $this -> generateSearchParams();
756     if ($this -> _searchParams['filter'] instanceof Net_LDAP2_Filter) {
757       LSdebug('LSsearch : filter : '.$this -> _searchParams['filter']->asString());
758     }
759     LSdebug('LSsearch : basedn : '.$this -> _searchParams['basedn'].' - scope : '.$this -> _searchParams['scope']);
760     
761     if( $cache && (!isset($_REQUEST['refresh'])) && (!$this -> params['withoutCache']) ) {
762       LSdebug('LSsearch : with the cache');
763       $this -> result = $this -> getResultFromCache();
764     }
765     else {
766       LSdebug('LSsearch : without the cache');
767       $this -> setParam('withoutCache',false);
768     }
769     
770     if (!$this -> result) {
771       LSdebug('LSsearch : Not in cache');
772       $this -> result=array(
773         'sortBy' => NULL,
774         'sortDirection' => NULL
775       );
776       $this -> result['list'] = LSldap :: search(
777         $this -> _searchParams['filter'],
778         $this -> _searchParams['basedn'],
779         $this -> _searchParams
780       );
781       if ($this -> result['list'] === false) {
782         LSerror :: addErrorCode('LSsearch_12');
783         unset($this -> result['list']);
784         return;
785       }
786       $this -> addResultToCache();
787     }
788     
789     $this -> doSort();
790     
791     return true;
792   }
793   
794   /**
795    * Return an hash corresponding to the parameters of the search
796    * 
797    * @param[in] $searchParams array An optional search params array
798    * 
799    * @retval string The hash of the parameters of the search
800    **/  
801   public function getHash($searchParams=null) {
802     if(is_null($searchParams)) {
803       $searchParams=$this -> _searchParams;
804       if ($this -> _hash) {
805         return $this -> _hash;
806       }
807     }
808     if ($searchParams['filter'] instanceof Net_LDAP_Filter) {
809       $searchParams['filter']=$searchParams['filter']->asString();
810     }
811     return hash('md5',print_r($searchParams,true));
812   }
813   
814   /**
815    * Add the result of the search to cache of the session
816    * 
817    * @retval void
818    **/  
819   public function addResultToCache() {
820     if ($this -> cacheIsEnabled()) {
821       LSdebug('LSsearch : Save result in cache.');
822       $hash=$this->getHash();
823       $_SESSION['LSsession']['LSsearch'][$this -> LSobject][$hash]=$this->result;
824     }
825   }
826   
827   /**
828    * Get the result of the search from cache of the session
829    * 
830    * @retval array | False The array of the result of the search or False
831    **/  
832   private function getResultFromCache() {
833     if ($this -> cacheIsEnabled()) {
834       $hash=$this->getHash();
835       if (isset($_SESSION['LSsession']['LSsearch'][$this -> LSobject][$hash])) {
836         LSdebug('LSsearch : Load result from cache.');
837         return $_SESSION['LSsession']['LSsearch'][$this -> LSobject][$hash];
838       }
839     }
840     return;
841   }
842   
843   /**
844    * Get page informations to display
845    * 
846    * @param[in] $page integer The number of the page
847    * 
848    * @retval array The information of the page
849    **/
850   public function getPage($page=0) {
851     if (!LSsession::loadLSclass('LSsearchEntry')) {
852       LSerror::addErrorCode('LSsession_05',$this -> LSobject);
853       return;
854     }
855     $page = (int)$page;
856
857     $retval=array(
858       'nb' => $page,
859       'nbPages' => 1,
860       'list' => array(),
861       'total' => $this -> total
862     );
863     
864     if ($retval['total']>0) {
865       LSdebug('Total : '.$retval['total']);
866       
867       if (!$this->params['nbObjectsByPage']) {
868         $this->params['nbObjectsByPage']=NB_LSOBJECT_LIST;
869       }
870       $retval['nbPages']=ceil($retval['total']/$this->params['nbObjectsByPage']);
871       
872       $sortTable=$this -> getSortTable();
873       
874       $list = array_slice(
875         $sortTable,
876         ($page * $this->params['nbObjectsByPage']),
877         $this->params['nbObjectsByPage']
878       );
879       
880       foreach ($list as $key => $id) {
881         $retval['list'][]=new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$id);
882       }
883     }
884     return $retval;
885   }
886   
887   /**
888    * Get search entries
889    * 
890    * @retval array The entries
891    **/
892   public function getSearchEntries() {
893     if (!LSsession::loadLSclass('LSsearchEntry')) {
894       LSerror::addErrorCode('LSsession_05',$this -> LSobject);
895       return;
896     }
897     $retval=array();
898     if ($this -> total>0) {
899       $sortTable=$this -> getSortTable();
900       
901       foreach ($sortTable as $key => $id) {
902         $retval[]=new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$id);
903       }
904     }
905     return $retval;
906   }
907   
908   /**
909    * Access to information of this object
910    * 
911    * @param[in] $key string The key of the info
912    * 
913    * @retval mixed The info
914    **/
915   public function __get($key) {
916     $params = array (
917       'sortBy',
918       'sortDirection'
919     );
920     if ($key=='LSobject') {
921       return $this -> LSobject;
922     }
923     elseif (in_array($key,$params)) {
924       return $this -> params[$key];
925     }
926     elseif ($key=='label_objectName') {
927       return LSldapObject::getLabel($this -> LSobject);
928     }
929     elseif ($key=='label_level') {
930       return LSsession :: getSubDnLabel();
931     }
932     elseif ($key=='label_actions') {
933       return _('Actions');
934     }
935     elseif ($key=='label_no_result') {
936       return _("This search didn't get any result.");
937     }
938     elseif ($key=='sort') {
939       if (isset($this -> params['sortlimit']) && ($this -> params['sortlimit']>0)) {
940         return ($this -> total < $this -> params['sortlimit']);
941       }
942       return true;
943     }
944     elseif ($key=='sortlimit') {
945       return $this -> params['sortlimit'];
946     }
947     elseif ($key=='total') {
948       return count($this -> result['list']);
949     }
950     elseif ($key=='label_total') {
951       return $this -> total." ".$this -> label_objectName;
952     }
953     elseif ($key=='displaySubDn') {
954       if (LSsession :: subDnIsEnabled()) {
955         if (!is_null($this -> params[$key])) {
956           return $this -> params[$key];
957         }
958         else {
959           return (! LSsession :: isSubDnLSobject($this -> LSobject) );
960         }
961       }
962       return false;
963     }
964     elseif ($key=='canCopy') {
965       if (!is_null($this -> _canCopy))
966         return $this -> _canCopy;
967       $this -> _canCopy = LSsession :: canCreate($this -> LSobject);
968       return $this -> _canCopy;
969     }
970     elseif ($key=='predefinedFilters') {
971       return ((is_array($this -> config['predefinedFilters']))?$this -> config['predefinedFilters']:array());
972     }
973     else {
974       throw new Exception('Incorrect property !');
975     }
976   }
977   
978   /**
979    * Function use with uasort to sort two entry
980    * 
981    * @param[in] $a array One line of result
982    * @param[in] $b array One line of result
983    * 
984    * @retval int Value for uasort
985    **/
986   private function _sortTwoEntry(&$a,&$b) {
987     $sortBy = $this -> params['sortBy'];
988     $sortDirection = $this -> params['sortDirection'];
989     if ($sortDirection=='ASC') {
990       $dir = -1;
991     }
992     else {
993       $dir = 1;
994     }
995     $oa = new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$a);
996     $va = $oa->$sortBy;
997     $ob = new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$b);
998     $vb = $ob->$sortBy;
999     
1000     if ($va == $vb) return 0;
1001     
1002     $val = array($va,$vb);
1003     sort($val);
1004     
1005     if ($val[0]==$va)
1006       return 1*$dir;
1007       
1008     return -1*$dir;
1009   }
1010   
1011   /**
1012    * Function to run after using the result. It's update the cache
1013    * 
1014    * IT'S FUNCTION IS VERY IMPORTANT !!!
1015    * 
1016    * @retval void
1017    **/
1018   function afterUsingResult() {
1019     $this -> addResultToCache();
1020   }
1021   
1022   /**
1023    * Run the sort if it's enabled and if the result is not in the cache
1024    * 
1025    * @retval boolean True on success or false
1026    **/
1027   function doSort() {
1028     if (!$this -> sort) {
1029       LSdebug('doSort : sort is disabled');
1030       return true;
1031     }
1032     if (is_null($this -> params['sortBy'])) {
1033       return;
1034     }
1035     if (is_null($this -> params['sortDirection'])) {
1036       $this -> params['sortDirection']='ASC';
1037     }
1038
1039     if ($this->total==0) {
1040       return true;
1041     }
1042     
1043     if (isset($this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']])) {
1044       LSdebug('doSort : from cache');
1045       return true;
1046     }
1047      
1048     LSdebug('doSort : '.$this -> params['sortBy'].' - '.$this -> params['sortDirection']);
1049     
1050     $this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']]=range(0,($this -> total-1));
1051     
1052     if (!LSsession :: loadLSClass('LSsearchEntry')) {
1053       LSerror::addErrorCode('LSsession_05','LSsearchEntry');
1054       return;
1055     }
1056     
1057     if (!uasort(
1058       $this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']],
1059       array($this,'_sortTwoEntry')
1060     )) {
1061       LSerror :: addErrorCode('LSsearch_13');
1062       return;
1063     }
1064     
1065     return true;
1066   }
1067   
1068   /**
1069    * Returns the id of table rows in the result sorted according to criteria 
1070    * defined in the parameters
1071    * 
1072    * @retval array The Table of id lines of results sorted
1073    **/
1074   function getSortTable() {
1075     if ($this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']]) {
1076       return $this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']];
1077     }
1078     return range(0,($this -> total-1));
1079   }
1080   
1081   /**
1082    * List objects name
1083    * 
1084    * @retval Array DN associate with name
1085    **/
1086   public function listObjectsName() {
1087     if (!LSsession::loadLSclass('LSsearchEntry')) {
1088       LSerror::addErrorCode('LSsession_05',$this -> LSobject);
1089       return;
1090     }
1091     
1092     $retval=array();
1093     
1094     if ($this -> total>0) {
1095       $sortTable=$this -> getSortTable();
1096       
1097       foreach ($sortTable as $key => $id) {
1098         $entry=new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$id);
1099         $retval[$entry->dn]=$entry->displayName;
1100       }
1101     }
1102     
1103     return $retval;
1104   }
1105   
1106   /**
1107    * List LSldapObjects 
1108    * 
1109    * @retval Array of LSldapObjects
1110    **/
1111   public function listObjects() {    
1112     $retval=array();
1113     
1114     if ($this -> total>0) {
1115       $sortTable=$this -> getSortTable();
1116
1117       $c=0;      
1118       foreach ($sortTable as $key => $id) {
1119         $retval[$c]=new $this -> LSobject();
1120         $retval[$c] -> loadData($this -> result['list'][$id]['dn']);
1121         $c++;
1122       }
1123     }
1124     
1125     return $retval;
1126   }
1127   
1128   /**
1129    * List objects dn
1130    * 
1131    * @retval Array of DN
1132    **/
1133   public function listObjectsDn() {    
1134     $retval=array();
1135     
1136     if ($this -> total>0) {
1137       $sortTable=$this -> getSortTable();
1138
1139       $c=0;      
1140       foreach ($sortTable as $key => $id) {
1141         $retval[$c] = $this -> result['list'][$id]['dn'];
1142         $c++;
1143       }
1144     }
1145     
1146     return $retval;
1147   }
1148   
1149 }
1150
1151 /**
1152  * Error Codes
1153  **/
1154 LSerror :: defineError('LSsearch_01',
1155 _("LSsearch : Invalid filter : %{filter}.")
1156 );
1157 LSerror :: defineError('LSsearch_02',
1158 _("LSsearch : Invalid basedn : %{basedn}.")
1159 );
1160 LSerror :: defineError('LSsearch_03',
1161 _("LSsearch : Invalid value for %{param} parameter.")
1162 );
1163 LSerror :: defineError('LSsearch_04',
1164 _("LSsearch : Invalid size limit. Must be an integer greater or equal to 0.")
1165 );
1166 LSerror :: defineError('LSsearch_05',
1167 _("LSsearch : Invalid parameter %{attr}. Must be an boolean.")
1168 );
1169 LSerror :: defineError('LSsearch_06',
1170 _("LSsearch : Invalid parameter attributes. Must be an string or an array of strings.")
1171 );
1172 LSerror :: defineError('LSsearch_07',
1173 _("LSsearch : Can't build attributes list for make filter.")
1174 );
1175 LSerror :: defineError('LSsearch_08',
1176 _("LSsearch : Error building filter with attribute '%{attr}' and pattern '%{pattern}'")
1177 );
1178 LSerror :: defineError('LSsearch_09',
1179 _("LSsearch : Error combining filters.")
1180 );
1181 LSerror :: defineError('LSsearch_10',
1182 _("LSsearch : Invalid pattern.")
1183 );
1184 LSerror :: defineError('LSsearch_11',
1185 _("LSsearch : Invalid attribute %{attr} in parameters.")
1186 );
1187 LSerror :: defineError('LSsearch_12',
1188 _("LSsearch : Error during the search.")
1189 );
1190 LSerror :: defineError('LSsearch_13',
1191 _("LSsearch : Error sorting the search.")
1192 );
1193 LSerror :: defineError('LSsearch_14',
1194 _("LSsearch : The function of the custum information %{name} is not callable.")
1195 );
1196
1197 ?>