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