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