- LSldapObject : Modification de la méthode searchObject() pour prendre eventuellement
[ldapsaisie.git] / trunk / includes / class / class.LSldapObject.php
index 1b8d7a7..4e8f8dd 100644 (file)
 
 ******************************************************************************/
 
-$GLOBALS['LSsession'] -> loadLSclass('LSattribute');
+LSsession :: loadLSclass('LSattribute');
 
 /**
  * Base d'un objet ldap
  *
- * Cette classe définis la base de tout objet ldap géré par LdapSaisie
+ * Cette classe définis la base de tout objet ldap géré par LdapSaisie
  *
  * @author Benjamin Renard <brenard@easter-eggs.com>
  */
 class LSldapObject { 
   
-  var $config;
+  var $config = array();
   var $type_name;
-  var $attrs;
+  var $attrs = array();
   var $forms;
   var $view;
   var $dn=false;
+  var $oldDn=false;
   var $other_values=array();
   var $submitError=true;
   var $_whoami=NULL;
+  var $_subDn_value=NULL;
+  var $_LSrelationsCache=array();
+
+  var $_events=array();
+  var $_objectEvents=array();
   
   /**
    * Constructeur
    *
-   * Cette methode construit l'objet et définis la configuration.
-   * Elle lance la construction du tableau d'attributs représentés par un objet LSattribute.
+   * Cette methode construit l'objet et définis la configuration.
+   * Elle lance la construction du tableau d'attributs représentés par un objet LSattribute.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
    * @param[in] $type_name [<b>required</b>] string Le nom du type de l'objet
    * @param[in] $config array La configuration de l'objet
    *
-   * @retval boolean true si l'objet a été construit, false sinon.
+   * @retval boolean true si l'objet a Ã©té construit, false sinon.
    */ 
   function LSldapObject($type_name,$config='auto') {
     $this -> type_name = $type_name;
@@ -62,7 +68,7 @@ class LSldapObject {
         $this -> config = $GLOBALS['LSobjects'][$type_name];
       }
       else {
-        $GLOBALS['LSerror'] -> addErrorCode(21);
+        LSerror :: addErrorCode('LSldapObject_01');
         return;
       }
     }
@@ -71,24 +77,25 @@ class LSldapObject {
         return;
       }
     }
+    
     return true;
   }
   
   /**
-   * Charge les données de l'objet
+   * Charge les données de l'objet
    *
-   * Cette methode définis le DN de l'objet et charge les valeurs de attributs de l'objet
-   * à partir de l'annuaire.
+   * Cette methode définis le DN de l'objet et charge les valeurs de attributs de l'objet
+   * Ã  partir de l'annuaire.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
    * @param[in] $dn string Le DN de l'objet.
    *
-   * @retval boolean true si la chargement a réussi, false sinon.
+   * @retval boolean true si la chargement a réussi, false sinon.
    */ 
   function loadData($dn) {
     $this -> dn = $dn;
-    $data = $GLOBALS['LSldap'] -> getAttrs($dn);
+    $data = LSldap :: getAttrs($dn);
     if(!empty($data)) {
       foreach($this -> attrs as $attr_name => $attr) {
         if(!$this -> attrs[$attr_name] -> loadData($data[$attr_name]))
@@ -100,14 +107,14 @@ class LSldapObject {
   }
   
   /**
-   * Recharge les données de l'objet
+   * Recharge les données de l'objet
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval boolean true si la rechargement a réussi, false sinon.
+   * @retval boolean true si la rechargement a réussi, false sinon.
    */ 
   function reloadData() {
-    $data = $GLOBALS['LSldap'] -> getAttrs($this -> dn);
+    $data = LSldap :: getAttrs($this -> dn);
     foreach($this -> attrs as $attr_name => $attr) {
       if(!$this -> attrs[$attr_name] -> reloadData($data[$attr_name]))
         return;
@@ -122,40 +129,46 @@ class LSldapObject {
    *
    * @retval string Format d'affichage de l'objet.
    */ 
-  function getDisplayAttributes() {
-    return $this -> config['select_display_attrs'];
+  function getDisplayNameFormat() {
+    return $this -> config['display_name_format'];
   }
   
   /**
    * Retourne la valeur descriptive d'affichage de l'objet
    * 
    * Cette fonction retourne la valeur descriptive d'affichage de l'objet en fonction
-   * du format défini dans la configuration de l'objet ou spécifié en paramètre.
+   * du format défini dans la configuration de l'objet ou spécifié en paramètre.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
    * @param[in] $spe [<i>optionnel</i>] string Format d'affichage de l'objet
+   * @param[in] $full [<i>optionnel</i>] boolean True pour afficher en plus le
+   *                                             subDnName
    *
    * @retval string Valeur descriptive d'affichage de l'objet
    */ 
-  function getDisplayValue($spe='') {
+  function getDisplayName($spe='',$full=false) {
     if ($spe=='') {
-      $spe = $this -> getDisplayAttributes();
+      $spe = $this -> getDisplayNameFormat();
     }
-    return $this -> getFData($spe,&$this -> attrs,'getDisplayValue');
+    $val = $this -> getFData($spe,&$this -> attrs,'getDisplayValue');
+    if (LSsession :: haveSubDn() && $full) {
+      $val.=' ('.$this -> getSubDnName().')';
+    }
+    return $val;
   }
   
   /**
-   * Chaine formatée
+   * Chaine formatée
    * 
-   * Cette fonction retourne la valeur d'une chaine formatée en prennant les valeurs
+   * Cette fonction retourne la valeur d'une chaine formatée en prennant les valeurs
    * de l'objet.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
    * @param[in] $format string Format de la chaine
    *
-   * @retval string Valeur d'une chaine formatée
+   * @retval string Valeur d'une chaine formatée
    */ 
   function getFData($format) {
     $format=getFData($format,$this,'getValue');
@@ -165,18 +178,18 @@ class LSldapObject {
   /**
    * Construit un formulaire de l'objet
    * 
-   * Cette méthode construit un formulaire LSform à partir de la configuration de l'objet
+   * Cette méthode construit un formulaire LSform Ã  partir de la configuration de l'objet
    * et de chaque attribut.
    *
-   * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
-   * @param[in] $load DN d'un objet similaire dont la valeur des attribut doit être chargé dans le formulaire.
+   * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
+   * @param[in] $load DN d'un objet similaire dont la valeur des attribut doit Ãªtre chargé dans le formulaire.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval LSform Le formulaire crée
+   * @retval LSform Le formulaire crée
    */ 
   function getForm($idForm,$load=NULL) {
-    $GLOBALS['LSsession'] -> loadLSclass('LSform');
+    LSsession :: loadLSclass('LSform');
     $LSform = new LSform($this,$idForm);
     $this -> forms[$idForm] = array($LSform,$load);
     
@@ -190,7 +203,7 @@ class LSldapObject {
     
     if ($load) {
       foreach($this -> attrs as $attr_name => $attr) {
-        if(!$this -> attrs[$attr_name] -> addToForm($LSform,$idForm,$this,$loadObject -> getValue($attr_name))) {
+        if(!$this -> attrs[$attr_name] -> addToForm($LSform,$idForm,$this,$loadObject -> attrs[$attr_name] -> getFormVal())) {
           $LSform -> can_validate = false;
         }
       }
@@ -202,24 +215,27 @@ class LSldapObject {
         }
       }      
     }
+    LSsession :: addJSconfigParam('LSform_'.$idForm,array(
+      'ajaxSubmit' => ((isset($this -> config['LSform']['ajaxSubmit']))?$this -> config['LSform']['ajaxSubmit']:1)
+    ));
     return $LSform;
   }
   
   /**
    * Construit un formulaire de l'objet
    * 
-   * Cette méthode construit un formulaire LSform à partir de la configuration de l'objet
+   * Cette méthode construit un formulaire LSform Ã  partir de la configuration de l'objet
    * et de chaque attribut.
    *
-   * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
-   * @param[in] $config Configuration spécifique pour le formulaire
+   * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
+   * @param[in] $config Configuration spécifique pour le formulaire
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval LSform Le formulaire crée
+   * @retval LSform Le formulaire crée
    */ 
   function getView() {
-    $GLOBALS['LSsession'] -> loadLSclass('LSform');
+    LSsession :: loadLSclass('LSform');
     $this -> view = new LSform($this,'view');
     foreach($this -> attrs as $attr_name => $attr) {
       $this -> attrs[$attr_name] -> addToView($this -> view);
@@ -231,13 +247,13 @@ class LSldapObject {
   /**
    * Rafraichis le formulaire de l'objet
    * 
-   * Cette méthode recharge les données d'un formulaire LSform.
+   * Cette méthode recharge les données d'un formulaire LSform.
    *
-   * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
+   * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval boolean true sile formulaire a été rafraichis, false sinon
+   * @retval boolean true sile formulaire a Ã©té rafraichis, false sinon
    */ 
   function refreshForm($idForm) {
     $LSform = $this -> forms[$idForm][0];
@@ -250,15 +266,15 @@ class LSldapObject {
   }
   
   /**
-   * Met à jour les données de l'objet et de l'entré de l'annuaire
+   * Met Ã  jour les données de l'objet et de l'entré de l'annuaire
    * 
-   * Met à jour les données de l'objet à partir d'un retour d'un formulaire.
+   * Met Ã  jour les données de l'objet Ã  partir d'un retour d'un formulaire.
    *
    * @param[in] $idForm Identifiant du formulaire d'origine
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval boolean true si la mise à jour a réussi, false sinon
+   * @retval boolean true si la mise Ã  jour a réussi, false sinon
    *
    * @see validateAttrsData()
    * @see submitChange()
@@ -268,7 +284,7 @@ class LSldapObject {
       if(isset($this -> forms[$idForm]))
         $LSform = $this -> forms[$idForm][0];
       else {
-        $GLOBALS['LSerror'] -> addErrorCode(22,$this -> getType());
+        LSerror :: addErrorCode('LSldapObject_02',$this -> getType());
         return;
       }
     }
@@ -281,32 +297,35 @@ class LSldapObject {
         $LSform = $LSform[0];
       }
       else {
-        $GLOBALS['LSerror'] -> addErrorCode(23,$this -> getType());
+        LSerror :: addErrorCode('LSldapObject_03',$this -> getType());
         return;
       }
     }
     $new_data = $LSform -> exportValues();
+    if(!is_array($new_data)) {
+      return;
+    }
     foreach($new_data as $attr_name => $attr_val) {
       if(isset($this -> attrs[$attr_name])) {
         $this -> attrs[$attr_name] -> setUpdateData($attr_val);
       }
     }
     if($this -> validateAttrsData($idForm)) {
-      debug("les données sont validées");
-      if(isset($this -> config['before_save'])) {
-        if(function_exists($this -> config['before_save'])) {
-          if(!$this -> config['before_save']($this)) {
-            $GLOBALS['LSerror'] -> addErrorCode(28,$this -> config['before_save']);
-            return;
-          }
-        }
-        else {
-          $GLOBALS['LSerror'] -> addErrorCode(27,$this -> config['before_save']);
+      LSdebug("les données sont validées");
+      
+      if (!$this -> fireEvent('before_modify')) {
+        return;
+      }
+      
+      // $this -> attrs[ {inNewData} ] -> fireEvent('before_modify')
+      foreach($new_data as $attr_name => $attr_val) {
+        if (!$this -> attrs[$attr_name] -> fireEvent('before_modify')) {
           return;
         }
       }
+      
       if ($this -> submitChange($idForm)) {
-        debug('Les modifications sont submitées');
+        LSdebug('Les modifications sont submitées');
         $this -> submitError = false;
         $this -> reloadData();
         $this -> refreshForm($idForm);
@@ -314,17 +333,15 @@ class LSldapObject {
       else {
         return;
       }
-      if((isset($this -> config['after_save']))&&(!$this -> submitError)) {
-        if(function_exists($this -> config['after_save'])) {
-          if(!$this -> config['after_save']($this)) {
-            $GLOBALS['LSerror'] -> addErrorCode(30,$this -> config['after_save']);
-            return;
-          }
-        }
-        else {
-          $GLOBALS['LSerror'] -> addErrorCode(29,$this -> config['after_save']);
-          return;
-        }
+      
+      // Event After Modify
+      if(!$this -> submitError) {
+        $this -> fireEvent('after_modify');
+      }
+      
+      // $this -> attrs[*] => After Modify
+      foreach($new_data as $attr_name => $attr_val) {
+        $this -> attrs[$attr_name] -> fireEvent('after_modify');
       }
       return true;
     }
@@ -334,58 +351,61 @@ class LSldapObject {
   }
   
   /**
-   * Valide les données retournées par un formulaire
+   * Valide les données retournées par un formulaire
    *
    * @param[in] $idForm Identifiant du formulaire d'origine
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval boolean true si les données sont valides, false sinon
+   * @retval boolean true si les données sont valides, false sinon
    */ 
   function validateAttrsData($idForm) {
+    $retval = true;
     $LSform=$this -> forms[$idForm][0];
     foreach($this -> attrs as $attr) {
+      $attr_values = $attr -> getValue();
       if (!$attr -> isValidate()) {
         if($attr -> isUpdate()) {
           if (!$this -> validateAttrData($LSform, $attr)) {
-            return;
+            $retval = false;
           }
         }
-        else if( ($attr -> getValue() == '') && ($attr -> isRequired()) ) { 
+        else if( (empty($attr_values)) && ($attr -> isRequired()) ) { 
           if ( $attr -> canBeGenerated()) {
             if ($attr -> generateValue()) {
               if (!$this -> validateAttrData($LSform, $attr)) {
-                $GLOBALS['LSerror'] -> addErrorCode(48,$attr -> getLabel());
-                return;
+                LSerror :: addErrorCode('LSattribute_08',$attr -> getLabel());
+                $retval = false;
               }
             }
             else {
-              $GLOBALS['LSerror'] -> addErrorCode(47,$attr -> getLabel());
-              return;
+              LSerror :: addErrorCode('LSattribute_07',$attr -> getLabel());
+              $retval = false;
             }
           }
           else {
-            $GLOBALS['LSerror'] -> addErrorCode(46,$attr -> getLabel());
-            return;
+            LSerror :: addErrorCode('LSattribute_06',$attr -> getLabel());
+            $retval = false;
           }
-
         }
       }
     }
-    return true;
+    return $retval;
   }
 
    /**
-   * Valide les données d'un attribut
+   * Valide les données d'un attribut
    *
    * @param[in] $LSForm Formulaire d'origine
-   * @param[in] &$attr Attribut à valider
+   * @param[in] &$attr Attribut Ã  valider
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval boolean true si les données sont valides, false sinon
+   * @retval boolean true si les données sont valides, false sinon
    */
   function validateAttrData(&$LSform,&$attr) {
+    $retval = true;
+    
     $vconfig=$attr -> getValidateConfig();
 
     $data=$attr -> getUpdateData();
@@ -396,17 +416,17 @@ class LSldapObject {
     // Validation des valeurs de l'attribut
     if(is_array($vconfig)) {
       foreach($vconfig as $test) {
-        // Définition du basedn par défaut
+        // Définition du basedn par défaut
         if (!isset($test['basedn'])) {
-          $test['basedn']=$GLOBALS['LSsession']->topDn;
+          $test['basedn']=LSsession :: getTopDn();
         }
 
-        // Définition du message d'erreur
+        // Définition du message d'erreur
         if (!empty($test['msg'])) {
           $msg_error=getFData($test['msg'],$this,'getValue');
         }
         else {
-          $msg_error=getFData(_("L'attribut %{attr} n'est pas valide."),$attr -> getLabel());
+          $msg_error=getFData(_("The attribute %{attr} is not valid."),$attr -> getLabel());
         }
         foreach($data as $val) {
           // validation par check LDAP
@@ -429,17 +449,17 @@ class LSldapObject {
               $sfilter=$sfilter_user;
             }
             $sbasedn=(isset($test['basedn']))?getFData($test['basedn'],$this,'getValue'):NULL;
-            $ret=$GLOBALS['LSldap'] -> getNumberResult ($sfilter,$sbasedn,$sparams);
+            $ret=LSldap :: getNumberResult ($sfilter,$sbasedn,$sparams);
             if($test['result']==0) {
               if($ret!=0) {
                 $LSform -> setElementError($attr,$msg_error);
-                return;
+                $retval = false;
               }
             }
             else {
-              if($ret<=0) {
+              if($ret<0) {
                 $LSform -> setElementError($attr,$msg_error);
-                return;
+                $retval = false;
               }
             }
           }
@@ -448,72 +468,109 @@ class LSldapObject {
             if (function_exists($test['function'])) {
               if(!$test['function']($this)) {
                 $LSform -> setElementError($attr,$msg_error);
-              return;
+                $retval = false;
               }
             }
             else {
-              $GLOBALS['LSerror'] -> addErrorCode(24,array('attr' => $attr->name,'obj' => $this->getType(),'func' => $test['function']));
-              return;
+              LSerror :: addErrorCode('LSldapObject_04',array('attr' => $attr->name,'obj' => $this->getType(),'func' => $test['function']));
+              $retval = false;
             }
           }
           else {
-            $GLOBALS['LSerror'] -> addErrorCode(25,array('attr' => $attr->name,'obj' => $this->getType()));
-            return;
+            LSerror :: addErrorCode('LSldapObject_05',array('attr' => $attr->name,'obj' => $this->getType()));
+            $retval = false;
           }
         }
       }
     }
-    // Génération des valeurs des attributs dépendants
+    // Génération des valeurs des attributs dépendants
     $dependsAttrs=$attr->getDependsAttrs();
     if (!empty($dependsAttrs)) {
       foreach($dependsAttrs as $dependAttr) {
         if(!isset($this -> attrs[$dependAttr])){
-          $GLOBALS['LSerror'] -> addErrorCode(34,array('attr_depend' => $dependAttr, 'attr' => $attr -> getLabel()));
+          LSerror :: addErrorCode('LSldapObject_14',array('attr_depend' => $dependAttr, 'attr' => $attr -> getLabel()));
           continue;
         }
         if($this -> attrs[$dependAttr] -> canBeGenerated()) {
           if (!$this -> attrs[$dependAttr] -> generateValue()) {
-            $GLOBALS['LSerror'] -> addErrorCode(47,$this -> attrs[$dependAttr] -> getLabel());
-            return;
+            LSerror :: addErrorCode('LSattribute_07',$this -> attrs[$dependAttr] -> getLabel());
+            $retval = false;
           }
         }
         else {
-          $GLOBALS['LSerror'] -> addErrorCode(46,$this -> attrs[$dependAttr] -> getLabel());
-          return;
+          LSerror :: addErrorCode('LSattribute_06',$this -> attrs[$dependAttr] -> getLabel());
+          $retval = false;
         }
       }
     }
 
     $attr -> validate();
     unset($this -> other_values['val']);
-    return true;
+    return $retval;
   }
 
   /**
-   * Met à jour les données modifiés dans l'annuaire
+   * Met Ã  jour les données modifiés dans l'annuaire
    *
    * @param[in] $idForm Identifiant du formulaire d'origine
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval boolean true si la mise à jour a réussi, false sinon
+   * @retval boolean true si la mise Ã  jour a réussi, false sinon
    */ 
   function submitChange($idForm) {
     $submitData=array();
+    $new = $this -> isNew();
     foreach($this -> attrs as $attr) {
       if(($attr -> isUpdate())&&($attr -> isValidate())) {
-        $submitData[$attr -> name] = $attr -> getUpdateData();
+        if(($attr -> name == $this -> config['rdn'])&&(!$new)) {
+          $new = true;
+          LSdebug('Rename');
+          if (!$this -> fireEvent('before_rename')) {
+            LSerror :: addErrorCode('LSldapObject_16');
+            return;
+          }
+          $oldDn = $this -> getDn();
+          $this -> dn = false;
+          $newDn = $this -> getDn();
+          if ($newDn) {
+            if (!LSldap :: move($oldDn,$newDn)) {
+              return;
+            }
+            $this -> dn = $newDn;
+            $this -> oldDn = $oldDn;
+            if (!$this -> fireEvent('after_rename')) {
+              LSerror :: addErrorCode('LSldapObject_17');
+              return;
+            }
+          }
+          else {
+            return;
+          }
+        }
+        else {
+          $submitData[$attr -> name] = $attr -> getUpdateData();
+        }
       }
     }
     if(!empty($submitData)) {
       $dn=$this -> getDn();
       if($dn) {
         $this -> dn=$dn;
-        debug($submitData);
-        return $GLOBALS['LSldap'] -> update($this -> getType(),$dn, $submitData);
+        LSdebug($submitData);
+        if (!LSldap :: update($this -> getType(),$dn, $submitData)) {
+          return;
+        }
+        if ($new) {
+          if (!$this -> fireEvent('after_create')) {
+            LSerror :: addErrorCode('LSldapObject_21');
+            return;
+          }
+        }
+        return true;
       }
       else {
-        $GLOBALS['LSerror'] -> addErrorCode(33);
+        LSerror :: addErrorCode('LSldapObject_13');
         return;
       }
     }
@@ -530,8 +587,8 @@ class LSldapObject {
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
    * @retval array Tableau : 
-   *                  - [0] : le premier paramètre
-   *                  - [1] : les paramètres suivants
+   *                  - [0] : le premier paramètre
+   *                  - [1] : les paramètres suivants
    */ 
   function getDnInfos($dn) {
     $infos=ldap_explode_dn($dn,0);
@@ -557,26 +614,98 @@ class LSldapObject {
    */ 
   function getObjectFilter() {
     if(!isset($this -> config['objectclass'])) return;
-    foreach ($this -> config['objectclass'] as $class)
+    $filter="(&";
+    foreach ($this -> config['objectclass'] as $class) {
       $filter.='(objectClass='.$class.')';
+    }
+    $filter.=')';
+    if ($this -> config['filter']) {
+      $filter="(&(".$this -> config['filter'].")$filter)";
+    }
     return $filter;
   }
   
   /**
-   * Retourne une liste d'objet du même type.
+   * Retourne le filtre correpondants au pattern passé
+   * 
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
+   * @param[in] $pattern string Le mot clé recherché
+   * @param[in] $approx booléen Booléen activant ou non la recherche approximative
+   *
+   * @retval string le filtre ldap correspondant
+   */ 
+  function getPatternFilter($pattern=null,$approx=null) {
+    if ($pattern!=NULL) {
+      if (is_array($GLOBALS['LSobjects'][$this -> getType()]['LSsearch']['attrs'])) {
+        $attrs=$GLOBALS['LSobjects'][$this -> getType()]['LSsearch']['attrs'];
+      }
+      else {
+        $attrs=array($GLOBALS['LSobjects'][$this -> getType()]['rdn']);
+      }
+      $pfilter='(|';
+      if ($approx) {
+        foreach ($attrs as $attr_name) {
+          $pfilter.='('.$attr_name.'~='.$pattern.')';
+        }
+      }
+      else {
+        foreach ($attrs as $attr_name) {
+          $pfilter.='('.$attr_name.'=*'.$pattern.'*)';
+        }
+      }
+      $pfilter.=')';
+      return $pfilter;
+    }
+    else {
+      return NULL;
+    }
+  }
+  
+  /**
+   * Retourne une liste d'objet du même type.
    *
-   * Effectue une recherche en fonction des paramètres passé et retourne un
+   * Effectue une recherche en fonction des paramètres passé et retourne un
    * tableau d'objet correspond au resultat de la recherche.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
    * @param[in] $filter array (ou string) Filtre de recherche Ldap / Tableau de filtres de recherche
    * @param[in] $basedn string DN de base pour la recherche
-   * @param[in] $params array Paramètres de recherche au format Net_LDAP::search()
+   * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
    *
-   * @retval array Tableau d'objet correspondant au resultat de la recherche
+   * @retval array Tableau d'objets correspondant au resultat de la recherche
    */ 
-  function listObjects($filter='',$basedn=NULL,$params=array()) {
+  function listObjects($filter=NULL,$basedn=NULL,$params=array()) {
+    $retInfos=array();
+    
+    $ret = $this -> search($filter,$basedn,$params);
+    
+    // Création d'un tableau d'objet correspondant au valeur retourné
+    for($i=0;$i<count($ret);$i++) {
+      $retInfos[$i] = new $this -> type_name($this -> config);
+      $retInfos[$i] -> loadData($ret[$i]['dn']);
+    }
+    
+    return $retInfos;
+  }
+  
+  /**
+   * Recherche les objets du même type dans l'annuaire
+   *
+   * Effectue une recherche en fonction des paramètres passé et retourne un
+   * tableau array(dn => '', attrs => array()) d'objet correspondant au resultat*
+   * de la recherche.
+   *
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   *
+   * @param[in] $filter array (ou string) Filtre de recherche Ldap / Tableau de filtres de recherche
+   * @param[in] $basedn string DN de base pour la recherche
+   * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
+   *
+   * @retval array Tableau d'objets correspondant au resultat de la recherche
+   */ 
+  function search($filter='',$basedn=NULL,$params=array()) {
     $retInfos=array();
     $attrs=false;
     $check_final_dn=false;
@@ -588,22 +717,22 @@ class LSldapObject {
 
     for($i=0;$i<$nbFilter;$i++) {
       $new_attrs=array();
-      // Défintion des paramètres de base pour la recherche
+      // Défintion des paramètres de base pour la recherche
       $sbasedn=$basedn;
       $sparams=$params;
       $ret=array();
       if (isset($filter[$i]['scope']))
         $sparams["scope"]=$filter[$i]['scope'];
       
-      // Definition des critères de recherche correspondant au type d'objet à lister
+      // Definition des critères de recherche correspondant au type d'objet Ã  lister
       if(($nbFilter==1)||(!isset($filter[$i]['attr']))) {
-        // Filtre sur l'objet souhaité
+        // Filtre sur l'objet souhaité
         $sfilter='(&';
         $sfilter.=$this -> getObjectFilter();
         $sfilter_end=')';
         $check_final_dn=true;
       }
-      // Initialisation des critères d'une recherche intermédiaire
+      // Initialisation des critères d'une recherche intermédiaire
       else {
         if(isset($filter[$i]['object_type'])) {
           $obj_tmp=new $filter[$i]['object_type']();
@@ -622,17 +751,17 @@ class LSldapObject {
           $sbasedn=$filter[$i]['basedn'];
         }
       }
-      // Dans le cas d'une recherche intermédiaire ou finale
+      // Dans le cas d'une recherche intermédiaire ou finale
       if($attrs!=false) {
         // Initialisation des variables
         $ret_gen=array();
         $new_attrs=array();
         
-        // Pour tout les attributs retournés
+        // Pour tout les attributs retournés
         for($ii=0;$ii<count($attrs);$ii++) {
           $sfilter_for='';
-          // Définition du filtre de recherche à partir des paramètres utilisateurs et
-          // des paramètres de recherche de l'objet à listé (dans le cas d'une recherche finale
+          // Définition du filtre de recherche Ã  partir des paramètres utilisateurs et
+          // des paramètres de recherche de l'objet Ã  listé (dans le cas d'une recherche finale
           if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
             $sfilter_user=getFData($filter[$i]['filter'],$attrs[$ii]);
             if($sfilter_user[0]=='(')
@@ -649,17 +778,25 @@ class LSldapObject {
             if ((!$this -> isCompatibleDNs($sbasedn,$basedn))&&($check_final_dn)) continue;
           }
         
-          // Vérification de la compatibilité du basedn de la recherche et du basedn générale
+          // Vérification de la compatibilité du basedn de la recherche et du basedn générale
           // Finalisation du filtre
           $sfilter_for.=$sfilter_end;
         
         
+          // Attributes
+          if ($filter[$i]['attr']) {
+            $sparams['attributes'] = array($filter[$i]['attr']);
+          }
+          else if (!isset($sparams['attributes'])) {
+            $sparams['attributes'] = array($this -> config['rdn']);
+          }
+        
           // Execution de la recherche
-          $ret=$GLOBALS['LSldap'] -> search ($sfilter_for,$sbasedn,$sparams);
+          $ret=LSldap :: search ($sfilter_for,$sbasedn,$sparams);
           
           // Si il y un retour
           if(isset($ret[0])) {
-            // si il ya une suite (recherche intermédiaire)
+            // si il ya une suite (recherche intermédiaire)
             if($filter[$i]['attr']){
               for($iii=0;$iii<count($ret);$iii++) {
                 if(isset($ret[$iii]['attrs'][$filter[$i]['attr']])) {
@@ -676,25 +813,15 @@ class LSldapObject {
                 }
               }
             }
-            else {
-              // vérification de la compatibilité de la compatibilité du DN resultant
-              // et du basedn de recherche 
-              if (!$this -> isCompatibleDNs($ret[0]['dn'],$basedn))
-                continue;
-              // ajout du DN au resultat finale
-              $ret_gen[]=$ret[0]['dn'];
-            }
           }
         }
         // cas du dernier filtre
         if(!empty($ret_gen)) {
-          // on quitte la boucle des filtres de la conf
-          $ret=$ret_gen;
           break;
         }
-        // dans le cas d'une suite prévu mais d'un retour nul de la précédente recherche
+        // dans le cas d'une suite prévu mais d'un retour nul de la précédente recherche
         else if(empty($new_attrs)) {
-            // retour vide et arrêt de la recherche
+            // retour vide et arrêt de la recherche
             $ret=array();
             break;
         }
@@ -704,7 +831,7 @@ class LSldapObject {
       }
       // Dans le cas de la recherche initiale
       else {
-        // Déclaration du filtre de recherche
+        // Déclaration du filtre de recherche
         if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
           if($filter[$i]['filter'][0]=='(') {
             $sfilter.=$filter[$i]['filter'];
@@ -716,10 +843,15 @@ class LSldapObject {
         // fermeture du filtre
         $sfilter.=$sfilter_end;
         
+        // Attributes
+        if (!isset($sparams['attributes'])) {
+          $sparams['attributes'] = array($this -> config['rdn']);
+        }
+        
         // Lancement de la recherche
-        $ret=$GLOBALS['LSldap'] -> search ($sfilter,$sbasedn,$sparams);
+        $ret=LSldap :: search ($sfilter,$sbasedn,$sparams);
         
-        //Si filtre multiple => on recupère une liste d'attributs
+        //Si filtre multiple => on recupère une liste d'attributs
         if(isset($filter[$i]['attr'])) {
           for($ii=0;$ii<count($ret);$ii++) {
             if(isset($ret[$ii]['attrs'][$filter[$i]['attr']])) {
@@ -736,43 +868,93 @@ class LSldapObject {
             }
           }
           
-          // Si aucunne valeur n'est retournées
+          // Si aucunne valeur n'est retournées
           if(empty($attrs)){
-            // arrêt et retour à zéro
+            // arrêt et retour Ã  zéro
             $ret=array();
             break;
           }
         }
         // Si recherche unique
         else {
-          // préparation du retour finale
-          $ret_final=array();
-          foreach($ret as $obj)
-            $ret_final[]=$obj['dn'];
-          $ret=$ret_final;
+          // préparation du retour finale
+          if (!is_array($ret)) {
+            $ret=array();
+          }
           break;
         }
       }
     }
+    return $ret;
+  }
+  
+  /**
+   * Retourne une liste d'objet du même type et retourne leur noms
+   *
+   * Effectue une recherche en fonction des paramètres passé et retourne un
+   * tableau (dn => nom) correspondant au resultat de la recherche.
+   *
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   *
+   * @param[in] $filter string Filtre de recherche Ldap
+   * @param[in] $basedn string DN de base pour la recherche
+   * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
+   * @param[in] $displayFormat string Format d'affichage du nom des objets
+   *
+   * @retval array Tableau dn => name correspondant au resultat de la recherche
+   */ 
+  function listObjectsName($filter=NULL,$sbasedn=NULL,$sparams=array(),$displayFormat=false) {
+    $retInfos=array();
     
-    // Création d'un tableau d'objet correspondant au valeur retourné
-    for($i=0;$i<count($ret);$i++) {
-      $retInfos[$i] = new $this -> type_name($this -> config);
-      $retInfos[$i] -> loadData($ret[$i]);
+    if (!$displayFormat) {
+      $displayFormat = $this -> getDisplayNameFormat();
+    }
+    // Attributes
+    $attrs = getFieldInFormat($displayFormat);
+    if(!empty($attrs)) {
+      $sparams['attributes'] = $attrs;
+    }
+    
+    // Lancement de la recherche
+    $ret=$this -> search ($filter,$sbasedn,$sparams);
+
+    if (is_array($ret)) {
+      foreach($ret as $obj) {
+        $retInfos[$obj['dn']] = getFData($displayFormat,$obj['attrs']);
+      }
     }
     
     return $retInfos;
   }
  
-  function searchObject($name,$basedn=NULL) {
-    $filter = $this -> config['rdn'].'='.$name; 
+  /**
+   * Recherche un objet à partir de la valeur exact de son RDN ou d'un filtre de
+   * recherche LDAP sous la forme d'un LSformat qui sera construit avec la valeur
+   * de $name.
+   * 
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
+   * @param[in] $name string Valeur de son RDN ou de la valeur pour composer le filtre
+   * @param[in] $basedn string Le DN de base de la recherche
+   * @param[in] $filter string Le filtre de recherche de l'objet
+   * 
+   * @retval array Tableau d'objets correspondant au resultat de la recherche
+   */
+  function searchObject($name,$basedn=NULL,$filter=NULL) {
+    if (!$filter) {
+      $filter = $this -> config['rdn'].'='.$name;
+    }
+    else {
+      $filter = getFData($filter,$name);
+    }
     return $this -> listObjects($filter,$basedn); 
   }
 
   /**
    * Retourne une valeur de l'objet
    *
-   * Retourne une valeur en fonction du paramètre. Si la valeur est inconnue, la valeur retourné est ' '.
+   * Retourne une valeur en fonction du paramètre. Si la valeur est inconnue, la valeur retourné est ' '.
    * tableau d'objet correspond au resultat de la recherche.
    *
    * Valeurs possibles :
@@ -782,9 +964,9 @@ class LSldapObject {
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @param[in] $val string Le nom de la valeur demandée
+   * @param[in] $val string Le nom de la valeur demandée
    *
-   * @retval mixed la valeur demandé ou ' ' si celle-ci est inconnue.
+   * @retval mixed la valeur demandé ou ' ' si celle-ci est inconnue.
    */ 
   function getValue($val) {
     if(($val=='dn')||($val=='%{dn}')) {
@@ -793,6 +975,12 @@ class LSldapObject {
     else if(($val=='rdn')||($val=='%{rdn}')) {
       return $this -> attrs[ $this -> config['rdn'] ] -> getValue();
     }
+    else if(($val=='subDn')||($val=='%{subDn}')) {
+      return $this -> getSubDnValue();
+    }
+    else if(($val=='subDnName')||($val=='%{subDnName}')) {
+      return $this -> getSubDnName();
+    }
     else if(isset($this ->  attrs[$val])){
       if (method_exists($this ->  attrs[$val],'getValue'))
         return $this -> attrs[$val] -> getValue();
@@ -806,44 +994,23 @@ class LSldapObject {
       return ' ';
     }
   }
-  /**
-   * Retourn une liste d'option pour un select d'un objet du même type
-   * 
-   * @author Benjamin Renard <brenard@easter-eggs.com>
-   *
-   * @retval string HTML code
-   */
-  function getSelectOptions() {
-    $list = $this -> listObjects();
-    $display='';
-    foreach($list as $object) {
-      $display.="<option value=\"".$object -> getDn()."\">".$object -> getDisplayValue()."</option>\n"; 
-    }
-    return $display;
-  }
 
   /**
-   * Retourn un tableau pour un select d'un objet du même type
+   * Retourn un tableau pour un select d'un objet du même type
    * 
    * @author Benjamin Renard <brenard@easter-eggs.com>
    *
-   * @retval array['dn','display']
+   * @retval array('dn' => 'display')
    */
-  function getSelectArray() {
-    $list = $this -> listObjects();
-    $return=array();
-    foreach($list as $object) {
-      $return['dn'][] = $object -> getDn();
-      $return['display'][] = $object -> getDisplayValue();
-    }
-    return $return;
+  function getSelectArray($pattern=NULL,$topDn=NULL,$displayFormat=NULL,$approx=false) {
+    $filter=$this -> getPatternFilter($pattern,$approx);
+    return $this -> listObjectsName($filter,$topDn,array(),$displayFormat);
   }
 
   /**
    * Retourne le DN de l'objet
    *
-   * Cette methode retourne le DN de l'objet. Si celui-ci n'existe pas, il le construit à partir de la 
+   * Cette methode retourne le DN de l'objet. Si celui-ci n'existe pas, il le construit Ã  partir de la 
    * configuration de l'objet et la valeur de son attribut rdn.
    *
    * @author Benjamin Renard <brenard@easter-eggs.com>
@@ -856,18 +1023,19 @@ class LSldapObject {
     }
     else {
       $rdn_attr=$this -> config['rdn'];
-      if( (isset($this -> config['rdn'])) && (isset($this -> attrs[$rdn_attr])) && (isset($this -> config['container_dn'])) && (isset($GLOBALS['LSsession']->topDn)) ) {
+      $topDn = LSsession :: getTopDn();
+      if( (isset($this -> config['rdn'])) && (isset($this -> attrs[$rdn_attr])) && (isset($this -> config['container_dn'])) && ($topDn) ) {
         $rdn_val=$this -> attrs[$rdn_attr] -> getUpdateData();
         if (!empty($rdn_val)) {
-          return $rdn_attr.'='.$rdn_val[0].','.$this -> config['container_dn'].','.$GLOBALS['LSsession']->topDn;
+          return $rdn_attr.'='.$rdn_val[0].','.$this -> config['container_dn'].','.$topDn;
         }
         else {
-          $GLOBALS['LSerror'] -> addErrorCode(32,$this -> config['rdn']);
+          LSerror :: addErrorCode('LSldapObject_12',$this -> config['rdn']);
           return;
         }
       }
       else {
-        $GLOBALS['LSerror'] -> addErrorCode(31,$this -> getType());
+        LSerror :: addErrorCode('LSldapObject_11',$this -> getType());
         return;
       }
     }
@@ -876,6 +1044,8 @@ class LSldapObject {
   /**
    * Retourne le type de l'objet
    *
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
    * @retval string Le type de l'objet ($this -> type_name)
    */
   function getType() {
@@ -883,19 +1053,23 @@ class LSldapObject {
   }
   
   /**
-   * Retourne qui est l'utilisateur par rapport à cet object
+   * Retourne qui est l'utilisateur par rapport Ã  cet object
    *
-   * @retval string 'admin'/'self'/'user' pour Admin , l'utilisateur lui même ou un simple utilisateur
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
+   * @retval string 'admin'/'self'/'user' pour Admin , l'utilisateur lui même ou un simple utilisateur
    */
   function whoami() {
     if (!$this -> _whoami)
-      $this -> _whoami = $GLOBALS['LSsession'] -> whoami($this -> dn);
+      $this -> _whoami = LSsession :: whoami($this -> dn);
     return $this -> _whoami;
   }
   
   /**
    * Retourne le label de l'objet
    *
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
    * @retval string Le label de l'objet ($this -> config['label'])
    */
   function getLabel() {
@@ -906,11 +1080,850 @@ class LSldapObject {
   /**
    * Supprime l'objet dans l'annuaire
    *
-   * @retval boolean True si l'objet à été supprimé, false sinon
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
+   * @retval boolean True si l'objet Ã  Ã©té supprimé, false sinon
    */
   function remove() {
-    return $GLOBALS['LSldap'] -> remove($this -> getDn());
+    if ($this -> fireEvent('before_delete')) {
+      if (LSldap :: remove($this -> getDn())) {
+        if ($this -> fireEvent('after_delete')) {
+          return true;
+        }
+        LSerror :: addErrorCode('LSldapObject_19');
+      }
+    }
+    else {
+      LSerror :: addErrorCode('LSldapObject_18');
+    }
+    return;
+  }
+  
+  /**
+   * L'objet est-il nouveau
+   * 
+   * @author Benjamin Renard <brenard@easter-eggs.com>
+   * 
+   * @retval boolean True si l'objet est nouveau, false sinon
+   */
+  function isNew() {
+    return (!$this -> dn);
+  }
+
+  /**
+   * Retourne la valeur (DN) du subDn de l'objet  
+   * 
+   * @parram[in] $dn string Un DN
+   * 
+   * @return string La valeur du subDn de l'object
+   */
+  function getSubDnValue($dn=NULL) {
+    if (!$dn) {
+      $dn = $this -> getValue('dn');
+    }
+    if ($this -> _subDn_value[$dn]) {
+      return $this -> _subDn_value[$dn];
+    }
+    $subDn_value='';
+    $subDnLdapServer = LSsession :: getSortSubDnLdapServer();
+    foreach ($subDnLdapServer as $subDn => $subDn_name) {
+      if (isCompatibleDNs($subDn,$dn)&&($subDn!=$dn)) {
+        $subDn_value=$subDn;
+        break;
+      }
+    }
+    $this -> _subDn_value[$dn] = $subDn_value;
+    return $subDn_value;
+  }
+
+  /**
+   * Retourne la nom du subDn de l'objet  
+   * 
+   * @parram[in] $dn string Un DN
+   * 
+   * @return string Le nom du subDn de l'object
+   */
+  function getSubDnName($dn=NULL) {
+    $subDnLdapServer = LSsession :: getSortSubDnLdapServer();
+    return $subDnLdapServer[$this -> getSubDnValue($dn)];
+  }
+  
+  /**
+   * Methode créant la liste des objets en relations avec l'objet courant et qui
+   * la met en cache ($this -> _LSrelationsCache)
+   * 
+   * @retval True en cas de cas ce succès, False sinon.
+   */
+  function updateLSrelationsCache() {
+    $this -> _LSrelationsCache=array();
+    if (is_array($this->config['LSrelation'])) {
+      $type = $this -> getType();
+      $me = new $type();
+      $me -> loadData($this -> getDn());
+      foreach($this->config['LSrelation'] as $relation_name => $relation_conf) {
+        if ( isset($relation_conf['list_function']) ) {
+          if (LSsession :: loadLSobject($relation_conf['LSobject'])) {
+            $obj = new $relation_conf['LSobject']();
+            if ((method_exists($obj,$relation_conf['list_function']))&&(method_exists($obj,$relation_conf['getkeyvalue_function']))) {
+              $list = $obj -> $relation_conf['list_function']($me);
+              if (is_array($list)) {
+                // Key Value
+                $key = $obj -> $relation_conf['getkeyvalue_function']($me);
+                
+                $this -> _LSrelationsCache[$relation_name] = array(
+                  'list' => $list,
+                  'keyvalue' => $key
+                );
+              }
+              else {
+                LSdebug('Problème durant la mise en cache de la relation '.$relation_name);
+                return;
+              }
+            }
+            else {
+              LSdebug('Les méthodes de mise en cache de la relation '.$relation_name. ' ne sont pas toutes disponibles.');
+              return;
+            }
+          }
+          else {
+            return;
+          }
+        }
+      }
+    }
+    return true;
+  }
+  
+  /**
+   * Methode executant les actions nécéssaires avant le changement du DN de
+   * l'objet.
+   * 
+   * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
+   * pour les objets plus complexe.
+   * 
+   * @retval True en cas de cas ce succès, False sinon.
+   */
+  function beforeRename() {
+    // LSrelations
+    return $this -> updateLSrelationsCache();
+  }
+  
+  /**
+   * Methode executant les actions nécéssaires après le changement du DN de
+   * l'objet.
+   * 
+   * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
+   * pour les objets plus complexe.
+   * 
+   * @retval True en cas de cas ce succès, False sinon.
+   */
+  function afterRename() {
+    $error = 0;
+    
+    // Change LSsession -> userObject Dn
+    if(LSsession :: getLSuserObjectDn() == $this -> oldDn) {
+      LSsession :: changeAuthUser($this);
+    }
+    
+    // LSrelations
+    foreach($this -> _LSrelationsCache as $relation_name => $objInfos) {
+      if ((isset($this->config['LSrelation'][$relation_name]['rename_function']))&&(is_array($objInfos['list']))) {
+        foreach($objInfos['list'] as $obj) {
+          $meth = $this->config['LSrelation'][$relation_name]['rename_function'];
+          if (method_exists($obj,$meth)) {
+            if (!($obj -> $meth($this,$objInfos['keyvalue']))) {
+              $error=1;
+            }
+          }
+          else {
+            $error=1;
+          }
+        }
+      }
+    }
+    return !$error;
+  }
+  
+  /**
+   * Methode executant les actions nécéssaires avant la suppression de
+   * l'objet.
+   * 
+   * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
+   * pour les objets plus complexe.
+   * 
+   * @retval True en cas de cas ce succès, False sinon.
+   */
+  function beforeDelete() {
+    $return = $this -> updateLSrelationsCache();
+    
+    foreach(array_keys($this -> attrs) as $attr_name) {
+      if (!$this -> attrs[$attr_name] -> fireEvent('before_delete')) {
+        $return = false;
+      }
+    }
+    
+    return $return;
   }
+  
+  /**
+   * Methode executant les actions nécéssaires après la suppression de
+   * l'objet.
+   * 
+   * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
+   * pour les objets plus complexe.
+   * 
+   * @retval True en cas de cas ce succès, False sinon.
+   */
+  function afterDelete() {
+    $error = 0;
+    
+    // LSrelations
+    foreach($this -> _LSrelationsCache as $relation_name => $objInfos) {
+      if ((isset($this->config['LSrelation'][$relation_name]['remove_function']))&&(is_array($objInfos['list']))) {
+        foreach($objInfos['list'] as $obj) {
+          $meth = $this->config['LSrelation'][$relation_name]['remove_function'];
+          if (method_exists($obj,$meth)) {
+            if (!($obj -> $meth($this))) {
+              $error=1;
+            }
+          }
+          else {
+            $error=1;
+          }
+        }
+      }
+    }
+    
+    // Binding LSattributes
+    foreach(array_keys($this -> attrs) as $attr_name) {
+      if (!$this -> attrs[$attr_name] -> fireEvent('after_delete')) {
+        $error = true;
+      }
+    }
+    
+    return !$error;
+  }
+  
+  /**
+   * Methode executant les actions nécéssaires après la création de
+   * l'objet.
+   * 
+   * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
+   * pour les objets plus complexe.
+   * 
+   * @retval True en cas de cas ce succès, False sinon.
+   */
+  function afterCreate() {
+    LSdebug('after');
+    $error = 0;
+    
+    // container_auto_create
+    if (LSsession :: isSubDnLSobject($this -> getType())) {
+      if (is_array(LSsession :: $ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'])) {
+        foreach(LSsession :: $ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'] as $type) {
+          if (LSsession :: loadLSobject($type)) {
+            if (isset($GLOBALS['LSobjects'][$type]['container_auto_create'])&&isset($GLOBALS['LSobjects'][$type]['container_dn'])) {
+              $dn = $GLOBALS['LSobjects'][$type]['container_dn'].','.$this -> getDn();
+              if(!LSldap :: getNewEntry($dn,$GLOBALS['LSobjects'][$type]['container_auto_create']['objectclass'],$GLOBALS['LSobjects'][$type]['container_auto_create']['attrs'],true)) {
+                LSdebug("Impossible de créer l'entrée fille : ".print_r(
+                  array(
+                    'dn' => $dn,
+                    'objectClass' => $GLOBALS['LSobjects'][$type]['container_auto_create']['objectclass'],
+                    'attrs' => $GLOBALS['LSobjects'][$type]['container_auto_create']['attrs']
+                  )
+                ,true));
+                $error=1;
+              }
+            }
+          }
+          else {
+            $error=1;
+          }
+        }
+      }
+    }
+    
+    return !$error;
+  }
+  
+  /**
+   * Retourne la valeur clef d'un objet en relation
+   * 
+   * @param[in] $object Un object de type $objectType
+   * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
+   * @param[in] $objectType Le type d'objet en relation
+   * @param[in] $value La valeur que doit avoir l'attribut :
+   *                      - soit le dn (par defaut)
+   *                      - soit la valeur [0] d'un attribut
+   * 
+   * @retval Mixed La valeur clef d'un objet en relation
+   **/
+  function getObjectKeyValueInRelation($object,$attr,$objectType,$attrValue='dn') {
+    if ((!$attr)||(!$objectType)) {
+      LSerror :: addErrorCode('LSrelations_05','getObjectKeyValueInRelation');
+      return;
+    }
+    if ($attrValue=='dn') {
+      $val = $object -> getDn();
+    }
+    else {
+      $val = $object -> getValue($attrValue);
+      $val = $val[0];
+    }
+    return $val;
+  }
+  
+  /**
+   * Retourne la liste des relations pour l'objet en fonction de sa présence 
+   * dans un des attributs
+   * 
+   * Retourne un tableau de d'objet (type : $objectType) correspondant à la 
+   * relation entre l'objet $object et les objets de type $objectType. Cette relation
+   * est établis par la présence de la valeur de référence à l'objet dans 
+   * l'attribut des objets de type $objectType.
+   * 
+   * @param[in] $object Un object de type $objectType
+   * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
+   * @param[in] $objectType Le type d'objet en relation
+   * @param[in] $value La valeur que doit avoir l'attribut :
+   *                      - soit le dn (par defaut)
+   *                      - soit la valeur [0] d'un attribut
+   * 
+   * @retval Array of $objectType Les objets en relations
+   **/
+  function listObjectsInRelation($object,$attr,$objectType,$attrValue='dn') {
+    if ((!$attr)||(!$objectType)) {
+      LSerror :: addErrorCode('LSrelations_05','listObjectsInRelation');
+      return;
+    }
+    if ($attrValue=='dn') {
+      $val = $object -> getDn();
+    }
+    else {
+      $val = $object -> getValue($attrValue);
+      $val = $val[0];
+    }
+    if ($val) {
+      $filter = $this -> getObjectFilter();
+      $filter = '(&'.$filter.'('.$attr.'='.$val.'))';
+      return $this -> listObjects($filter,LSsession :: $ldapServer['ldap_config']['basedn'],array('scope' => 'sub'));
+    }
+    return;
+  }
+
+  /**
+   * Ajoute un objet en relation dans l'attribut $attr de $this
+   * 
+   * @param[in] $object Un objet de type $objectType à ajouter
+   * @param[in] $attr L'attribut dans lequel l'objet doit être ajouté
+   * @param[in] $objectType Le type d'objet en relation
+   * @param[in] $attrValue La valeur que doit avoir l'attribut :
+   *                      - soit le dn (par defaut)
+   *                      - soit la valeur [0] d'un attribut
+   * 
+   * @retval boolean true si l'objet à été ajouté, False sinon
+   **/  
+  function addOneObjectInRelation($object,$attr,$objectType,$attrValue='dn') {
+    if ((!$attr)||(!$objectType)) {
+      LSerror :: addErrorCode('LSrelations_05','addOneObjectInRelation');
+      return;
+    }
+    if ($object instanceof $objectType) {
+      if ($this -> attrs[$attr] instanceof LSattribute) {
+        if ($attrValue=='dn') {
+          $val = $object -> getDn();
+        }
+        else {
+          $val = $object -> getValue($attrValue);
+          $val = $val[0];
+        }
+        $values = $this -> attrs[$attr] -> getValue();
+        if ($this -> attrs[$attr] -> config['multiple']) {
+          if (!is_array($values)) {
+            $updateData = array($val);
+          }
+          else if (!in_array($val,$values)) {
+            $values[]=$val;
+            $updateData = $values;
+          }
+        }
+        else {
+          if (($values[0]!=$val)&&($values!=$val)) {
+            $updateData = array($val);
+          }
+        }
+        if (isset($updateData)) {
+          return LSldap :: update($this -> getType(),$this -> getDn(), array($attr => $updateData));
+        }
+        return true;
+      }
+    }
+    return;
+  }
+  
+  /**
+   * Supprime un objet en relation dans l'attribut $attr de $this
+   * 
+   * @param[in] $object Un objet de type $objectType à supprimer
+   * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
+   * @param[in] $objectType Le type d'objet en relation
+   * @param[in] $attrValue La valeur que doit avoir l'attribut :
+   *                      - soit le dn (par defaut)
+   *                      - soit la valeur [0] d'un attribut
+   * 
+   * @retval boolean true si l'objet à été supprimé, False sinon
+   **/  
+  function deleteOneObjectInRelation($object,$attr,$objectType,$attrValue='dn') {
+    if ((!$attr)||(!$objectType)) {
+      LSerror :: addErrorCode('LSrelations_05','deleteOneObjectInRelation');
+      return;
+    }
+    if ($object instanceof $objectType) {
+      if ($this -> attrs[$attr] instanceof LSattribute) {
+        if ($attrValue=='dn') {
+          $val = $object -> getDn();
+        }
+        else {
+          $val = $object -> getValue($attrValue);
+          $val = $val[0];
+        }
+        $values = $this -> attrs[$attr] -> getValue();
+        if ((!is_array($values)) && (!empty($values))) {
+          $values = array($values);
+        }
+        if (is_array($values)) {
+          $updateData=array();
+          foreach($values as $value) {
+            if ($value!=$val) {
+              $updateData[]=$value;
+            }
+          }
+          return LSldap :: update($this -> getType(),$this -> getDn(), array($attr => $updateData));
+        }
+      }
+    }
+    return;
+  }
+  
+ /**
+  * Renome un objet en relation dans l'attribut $attr de $this
+  * 
+  * @param[in] $object Un objet de type $objectType à renomer
+  * @param[in] $oldValue string L'ancienne valeur faisant référence à l'objet
+  * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
+  * @param[in] $objectType Le type d'objet en relation
+  * @param[in] $attrValue La valeur que doit avoir l'attribut :
+  *                      - soit le dn (par defaut)
+  *                      - soit la valeur [0] d'un attribut
+  *  
+  * @retval boolean True en cas de succès, False sinon
+  */
+  function renameOneObjectInRelation($object,$oldValue,$attr,$objectType,$attrValue='dn') {
+    if ((!$attr)||(!$objectType)) {
+      LSerror :: addErrorCode('LSrelations_05','renameOneObjectInRelation');
+      return;
+    }
+    if ($object instanceof $objectType) {
+      if ($this -> attrs[$attr] instanceof LSattribute) {
+        $values = $this -> attrs[$attr] -> getValue();
+        if ((!is_array($values)) && (!empty($values))) {
+          $values = array($values);
+        }
+        if (is_array($values)) {
+          $updateData=array();
+          foreach($values as $value) {
+            if ($value!=$oldValue) {
+              $updateData[] = $value;
+            }
+            else {
+              if ($attrValue=='dn') {
+                $val = $object -> getDn();
+              }
+              else {
+                $val = $object -> getValue($attrValue);
+                $val = $val[0];
+              }
+              $updateData[] = $val;
+            }
+          }
+          return LSldap :: update($this -> getType(),$this -> getDn(), array($attr => $updateData));
+        }
+      }
+    }
+    return;
+  }
+  
+  /**
+   * Met à jour les objets du meme type que $this en relation avec l'objet $object
+   * de type $objectType en modifiant la valeur de leur attribut $attr des objets
+   * en relation
+   * 
+   * @param[in] $object Mixed Un object (type : $this -> userObjectType) : l'utilisateur
+   * @param[in] $listDns Array(string) Un tableau des DNs des objets en relation
+   * @param[in] $attr L'attribut dans lequel l'utilisateur doit apparaitre
+   * @param[in] $objectType Le type d'objet en relation
+   * @param[in] $attrValue La valeur que doit avoir l'attribut :
+   *                      - soit le dn (par defaut)
+   *                      - soit la valeur [0] d'un attribut
+   * 
+   * @retval boolean true si tout c'est bien passé, False sinon
+   **/  
+  function updateObjectsInRelation($object,$listDns,$attr,$objectType,$attrValue='dn') {
+    if ((!$attr)||(!$objectType)) {
+      LSerror :: addErrorCode('LSrelations_05','updateObjectsInRelation');
+      return;
+    }
+    $currentObjects = $this -> listObjectsInRelation($object,$attr,$objectType,$attrValue);
+    $type=$this -> getType();
+    if(is_array($currentObjects)) {
+      if (is_array($listDns)) {
+        $values=array();
+        if ($attrValue!='dn') {
+          $obj=new $objectType();
+          foreach ($listDns as $dn) {
+            $obj -> loadData($dn);
+            $val = $obj -> getValue($attrValue);
+            $values[$dn] = $val[0];
+          }
+        }
+        else {
+          foreach($listDns as $dn) {
+            $values[$dn] = $dn;
+          }
+        }
+        $dontDelete=array();
+        $dontAdd=array();
+        for ($i=0;$i<count($currentObjects);$i++) {
+          if ($attrValue=='dn') {
+            $val = $currentObjects[$i] -> getDn();
+          }
+          else {
+            $val = $currentObjects[$i] -> getValue($attrValue);
+            $val = $val[0];
+          }
+          if (in_array($val, $listDns)) {
+            $dontDelete[$i]=true;
+            $dontAdd[]=$val;
+          }
+        }
+        
+        for($i=0;$i<count($currentObjects);$i++) {
+          if ($dontDelete[$i]) {
+            continue;
+          }
+          else {
+            if (!$currentObjects[$i] -> deleteOneObjectInRelation($object,$attr,$objectType,$attrValue)) {
+              return;
+            }
+          }
+        }
+        
+        foreach($values as $dn => $val) {
+          if (in_array($val,$dontAdd)) {
+            continue;
+          }
+          else {
+            $obj = new $type();
+            if ($obj -> loadData($dn)) {
+              if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue)) {
+                return;
+              }
+            }
+            else {
+              return;
+            }
+          }
+        }
+        return true;
+      }
+    }
+    else {
+      if(!is_array($listDns)) {
+        return true;
+      }
+      foreach($listDns as $dn) {
+        $obj = new $type();
+        if ($obj -> loadData($dn)) {
+          if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue)) {
+            return;
+          }
+        }
+        else {
+          return;
+        }
+      }
+    }
+  }
+  
+  /**
+   * Ajouter une action lors d'un événement
+   * 
+   * @param[in] $event string Le nom de l'événement
+   * @param[in] $fct string Le nom de la fonction à exectuer
+   * @param[in] $param mixed Paramètre pour le lancement de la fonction
+   * @param[in] $class Nom de la classe possèdant la méthode $fct à executer
+   * 
+   * @retval void
+   */
+  function addEvent($event,$fct,$param=NULL,$class=NULL) {
+    $this -> _events[$event][] = array(
+      'function'  => $fct,
+      'param'    => $param,
+      'class'     => $class
+    );
+  }
+  
+  /**
+   * Ajouter une action sur un objet lors d'un événement
+   * 
+   * @param[in] $event string Le nom de l'événement
+   * @param[in] $obj object L'objet dont la méthode doit être executé
+   * @param[in] $meth string Le nom de la méthode
+   * @param[in] $param mixed Paramètre d'execution de la méthode
+   * 
+   * @retval void
+   */
+  function addObjectEvent($event,&$obj,$meth,$param=NULL) {
+    $this -> _objectEvents[$event][] = array(
+      'obj'  => $obj,
+      'meth'  => $meth,
+      'param'    => $param
+    );
+  }
+  
+  /**
+   * Lance les actions à executer lors d'un événement
+   * 
+   * @param[in] $event string Le nom de l'événement
+   * 
+   * @retval boolean True si tout c'est bien passé, false sinon
+   */
+  function fireEvent($event) {
+    
+    // Object event
+    $return = $this -> fireObjectEvent($event);
+    
+    // Config
+    if(isset($this -> config[$event])) {
+      if (!is_array($this -> config[$event])) {
+        $funcs = array($this -> config[$event]);
+      }
+      else {
+        $funcs = $this -> config[$event];
+      }
+      foreach($funcs as $func) {
+        if(function_exists($func)) {
+          if(!$func($this)) {
+            $return = false;
+            LSerror :: addErrorCode('LSldapObject_07',array('func' => $func,'event' => $event));
+          }
+        }
+        else {
+          $return = false;
+          LSerror :: addErrorCode('LSldapObject_06',array('func' => $func,'event' => $event));
+        }
+      }
+    }
+    
+    // Binding via addEvent
+    if (is_array($this -> _events[$event])) {
+      foreach ($this -> _events[$event] as $e) {
+        if ($e['class']) {
+          if (class_exists($e['class'])) {
+            $obj = new $e['class']();
+            if (method_exists($obj,$e['fct'])) {
+              try {
+                $obj -> $e['fct']($e['param']);
+              }
+              catch(Exception $er) {
+                LSerror :: addErrorCode('LSldapObject_10',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
+                $return = false;
+              }
+            }
+            else {
+              LSerror :: addErrorCode('LSldapObject_09',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
+              $return = false;
+            }
+          }
+          else {
+            LSerror :: addErrorCode('LSldapObject_08',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
+            $return = false;
+          }
+        }
+        else {
+          if (function_exists($e['fct'])) {
+            try {
+              $e['fct']($e['param']);
+            }
+            catch(Exception $er) {
+              LSerror :: addErrorCode('LSldapObject_27',array('func' => $e['fct'],'event' => $event));
+              $return = false;
+            }
+          }
+          else {
+            LSerror :: addErrorCode('LSldapObject_26',array('func' => $e['fct'],'event' => $event));
+            $return = false;
+          }
+        }
+      }
+    }
+    
+    // Binding via addObjectEvent
+    if (is_array($this -> _objectEvents[$event])) {
+      foreach ($this -> _objectEvents[$event] as $e) {
+        if (method_exists($e['obj'],$e['meth'])) {
+          try {
+            $e['obj'] -> $e['meth']($e['param']);
+          }
+          catch(Exception $er) {
+            LSerror :: addErrorCode('LSldapObject_29',array('meth' => $e['meth'],'event' => $event));
+            $return = false;
+          }
+        }
+        else {
+          LSerror :: addErrorCode('LSldapObject_28',array('meth' => $e['meth'],'event' => $event));
+          $return = false;
+        }
+      }
+    }
+    
+    return $return;
+  }
+  
+  /**
+   * Lance les actions à executer lors d'un événement sur l'objet lui-même
+   * 
+   * @param[in] $event string Le nom de l'événement
+   * 
+   * @retval boolean True si tout c'est bien passé, false sinon
+   */
+  function fireObjectEvent($event) {
+    switch($event) {
+      case 'after_create':
+        return $this -> afterCreate();
+      case 'after_delete':
+        return $this -> afterDelete();
+      case 'after_rename':
+        return $this -> afterRename();
+/*
+      case 'after_modify':
+        return $this -> afterModify();
+*/
+/*
+      case 'before_create':
+        return $this -> beforeCreate();
+*/
+      case 'before_delete':
+        return $this -> beforeDelete();
+      case 'before_rename':
+        return $this -> beforeRename();
+/*
+      case 'before_modify':
+        return $this -> beforeModify();
+*/
+    }
+    return true;
+  }
+  
 }
 
+/**
+ * Error Codes
+ **/
+LSerror :: defineError('LSldapObject_01',
+_("LSldapObject : Object type unknown.")
+);
+LSerror :: defineError('LSldapObject_02',
+_("LSldapObject : Update form is not defined for the object %{obj}.")
+);
+LSerror :: defineError('LSldapObject_03',
+_("LSldapObject : No form exists for the object %{obj}.")
+);
+LSerror :: defineError('LSldapObject_04',
+_("LSldapObject : The function %{func} to validate the attribute %{attr} the object %{obj} is unknow.")
+);
+LSerror :: defineError('LSldapObject_05',
+_("LSldapObject : Configuration data are missing to validate the attribute %{attr} of the object %{obj}.")
+);
+
+LSerror :: defineError('LSldapObject_06',
+_("LSldapObject : The function %{func} to be executed on the object event %{event} doesn't exist.")
+);
+LSerror :: defineError('LSldapObject_07',
+_("LSldapObject : The execution of the function %{func} to be executed on the object event %{event} failed.")
+);
+
+LSerror :: defineError('LSldapObject_08',
+_("LSldapObject : La classe %{class}, dont la méthode %{meth} doit être executée lors de l'évenement %{event} de l'objet, n'existe pas.")
+);
+LSerror :: defineError('LSldapObject_09',
+_("LSldapObject : La méthode %{meth} de la classe %{class} devant être executée lors de l'évenemt %{event} de l'objet, n'existe pas.")
+);
+LSerror :: defineError('LSldapObject_10',
+_("LSldapObject : Erreur durant l'execution de la méthode %{meth} de la classe %{class} devant être executée lors de l'évenemt %{event} de l'objet.")
+);
+
+LSerror :: defineError('LSldapObject_11',
+_("LSldapObject : Some configuration data of the object type %{obj} are missing to generate the DN of the new object.")
+);
+LSerror :: defineError('LSldapObject_12',
+_("LSldapObject : The attibute %{attr} of the object is not yet defined. Impossible to generate DN.")
+);
+LSerror :: defineError('LSldapObject_13',
+_("LSldapObject : Without DN, the object could not be changed.")
+);
+LSerror :: defineError('LSldapObject_14',
+_("LSldapObject : The attribute %{attr_depend} depending on the attribute %{attr} doesn't exist.")
+);
+LSerror :: defineError('LSldapObject_15',
+_("LSldapObject : Error during deleting the object %{objectname}.")
+);
+
+LSerror :: defineError('LSldapObject_16',
+_("LSldapObject : Error during actions to be executed before renaming the objet.")
+);
+LSerror :: defineError('LSldapObject_17',
+_("LSldapObject : Error during actions to be executed after renaming the objet.")
+);
+
+LSerror :: defineError('LSldapObject_18',
+_("LSldapObject : Error during actions to be executed before deleting the objet.")
+);
+LSerror :: defineError('LSldapObject_19',
+_("LSldapObject : Error during actions to be executed after deleting the objet.")
+);
+
+// 20 : not used
+
+LSerror :: defineError('LSldapObject_21',
+_("LSldapObject : Error during the actions to be executed after creating the object. It was created anyway.")
+);
+
+LSerror :: defineError('LSldapObject_22',
+_("LSldapObject : The function %{func} to be generated before creating the object doesn't exist.")
+);
+LSerror :: defineError('LSldapObject_23',
+_("LSldapObject : Error during the execution of the function %{func} to be generated after deleting the object.")
+);
+LSerror :: defineError('LSldapObject_24',
+_("LSldapObject : The function %{func} to be generated after deleting the object doesn't exist.")
+);
+LSerror :: defineError('LSldapObject_25',
+_("LSldapObject : Error during the execution of the function %{func} to be generated after creating the object.")
+);
+
+LSerror :: defineError('LSldapObject_26',
+_("LSldapObject : La function %{func}, devant être executée lors de l'évenement %{event} de l'objet, n'existe pas.")
+);
+LSerror :: defineError('LSldapObject_27',
+_("LSldapObject : Erreur durant l'execution de la function %{func} lors de l'évenement %{event} de l'objet.")
+);
+
+LSerror :: defineError('LSldapObject_28',
+_("LSldapObject : La méthode %{meth}, devant être executée lors de l'évenement %{event} de l'objet, n'existe pas.")
+);
+LSerror :: defineError('LSldapObject_29',
+_("LSldapObject : Erreur durant l'execution de la méthode %{meth} lors de l'évenement %{event} de l'objet.")
+);
+
 ?>