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