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