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