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