8f8703dffc39cdee0a4be5c53560d3247a7033ae
[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    * Return true only if the form is submited
500    * 
501    * @retval boolean True only if the is submited
502    **/
503   private function formIsSubmited() {
504     return isset($_REQUEST['LSsearch_submit']);
505   }
506
507   /**
508    * Define search parameters by reading Post Data ($_REQUEST)
509    * 
510    * @retval void
511    */
512   public function setParamsFormPostData() {
513     $data = $_REQUEST;
514     
515     if (self::formIsSubmited()) {
516       // Recursive 
517       if (is_null($data['recursive'])) {
518         $data['recursive']=false;
519       }
520       else {
521         $data['recursive']=true;
522       }
523       
524       // Approx 
525       if (is_null($data['approx'])) {
526         $data['approx']=false;
527       }
528       else {
529         $data['approx']=true;
530       }
531       
532       if (isset($data['ajax']) && !isset($data['pattern'])) {
533         $data['pattern']="";
534       }
535     }
536     
537     $this -> setParams($data);
538   }
539   
540   /**
541    * Toggle the sort direction
542    * 
543    * @retval void
544    **/
545   private function toggleSortDirection() {
546     if ($this -> params['sortDirection']=="ASC") {
547       $this -> params['sortDirection'] = "DESC";
548     }
549     else {
550       $this -> params['sortDirection'] = "ASC";
551     }
552   }
553   
554   /**
555    * Make a filter object with a pattern of search
556    *
557    * @param[in] $pattern The pattern of search. If is null, the pattern in params will be used.
558    * 
559    * @retval mixed Net_LDAP2_Filter on success or False
560    */ 
561   function getFilterFromPattern($pattern=NULL) {
562     if ($pattern==NULL) {
563       $pattern=$this -> params['pattern'];
564     }
565     if (self :: isValidPattern($pattern)) {
566       $operator=( ($params['approx'])?'approx':'contains' );
567       $attrsList=LSconfig::get("LSobjects.".$this -> LSobject.".LSsearch.attrs");
568       if (!is_array($attrsList)) {
569         $attrsList = array_keys(LSconfig::get("LSobjects.".$this -> LSobject.".attrs"));
570       }
571       
572       if (empty($attrsList)) {
573         LSerror :: addErrorCode('LSsearch_07');
574         return;
575       }
576       
577       $filters=array();
578       foreach ($attrsList as $attr) {
579         $filter=Net_LDAP2_Filter::create($attr,$operator,$pattern);
580         if (!Net_LDAP2::isError($filter)) {
581           $filters[]=$filter;
582         }
583         else {
584           LSerror :: addErrorCode('LSsearch_08',array('attr' => $attr,'pattern' => $pattern));
585           return;
586         }
587       }
588       if(!empty($filters)) {
589         $filter=LSldap::combineFilters('or',$filters);
590         if ($filter) {
591           return $filter;
592         }
593         else {
594           LSerror :: addErrorCode('LSsearch_09');
595         }
596       }
597     }
598     else {
599       LSerror :: addErrorCode('LSsearch_10');
600     }
601     return;
602   }
603   
604   /**
605    * Check if search pattern is valid
606    * 
607    * @param[in] $pattern string The pattern
608    * 
609    * @retval boolean True if pattern is valid or False
610    **/
611   static function isValidPattern($pattern) {
612     return (is_string($pattern) && $pattern!= "" && $pattern!="*");
613   }
614   
615   /**
616    * Check if cache is enabled
617    * 
618    * @retval boolean True if cache is enabled or False
619    **/
620   public function cacheIsEnabled() {
621     if (isset($this -> config['cache'])) {
622       $conf=$this -> config['cache'];
623       if (is_bool($conf) || $conf==0 || $conf==1) {
624         return (bool)$conf;
625       }
626       else {
627         LSerror :: addErrorCode('LSsearch_03','cache');
628       }
629     }
630     return LSsession :: cacheSearch();
631   }
632   
633   /**
634    * Methode for parameters value access
635    * 
636    * @param[in] $key string The parameter name
637    * 
638    * @retval mixed The parameter value or NULL
639    **/
640   public function getParam($key) {
641     if(in_array($key,array_keys($this -> params))) {
642       return $this -> params[$key];
643     }
644     return NULL;
645   }
646   
647   /**
648    * Return hidden fileds to add in search form
649    * 
650    * @retval array The hield fields whith their values
651    **/
652   public function getHiddenFieldForm() {
653     return array (
654       'LSobject' => $this -> LSobject
655     );
656   }
657   
658   /**
659    * Generate an array with search parameters, only parameters whitch have to be
660    * passed to Net_LDAP2 for the LDAP search. This array will be store in 
661    * $this -> _searchParams private variable.
662    * 
663    * @retval void
664    **/
665   private function generateSearchParams() {
666     // Purge the cache of the hash
667     $this -> _hash = NULL;
668     
669     // Base
670     $retval = array(
671       'filter' => $this -> params['filter'],
672       'basedn' => $this -> params['basedn'],
673       'scope' => $this -> params['scope'],
674       'sizelimit' => $this -> params['sizelimit'],
675       'attronly' => $this -> params['attronly'],
676       'attributes' => $this -> params['attributes']
677     );
678     
679     // Pattern
680     if (!is_null($this -> params['pattern'])) {
681       $filter=$this ->getFilterFromPattern();
682       if (is_null($retval['filter'])) {
683         $retval['filter']=$filter;
684       }
685       else {
686         $retval['filter']=LSldap::combineFilters('and',array($retval['filter'],$filter));
687       }
688     }
689     
690     // predefinedFilter
691     if (is_string($this -> params['predefinedFilter'])) {
692       if (!is_null($retval['filter'])) {
693         $filter=LSldap::combineFilters('and',array($this -> params['predefinedFilter'],$retval['filter']));
694         if ($filter) {
695           $retval['filter']=$filter;
696         }
697       }
698       else {
699         $retval['filter']=$this -> params['predefinedFilter'];
700       }
701     }
702     
703     // Filter
704     $objFilter=LSldapObject::getObjectFilter($this -> LSobject);
705     if ($objFilter) {
706       if (!is_null($retval['filter'])) {
707         $filter=LSldap::combineFilters('and',array($objFilter,$retval['filter']));
708         if ($filter) {
709           $retval['filter']=$filter;
710         }
711       }
712       else {
713         $retval['filter']=$objFilter;
714       }
715     }
716     
717     // Recursive
718     if (is_null($retval['basedn'])) {
719       if (!is_null($this -> params['subDn'])) {
720         if ($this -> params['recursive']) {
721           $retval['basedn'] = $this -> params['subDn'];
722         }
723         else {
724           $retval['basedn'] = LSconfig::get("LSobjects.".$this -> LSobject.".container_dn").','.$this -> params['subDn'];
725         }
726       }
727       else {
728         if ($this -> params['recursive']) {
729           $retval['basedn'] = LSsession :: getTopDn();
730         }
731         else {
732           $retval['basedn'] = LSconfig::get("LSobjects.".$this -> LSobject.".container_dn").','.LSsession :: getTopDn();
733         }
734       }
735     }
736     if ($this -> params['recursive'] || !isset($retval['scope'])) {
737       $retval['scope'] = 'sub';
738     }
739     
740     if (is_null($this -> params['displayFormat'])) {
741       $this -> params['displayFormat']=LSconfig::get("LSobjects.".$this -> LSobject.".display_name_format");
742     }
743     
744     // Display Format
745     $attrs=getFieldInFormat($this -> params['displayFormat']);
746     if(is_array($retval['attributes'])) {
747       $retval['attributes']=array_merge($attrs,$retval['attributes']);
748     }
749     else {
750       $retval['attributes']=$attrs;
751     }
752     
753     $this -> _searchParams = $retval;
754   }
755   
756   /**
757    * Run the search
758    *
759    * @param[in] $cache boolean Define if the cache can be used
760    * 
761    * @retval boolean True on success or False
762    */ 
763   public function run($cache=true) {
764     $this -> generateSearchParams();
765     if ($this -> _searchParams['filter'] instanceof Net_LDAP2_Filter) {
766       LSdebug('LSsearch : filter : '.$this -> _searchParams['filter']->asString());
767     }
768     LSdebug('LSsearch : basedn : '.$this -> _searchParams['basedn'].' - scope : '.$this -> _searchParams['scope']);
769     
770     if( $cache && (!isset($_REQUEST['refresh'])) && (!$this -> params['withoutCache']) ) {
771       LSdebug('LSsearch : with the cache');
772       $this -> result = $this -> getResultFromCache();
773     }
774     else {
775       LSdebug('LSsearch : without the cache');
776       $this -> setParam('withoutCache',false);
777     }
778     
779     if (!$this -> result) {
780       LSdebug('LSsearch : Not in cache');
781       $this -> result=array(
782         'sortBy' => NULL,
783         'sortDirection' => NULL
784       );
785       $this -> result['list'] = LSldap :: search(
786         $this -> _searchParams['filter'],
787         $this -> _searchParams['basedn'],
788         $this -> _searchParams
789       );
790       if ($this -> result['list'] === false) {
791         LSerror :: addErrorCode('LSsearch_12');
792         unset($this -> result['list']);
793         return;
794       }
795       $this -> addResultToCache();
796     }
797     
798     $this -> doSort();
799     
800     return true;
801   }
802   
803   /**
804    * Return an hash corresponding to the parameters of the search
805    * 
806    * @param[in] $searchParams array An optional search params array
807    * 
808    * @retval string The hash of the parameters of the search
809    **/  
810   public function getHash($searchParams=null) {
811     if(is_null($searchParams)) {
812       $searchParams=$this -> _searchParams;
813       if ($this -> _hash) {
814         return $this -> _hash;
815       }
816     }
817     if ($searchParams['filter'] instanceof Net_LDAP_Filter) {
818       $searchParams['filter']=$searchParams['filter']->asString();
819     }
820     return hash('md5',print_r($searchParams,true));
821   }
822   
823   /**
824    * Add the result of the search to cache of the session
825    * 
826    * @retval void
827    **/  
828   public function addResultToCache() {
829     if ($this -> cacheIsEnabled()) {
830       LSdebug('LSsearch : Save result in cache.');
831       $hash=$this->getHash();
832       $_SESSION['LSsession']['LSsearch'][$this -> LSobject][$hash]=$this->result;
833     }
834   }
835   
836   /**
837    * Get the result of the search from cache of the session
838    * 
839    * @retval array | False The array of the result of the search or False
840    **/  
841   private function getResultFromCache() {
842     if ($this -> cacheIsEnabled()) {
843       $hash=$this->getHash();
844       if (isset($_SESSION['LSsession']['LSsearch'][$this -> LSobject][$hash])) {
845         LSdebug('LSsearch : Load result from cache.');
846         return $_SESSION['LSsession']['LSsearch'][$this -> LSobject][$hash];
847       }
848     }
849     return;
850   }
851   
852   /**
853    * Get page informations to display
854    * 
855    * @param[in] $page integer The number of the page
856    * 
857    * @retval array The information of the page
858    **/
859   public function getPage($page=0) {
860     if (!LSsession::loadLSclass('LSsearchEntry')) {
861       LSerror::addErrorCode('LSsession_05',$this -> LSobject);
862       return;
863     }
864     $page = (int)$page;
865
866     $retval=array(
867       'nb' => $page,
868       'nbPages' => 1,
869       'list' => array(),
870       'total' => $this -> total
871     );
872     
873     if ($retval['total']>0) {
874       LSdebug('Total : '.$retval['total']);
875       
876       if (!$this->params['nbObjectsByPage']) {
877         $this->params['nbObjectsByPage']=NB_LSOBJECT_LIST;
878       }
879       $retval['nbPages']=ceil($retval['total']/$this->params['nbObjectsByPage']);
880       
881       $sortTable=$this -> getSortTable();
882       
883       $list = array_slice(
884         $sortTable,
885         ($page * $this->params['nbObjectsByPage']),
886         $this->params['nbObjectsByPage']
887       );
888       
889       foreach ($list as $key => $id) {
890         $retval['list'][]=new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$id);
891       }
892     }
893     return $retval;
894   }
895   
896   /**
897    * Get search entries
898    * 
899    * @retval array The entries
900    **/
901   public function getSearchEntries() {
902     if (!LSsession::loadLSclass('LSsearchEntry')) {
903       LSerror::addErrorCode('LSsession_05',$this -> LSobject);
904       return;
905     }
906     $retval=array();
907     if ($this -> total>0) {
908       $sortTable=$this -> getSortTable();
909       
910       foreach ($sortTable as $key => $id) {
911         $retval[]=new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$id);
912       }
913     }
914     return $retval;
915   }
916   
917   /**
918    * Access to information of this object
919    * 
920    * @param[in] $key string The key of the info
921    * 
922    * @retval mixed The info
923    **/
924   public function __get($key) {
925     $params = array (
926       'sortBy',
927       'sortDirection'
928     );
929     if ($key=='LSobject') {
930       return $this -> LSobject;
931     }
932     elseif (in_array($key,$params)) {
933       return $this -> params[$key];
934     }
935     elseif ($key=='label_objectName') {
936       return LSldapObject::getLabel($this -> LSobject);
937     }
938     elseif ($key=='label_level') {
939       return LSsession :: getSubDnLabel();
940     }
941     elseif ($key=='label_actions') {
942       return _('Actions');
943     }
944     elseif ($key=='label_no_result') {
945       return _("This search didn't get any result.");
946     }
947     elseif ($key=='sort') {
948       if (isset($this -> params['sortlimit']) && ($this -> params['sortlimit']>0)) {
949         return ($this -> total < $this -> params['sortlimit']);
950       }
951       return true;
952     }
953     elseif ($key=='sortlimit') {
954       return $this -> params['sortlimit'];
955     }
956     elseif ($key=='total') {
957       return count($this -> result['list']);
958     }
959     elseif ($key=='label_total') {
960       return $this -> total." ".$this -> label_objectName;
961     }
962     elseif ($key=='displaySubDn') {
963       if (LSsession :: subDnIsEnabled()) {
964         if (!is_null($this -> params[$key])) {
965           return $this -> params[$key];
966         }
967         else {
968           return (! LSsession :: isSubDnLSobject($this -> LSobject) );
969         }
970       }
971       return false;
972     }
973     elseif ($key=='canCopy') {
974       if (!is_null($this -> _canCopy))
975         return $this -> _canCopy;
976       $this -> _canCopy = LSsession :: canCreate($this -> LSobject);
977       return $this -> _canCopy;
978     }
979     elseif ($key=='predefinedFilters') {
980       return ((is_array($this -> config['predefinedFilters']))?$this -> config['predefinedFilters']:array());
981     }
982     else {
983       throw new Exception('Incorrect property !');
984     }
985   }
986   
987   /**
988    * Function use with uasort to sort two entry
989    * 
990    * @param[in] $a array One line of result
991    * @param[in] $b array One line of result
992    * 
993    * @retval int Value for uasort
994    **/
995   private function _sortTwoEntry(&$a,&$b) {
996     $sortBy = $this -> params['sortBy'];
997     $sortDirection = $this -> params['sortDirection'];
998     if ($sortDirection=='ASC') {
999       $dir = -1;
1000     }
1001     else {
1002       $dir = 1;
1003     }
1004     $oa = new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$a);
1005     $va = $oa->$sortBy;
1006     $ob = new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$b);
1007     $vb = $ob->$sortBy;
1008     
1009     if ($va == $vb) return 0;
1010     
1011     $val = array($va,$vb);
1012     sort($val);
1013     
1014     if ($val[0]==$va)
1015       return 1*$dir;
1016       
1017     return -1*$dir;
1018   }
1019   
1020   /**
1021    * Function to run after using the result. It's update the cache
1022    * 
1023    * IT'S FUNCTION IS VERY IMPORTANT !!!
1024    * 
1025    * @retval void
1026    **/
1027   function afterUsingResult() {
1028     $this -> addResultToCache();
1029   }
1030   
1031   /**
1032    * Redirect user to object view if the search have only one result
1033    * 
1034    * @retval boolean True only if user have been redirected
1035    **/
1036   function redirectWhenOnlyOneResult() {
1037     if ($this -> total == 1 && $this -> result && self::formIsSubmited()) {
1038       LSsession :: redirect('view.php?LSobject='.$this -> LSobject.'&dn='.$this -> result['list'][0]['dn']);
1039     }
1040     return;
1041   }
1042   
1043   /**
1044    * Run the sort if it's enabled and if the result is not in the cache
1045    * 
1046    * @retval boolean True on success or false
1047    **/
1048   function doSort() {
1049     if (!$this -> sort) {
1050       LSdebug('doSort : sort is disabled');
1051       return true;
1052     }
1053     if (is_null($this -> params['sortBy'])) {
1054       return;
1055     }
1056     if (is_null($this -> params['sortDirection'])) {
1057       $this -> params['sortDirection']='ASC';
1058     }
1059
1060     if ($this->total==0) {
1061       return true;
1062     }
1063     
1064     if (isset($this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']])) {
1065       LSdebug('doSort : from cache');
1066       return true;
1067     }
1068      
1069     LSdebug('doSort : '.$this -> params['sortBy'].' - '.$this -> params['sortDirection']);
1070     
1071     $this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']]=range(0,($this -> total-1));
1072     
1073     if (!LSsession :: loadLSClass('LSsearchEntry')) {
1074       LSerror::addErrorCode('LSsession_05','LSsearchEntry');
1075       return;
1076     }
1077     
1078     if (!uasort(
1079       $this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']],
1080       array($this,'_sortTwoEntry')
1081     )) {
1082       LSerror :: addErrorCode('LSsearch_13');
1083       return;
1084     }
1085     
1086     return true;
1087   }
1088   
1089   /**
1090    * Returns the id of table rows in the result sorted according to criteria 
1091    * defined in the parameters
1092    * 
1093    * @retval array The Table of id lines of results sorted
1094    **/
1095   function getSortTable() {
1096     if ($this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']]) {
1097       return $this -> result['sort'][$this -> params['sortBy']][$this -> params['sortDirection']];
1098     }
1099     return range(0,($this -> total-1));
1100   }
1101   
1102   /**
1103    * List objects name
1104    * 
1105    * @retval Array DN associate with name
1106    **/
1107   public function listObjectsName() {
1108     if (!LSsession::loadLSclass('LSsearchEntry')) {
1109       LSerror::addErrorCode('LSsession_05',$this -> LSobject);
1110       return;
1111     }
1112     
1113     $retval=array();
1114     
1115     if ($this -> total>0) {
1116       $sortTable=$this -> getSortTable();
1117       
1118       foreach ($sortTable as $key => $id) {
1119         $entry=new LSsearchEntry($this,$this -> LSobject,$this -> params,$this -> _hash,$this -> result['list'],$id);
1120         $retval[$entry->dn]=$entry->displayName;
1121       }
1122     }
1123     
1124     return $retval;
1125   }
1126   
1127   /**
1128    * List LSldapObjects 
1129    * 
1130    * @retval Array of LSldapObjects
1131    **/
1132   public function listObjects() {    
1133     $retval=array();
1134     
1135     if ($this -> total>0) {
1136       $sortTable=$this -> getSortTable();
1137
1138       $c=0;      
1139       foreach ($sortTable as $key => $id) {
1140         $retval[$c]=new $this -> LSobject();
1141         $retval[$c] -> loadData($this -> result['list'][$id]['dn']);
1142         $c++;
1143       }
1144     }
1145     
1146     return $retval;
1147   }
1148   
1149   /**
1150    * List objects dn
1151    * 
1152    * @retval Array of DN
1153    **/
1154   public function listObjectsDn() {    
1155     $retval=array();
1156     
1157     if ($this -> total>0) {
1158       $sortTable=$this -> getSortTable();
1159
1160       $c=0;      
1161       foreach ($sortTable as $key => $id) {
1162         $retval[$c] = $this -> result['list'][$id]['dn'];
1163         $c++;
1164       }
1165     }
1166     
1167     return $retval;
1168   }
1169   
1170 }
1171
1172 /**
1173  * Error Codes
1174  **/
1175 LSerror :: defineError('LSsearch_01',
1176 _("LSsearch : Invalid filter : %{filter}.")
1177 );
1178 LSerror :: defineError('LSsearch_02',
1179 _("LSsearch : Invalid basedn : %{basedn}.")
1180 );
1181 LSerror :: defineError('LSsearch_03',
1182 _("LSsearch : Invalid value for %{param} parameter.")
1183 );
1184 LSerror :: defineError('LSsearch_04',
1185 _("LSsearch : Invalid size limit. Must be an integer greater or equal to 0.")
1186 );
1187 LSerror :: defineError('LSsearch_05',
1188 _("LSsearch : Invalid parameter %{attr}. Must be an boolean.")
1189 );
1190 LSerror :: defineError('LSsearch_06',
1191 _("LSsearch : Invalid parameter attributes. Must be an string or an array of strings.")
1192 );
1193 LSerror :: defineError('LSsearch_07',
1194 _("LSsearch : Can't build attributes list for make filter.")
1195 );
1196 LSerror :: defineError('LSsearch_08',
1197 _("LSsearch : Error building filter with attribute '%{attr}' and pattern '%{pattern}'")
1198 );
1199 LSerror :: defineError('LSsearch_09',
1200 _("LSsearch : Error combining filters.")
1201 );
1202 LSerror :: defineError('LSsearch_10',
1203 _("LSsearch : Invalid pattern.")
1204 );
1205 LSerror :: defineError('LSsearch_11',
1206 _("LSsearch : Invalid attribute %{attr} in parameters.")
1207 );
1208 LSerror :: defineError('LSsearch_12',
1209 _("LSsearch : Error during the search.")
1210 );
1211 LSerror :: defineError('LSsearch_13',
1212 _("LSsearch : Error sorting the search.")
1213 );
1214 LSerror :: defineError('LSsearch_14',
1215 _("LSsearch : The function of the custum information %{name} is not callable.")
1216 );
1217
1218 ?>