LSldapObject : fixed bug in loadData() method that does not clear the object cache.
[ldapsaisie.git] / public_html / includes / class / class.LSldapObject.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 LSsession :: loadLSclass('LSattribute');
24
25 /**
26  * Base d'un objet ldap
27  *
28  * Cette classe définis la base de tout objet ldap géré par LdapSaisie
29  *
30  * @author Benjamin Renard <brenard@easter-eggs.com>
31  */
32 class LSldapObject { 
33   
34   var $config = array();
35   var $type_name;
36   var $attrs = array();
37   var $forms;
38   var $view;
39   var $dn=false;
40   var $oldDn=false;
41   var $other_values=array();
42   var $submitError=true;
43   var $_whoami=NULL;
44   var $_LSrelationsCache=array();
45
46   var $_events=array();
47   var $_objectEvents=array();
48   
49   var $cache=array();
50   
51   /**
52    * Constructeur
53    *
54    * Cette methode construit l'objet et définis la configuration.
55    * Elle lance la construction du tableau d'attributs représentés par un objet LSattribute.
56    *
57    * @author Benjamin Renard <brenard@easter-eggs.com>
58    *
59    * @retval boolean true si l'objet a Ã©té construit, false sinon.
60    */ 
61   function LSldapObject() {
62     $this -> type_name = get_class($this);
63     $config = LSconfig :: get('LSobjects.'.$this -> type_name);
64     if(is_array($config)) {
65       $this -> config = $config;
66     }
67     else {
68       LSerror :: addErrorCode('LSldapObject_01');
69       return;
70     }
71     
72     foreach($this -> config['attrs'] as $attr_name => $attr_config) {
73       if(!$this -> attrs[$attr_name]=new LSattribute($attr_name,$attr_config,$this)) {
74         return;
75       }
76     }
77     
78     return true;
79   }
80   
81   /**
82    * Charge les données de l'objet
83    *
84    * Cette methode définis le DN de l'objet et charge les valeurs de attributs de l'objet
85    * Ã  partir de l'annuaire.
86    *
87    * @author Benjamin Renard <brenard@easter-eggs.com>
88    *
89    * @param[in] $dn string Le DN de l'objet.
90    *
91    * @retval boolean true si la chargement a réussi, false sinon.
92    */ 
93   function loadData($dn) {
94     $this -> dn = $dn;
95     $data = LSldap :: getAttrs($dn);
96     if(!empty($data)) {
97       foreach($this -> attrs as $attr_name => $attr) {
98         if(!$this -> attrs[$attr_name] -> loadData($data[$attr_name]))
99           return;
100       }
101       $this->cache=array();
102       return true;
103     }
104     return;
105   }
106   
107   /**
108    * Recharge les données de l'objet
109    *
110    * @author Benjamin Renard <brenard@easter-eggs.com>
111    *
112    * @retval boolean true si la rechargement a réussi, false sinon.
113    */ 
114   function reloadData() {
115     $data = LSldap :: getAttrs($this -> dn);
116     foreach($this -> attrs as $attr_name => $attr) {
117       if(!$this -> attrs[$attr_name] -> reloadData($data[$attr_name]))
118         return;
119     }
120     return true;
121   }
122   
123   /**
124    * Retourne le format d'affichage de l'objet
125    *
126    * @author Benjamin Renard <brenard@easter-eggs.com>
127    *
128    * @retval string Format d'affichage de l'objet.
129    */ 
130   function getDisplayNameFormat() {
131     return $this -> config['display_name_format'];
132   }
133   
134   /**
135    * Retourne la valeur descriptive d'affichage de l'objet
136    * 
137    * Cette fonction retourne la valeur descriptive d'affichage de l'objet en fonction
138    * du format défini dans la configuration de l'objet ou spécifié en paramètre.
139    *
140    * @author Benjamin Renard <brenard@easter-eggs.com>
141    *
142    * @param[in] $spe [<i>optionnel</i>] string Format d'affichage de l'objet
143    * @param[in] $full [<i>optionnel</i>] boolean True pour afficher en plus le
144    *                                             subDnName
145    *
146    * @retval string Valeur descriptive d'affichage de l'objet
147    */ 
148   function getDisplayName($spe='',$full=false) {
149     if ($spe=='') {
150       $spe = $this -> getDisplayNameFormat();
151     }
152     $val = $this -> getFData($spe,&$this -> attrs,'getDisplayValue');
153     if (LSsession :: haveSubDn() && $full) {
154       $val.=' ('.$this -> subDnName.')';
155     }
156     return $val;
157   }
158   
159   /**
160    * Chaine formatée
161    * 
162    * Cette fonction retourne la valeur d'une chaine formatée en prennant les valeurs
163    * de l'objet.
164    *
165    * @author Benjamin Renard <brenard@easter-eggs.com>
166    *
167    * @param[in] $format string Format de la chaine
168    *
169    * @retval string Valeur d'une chaine formatée
170    */ 
171   function getFData($format) {
172     $format=getFData($format,$this,'getValue');
173     return $format;
174   }
175   
176   /**
177    * Construit un formulaire de l'objet
178    * 
179    * Cette méthode construit un formulaire LSform Ã  partir de la configuration de l'objet
180    * et de chaque attribut.
181    *
182    * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
183    * @param[in] $load DN d'un objet similaire dont la valeur des attribut doit Ãªtre chargé dans le formulaire.
184    *
185    * @author Benjamin Renard <brenard@easter-eggs.com>
186    *
187    * @retval LSform Le formulaire crée
188    */ 
189   function getForm($idForm,$load=NULL) {
190     LSsession :: loadLSclass('LSform');
191     $LSform = new LSform($this,$idForm);
192     $this -> forms[$idForm] = array($LSform,$load);
193     
194     if ($load) {
195       $type = $this -> getType();
196       $loadObject = new $type();
197       if (!$loadObject -> loadData($load)) {
198         $load=false;
199       }
200     }
201     
202     if ($load) {
203       foreach($this -> attrs as $attr_name => $attr) {
204         if(!$this -> attrs[$attr_name] -> addToForm($LSform,$idForm,$this,$loadObject -> attrs[$attr_name] -> getFormVal())) {
205           $LSform -> can_validate = false;
206         }
207       }
208     }
209     else {
210       foreach($this -> attrs as $attr_name => $attr) {
211         if(!$this -> attrs[$attr_name] -> addToForm($LSform,$idForm,$this)) {
212           $LSform -> can_validate = false;
213         }
214       }      
215     }
216     LSsession :: addJSconfigParam('LSform_'.$idForm,array(
217       'ajaxSubmit' => ((isset($this -> config['LSform']['ajaxSubmit']))?$this -> config['LSform']['ajaxSubmit']:1)
218     ));
219     return $LSform;
220   }
221   
222   /**
223    * Construit un formulaire de l'objet
224    * 
225    * Cette méthode construit un formulaire LSform Ã  partir de la configuration de l'objet
226    * et de chaque attribut.
227    *
228    * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
229    * @param[in] $config Configuration spécifique pour le formulaire
230    *
231    * @author Benjamin Renard <brenard@easter-eggs.com>
232    *
233    * @retval LSform Le formulaire crée
234    */ 
235   function getView() {
236     LSsession :: loadLSclass('LSform');
237     $this -> view = new LSform($this,'view');
238     foreach($this -> attrs as $attr_name => $attr) {
239       $this -> attrs[$attr_name] -> addToView($this -> view);
240     }
241     $this -> view -> can_validate = false;
242     return $this -> view;
243   }  
244   
245   /**
246    * Rafraichis le formulaire de l'objet
247    * 
248    * Cette méthode recharge les données d'un formulaire LSform.
249    *
250    * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
251    *
252    * @author Benjamin Renard <brenard@easter-eggs.com>
253    *
254    * @retval boolean true sile formulaire a Ã©té rafraichis, false sinon
255    */ 
256   function refreshForm($idForm) {
257     $LSform = $this -> forms[$idForm][0];
258     foreach($this -> attrs as $attr_name => $attr) {
259       if(!$this -> attrs[$attr_name] -> refreshForm($LSform,$idForm)) {
260         return;
261       }
262     }
263     return true;
264   }
265   
266   /**
267    * Met Ã  jour les données de l'objet Ã  partir d'un retour d'un formulaire.
268    *
269    * @param[in] $idForm Identifiant du formulaire d'origine
270    *
271    * @author Benjamin Renard <brenard@easter-eggs.com>
272    *
273    * @retval boolean true si la mise Ã  jour a réussi, false sinon
274    */ 
275   public function updateData($idForm=NULL) {
276     if($idForm!=NULL) {
277       if(isset($this -> forms[$idForm]))
278         $LSform = $this -> forms[$idForm][0];
279       else {
280         LSerror :: addErrorCode('LSldapObject_02',$this -> getType());
281         return;
282       }
283     }
284     else {
285       if(count($this -> forms) > 0) {
286         reset($this -> forms);
287         $idForm = key($this -> forms);
288         $LSform = current($this -> forms);
289         $config = $LSform[1];
290         $LSform = $LSform[0];
291       }
292       else {
293         LSerror :: addErrorCode('LSldapObject_03',$this -> getType());
294         return;
295       }
296     }
297     $new_data = $LSform -> exportValues();
298     return $this -> _updateData($new_data,$idForm);
299   }
300
301   /**
302    * Met Ã  jour les données de l'objet et de l'entré de l'annuaire
303    * 
304    * @param[in] $new_data Tableau des données de modification de l'objet
305    *
306    * @author Benjamin Renard <brenard@easter-eggs.com>
307    *
308    * @retval boolean true si la mise Ã  jour a réussi, false sinon
309    *
310    * @see validateAttrsData()
311    * @see submitChange()
312    */ 
313   private function _updateData($new_data,$idForm=null) {
314     if(!is_array($new_data)) {
315       return;
316     }
317     foreach($new_data as $attr_name => $attr_val) {
318       if(isset($this -> attrs[$attr_name])) {
319         $this -> attrs[$attr_name] -> setUpdateData($attr_val);
320       }
321     }
322     if($this -> validateAttrsData($idForm)) {
323       LSdebug("les données sont validées");
324       
325       if (!$this -> fireEvent('before_modify')) {
326         return;
327       }
328       
329       // $this -> attrs[ {inNewData} ] -> fireEvent('before_modify')
330       foreach($new_data as $attr_name => $attr_val) {
331         if (!$this -> attrs[$attr_name] -> fireEvent('before_modify')) {
332           return;
333         }
334       }
335       
336       if ($this -> submitChange($idForm)) {
337         LSdebug('Les modifications sont submitées');
338         $this -> submitError = false;
339         $this -> reloadData();
340         $this -> refreshForm($idForm);
341       }
342       else {
343         return;
344       }
345       
346       // Event After Modify
347       if(!$this -> submitError) {
348         $this -> fireEvent('after_modify');
349       }
350       
351       // $this -> attrs[*] => After Modify
352       foreach($new_data as $attr_name => $attr_val) {
353         $this -> attrs[$attr_name] -> fireEvent('after_modify');
354       }
355       return true;
356     }
357     else {
358       return;
359     }
360   }
361   
362   /**
363    * Valide les données retournées par un formulaire
364    *
365    * @param[in] $idForm Identifiant du formulaire d'origine
366    *
367    * @author Benjamin Renard <brenard@easter-eggs.com>
368    *
369    * @retval boolean true si les données sont valides, false sinon
370    */ 
371   function validateAttrsData($idForm=null) {
372     $retval = true;
373     if ($idForm) {
374       $LSform=$this -> forms[$idForm][0];
375     }
376     else {
377       $LSform=false;
378     }
379     foreach($this -> attrs as $attr) {
380       $attr_values = $attr -> getValue();
381       if (!$attr -> isValidate()) {
382         if($attr -> isUpdate()) {
383           if (!$this -> validateAttrData($LSform, $attr)) {
384             $retval = false;
385           }
386         }
387         else if( (empty($attr_values)) && ($attr -> isRequired()) ) { 
388           if ( $attr -> canBeGenerated()) {
389             if ($attr -> generateValue()) {
390               if (!$this -> validateAttrData($LSform, $attr)) {
391                 LSerror :: addErrorCode('LSattribute_08',$attr -> getLabel());
392                 $retval = false;
393               }
394             }
395             else {
396               LSerror :: addErrorCode('LSattribute_07',$attr -> getLabel());
397               $retval = false;
398             }
399           }
400           else {
401             LSerror :: addErrorCode('LSattribute_06',$attr -> getLabel());
402             $retval = false;
403           }
404         }
405       }
406     }
407     return $retval;
408   }
409
410    /**
411    * Valide les données d'un attribut
412    *
413    * @param[in] $LSForm Formulaire d'origine
414    * @param[in] &$attr Attribut Ã  valider
415    *
416    * @author Benjamin Renard <brenard@easter-eggs.com>
417    *
418    * @retval boolean true si les données sont valides, false sinon
419    */
420   function validateAttrData(&$LSform,&$attr) {
421     $retval = true;
422     
423     $vconfig=$attr -> getValidateConfig();
424
425     $data=$attr -> getUpdateData();
426     if(!is_array($data)) {
427       $data=array($data);
428     }
429
430     // Validation des valeurs de l'attribut
431     if(is_array($vconfig)) {
432       foreach($vconfig as $test) {
433         // Définition du basedn par défaut
434         if (!isset($test['basedn'])) {
435           $test['basedn']=LSsession :: getTopDn();
436         }
437
438         // Définition du message d'erreur
439         if (!empty($test['msg'])) {
440           $msg_error=getFData($test['msg'],$this,'getValue');
441         }
442         else {
443           $msg_error=getFData(_("The attribute %{attr} is not valid."),$attr -> getLabel());
444         }
445         foreach($data as $val) {
446           // validation par check LDAP
447           if((isset($test['filter'])||isset($test['basedn']))&&(isset($test['result']))) {
448             $sparams=(isset($test['scope']))?array('scope' => $test['scope']):array();
449             $this -> other_values['val']=$val;
450             $sfilter_user=(isset($test['basedn']))?getFData($test['filter'],$this,'getValue'):NULL;
451             if(isset($test['object_type'])) {
452               $test_obj = new $test['object_type']();
453               $sfilter=$test_obj->getObjectFilter();
454               $sfilter='(&'.$sfilter;
455               if($sfilter_user[0]=='(') {
456                 $sfilter=$sfilter.$sfilter_user.')';
457               }
458               else {
459                 $sfilter=$sfilter.'('.$sfilter_user.'))';
460               }
461             }
462             else {
463               $sfilter=$sfilter_user;
464             }
465             $sbasedn=(isset($test['basedn']))?getFData($test['basedn'],$this,'getValue'):NULL;
466             $ret=LSldap :: getNumberResult ($sfilter,$sbasedn,$sparams);
467             if($test['result']==0) {
468               if($ret!=0) {
469                 if ($LSform) $LSform -> setElementError($attr,$msg_error);
470                 $retval = false;
471               }
472             }
473             else {
474               if($ret<0) {
475                 if ($LSform) $LSform -> setElementError($attr,$msg_error);
476                 $retval = false;
477               }
478             }
479           }
480           // Validation par fonction externe
481           else if(isset($test['function'])) {
482             if (function_exists($test['function'])) {
483               if(!$test['function']($this)) {
484                 if ($LSform) $LSform -> setElementError($attr,$msg_error);
485                 $retval = false;
486               }
487             }
488             else {
489               LSerror :: addErrorCode('LSldapObject_04',array('attr' => $attr->name,'obj' => $this->getType(),'func' => $test['function']));
490               $retval = false;
491             }
492           }
493           else {
494             LSerror :: addErrorCode('LSldapObject_05',array('attr' => $attr->name,'obj' => $this->getType()));
495             $retval = false;
496           }
497         }
498       }
499     }
500     // Génération des valeurs des attributs dépendants
501     $dependsAttrs=$attr->getDependsAttrs();
502     if (!empty($dependsAttrs)) {
503       foreach($dependsAttrs as $dependAttr) {
504         if(!isset($this -> attrs[$dependAttr])){
505           LSerror :: addErrorCode('LSldapObject_14',array('attr_depend' => $dependAttr, 'attr' => $attr -> getLabel()));
506           continue;
507         }
508         if($this -> attrs[$dependAttr] -> canBeGenerated()) {
509           if (!$this -> attrs[$dependAttr] -> generateValue()) {
510             LSerror :: addErrorCode('LSattribute_07',$this -> attrs[$dependAttr] -> getLabel());
511             $retval = false;
512           }
513         }
514         else {
515           LSerror :: addErrorCode('LSattribute_06',$this -> attrs[$dependAttr] -> getLabel());
516           $retval = false;
517         }
518       }
519     }
520
521     $attr -> validate();
522     unset($this -> other_values['val']);
523     return $retval;
524   }
525
526   /**
527    * Met Ã  jour les données modifiés dans l'annuaire
528    *
529    * @param[in] $idForm Identifiant du formulaire d'origine
530    *
531    * @author Benjamin Renard <brenard@easter-eggs.com>
532    *
533    * @retval boolean true si la mise Ã  jour a réussi, false sinon
534    */ 
535   function submitChange($idForm) {
536     $submitData=array();
537     $new = $this -> isNew();
538     foreach($this -> attrs as $attr) {
539       if(($attr -> isUpdate())&&($attr -> isValidate())) {
540         if(($attr -> name == $this -> config['rdn'])&&(!$new)) {
541           $new = true;
542           LSdebug('Rename');
543           if (!$this -> fireEvent('before_rename')) {
544             LSerror :: addErrorCode('LSldapObject_16');
545             return;
546           }
547           $oldDn = $this -> getDn();
548           $this -> dn = false;
549           $newDn = $this -> getDn();
550           if ($newDn) {
551             if (!LSldap :: move($oldDn,$newDn)) {
552               return;
553             }
554             $this -> dn = $newDn;
555             $this -> oldDn = $oldDn;
556             if (!$this -> fireEvent('after_rename')) {
557               LSerror :: addErrorCode('LSldapObject_17');
558               return;
559             }
560           }
561           else {
562             return;
563           }
564         }
565         else {
566           $submitData[$attr -> name] = $attr -> getUpdateData();
567         }
568       }
569     }
570     if(!empty($submitData)) {
571       $dn=$this -> getDn();
572       if($dn) {
573         $this -> dn=$dn;
574         LSdebug($submitData);
575         if ($new) {
576           if (!$this -> fireEvent('before_create')) {
577             LSerror :: addErrorCode('LSldapObject_20');
578             return;
579           }
580           foreach ($submitData as $attr_name => $attr) {
581             if (!$this -> attrs[$attr_name] -> fireEvent('before_create')) {
582               LSerror :: addErrorCode('LSldapObject_20');
583               return;
584             }
585           }
586         }
587         if (!LSldap :: update($this -> getType(),$dn, $submitData)) {
588           return;
589         }
590         if ($new) {
591           if (!$this -> fireEvent('after_create')) {
592             LSerror :: addErrorCode('LSldapObject_21');
593             return;
594           }
595           foreach ($submitData as $attr_name => $attr) {
596             if (!$this -> attrs[$attr_name] -> fireEvent('after_create')) {
597               LSerror :: addErrorCode('LSldapObject_21');
598               return;
599             }
600           }
601         }
602         return true;
603       }
604       else {
605         LSerror :: addErrorCode('LSldapObject_13');
606         return;
607       }
608     }
609     else {
610       return true;
611     }
612   }
613   
614   /**
615    * Retourne les informations issus d'un DN
616    *
617    * @param[in] $dn Un DN.
618    *
619    * @author Benjamin Renard <brenard@easter-eggs.com>
620    *
621    * @retval array Tableau : 
622    *                  - [0] : le premier paramètre
623    *                  - [1] : les paramètres suivants
624    */ 
625   function getDnInfos($dn) {
626     $infos=ldap_explode_dn($dn,0);
627     if(!$infos)
628       return;
629     $first=true;
630     for($i=1;$i<$infos['count'];$i++)
631       if($first) {
632         $basedn.=$infos[$i];
633         $first=false;
634       }
635       else
636         $basedn.=','.$infos[$i];
637     return array($infos[0],$basedn);
638   }
639   
640   /**
641    * Retourne le filtre correpondants aux objetcClass de l'objet
642    *
643    * @author Benjamin Renard <brenard@easter-eggs.com>
644    *
645    * @retval string le filtre ldap correspondant au type de l'objet
646    */ 
647   function getObjectFilter($type=null) {
648     if (is_null($type)) {
649       $type = $this -> type_name;
650     }
651     $oc=LSconfig::get("LSobjects.$type.objectclass");
652     if(!is_array($oc)) return;
653     $filters=array();
654     foreach ($oc as $class) {
655       $filters[]=Net_LDAP2_Filter::create('objectClass','equals',$class);
656     }
657     
658     $filter=LSconfig::get("LSobjects.$type.filter");
659     if ($filter) {
660       $filters[]=$filter;
661     }
662
663     $filter = LSldap::combineFilters('and',$filters,true);
664     if ($filter)
665       return $filter;
666     LSerror :: addErrorCode('LSldapObject_30',$type);
667     return;
668   }
669   
670   /**
671    * Retourne le filtre correpondants au pattern passé
672    * 
673    * @author Benjamin Renard <brenard@easter-eggs.com>
674    * 
675    * @param[in] $pattern string Le mot clé recherché
676    * @param[in] $approx booléen Booléen activant ou non la recherche approximative
677    *
678    * @retval string le filtre ldap correspondant
679    */ 
680   function getPatternFilter($pattern=null,$approx=null) {
681     if ($pattern!=NULL) {
682       if (is_array($this -> config['LSsearch']['attrs'])) {
683         $attrs=$this -> config['LSsearch']['attrs'];
684       }
685       else {
686         $attrs=array($this -> config['rdn']);
687       }
688       $pfilter='(|';
689       if ($approx) {
690         foreach ($attrs as $attr_name) {
691           $pfilter.='('.$attr_name.'~='.$pattern.')';
692         }
693       }
694       else {
695         foreach ($attrs as $attr_name) {
696           $pfilter.='('.$attr_name.'=*'.$pattern.'*)';
697         }
698       }
699       $pfilter.=')';
700       return $pfilter;
701     }
702     else {
703       return NULL;
704     }
705   }
706   
707   /**
708    * Retourne une liste d'objet du même type.
709    *
710    * Effectue une recherche en fonction des paramètres passé et retourne un
711    * tableau d'objet correspond au resultat de la recherche.
712    *
713    * @author Benjamin Renard <brenard@easter-eggs.com>
714    *
715    * @param[in] $filter array (ou string) Filtre de recherche Ldap / Tableau de filtres de recherche
716    * @param[in] $basedn string DN de base pour la recherche
717    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
718    *
719    * @retval array Tableau d'objets correspondant au resultat de la recherche
720    */ 
721   function listObjects($filter=NULL,$basedn=NULL,$params=array()) {
722     if (!LSsession :: loadLSclass('LSsearch')) {
723       LSerror::addErrorCode('LSsession_05','LSsearch');
724       return;
725     }
726     
727     $sparams = array(
728       'basedn' => $basedn,
729       'filter' => $filter,
730       'attributes' => array('dn')
731     );
732
733     if (is_array($params)) {    
734       $sparams=array_merge($sparams,$params);
735     }
736     $LSsearch = new LSsearch($this -> type_name,'LSldapObjet::listObjects',$sparams,true);
737     
738     $LSsearch -> run();
739     
740     return $LSsearch -> listObjects();
741     
742 /*
743     for($i=0;$i<count($ret);$i++) {
744       $retInfos[$i] = new $this -> type_name($this -> config);
745       $retInfos[$i] -> loadData($ret[$i]['dn']);
746     }
747     
748     return $retInfos;
749 */
750   }
751   
752   /**
753    * Recherche les objets du même type dans l'annuaire
754    *
755    * Effectue une recherche en fonction des paramètres passé et retourne un
756    * tableau array(dn => '', attrs => array()) d'objet correspondant au resultat*
757    * de la recherche.
758    *
759    * @author Benjamin Renard <brenard@easter-eggs.com>
760    *
761    * @param[in] $filter array (ou string) Filtre de recherche Ldap / Tableau de filtres de recherche
762    * @param[in] $basedn string DN de base pour la recherche
763    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
764    *
765    * @retval array Tableau d'objets correspondant au resultat de la recherche
766    */ 
767 /*
768   function search($filter='',$basedn=NULL,$params=array()) {
769     $retInfos=array();
770     $attrs=false;
771     $check_final_dn=false;
772
773     if(!is_array($filter))
774       $filter=array(array('filter' => $filter));
775     
776     $nbFilter=count($filter);
777
778     for($i=0;$i<$nbFilter;$i++) {
779       $new_attrs=array();
780       // Défintion des paramètres de base pour la recherche
781       $sbasedn=$basedn;
782       $sparams=$params;
783       $ret=array();
784       if (isset($filter[$i]['scope']))
785         $sparams["scope"]=$filter[$i]['scope'];
786       
787       // Definition des critères de recherche correspondant au type d'objet Ã  lister
788       if(($nbFilter==1)||(!isset($filter[$i]['attr']))) {
789         // Filtre sur l'objet souhaité
790         $sfilter='(&';
791         $sfilter.=$this -> getObjectFilter();
792         $sfilter_end=')';
793         $check_final_dn=true;
794       }
795       // Initialisation des critères d'une recherche intermédiaire
796       else {
797         if(isset($filter[$i]['object_type'])) {
798           $obj_tmp=new $filter[$i]['object_type']();
799           $obj_filter=$obj_tmp->getObjectFilter();
800           $sfilter='(&'.$obj_filter;
801           $sfilter_end=')';
802         }
803         else {
804           $sfilter='';
805           $sfilter_end='';
806         }
807         if(isset($filter[$i]['scope'])) {
808           $sparams['scope']=$filter[$i]['scope'];
809         }
810         if(isset($filter[$i]['basedn'])) {
811           $sbasedn=$filter[$i]['basedn'];
812         }
813       }
814       // Dans le cas d'une recherche intermédiaire ou finale
815       if($attrs!=false) {
816         // Initialisation des variables
817         $ret_gen=array();
818         $new_attrs=array();
819         
820         // Pour tout les attributs retournés
821         for($ii=0;$ii<count($attrs);$ii++) {
822           $sfilter_for='';
823           // Définition du filtre de recherche Ã  partir des paramètres utilisateurs et
824           // des paramètres de recherche de l'objet Ã  listé (dans le cas d'une recherche finale
825           if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
826             $sfilter_user=getFData($filter[$i]['filter'],$attrs[$ii]);
827             if($sfilter_user[0]=='(')
828               $sfilter_for=$sfilter.$sfilter_user;
829             else
830               $sfilter_for=$sfilter.'('.$sfilter_user.')';
831           }
832           else {
833             $sfilter_for=$sfilter;
834           }
835           
836           if(isset($filter[$i]['basedn'])) {
837             $sbasedn=getFData($filter[$i]['basedn'],$attrs[$ii]);
838             if ((!$this -> isCompatibleDNs($sbasedn,$basedn))&&($check_final_dn)) continue;
839           }
840         
841           // Vérification de la compatibilité du basedn de la recherche et du basedn générale
842           // Finalisation du filtre
843           $sfilter_for.=$sfilter_end;
844         
845         
846           // Attributes
847           if ($filter[$i]['attr']) {
848             $sparams['attributes'] = array($filter[$i]['attr']);
849           }
850           else if (!isset($sparams['attributes'])) {
851             $sparams['attributes'] = array($this -> config['rdn']);
852           }
853         
854           // Execution de la recherche
855           $ret=LSldap :: search ($sfilter_for,$sbasedn,$sparams);
856           
857           // Si il y un retour
858           if(isset($ret[0])) {
859             // si il ya une suite (recherche intermédiaire)
860             if($filter[$i]['attr']){
861               for($iii=0;$iii<count($ret);$iii++) {
862                 if(isset($ret[$iii]['attrs'][$filter[$i]['attr']])) {
863                   // cas de valeur multiple
864                   if(is_array($ret[$iii]['attrs'][$filter[$i]['attr']])) {
865                     foreach($ret[$iii]['attrs'][$filter[$i]['attr']] as $val_attr) {
866                       $new_attrs[]=$val_attr;
867                     }
868                   }
869                   // cas de valeur unique
870                   else {
871                     $new_attrs[]=$ret[$iii]['attrs'][$filter[$i]['attr']];
872                   }
873                 }
874               }
875             }
876           }
877         }
878         // cas du dernier filtre
879         if(!empty($ret_gen)) {
880           break;
881         }
882         // dans le cas d'une suite prévu mais d'un retour nul de la précédente recherche
883         else if(empty($new_attrs)) {
884             // retour vide et arrêt de la recherche
885             $ret=array();
886             break;
887         }
888         else {
889           $attrs=$new_attrs;
890         }
891       }
892       // Dans le cas de la recherche initiale
893       else {
894         // Déclaration du filtre de recherche
895         if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
896           if($filter[$i]['filter'][0]=='(') {
897             $sfilter.=$filter[$i]['filter'];
898           }
899           else {
900             $sfilter.='('.$filter[$i]['filter'].')';
901           }
902         }
903         // fermeture du filtre
904         $sfilter.=$sfilter_end;
905         
906         // Attributes
907         if (!isset($sparams['attributes'])) {
908           $sparams['attributes'] = array($this -> config['rdn']);
909         }
910         
911         // Lancement de la recherche
912         $ret=LSldap :: search ($sfilter,$sbasedn,$sparams);
913         
914         //Si filtre multiple => on recupère une liste d'attributs
915         if(isset($filter[$i]['attr'])) {
916           for($ii=0;$ii<count($ret);$ii++) {
917             if(isset($ret[$ii]['attrs'][$filter[$i]['attr']])) {
918               // cas de valeur multiple
919               if(is_array($ret[$ii]['attrs'][$filter[$i]['attr']])) {
920                 foreach($ret[$ii]['attrs'][$filter[$i]['attr']] as $val_attr) {
921                   $attrs[]=$val_attr;
922                 }
923               }
924               // cas de valeur unique
925               else {
926                 $attrs[]=$ret[$ii]['attrs'][$filter[$i]['attr']];
927               }
928             }
929           }
930           
931           // Si aucunne valeur n'est retournées
932           if(empty($attrs)){
933             // arrêt et retour Ã  zéro
934             $ret=array();
935             break;
936           }
937         }
938         // Si recherche unique
939         else {
940           // préparation du retour finale
941           if (!is_array($ret)) {
942             $ret=array();
943           }
944           break;
945         }
946       }
947     }
948     return $ret;
949   }
950 */
951   
952   /**
953    * Retourne une liste d'objet du même type et retourne leur noms
954    *
955    * Effectue une recherche en fonction des paramètres passé et retourne un
956    * tableau (dn => nom) correspondant au resultat de la recherche.
957    *
958    * @author Benjamin Renard <brenard@easter-eggs.com>
959    *
960    * @param[in] $filter string Filtre de recherche Ldap
961    * @param[in] $basedn string DN de base pour la recherche
962    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
963    * @param[in] $displayFormat string Format d'affichage du nom des objets
964    *
965    * @retval array Tableau dn => name correspondant au resultat de la recherche
966    */ 
967   function listObjectsName($filter=NULL,$sbasedn=NULL,$sparams=array(),$displayFormat=false,$cache=true) {
968     if (!LSsession :: loadLSclass('LSsearch')) {
969       LSerror::addErrorCode('LSsession_05','LSsearch');
970       return;
971     }
972     
973     if (!$displayFormat) {
974       $displayFormat = $this -> getDisplayNameFormat();
975     }
976     
977     $params = array(
978       'displayFormat' => $displayFormat,
979       'basedn' => $sbasedn,
980       'filter' => $filter
981     );
982
983     if (is_array($sparams)) {    
984       $params=array_merge($sparams,$params);
985     }
986     
987     $LSsearch = new LSsearch($this -> type_name,'LSldapObject::listObjectsName',$params,true);
988     
989     $LSsearch -> run($cache);
990     
991     return $LSsearch -> listObjectsName();
992   }
993  
994  
995   /**
996    * Recherche un objet à partir de la valeur exact de son RDN ou d'un filtre de
997    * recherche LDAP sous la forme d'un LSformat qui sera construit avec la valeur
998    * de $name.
999    * 
1000    * @author Benjamin Renard <brenard@easter-eggs.com>
1001    * 
1002    * @param[in] $name string Valeur de son RDN ou de la valeur pour composer le filtre
1003    * @param[in] $basedn string Le DN de base de la recherche
1004    * @param[in] $filter string Le filtre de recherche de l'objet
1005    * @param[in] $params array Tableau de paramètres
1006    * 
1007    * @retval array Tableau d'objets correspondant au resultat de la recherche
1008    */
1009   function searchObject($name,$basedn=NULL,$filter=NULL,$params=NULL) {
1010     if (!$filter) {
1011       $filter = '('.$this -> config['rdn'].'='.$name.')';
1012     }
1013     else {
1014       $filter = getFData($filter,$name);
1015     }
1016     return $this -> listObjects($filter,$basedn,$params); 
1017   }
1018
1019   /**
1020    * Retourne une valeur de l'objet
1021    *
1022    * Retourne une valeur en fonction du paramètre. Si la valeur est inconnue, la valeur retourné est ' '.
1023    * tableau d'objet correspond au resultat de la recherche.
1024    *
1025    * Valeurs possibles :
1026    * - 'dn' ou '%{dn} : DN de l'objet
1027    * - [nom d'un attribut] : valeur de l'attribut
1028    * - [clef de $this -> other_values] : valeur de $this -> other_values
1029    *
1030    * @author Benjamin Renard <brenard@easter-eggs.com>
1031    *
1032    * @param[in] $val string Le nom de la valeur demandée
1033    *
1034    * @retval mixed la valeur demandé ou ' ' si celle-ci est inconnue.
1035    */ 
1036   function getValue($val) {
1037     if(($val=='dn')||($val=='%{dn}')) {
1038       return $this -> dn;
1039     }
1040     else if(($val=='rdn')||($val=='%{rdn}')) {
1041       return $this -> attrs[ $this -> config['rdn'] ] -> getValue();
1042     }
1043     else if(($val=='subDn')||($val=='%{subDn}')) {
1044       return $this -> subDnValue;
1045     }
1046     else if(($val=='subDnName')||($val=='%{subDnName}')) {
1047       return $this -> subDnName;
1048     }
1049     else if(isset($this ->  attrs[$val])){
1050       if (method_exists($this ->  attrs[$val],'getValue'))
1051         return $this -> attrs[$val] -> getValue();
1052       else
1053         return ' ';
1054     }
1055     else if(isset($this -> other_values[$val])){
1056       return $this -> other_values[$val];
1057     }
1058     else {
1059       return ' ';
1060     }
1061   }
1062
1063   /**
1064    * Retourn un tableau pour un select d'un objet du même type
1065    * 
1066    * @author Benjamin Renard <brenard@easter-eggs.com>
1067    *
1068    * @retval array('dn' => 'display')
1069    */
1070   function getSelectArray($pattern=NULL,$topDn=NULL,$displayFormat=NULL,$approx=false,$cache=true) {
1071     return $this -> listObjectsName($filter,$topDn,array('pattern' => $pattern),$displayFormat,$cache);
1072   }
1073
1074   /**
1075    * Retourne le DN de l'objet
1076    *
1077    * Cette methode retourne le DN de l'objet. Si celui-ci n'existe pas, il le construit Ã  partir de la 
1078    * configuration de l'objet et la valeur de son attribut rdn.
1079    *
1080    * @author Benjamin Renard <brenard@easter-eggs.com>
1081    *
1082    * @retval string Le DN de l'objet
1083    */   
1084   function getDn() {
1085     if($this -> dn) {
1086       return $this -> dn;
1087     }
1088     else {
1089       $rdn_attr=$this -> config['rdn'];
1090       $topDn = LSsession :: getTopDn();
1091       if( (isset($this -> config['rdn'])) && (isset($this -> attrs[$rdn_attr])) && (isset($this -> config['container_dn'])) && ($topDn) ) {
1092         $rdn_val=$this -> attrs[$rdn_attr] -> getUpdateData();
1093         if (!empty($rdn_val)) {
1094           return $rdn_attr.'='.$rdn_val[0].','.$this -> config['container_dn'].','.$topDn;
1095         }
1096         else {
1097           LSerror :: addErrorCode('LSldapObject_12',$this -> config['rdn']);
1098           return;
1099         }
1100       }
1101       else {
1102         LSerror :: addErrorCode('LSldapObject_11',$this -> getType());
1103         return;
1104       }
1105     }
1106   }
1107
1108   /**
1109    * Retourne le type de l'objet
1110    *
1111    * @author Benjamin Renard <brenard@easter-eggs.com>
1112    * 
1113    * @retval string Le type de l'objet ($this -> type_name)
1114    */
1115   function getType() {
1116     return $this -> type_name;
1117   }
1118   
1119   /**
1120    * Retourne qui est l'utilisateur par rapport Ã  cet object
1121    *
1122    * @author Benjamin Renard <brenard@easter-eggs.com>
1123    * 
1124    * @retval string 'admin'/'self'/'user' pour Admin , l'utilisateur lui même ou un simple utilisateur
1125    */
1126   function whoami() {
1127     if (!$this -> _whoami)
1128       $this -> _whoami = LSsession :: whoami($this -> dn);
1129     return $this -> _whoami;
1130   }
1131   
1132   /**
1133    * Retourne le label de l'objet
1134    *
1135    * @author Benjamin Renard <brenard@easter-eggs.com>
1136    * 
1137    * @retval string Le label de l'objet ($this -> config['label'])
1138    */
1139   function getLabel($type=null) {
1140     if (is_null($type)) {
1141       $type = $this -> type_name;
1142     }
1143     return __(LSconfig::get("LSobjects.$type.label"));
1144   }
1145   
1146   
1147   /**
1148    * Supprime l'objet dans l'annuaire
1149    *
1150    * @author Benjamin Renard <brenard@easter-eggs.com>
1151    * 
1152    * @retval boolean True si l'objet Ã  Ã©té supprimé, false sinon
1153    */
1154   function remove() {
1155     if ($this -> fireEvent('before_delete')) {
1156       if (LSldap :: remove($this -> getDn())) {
1157         if ($this -> fireEvent('after_delete')) {
1158           return true;
1159         }
1160         LSerror :: addErrorCode('LSldapObject_19');
1161       }
1162     }
1163     else {
1164       LSerror :: addErrorCode('LSldapObject_18');
1165     }
1166     return;
1167   }
1168   
1169   /**
1170    * L'objet est-il nouveau
1171    * 
1172    * @author Benjamin Renard <brenard@easter-eggs.com>
1173    * 
1174    * @retval boolean True si l'objet est nouveau, false sinon
1175    */
1176   function isNew() {
1177     return (!$this -> dn);
1178   }
1179
1180   /**
1181    * Retourne la valeur (DN) du subDn de l'objet  
1182    * 
1183    * @parram[in] $dn string Un DN
1184    * 
1185    * @return string La valeur du subDn de l'object
1186    */
1187   public static function getSubDnValue($dn) {
1188     $subDn_value='';
1189     $subDnLdapServer = LSsession :: getSortSubDnLdapServer();
1190     foreach ($subDnLdapServer as $subDn => $subDn_name) {
1191       if (isCompatibleDNs($subDn,$dn)&&($subDn!=$dn)) {
1192         $subDn_value=$subDn;
1193         break;
1194       }
1195     }
1196     return $subDn_value;
1197   }
1198
1199   /**
1200    * Retourne la nom du subDn de l'objet  
1201    * 
1202    * @parram[in] $dn string Un DN
1203    * 
1204    * @return string Le nom du subDn de l'object
1205    */
1206   public static function getSubDnName($dn) {
1207     $subDnLdapServer = LSsession :: getSortSubDnLdapServer();
1208     return $subDnLdapServer[self :: getSubDnValue($dn)];
1209   }
1210   
1211   /**
1212    * Methode créant la liste des objets en relations avec l'objet courant et qui
1213    * la met en cache ($this -> _LSrelationsCache)
1214    * 
1215    * @retval True en cas de cas ce succès, False sinon.
1216    */
1217   function updateLSrelationsCache() {
1218     $this -> _LSrelationsCache=array();
1219     if (is_array($this->config['LSrelation'])) {
1220       $type = $this -> getType();
1221       $me = new $type();
1222       $me -> loadData($this -> getDn());
1223       foreach($this->config['LSrelation'] as $relation_name => $relation_conf) {
1224         if ( isset($relation_conf['list_function']) ) {
1225           if (LSsession :: loadLSobject($relation_conf['LSobject'])) {
1226             $obj = new $relation_conf['LSobject']();
1227             if ((method_exists($obj,$relation_conf['list_function']))&&(method_exists($obj,$relation_conf['getkeyvalue_function']))) {
1228               $list = $obj -> $relation_conf['list_function']($me);
1229               if (is_array($list)) {
1230                 // Key Value
1231                 $key = $obj -> $relation_conf['getkeyvalue_function']($me);
1232                 
1233                 $this -> _LSrelationsCache[$relation_name] = array(
1234                   'list' => $list,
1235                   'keyvalue' => $key
1236                 );
1237               }
1238               else {
1239                 LSdebug('Problème durant la mise en cache de la relation '.$relation_name);
1240                 return;
1241               }
1242             }
1243             else {
1244               LSdebug('Les méthodes de mise en cache de la relation '.$relation_name. ' ne sont pas toutes disponibles.');
1245               return;
1246             }
1247           }
1248           else {
1249             return;
1250           }
1251         }
1252       }
1253     }
1254     return true;
1255   }
1256   
1257   /**
1258    * Methode executant les actions nécéssaires avant le changement du DN de
1259    * l'objet.
1260    * 
1261    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1262    * pour les objets plus complexe.
1263    * 
1264    * @retval True en cas de cas ce succès, False sinon.
1265    */
1266   function beforeRename() {
1267     // LSrelations
1268     return $this -> updateLSrelationsCache();
1269   }
1270   
1271   /**
1272    * Methode executant les actions nécéssaires après le changement du DN de
1273    * l'objet.
1274    * 
1275    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1276    * pour les objets plus complexe.
1277    * 
1278    * @retval True en cas de cas ce succès, False sinon.
1279    */
1280   function afterRename() {
1281     $error = 0;
1282     
1283     // Change LSsession -> userObject Dn
1284     if(LSsession :: getLSuserObjectDn() == $this -> oldDn) {
1285       LSsession :: changeAuthUser($this);
1286     }
1287     
1288     // LSrelations
1289     foreach($this -> _LSrelationsCache as $relation_name => $objInfos) {
1290       if ((isset($this->config['LSrelation'][$relation_name]['rename_function']))&&(is_array($objInfos['list']))) {
1291         foreach($objInfos['list'] as $obj) {
1292           $meth = $this->config['LSrelation'][$relation_name]['rename_function'];
1293           if (method_exists($obj,$meth)) {
1294             if (!($obj -> $meth($this,$objInfos['keyvalue']))) {
1295               $error=1;
1296             }
1297           }
1298           else {
1299             $error=1;
1300           }
1301         }
1302       }
1303     }
1304     return !$error;
1305   }
1306   
1307   /**
1308    * Methode executant les actions nécéssaires avant la suppression de
1309    * l'objet.
1310    * 
1311    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1312    * pour les objets plus complexe.
1313    * 
1314    * @retval True en cas de cas ce succès, False sinon.
1315    */
1316   function beforeDelete() {
1317     $return = $this -> updateLSrelationsCache();
1318     
1319     foreach(array_keys($this -> attrs) as $attr_name) {
1320       if (!$this -> attrs[$attr_name] -> fireEvent('before_delete')) {
1321         $return = false;
1322       }
1323     }
1324     
1325     return $return;
1326   }
1327   
1328   /**
1329    * Methode executant les actions nécéssaires après la suppression de
1330    * l'objet.
1331    * 
1332    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1333    * pour les objets plus complexe.
1334    * 
1335    * @retval True en cas de cas ce succès, False sinon.
1336    */
1337   function afterDelete() {
1338     $error = 0;
1339     
1340     // LSrelations
1341     foreach($this -> _LSrelationsCache as $relation_name => $objInfos) {
1342       if ((isset($this->config['LSrelation'][$relation_name]['remove_function']))&&(is_array($objInfos['list']))) {
1343         foreach($objInfos['list'] as $obj) {
1344           $meth = $this->config['LSrelation'][$relation_name]['remove_function'];
1345           if (method_exists($obj,$meth)) {
1346             if (!($obj -> $meth($this))) {
1347               $error=1;
1348             }
1349           }
1350           else {
1351             $error=1;
1352           }
1353         }
1354       }
1355     }
1356     
1357     // Binding LSattributes
1358     foreach(array_keys($this -> attrs) as $attr_name) {
1359       if (!$this -> attrs[$attr_name] -> fireEvent('after_delete')) {
1360         $error = true;
1361       }
1362     }
1363     
1364     // LSsearch : Purge LSobject cache
1365     if (LSsession :: loadLSclass('LSsearch')) {
1366       LSsearch :: purgeCache($this -> type_name);
1367     }
1368     
1369     return !$error;
1370   }
1371   
1372   /**
1373    * Methode executant les actions nécéssaires après la création de
1374    * l'objet.
1375    * 
1376    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1377    * pour les objets plus complexe.
1378    * 
1379    * @retval True en cas de cas ce succès, False sinon.
1380    */
1381   function afterCreate() {
1382     LSdebug('after');
1383     $error = 0;
1384     
1385     // container_auto_create
1386     if (LSsession :: isSubDnLSobject($this -> getType())) {
1387       if (is_array(LSsession :: $ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'])) {
1388         foreach(LSsession :: $ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'] as $type) {
1389           if (LSsession :: loadLSobject($type)) {
1390             $conf_type=LSconfig :: get("LSobjects.$type");
1391             if (isset($conf_type['container_auto_create'])&&isset($conf_type['container_dn'])) {
1392               $dn = $conf_type['container_dn'].','.$this -> getDn();
1393               if(!LSldap :: getNewEntry($dn,$conf_type['container_auto_create']['objectclass'],$conf_type['container_auto_create']['attrs'],true)) {
1394                 LSdebug("Impossible de créer l'entrée fille : ".print_r(
1395                   array(
1396                     'dn' => $dn,
1397                     'objectClass' => $conf_type['container_auto_create']['objectclass'],
1398                     'attrs' => $conf_type['container_auto_create']['attrs']
1399                   )
1400                 ,true));
1401                 $error=1;
1402               }
1403             }
1404           }
1405           else {
1406             $error=1;
1407           }
1408         }
1409       }
1410     }
1411     
1412     // LSsearch : Purge LSobject cache
1413     if (LSsession :: loadLSclass('LSsearch')) {
1414       LSsearch :: purgeCache($this -> type_name);
1415     }
1416     
1417     return !$error;
1418   }
1419   
1420   /**
1421    * Methode executant les actions nécéssaires après la modification de
1422    * l'objet.
1423    * 
1424    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1425    * pour les objets plus complexe.
1426    * 
1427    * @retval True en cas de cas ce succès, False sinon.
1428    */
1429   function afterModify() {
1430     $error = 0;
1431     
1432     // LSsearch : Purge LSobject cache
1433     if (LSsession :: loadLSclass('LSsearch')) {
1434       LSsearch :: purgeCache($this -> type_name);
1435     }
1436     
1437     return !$error;
1438   }
1439   
1440   /**
1441    * Retourne la valeur clef d'un objet en relation
1442    * 
1443    * @param[in] $object Un object de type $objectType
1444    * @param[in] $objectType Le type d'objet en relation
1445    * @param[in] $value La valeur que doit avoir l'attribut :
1446    *                      - soit le dn (par defaut)
1447    *                      - soit la valeur [0] d'un attribut
1448    * 
1449    * @retval Mixed La valeur clef d'un objet en relation
1450    **/
1451   function getObjectKeyValueInRelation($object,$objectType,$attrValue='dn') {
1452     if (!$objectType) {
1453       LSerror :: addErrorCode('LSrelations_05','getObjectKeyValueInRelation');
1454       return;
1455     }
1456     if ($attrValue=='dn') {
1457       $val = $object -> getDn();
1458     }
1459     else {
1460       $val = $object -> getValue($attrValue);
1461       $val = $val[0];
1462     }
1463     return $val;
1464   }
1465   
1466   /**
1467    * Retourne la liste des relations pour l'objet en fonction de sa présence 
1468    * dans un des attributs
1469    * 
1470    * Retourne un tableau de d'objet (type : $objectType) correspondant à la 
1471    * relation entre l'objet $object et les objets de type $objectType. Cette relation
1472    * est établis par la présence de la valeur de référence à l'objet dans 
1473    * l'attribut des objets de type $objectType.
1474    * 
1475    * @param[in] $object Un object de type $objectType
1476    * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
1477    * @param[in] $objectType Le type d'objet en relation
1478    * @param[in] $value La valeur que doit avoir l'attribut :
1479    *                      - soit le dn (par defaut)
1480    *                      - soit la valeur [0] d'un attribut
1481    * 
1482    * @retval Array of $objectType Les objets en relations
1483    **/
1484   function listObjectsInRelation($object,$attr,$objectType,$attrValue='dn') {
1485     if ((!$attr)||(!$objectType)) {
1486       LSerror :: addErrorCode('LSrelations_05','listObjectsInRelation');
1487       return;
1488     }
1489     if ($attrValue=='dn') {
1490       $val = $object -> getDn();
1491     }
1492     else {
1493       $val = $object -> getValue($attrValue);
1494       $val = $val[0];
1495     }
1496     if ($val) {
1497       $filter = Net_LDAP2_Filter::create($attr,'equals',$val);
1498       return $this -> listObjects($filter,LSsession :: getRootDn(),array('scope' => 'sub','recursive' => true,'withoutCache'=>true));
1499     }
1500     return;
1501   }
1502
1503   /**
1504    * Ajoute un objet en relation dans l'attribut $attr de $this
1505    * 
1506    * @param[in] $object Un objet de type $objectType à ajouter
1507    * @param[in] $attr L'attribut dans lequel l'objet doit être ajouté
1508    * @param[in] $objectType Le type d'objet en relation
1509    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1510    *                      - soit le dn (par defaut)
1511    *                      - soit la valeur [0] d'un attribut
1512    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1513    *                              relation avec l'objet est éditable par le user
1514    * 
1515    * @retval boolean true si l'objet à été ajouté, False sinon
1516    **/  
1517   function addOneObjectInRelation($object,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1518     if ((!$attr)||(!$objectType)) {
1519       LSerror :: addErrorCode('LSrelations_05','addOneObjectInRelation');
1520       return;
1521     }
1522     if ($object instanceof $objectType) {
1523       if ($canEditFunction) {
1524         if (!$this -> $canEditFunction()) {
1525           LSerror :: addErrorCode('LSsession_11');
1526           return;
1527         }
1528       }
1529       if ($this -> attrs[$attr] instanceof LSattribute) {
1530         if ($attrValue=='dn') {
1531           $val = $object -> getDn();
1532         }
1533         else {
1534           $val = $object -> getValue($attrValue);
1535           $val = $val[0];
1536         }
1537         $values = $this -> attrs[$attr] -> getValue();
1538         if ($this -> attrs[$attr] -> config['multiple']) {
1539           if (!is_array($values)) {
1540             $updateData = array($val);
1541           }
1542           else if (!in_array($val,$values)) {
1543             $values[]=$val;
1544             $updateData = $values;
1545           }
1546         }
1547         else {
1548           if (($values[0]!=$val)&&($values!=$val)) {
1549             $updateData = array($val);
1550           }
1551         }
1552         if (isset($updateData)) {
1553           return $this -> _updateData(array($attr => $updateData));
1554         }
1555         return true;
1556       }
1557     }
1558     return;
1559   }
1560   
1561   /**
1562    * Supprime un objet en relation dans l'attribut $attr de $this
1563    * 
1564    * @param[in] $object Un objet de type $objectType à supprimer
1565    * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1566    * @param[in] $objectType Le type d'objet en relation
1567    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1568    *                      - soit le dn (par defaut)
1569    *                      - soit la valeur [0] d'un attribut
1570    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1571    *                              relation avec l'objet est éditable par le user
1572    * 
1573    * @retval boolean true si l'objet à été supprimé, False sinon
1574    **/  
1575   function deleteOneObjectInRelation($object,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1576     if ((!$attr)||(!$objectType)) {
1577       LSerror :: addErrorCode('LSrelations_05','deleteOneObjectInRelation');
1578       return;
1579     }
1580     if ($object instanceof $objectType) {
1581       if ($canEditFunction) {
1582         if (!$this -> $canEditFunction()) {
1583           LSerror :: addErrorCode('LSsession_11');
1584           return;
1585         }
1586       }
1587       if ($this -> attrs[$attr] instanceof LSattribute) {
1588         if ($attrValue=='dn') {
1589           $val = $object -> getDn();
1590         }
1591         else {
1592           $val = $object -> getValue($attrValue);
1593           $val = $val[0];
1594         }
1595         $values = $this -> attrs[$attr] -> getValue();
1596         if ((!is_array($values)) && (!empty($values))) {
1597           $values = array($values);
1598         }
1599         if (is_array($values)) {
1600           $updateData=array();
1601           foreach($values as $value) {
1602             if ($value!=$val) {
1603               $updateData[]=$value;
1604             }
1605           }
1606           return $this -> _updateData(array($attr => $updateData));
1607         }
1608       }
1609     }
1610     return;
1611   }
1612   
1613  /**
1614   * Renome un objet en relation dans l'attribut $attr de $this
1615   * 
1616   * @param[in] $object Un objet de type $objectType à renomer
1617   * @param[in] $oldValue string L'ancienne valeur faisant référence à l'objet
1618   * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1619   * @param[in] $objectType Le type d'objet en relation
1620   * @param[in] $attrValue La valeur que doit avoir l'attribut :
1621   *                      - soit le dn (par defaut)
1622   *                      - soit la valeur [0] d'un attribut
1623   *  
1624   * @retval boolean True en cas de succès, False sinon
1625   */
1626   function renameOneObjectInRelation($object,$oldValue,$attr,$objectType,$attrValue='dn') {
1627     if ((!$attr)||(!$objectType)) {
1628       LSerror :: addErrorCode('LSrelations_05','renameOneObjectInRelation');
1629       return;
1630     }
1631     if ($object instanceof $objectType) {
1632       if ($this -> attrs[$attr] instanceof LSattribute) {
1633         $values = $this -> attrs[$attr] -> getValue();
1634         if ((!is_array($values)) && (!empty($values))) {
1635           $values = array($values);
1636         }
1637         if (is_array($values)) {
1638           $updateData=array();
1639           foreach($values as $value) {
1640             if ($value!=$oldValue) {
1641               $updateData[] = $value;
1642             }
1643             else {
1644               if ($attrValue=='dn') {
1645                 $val = $object -> getDn();
1646               }
1647               else {
1648                 $val = $object -> getValue($attrValue);
1649                 $val = $val[0];
1650               }
1651               $updateData[] = $val;
1652             }
1653           }
1654           return $this -> _updateData(array($attr => $updateData));
1655         }
1656       }
1657     }
1658     return;
1659   }
1660   
1661   /**
1662    * Met à jour les objets du meme type que $this en relation avec l'objet $object
1663    * de type $objectType en modifiant la valeur de leur attribut $attr des objets
1664    * en relation
1665    * 
1666    * @param[in] $object Mixed Un object (type : $this -> userObjectType) : l'utilisateur
1667    * @param[in] $listDns Array(string) Un tableau des DNs des objets en relation
1668    * @param[in] $attr L'attribut dans lequel l'utilisateur doit apparaitre
1669    * @param[in] $objectType Le type d'objet en relation
1670    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1671    *                      - soit le dn (par defaut)
1672    *                      - soit la valeur [0] d'un attribut
1673    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1674    *                              relation avec l'objet est éditable par le user
1675    * 
1676    * @retval boolean true si tout c'est bien passé, False sinon
1677    **/  
1678   function updateObjectsInRelation($object,$listDns,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1679     if ((!$attr)||(!$objectType)) {
1680       LSerror :: addErrorCode('LSrelations_05','updateObjectsInRelation');
1681       return;
1682     }
1683     $currentObjects = $this -> listObjectsInRelation($object,$attr,$objectType,$attrValue);
1684     $type=$this -> getType();
1685     if(is_array($currentObjects)) {
1686       if (is_array($listDns)) {
1687         $values=array();
1688         if ($attrValue!='dn') {
1689           $obj=new $objectType();
1690           foreach ($listDns as $dn) {
1691             $obj -> loadData($dn);
1692             $val = $obj -> getValue($attrValue);
1693             $values[$dn] = $val[0];
1694           }
1695         }
1696         else {
1697           foreach($listDns as $dn) {
1698             $values[$dn] = $dn;
1699           }
1700         }
1701         $dontDelete=array();
1702         $dontAdd=array();
1703         for ($i=0;$i<count($currentObjects);$i++) {
1704           if ($attrValue=='dn') {
1705             $val = $currentObjects[$i] -> getDn();
1706           }
1707           else {
1708             $val = $currentObjects[$i] -> getValue($attrValue);
1709             $val = $val[0];
1710           }
1711           if (in_array($val, $listDns)) {
1712             $dontDelete[$i]=true;
1713             $dontAdd[]=$val;
1714           }
1715         }
1716         
1717         for($i=0;$i<count($currentObjects);$i++) {
1718           if ($dontDelete[$i]) {
1719             continue;
1720           }
1721           else {
1722             if (!$currentObjects[$i] -> deleteOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1723               return;
1724             }
1725           }
1726         }
1727         
1728         foreach($values as $dn => $val) {
1729           if (in_array($val,$dontAdd)) {
1730             continue;
1731           }
1732           else {
1733             $obj = new $type();
1734             if ($obj -> loadData($dn)) {
1735               if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1736                 return;
1737               }
1738             }
1739             else {
1740               return;
1741             }
1742           }
1743         }
1744         return true;
1745       }
1746     }
1747     else {
1748       if(!is_array($listDns)) {
1749         return true;
1750       }
1751       foreach($listDns as $dn) {
1752         $obj = new $type();
1753         if ($obj -> loadData($dn)) {
1754           if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1755             return;
1756           }
1757         }
1758         else {
1759           return;
1760         }
1761       }
1762     }
1763   }
1764   
1765   /**
1766    * Ajouter une action lors d'un événement
1767    * 
1768    * @param[in] $event string Le nom de l'événement
1769    * @param[in] $fct string Le nom de la fonction à exectuer
1770    * @param[in] $param mixed Paramètre pour le lancement de la fonction
1771    * @param[in] $class Nom de la classe possèdant la méthode $fct à executer
1772    * 
1773    * @retval void
1774    */
1775   function addEvent($event,$fct,$param=NULL,$class=NULL) {
1776     $this -> _events[$event][] = array(
1777       'function'  => $fct,
1778       'param'    => $param,
1779       'class'     => $class
1780     );
1781   }
1782   
1783   /**
1784    * Ajouter une action sur un objet lors d'un événement
1785    * 
1786    * @param[in] $event string Le nom de l'événement
1787    * @param[in] $obj object L'objet dont la méthode doit être executé
1788    * @param[in] $meth string Le nom de la méthode
1789    * @param[in] $param mixed Paramètre d'execution de la méthode
1790    * 
1791    * @retval void
1792    */
1793   function addObjectEvent($event,&$obj,$meth,$param=NULL) {
1794     $this -> _objectEvents[$event][] = array(
1795       'obj'  => $obj,
1796       'meth'  => $meth,
1797       'param'    => $param
1798     );
1799   }
1800   
1801   /**
1802    * Lance les actions à executer lors d'un événement
1803    * 
1804    * @param[in] $event string Le nom de l'événement
1805    * 
1806    * @retval boolean True si tout c'est bien passé, false sinon
1807    */
1808   function fireEvent($event) {
1809     
1810     // Object event
1811     $return = $this -> fireObjectEvent($event);
1812     
1813     // Config
1814     if(isset($this -> config[$event])) {
1815       if (!is_array($this -> config[$event])) {
1816         $funcs = array($this -> config[$event]);
1817       }
1818       else {
1819         $funcs = $this -> config[$event];
1820       }
1821       foreach($funcs as $func) {
1822         if(function_exists($func)) {
1823           if(!$func($this)) {
1824             $return = false;
1825             LSerror :: addErrorCode('LSldapObject_07',array('func' => $func,'event' => $event));
1826           }
1827         }
1828         else {
1829           $return = false;
1830           LSerror :: addErrorCode('LSldapObject_06',array('func' => $func,'event' => $event));
1831         }
1832       }
1833     }
1834     
1835     // Binding via addEvent
1836     if (is_array($this -> _events[$event])) {
1837       foreach ($this -> _events[$event] as $e) {
1838         if ($e['class']) {
1839           if (class_exists($e['class'])) {
1840             $obj = new $e['class']();
1841             if (method_exists($obj,$e['fct'])) {
1842               try {
1843                 $obj -> $e['fct']($e['param']);
1844               }
1845               catch(Exception $er) {
1846                 LSerror :: addErrorCode('LSldapObject_10',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1847                 $return = false;
1848               }
1849             }
1850             else {
1851               LSerror :: addErrorCode('LSldapObject_09',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1852               $return = false;
1853             }
1854           }
1855           else {
1856             LSerror :: addErrorCode('LSldapObject_08',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1857             $return = false;
1858           }
1859         }
1860         else {
1861           if (function_exists($e['fct'])) {
1862             try {
1863               $e['fct']($e['param']);
1864             }
1865             catch(Exception $er) {
1866               LSerror :: addErrorCode('LSldapObject_27',array('func' => $e['fct'],'event' => $event));
1867               $return = false;
1868             }
1869           }
1870           else {
1871             LSerror :: addErrorCode('LSldapObject_26',array('func' => $e['fct'],'event' => $event));
1872             $return = false;
1873           }
1874         }
1875       }
1876     }
1877     
1878     // Binding via addObjectEvent
1879     if (is_array($this -> _objectEvents[$event])) {
1880       foreach ($this -> _objectEvents[$event] as $e) {
1881         if (method_exists($e['obj'],$e['meth'])) {
1882           try {
1883             $e['obj'] -> $e['meth']($e['param']);
1884           }
1885           catch(Exception $er) {
1886             LSerror :: addErrorCode('LSldapObject_29',array('meth' => $e['meth'],'event' => $event));
1887             $return = false;
1888           }
1889         }
1890         else {
1891           LSerror :: addErrorCode('LSldapObject_28',array('meth' => $e['meth'],'event' => $event));
1892           $return = false;
1893         }
1894       }
1895     }
1896     
1897     return $return;
1898   }
1899   
1900   /**
1901    * Lance les actions à executer lors d'un événement sur l'objet lui-même
1902    * 
1903    * @param[in] $event string Le nom de l'événement
1904    * 
1905    * @retval boolean True si tout c'est bien passé, false sinon
1906    **/
1907   function fireObjectEvent($event) {
1908     switch($event) {
1909       case 'after_create':
1910         return $this -> afterCreate();
1911       case 'after_delete':
1912         return $this -> afterDelete();
1913       case 'after_rename':
1914         return $this -> afterRename();
1915       case 'after_modify':
1916         return $this -> afterModify();
1917 /*
1918       case 'before_create':
1919         return $this -> beforeCreate();
1920 */
1921       case 'before_delete':
1922         return $this -> beforeDelete();
1923       case 'before_rename':
1924         return $this -> beforeRename();
1925 /*
1926       case 'before_modify':
1927         return $this -> beforeModify();
1928 */
1929     }
1930     return true;
1931   }
1932   
1933   /**
1934    * Access to infos of the object
1935    * 
1936    * @param[in] $key string The name of the value
1937    * 
1938    * @retval mixed The value
1939    **/
1940   function __get($key) {
1941     if ($key=='subDnValue') {
1942       if ($this -> cache['subDnValue']) {
1943         return $this -> cache['subDnValue'];
1944       }
1945       $this -> cache['subDnValue'] = self :: getSubDnValue($this -> dn);
1946       return $this -> cache['subDnValue'];
1947     }
1948     if ($key=='subDnName') {
1949       if ($this -> cache['subDnName']) {
1950         return $this -> cache['subDnName'];
1951       }
1952       $this -> cache['subDnName'] = self :: getSubDnName($this -> dn);
1953       return $this -> cache['subDnName'];
1954     }
1955   }
1956   
1957 }
1958
1959 /**
1960  * Error Codes
1961  **/
1962 LSerror :: defineError('LSldapObject_01',
1963 _("LSldapObject : Object type unknown.")
1964 );
1965 LSerror :: defineError('LSldapObject_02',
1966 _("LSldapObject : Update form is not defined for the object %{obj}.")
1967 );
1968 LSerror :: defineError('LSldapObject_03',
1969 _("LSldapObject : No form exists for the object %{obj}.")
1970 );
1971 LSerror :: defineError('LSldapObject_04',
1972 _("LSldapObject : The function %{func} to validate the attribute %{attr} the object %{obj} is unknow.")
1973 );
1974 LSerror :: defineError('LSldapObject_05',
1975 _("LSldapObject : Configuration data are missing to validate the attribute %{attr} of the object %{obj}.")
1976 );
1977
1978 LSerror :: defineError('LSldapObject_06',
1979 _("LSldapObject : The function %{func} to be executed on the object event %{event} doesn't exist.")
1980 );
1981 LSerror :: defineError('LSldapObject_07',
1982 _("LSldapObject : The %{func} execution on the object event %{event} failed.")
1983 );
1984
1985 LSerror :: defineError('LSldapObject_08',
1986 _("LSldapObject : Class %{class}, which method %{meth} to be executed on the object event %{event}, doesn't exist.")
1987 );
1988 LSerror :: defineError('LSldapObject_09',
1989 _("LSldapObject : Method %{meth} within %{class} class to be executed on object event %{event}, doesn't exist.")
1990 );
1991 LSerror :: defineError('LSldapObject_10',
1992 _("LSldapObject : Error during execute %{meth} method within %{class} class, to be executed on object event %{event}.")
1993 );
1994
1995 LSerror :: defineError('LSldapObject_11',
1996 _("LSldapObject : Some configuration data of the object type %{obj} are missing to generate the DN of the new object.")
1997 );
1998 LSerror :: defineError('LSldapObject_12',
1999 _("LSldapObject : The attibute %{attr} of the object is not yet defined. Can't generate DN.")
2000 );
2001 LSerror :: defineError('LSldapObject_13',
2002 _("LSldapObject : Without DN, the object could not be changed.")
2003 );
2004 LSerror :: defineError('LSldapObject_14',
2005 _("LSldapObject : The attribute %{attr_depend} depending on the attribute %{attr} doesn't exist.")
2006 );
2007 LSerror :: defineError('LSldapObject_15',
2008 _("LSldapObject : Error during deleting the object %{objectname}.")
2009 );
2010
2011 LSerror :: defineError('LSldapObject_16',
2012 _("LSldapObject : Error during actions to be executed before renaming the objet.")
2013 );
2014 LSerror :: defineError('LSldapObject_17',
2015 _("LSldapObject : Error during actions to be executed after renaming the objet.")
2016 );
2017
2018 LSerror :: defineError('LSldapObject_18',
2019 _("LSldapObject : Error during actions to be executed before deleting the objet.")
2020 );
2021 LSerror :: defineError('LSldapObject_19',
2022 _("LSldapObject : Error during actions to be executed after deleting the objet.")
2023 );
2024
2025 LSerror :: defineError('LSldapObject_20',
2026 _("LSldapObject : Error during the actions to be executed before creating the object.")
2027 );
2028 LSerror :: defineError('LSldapObject_21',
2029 _("LSldapObject : Error during the actions to be executed after creating the object. It was created anyway.")
2030 );
2031
2032 LSerror :: defineError('LSldapObject_22',
2033 _("LSldapObject : The function %{func} to be executed before creating the object doesn't exist.")
2034 );
2035 LSerror :: defineError('LSldapObject_23',
2036 _("LSldapObject : Error executing the function %{func} to be execute after deleting the object.")
2037 );
2038 LSerror :: defineError('LSldapObject_24',
2039 _("LSldapObject : The function %{func} to be executed after deleting the object doesn't exist.")
2040 );
2041 LSerror :: defineError('LSldapObject_25',
2042 _("LSldapObject : Error executing the function %{func} to be execute after creating the object.")
2043 );
2044
2045 LSerror :: defineError('LSldapObject_26',
2046 _("LSldapObject : %{func} function, to be executed on object event %{event}, doesn't exist.")
2047 );
2048 LSerror :: defineError('LSldapObject_27',
2049 _("LSldapObject : Error during the execution of %{func} function on object event %{event}.")
2050 );
2051
2052 LSerror :: defineError('LSldapObject_28',
2053 _("LSldapObject : %{meth} method, to be executed on object event %{event}, doesn't exist.")
2054 );
2055 LSerror :: defineError('LSldapObject_29',
2056 _("LSldapObject : Error during execution of %{meth} method on object event %{event}.")
2057 );
2058 LSerror :: defineError('LSldapObject_30',
2059 _("LSldapObject : Error during generate LDAP filter for %{LSobject}.")
2060 );
2061
2062 // LSrelation
2063 LSerror :: defineError('LSrelations_05',
2064 _("LSrelation : Some parameters are missing in the call of methods to handle standard relations (Method : %{meth}).")
2065 );
2066
2067 ?>