- LSldapObject :
[ldapsaisie.git] / trunk / 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 $GLOBALS['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 $other_values=array();
41   var $submitError=true;
42   var $_whoami=NULL;
43   var $_subDn_value=NULL;
44   var $_relationsCache=array();
45   
46   /**
47    * Constructeur
48    *
49    * Cette methode construit l'objet et définis la configuration.
50    * Elle lance la construction du tableau d'attributs représentés par un objet LSattribute.
51    *
52    * @author Benjamin Renard <brenard@easter-eggs.com>
53    *
54    * @param[in] $type_name [<b>required</b>] string Le nom du type de l'objet
55    * @param[in] $config array La configuration de l'objet
56    *
57    * @retval boolean true si l'objet a été construit, false sinon.
58    */ 
59   function LSldapObject($type_name,$config='auto') {
60     $this -> type_name = $type_name;
61     $this -> config = $config;
62     if($config=='auto') {
63       if(isset($GLOBALS['LSobjects'][$type_name])) {
64         $this -> config = $GLOBALS['LSobjects'][$type_name];
65       }
66       else {
67         $GLOBALS['LSerror'] -> addErrorCode(21);
68         return;
69       }
70     }
71     foreach($this -> config['attrs'] as $attr_name => $attr_config) {
72       if(!$this -> attrs[$attr_name]=new LSattribute($attr_name,$attr_config,$this)) {
73         return;
74       }
75     }
76     return true;
77   }
78   
79   /**
80    * Charge les données de l'objet
81    *
82    * Cette methode définis le DN de l'objet et charge les valeurs de attributs de l'objet
83    * à partir de l'annuaire.
84    *
85    * @author Benjamin Renard <brenard@easter-eggs.com>
86    *
87    * @param[in] $dn string Le DN de l'objet.
88    *
89    * @retval boolean true si la chargement a réussi, false sinon.
90    */ 
91   function loadData($dn) {
92     $this -> dn = $dn;
93     $data = $GLOBALS['LSldap'] -> getAttrs($dn);
94     if(!empty($data)) {
95       foreach($this -> attrs as $attr_name => $attr) {
96         if(!$this -> attrs[$attr_name] -> loadData($data[$attr_name]))
97           return;
98       }
99       return true;
100     }
101     return;
102   }
103   
104   /**
105    * Recharge les données de l'objet
106    *
107    * @author Benjamin Renard <brenard@easter-eggs.com>
108    *
109    * @retval boolean true si la rechargement a réussi, false sinon.
110    */ 
111   function reloadData() {
112     $data = $GLOBALS['LSldap'] -> getAttrs($this -> dn);
113     foreach($this -> attrs as $attr_name => $attr) {
114       if(!$this -> attrs[$attr_name] -> reloadData($data[$attr_name]))
115         return;
116     }
117     return true;
118   }
119   
120   /**
121    * Retourne le format d'affichage de l'objet
122    *
123    * @author Benjamin Renard <brenard@easter-eggs.com>
124    *
125    * @retval string Format d'affichage de l'objet.
126    */ 
127   function getDisplayAttributes() {
128     return $this -> config['select_display_attrs'];
129   }
130   
131   /**
132    * Retourne la valeur descriptive d'affichage de l'objet
133    * 
134    * Cette fonction retourne la valeur descriptive d'affichage de l'objet en fonction
135    * du format défini dans la configuration de l'objet ou spécifié en paramètre.
136    *
137    * @author Benjamin Renard <brenard@easter-eggs.com>
138    *
139    * @param[in] $spe [<i>optionnel</i>] string Format d'affichage de l'objet
140    * @param[in] $full [<i>optionnel</i>] boolean True pour afficher en plus le
141    *                                             subDnName
142    *
143    * @retval string Valeur descriptive d'affichage de l'objet
144    */ 
145   function getDisplayValue($spe='',$full=false) {
146     if ($spe=='') {
147       $spe = $this -> getDisplayAttributes();
148     }
149     $val = $this -> getFData($spe,&$this -> attrs,'getDisplayValue');
150     if ($GLOBALS['LSsession'] -> haveSubDn() && $full) {
151       $val.=' ('.$this -> getSubDnName().')';
152     }
153     return $val;
154   }
155   
156   /**
157    * Chaine formatée
158    * 
159    * Cette fonction retourne la valeur d'une chaine formatée en prennant les valeurs
160    * de l'objet.
161    *
162    * @author Benjamin Renard <brenard@easter-eggs.com>
163    *
164    * @param[in] $format string Format de la chaine
165    *
166    * @retval string Valeur d'une chaine formatée
167    */ 
168   function getFData($format) {
169     $format=getFData($format,$this,'getValue');
170     return $format;
171   }
172   
173   /**
174    * Construit un formulaire de l'objet
175    * 
176    * Cette méthode construit un formulaire LSform à partir de la configuration de l'objet
177    * et de chaque attribut.
178    *
179    * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
180    * @param[in] $load DN d'un objet similaire dont la valeur des attribut doit être chargé dans le formulaire.
181    *
182    * @author Benjamin Renard <brenard@easter-eggs.com>
183    *
184    * @retval LSform Le formulaire crée
185    */ 
186   function getForm($idForm,$load=NULL) {
187     $GLOBALS['LSsession'] -> loadLSclass('LSform');
188     $LSform = new LSform($this,$idForm);
189     $this -> forms[$idForm] = array($LSform,$load);
190     
191     if ($load) {
192       $type = $this -> getType();
193       $loadObject = new $type();
194       if (!$loadObject -> loadData($load)) {
195         $load=false;
196       }
197     }
198     
199     if ($load) {
200       foreach($this -> attrs as $attr_name => $attr) {
201         if(!$this -> attrs[$attr_name] -> addToForm($LSform,$idForm,$this,$loadObject -> getValue($attr_name))) {
202           $LSform -> can_validate = false;
203         }
204       }
205     }
206     else {
207       foreach($this -> attrs as $attr_name => $attr) {
208         if(!$this -> attrs[$attr_name] -> addToForm($LSform,$idForm,$this)) {
209           $LSform -> can_validate = false;
210         }
211       }      
212     }
213     return $LSform;
214   }
215   
216   /**
217    * Construit un formulaire de l'objet
218    * 
219    * Cette méthode construit un formulaire LSform à partir de la configuration de l'objet
220    * et de chaque attribut.
221    *
222    * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
223    * @param[in] $config Configuration spécifique pour le formulaire
224    *
225    * @author Benjamin Renard <brenard@easter-eggs.com>
226    *
227    * @retval LSform Le formulaire crée
228    */ 
229   function getView() {
230     $GLOBALS['LSsession'] -> loadLSclass('LSform');
231     $this -> view = new LSform($this,'view');
232     foreach($this -> attrs as $attr_name => $attr) {
233       $this -> attrs[$attr_name] -> addToView($this -> view);
234     }
235     $this -> view -> can_validate = false;
236     return $this -> view;
237   }  
238   
239   /**
240    * Rafraichis le formulaire de l'objet
241    * 
242    * Cette méthode recharge les données d'un formulaire LSform.
243    *
244    * @param[in] $idForm [<b>required</b>] Identifiant du formulaire a créer
245    *
246    * @author Benjamin Renard <brenard@easter-eggs.com>
247    *
248    * @retval boolean true sile formulaire a été rafraichis, false sinon
249    */ 
250   function refreshForm($idForm) {
251     $LSform = $this -> forms[$idForm][0];
252     foreach($this -> attrs as $attr_name => $attr) {
253       if(!$this -> attrs[$attr_name] -> refreshForm($LSform,$idForm)) {
254         return;
255       }
256     }
257     return true;
258   }
259   
260   /**
261    * Met à jour les données de l'objet et de l'entré de l'annuaire
262    * 
263    * Met à jour les données de l'objet à partir d'un retour d'un formulaire.
264    *
265    * @param[in] $idForm Identifiant du formulaire d'origine
266    *
267    * @author Benjamin Renard <brenard@easter-eggs.com>
268    *
269    * @retval boolean true si la mise à jour a réussi, false sinon
270    *
271    * @see validateAttrsData()
272    * @see submitChange()
273    */ 
274   function updateData($idForm=NULL) {
275     if($idForm!=NULL) {
276       if(isset($this -> forms[$idForm]))
277         $LSform = $this -> forms[$idForm][0];
278       else {
279         $GLOBALS['LSerror'] -> addErrorCode(22,$this -> getType());
280         return;
281       }
282     }
283     else {
284       if(count($this -> forms) > 0) {
285         reset($this -> forms);
286         $idForm = key($this -> forms);
287         $LSform = current($this -> forms);
288         $config = $LSform[1];
289         $LSform = $LSform[0];
290       }
291       else {
292         $GLOBALS['LSerror'] -> addErrorCode(23,$this -> getType());
293         return;
294       }
295     }
296     $new_data = $LSform -> exportValues();
297     if(!is_array($new_data)) {
298       return;
299     }
300     foreach($new_data as $attr_name => $attr_val) {
301       if(isset($this -> attrs[$attr_name])) {
302         $this -> attrs[$attr_name] -> setUpdateData($attr_val);
303       }
304     }
305     if($this -> validateAttrsData($idForm)) {
306       LSdebug("les données sont validées");
307       if(isset($this -> config['before_modify'])) {
308         if(function_exists($this -> config['before_modify'])) {
309           if(!$this -> config['before_modify']($this)) {
310             $GLOBALS['LSerror'] -> addErrorCode(28,$this -> config['before_modify']);
311             return;
312           }
313         }
314         else {
315           $GLOBALS['LSerror'] -> addErrorCode(27,$this -> config['before_modify']);
316           return;
317         }
318       }
319       if ($this -> submitChange($idForm)) {
320         LSdebug('Les modifications sont submitées');
321         $this -> submitError = false;
322         $this -> reloadData();
323         $this -> refreshForm($idForm);
324       }
325       else {
326         return;
327       }
328       if((isset($this -> config['after_modify']))&&(!$this -> submitError)) {
329         if(function_exists($this -> config['after_modify'])) {
330           if(!$this -> config['after_modify']($this)) {
331             $GLOBALS['LSerror'] -> addErrorCode(30,$this -> config['after_modify']);
332             return;
333           }
334         }
335         else {
336           $GLOBALS['LSerror'] -> addErrorCode(29,$this -> config['after_modify']);
337           return;
338         }
339       }
340       return true;
341     }
342     else {
343       return;
344     }
345   }
346   
347   /**
348    * Valide les données retournées par un formulaire
349    *
350    * @param[in] $idForm Identifiant du formulaire d'origine
351    *
352    * @author Benjamin Renard <brenard@easter-eggs.com>
353    *
354    * @retval boolean true si les données sont valides, false sinon
355    */ 
356   function validateAttrsData($idForm) {
357     $LSform=$this -> forms[$idForm][0];
358     foreach($this -> attrs as $attr) {
359       $attr_values = $attr -> getValue();
360       if (!$attr -> isValidate()) {
361         if($attr -> isUpdate()) {
362           if (!$this -> validateAttrData($LSform, $attr)) {
363             return;
364           }
365         }
366         else if( (empty($attr_values)) && ($attr -> isRequired()) ) { 
367           if ( $attr -> canBeGenerated()) {
368             if ($attr -> generateValue()) {
369               if (!$this -> validateAttrData($LSform, $attr)) {
370                 $GLOBALS['LSerror'] -> addErrorCode(48,$attr -> getLabel());
371                 return;
372               }
373             }
374             else {
375               $GLOBALS['LSerror'] -> addErrorCode(47,$attr -> getLabel());
376               return;
377             }
378           }
379           else {
380             $GLOBALS['LSerror'] -> addErrorCode(46,$attr -> getLabel());
381             return;
382           }
383
384         }
385       }
386     }
387     return true;
388   }
389
390    /**
391    * Valide les données d'un attribut
392    *
393    * @param[in] $LSForm Formulaire d'origine
394    * @param[in] &$attr Attribut à valider
395    *
396    * @author Benjamin Renard <brenard@easter-eggs.com>
397    *
398    * @retval boolean true si les données sont valides, false sinon
399    */
400   function validateAttrData(&$LSform,&$attr) {
401     $vconfig=$attr -> getValidateConfig();
402
403     $data=$attr -> getUpdateData();
404     if(!is_array($data)) {
405       $data=array($data);
406     }
407
408     // Validation des valeurs de l'attribut
409     if(is_array($vconfig)) {
410       foreach($vconfig as $test) {
411         // Définition du basedn par défaut
412         if (!isset($test['basedn'])) {
413           $test['basedn']=$GLOBALS['LSsession']->topDn;
414         }
415
416         // Définition du message d'erreur
417         if (!empty($test['msg'])) {
418           $msg_error=getFData($test['msg'],$this,'getValue');
419         }
420         else {
421           $msg_error=getFData(_("L'attribut %{attr} n'est pas valide."),$attr -> getLabel());
422         }
423         foreach($data as $val) {
424           // validation par check LDAP
425           if((isset($test['filter'])||isset($test['basedn']))&&(isset($test['result']))) {
426             $sparams=(isset($test['scope']))?array('scope' => $test['scope']):array();
427             $this -> other_values['val']=$val;
428             $sfilter_user=(isset($test['basedn']))?getFData($test['filter'],$this,'getValue'):NULL;
429             if(isset($test['object_type'])) {
430               $test_obj = new $test['object_type']();
431               $sfilter=$test_obj->getObjectFilter();
432               $sfilter='(&'.$sfilter;
433               if($sfilter_user[0]=='(') {
434                 $sfilter=$sfilter.$sfilter_user.')';
435               }
436               else {
437                 $sfilter=$sfilter.'('.$sfilter_user.'))';
438               }
439             }
440             else {
441               $sfilter=$sfilter_user;
442             }
443             $sbasedn=(isset($test['basedn']))?getFData($test['basedn'],$this,'getValue'):NULL;
444             $ret=$GLOBALS['LSldap'] -> getNumberResult ($sfilter,$sbasedn,$sparams);
445             if($test['result']==0) {
446               if($ret!=0) {
447                 $LSform -> setElementError($attr,$msg_error);
448                 return;
449               }
450             }
451             else {
452               if($ret<0) {
453                 $LSform -> setElementError($attr,$msg_error);
454                 return;
455               }
456             }
457           }
458           // Validation par fonction externe
459           else if(isset($test['function'])) {
460             if (function_exists($test['function'])) {
461               if(!$test['function']($this)) {
462                 $LSform -> setElementError($attr,$msg_error);
463               return;
464               }
465             }
466             else {
467               $GLOBALS['LSerror'] -> addErrorCode(24,array('attr' => $attr->name,'obj' => $this->getType(),'func' => $test['function']));
468               return;
469             }
470           }
471           else {
472             $GLOBALS['LSerror'] -> addErrorCode(25,array('attr' => $attr->name,'obj' => $this->getType()));
473             return;
474           }
475         }
476       }
477     }
478     // Génération des valeurs des attributs dépendants
479     $dependsAttrs=$attr->getDependsAttrs();
480     if (!empty($dependsAttrs)) {
481       foreach($dependsAttrs as $dependAttr) {
482         if(!isset($this -> attrs[$dependAttr])){
483           $GLOBALS['LSerror'] -> addErrorCode(34,array('attr_depend' => $dependAttr, 'attr' => $attr -> getLabel()));
484           continue;
485         }
486         if($this -> attrs[$dependAttr] -> canBeGenerated()) {
487           if (!$this -> attrs[$dependAttr] -> generateValue()) {
488             $GLOBALS['LSerror'] -> addErrorCode(47,$this -> attrs[$dependAttr] -> getLabel());
489             return;
490           }
491         }
492         else {
493           $GLOBALS['LSerror'] -> addErrorCode(46,$this -> attrs[$dependAttr] -> getLabel());
494           return;
495         }
496       }
497     }
498
499     $attr -> validate();
500     unset($this -> other_values['val']);
501     return true;
502   }
503
504   /**
505    * Met à jour les données modifiés dans l'annuaire
506    *
507    * @param[in] $idForm Identifiant du formulaire d'origine
508    *
509    * @author Benjamin Renard <brenard@easter-eggs.com>
510    *
511    * @retval boolean true si la mise à jour a réussi, false sinon
512    */ 
513   function submitChange($idForm) {
514     $submitData=array();
515     $new = $this -> isNew();
516     foreach($this -> attrs as $attr) {
517       if(($attr -> isUpdate())&&($attr -> isValidate())) {
518         if(($attr -> name == $this -> config['rdn'])&&(!$new)) {
519           $new = true;
520           LSdebug('Rename');
521           if (!$this -> beforeRename()) {
522             $GLOBALS['LSerror'] -> addErrorCode(36);
523             return;
524           }
525           $oldDn = $this -> getDn();
526           $this -> dn = false;
527           $newDn = $this -> getDn();
528           if ($newDn) {
529             if (!$GLOBALS['LSldap'] -> move($oldDn,$newDn)) {
530               return;
531             }
532             $this -> dn = $newDn;
533             if (!$this -> afterRename($oldDn,$newDn)) {
534               $GLOBALS['LSerror'] -> addErrorCode(37);
535               return;
536             }
537           }
538           else {
539             return;
540           }
541         }
542         else {
543           $submitData[$attr -> name] = $attr -> getUpdateData();
544         }
545       }
546     }
547     if(!empty($submitData)) {
548       $dn=$this -> getDn();
549       if($dn) {
550         $this -> dn=$dn;
551         LSdebug($submitData);
552         if (!$GLOBALS['LSldap'] -> update($this -> getType(),$dn, $submitData)) {
553           return;
554         }
555         if ($new) {
556           if (!$this -> afterCreate()) {
557             $GLOBALS['LSerror'] -> addErrorCode(301);
558             return;
559           }
560         }
561         return true;
562       }
563       else {
564         $GLOBALS['LSerror'] -> addErrorCode(33);
565         return;
566       }
567     }
568     else {
569       return true;
570     }
571   }
572   
573   /**
574    * Retourne les informations issus d'un DN
575    *
576    * @param[in] $dn Un DN.
577    *
578    * @author Benjamin Renard <brenard@easter-eggs.com>
579    *
580    * @retval array Tableau : 
581    *                  - [0] : le premier paramètre
582    *                  - [1] : les paramètres suivants
583    */ 
584   function getDnInfos($dn) {
585     $infos=ldap_explode_dn($dn,0);
586     if(!$infos)
587       return;
588     $first=true;
589     for($i=1;$i<$infos['count'];$i++)
590       if($first) {
591         $basedn.=$infos[$i];
592         $first=false;
593       }
594       else
595         $basedn.=','.$infos[$i];
596     return array($infos[0],$basedn);
597   }
598   
599   /**
600    * Retourne le filtre correpondants aux objetcClass de l'objet
601    *
602    * @author Benjamin Renard <brenard@easter-eggs.com>
603    *
604    * @retval string le filtre ldap correspondant au type de l'objet
605    */ 
606   function getObjectFilter() {
607     if(!isset($this -> config['objectclass'])) return;
608     foreach ($this -> config['objectclass'] as $class)
609       $filter.='(objectClass='.$class.')';
610     return $filter;
611   }
612   
613   /**
614    * Retourne une liste d'objet du même type.
615    *
616    * Effectue une recherche en fonction des paramètres passé et retourne un
617    * tableau d'objet correspond au resultat de la recherche.
618    *
619    * @author Benjamin Renard <brenard@easter-eggs.com>
620    *
621    * @param[in] $filter array (ou string) Filtre de recherche Ldap / Tableau de filtres de recherche
622    * @param[in] $basedn string DN de base pour la recherche
623    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
624    *
625    * @retval array Tableau d'objets correspondant au resultat de la recherche
626    */ 
627   function listObjects($filter='',$basedn=NULL,$params=array()) {
628     $retInfos=array();
629     $attrs=false;
630     $check_final_dn=false;
631
632     if(!is_array($filter))
633       $filter=array(array('filter' => $filter));
634     
635     $nbFilter=count($filter);
636
637     for($i=0;$i<$nbFilter;$i++) {
638       $new_attrs=array();
639       // Défintion des paramètres de base pour la recherche
640       $sbasedn=$basedn;
641       $sparams=$params;
642       $ret=array();
643       if (isset($filter[$i]['scope']))
644         $sparams["scope"]=$filter[$i]['scope'];
645       
646       // Definition des critères de recherche correspondant au type d'objet à lister
647       if(($nbFilter==1)||(!isset($filter[$i]['attr']))) {
648         // Filtre sur l'objet souhaité
649         $sfilter='(&';
650         $sfilter.=$this -> getObjectFilter();
651         $sfilter_end=')';
652         $check_final_dn=true;
653       }
654       // Initialisation des critères d'une recherche intermédiaire
655       else {
656         if(isset($filter[$i]['object_type'])) {
657           $obj_tmp=new $filter[$i]['object_type']();
658           $obj_filter=$obj_tmp->getObjectFilter();
659           $sfilter='(&'.$obj_filter;
660           $sfilter_end=')';
661         }
662         else {
663           $sfilter='';
664           $sfilter_end='';
665         }
666         if(isset($filter[$i]['scope'])) {
667           $sparams['scope']=$filter[$i]['scope'];
668         }
669         if(isset($filter[$i]['basedn'])) {
670           $sbasedn=$filter[$i]['basedn'];
671         }
672       }
673       // Dans le cas d'une recherche intermédiaire ou finale
674       if($attrs!=false) {
675         // Initialisation des variables
676         $ret_gen=array();
677         $new_attrs=array();
678         
679         // Pour tout les attributs retournés
680         for($ii=0;$ii<count($attrs);$ii++) {
681           $sfilter_for='';
682           // Définition du filtre de recherche à partir des paramètres utilisateurs et
683           // des paramètres de recherche de l'objet à listé (dans le cas d'une recherche finale
684           if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
685             $sfilter_user=getFData($filter[$i]['filter'],$attrs[$ii]);
686             if($sfilter_user[0]=='(')
687               $sfilter_for=$sfilter.$sfilter_user;
688             else
689               $sfilter_for=$sfilter.'('.$sfilter_user.')';
690           }
691           else {
692             $sfilter_for=$sfilter;
693           }
694           
695           if(isset($filter[$i]['basedn'])) {
696             $sbasedn=getFData($filter[$i]['basedn'],$attrs[$ii]);
697             if ((!$this -> isCompatibleDNs($sbasedn,$basedn))&&($check_final_dn)) continue;
698           }
699         
700           // Vérification de la compatibilité du basedn de la recherche et du basedn générale
701           // Finalisation du filtre
702           $sfilter_for.=$sfilter_end;
703         
704         
705           // Execution de la recherche
706           $ret=$GLOBALS['LSldap'] -> search ($sfilter_for,$sbasedn,$sparams);
707           
708           // Si il y un retour
709           if(isset($ret[0])) {
710             // si il ya une suite (recherche intermédiaire)
711             if($filter[$i]['attr']){
712               for($iii=0;$iii<count($ret);$iii++) {
713                 if(isset($ret[$iii]['attrs'][$filter[$i]['attr']])) {
714                   // cas de valeur multiple
715                   if(is_array($ret[$iii]['attrs'][$filter[$i]['attr']])) {
716                     foreach($ret[$iii]['attrs'][$filter[$i]['attr']] as $val_attr) {
717                       $new_attrs[]=$val_attr;
718                     }
719                   }
720                   // cas de valeur unique
721                   else {
722                     $new_attrs[]=$ret[$iii]['attrs'][$filter[$i]['attr']];
723                   }
724                 }
725               }
726             }
727             else {
728               // vérification de la compatibilité de la compatibilité du DN resultant
729               // et du basedn de recherche 
730               if (!$this -> isCompatibleDNs($ret[0]['dn'],$basedn))
731                 continue;
732               // ajout du DN au resultat finale
733               $ret_gen[]=$ret[0]['dn'];
734             }
735           }
736         }
737         // cas du dernier filtre
738         if(!empty($ret_gen)) {
739           // on quitte la boucle des filtres de la conf
740           $ret=$ret_gen;
741           break;
742         }
743         // dans le cas d'une suite prévu mais d'un retour nul de la précédente recherche
744         else if(empty($new_attrs)) {
745             // retour vide et arrêt de la recherche
746             $ret=array();
747             break;
748         }
749         else {
750           $attrs=$new_attrs;
751         }
752       }
753       // Dans le cas de la recherche initiale
754       else {
755         // Déclaration du filtre de recherche
756         if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
757           if($filter[$i]['filter'][0]=='(') {
758             $sfilter.=$filter[$i]['filter'];
759           }
760           else {
761             $sfilter.='('.$filter[$i]['filter'].')';
762           }
763         }
764         // fermeture du filtre
765         $sfilter.=$sfilter_end;
766         
767         // Lancement de la recherche
768         $ret=$GLOBALS['LSldap'] -> search ($sfilter,$sbasedn,$sparams);
769         
770         //Si filtre multiple => on recupère une liste d'attributs
771         if(isset($filter[$i]['attr'])) {
772           for($ii=0;$ii<count($ret);$ii++) {
773             if(isset($ret[$ii]['attrs'][$filter[$i]['attr']])) {
774               // cas de valeur multiple
775               if(is_array($ret[$ii]['attrs'][$filter[$i]['attr']])) {
776                 foreach($ret[$ii]['attrs'][$filter[$i]['attr']] as $val_attr) {
777                   $attrs[]=$val_attr;
778                 }
779               }
780               // cas de valeur unique
781               else {
782                 $attrs[]=$ret[$ii]['attrs'][$filter[$i]['attr']];
783               }
784             }
785           }
786           
787           // Si aucunne valeur n'est retournées
788           if(empty($attrs)){
789             // arrêt et retour à zéro
790             $ret=array();
791             break;
792           }
793         }
794         // Si recherche unique
795         else {
796           // préparation du retour finale
797           if (is_array($ret)) {
798             $ret_final=array();
799             foreach($ret as $obj)
800               $ret_final[]=$obj['dn'];
801             $ret=$ret_final;
802           }
803           else {
804             $ret=array();
805           }
806           break;
807         }
808       }
809     }
810     
811     // Création d'un tableau d'objet correspondant au valeur retourné
812     for($i=0;$i<count($ret);$i++) {
813       $retInfos[$i] = new $this -> type_name($this -> config);
814       $retInfos[$i] -> loadData($ret[$i]);
815     }
816     
817     return $retInfos;
818   }
819  
820  
821   /**
822    * Recherche un objet à partir de la valeur exact de son RDN
823    * 
824    * @author Benjamin Renard <brenard@easter-eggs.com>
825    * 
826    * @param[in] $name string Valeur de son RDN
827    * @param[in] $basedn string Le DN de base de la recherche
828    * 
829    * @retval array Tableau d'objets correspondant au resultat de la recherche
830    */
831   function searchObject($name,$basedn=NULL) {
832     $filter = $this -> config['rdn'].'='.$name; 
833     return $this -> listObjects($filter,$basedn); 
834   }
835
836   /**
837    * Retourne une valeur de l'objet
838    *
839    * Retourne une valeur en fonction du paramètre. Si la valeur est inconnue, la valeur retourné est ' '.
840    * tableau d'objet correspond au resultat de la recherche.
841    *
842    * Valeurs possibles :
843    * - 'dn' ou '%{dn} : DN de l'objet
844    * - [nom d'un attribut] : valeur de l'attribut
845    * - [clef de $this -> other_values] : valeur de $this -> other_values
846    *
847    * @author Benjamin Renard <brenard@easter-eggs.com>
848    *
849    * @param[in] $val string Le nom de la valeur demandée
850    *
851    * @retval mixed la valeur demandé ou ' ' si celle-ci est inconnue.
852    */ 
853   function getValue($val) {
854     if(($val=='dn')||($val=='%{dn}')) {
855       return $this -> dn;
856     }
857     else if(($val=='rdn')||($val=='%{rdn}')) {
858       return $this -> attrs[ $this -> config['rdn'] ] -> getValue();
859     }
860     else if(($val=='subDn')||($val=='%{subDn}')) {
861       return $this -> getSubDnValue();
862     }
863     else if(($val=='subDnName')||($val=='%{subDnName}')) {
864       return $this -> getSubDnName();
865     }
866     else if(isset($this ->  attrs[$val])){
867       if (method_exists($this ->  attrs[$val],'getValue'))
868         return $this -> attrs[$val] -> getValue();
869       else
870         return ' ';
871     }
872     else if(isset($this -> other_values[$val])){
873       return $this -> other_values[$val];
874     }
875     else {
876       return ' ';
877     }
878   }
879
880   /**
881    * Retourn un tableau pour un select d'un objet du même type
882    * 
883    * @author Benjamin Renard <brenard@easter-eggs.com>
884    *
885    * @retval array('dn' => 'display')
886    */
887   function getSelectArray($topDn=NULL) {
888     $list = $this -> listObjects(NULL,$topDn);
889     $return=array();
890     foreach($list as $object) {
891       $return[$object -> getDn()] = $object -> getDisplayValue(); 
892     }
893     return $return;
894   }
895
896   /**
897    * Retourne le DN de l'objet
898    *
899    * Cette methode retourne le DN de l'objet. Si celui-ci n'existe pas, il le construit à partir de la 
900    * configuration de l'objet et la valeur de son attribut rdn.
901    *
902    * @author Benjamin Renard <brenard@easter-eggs.com>
903    *
904    * @retval string Le DN de l'objet
905    */   
906   function getDn() {
907     if($this -> dn) {
908       return $this -> dn;
909     }
910     else {
911       $rdn_attr=$this -> config['rdn'];
912       if( (isset($this -> config['rdn'])) && (isset($this -> attrs[$rdn_attr])) && (isset($this -> config['container_dn'])) && (isset($GLOBALS['LSsession']->topDn)) ) {
913         $rdn_val=$this -> attrs[$rdn_attr] -> getUpdateData();
914         if (!empty($rdn_val)) {
915           return $rdn_attr.'='.$rdn_val[0].','.$this -> config['container_dn'].','.$GLOBALS['LSsession']->topDn;
916         }
917         else {
918           $GLOBALS['LSerror'] -> addErrorCode(32,$this -> config['rdn']);
919           return;
920         }
921       }
922       else {
923         $GLOBALS['LSerror'] -> addErrorCode(31,$this -> getType());
924         return;
925       }
926     }
927   }
928
929   /**
930    * Retourne le type de l'objet
931    *
932    * @author Benjamin Renard <brenard@easter-eggs.com>
933    * 
934    * @retval string Le type de l'objet ($this -> type_name)
935    */
936   function getType() {
937     return $this -> type_name;
938   }
939   
940   /**
941    * Retourne qui est l'utilisateur par rapport à cet object
942    *
943    * @author Benjamin Renard <brenard@easter-eggs.com>
944    * 
945    * @retval string 'admin'/'self'/'user' pour Admin , l'utilisateur lui même ou un simple utilisateur
946    */
947   function whoami() {
948     if (!$this -> _whoami)
949       $this -> _whoami = $GLOBALS['LSsession'] -> whoami($this -> dn);
950     return $this -> _whoami;
951   }
952   
953   /**
954    * Retourne le label de l'objet
955    *
956    * @author Benjamin Renard <brenard@easter-eggs.com>
957    * 
958    * @retval string Le label de l'objet ($this -> config['label'])
959    */
960   function getLabel() {
961     return $this -> config['label'];
962   }
963   
964   
965   /**
966    * Supprime l'objet dans l'annuaire
967    *
968    * @author Benjamin Renard <brenard@easter-eggs.com>
969    * 
970    * @retval boolean True si l'objet à été supprimé, false sinon
971    */
972   function remove() {
973     if ($this -> beforeDelete()) {
974       if ($GLOBALS['LSldap'] -> remove($this -> getDn())) {
975         if ($this -> afterDelete()) {
976           return true;
977         }
978         $GLOBALS['LSerror'] -> addErrorCode(39);
979       }
980     }
981     else {
982       $GLOBALS['LSerror'] -> addErrorCode(38);
983     }
984     return;
985   }
986   
987   /**
988    * L'objet est-il nouveau
989    * 
990    * @author Benjamin Renard <brenard@easter-eggs.com>
991    * 
992    * @retval boolean True si l'objet est nouveau, false sinon
993    */
994   function isNew() {
995     return (!$this -> dn);
996   }
997
998   /**
999    * Retourne la valeur (DN) du subDn de l'objet  
1000    * 
1001    * @return string La valeur du subDn de l'object
1002    */
1003   function getSubDnValue() {
1004     if ($this -> _subDn_value) {
1005       return $this -> _subDn_value;
1006     }
1007     $dn = $this -> getValue('dn');
1008     $subDn_value='';
1009     $subDnLdapServer = $GLOBALS['LSsession'] -> getSortSubDnLdapServer();
1010     foreach ($subDnLdapServer as $subDn => $subDn_name) {
1011       if (isCompatibleDNs($subDn,$dn)&&($subDn!=$dn)) {
1012         $subDn_value=$subDn;
1013         break;
1014       }
1015     }
1016     $this -> _subDn_value = $subDn_value;
1017     return $subDn_value;
1018   }
1019
1020   /**
1021    * Retourne la nom du subDn de l'objet  
1022    * 
1023    * @return string Le nom du subDn de l'object
1024    */
1025   function getSubDnName() {
1026     $subDnLdapServer = $GLOBALS['LSsession'] -> getSortSubDnLdapServer();
1027     return $subDnLdapServer[$this -> getSubDnValue()];
1028   }
1029   
1030   /**
1031    * Methode créant la liste des objets en relations avec l'objet courant et qui
1032    * la met en cache ($this -> _relationsCache)
1033    * 
1034    * @retval True en cas de cas ce succès, False sinon.
1035    */
1036   function updateRelationsCache() {
1037     $this -> _relationsCache=array();
1038     if (is_array($this->config['relations'])) {
1039       foreach($this->config['relations'] as $relation_name => $relation_conf) {
1040         if ( isset($relation_conf['list_function']) ) {
1041           if ($GLOBALS['LSsession'] -> loadLSobject($relation_conf['LSobject'])) {
1042             $obj = new $relation_conf['LSobject']();
1043             if ((method_exists($obj,$relation_conf['list_function']))&&(method_exists($obj,$relation_conf['getkeyvalue_function']))) {
1044               $list = $obj -> $relation_conf['list_function']($this);
1045               if (is_array($list)) {
1046                 // Key Value
1047                 $key = $obj -> $relation_conf['getkeyvalue_function']($this);
1048                 
1049                 $this -> _relationsCache[$relation_name] = array(
1050                   'list' => $list,
1051                   'keyvalue' => $key
1052                 );
1053               }
1054               else {
1055                 return;
1056               }
1057             }
1058             else {
1059               return;
1060             }
1061           }
1062           else {
1063             return;
1064           }
1065         }
1066       }
1067     }
1068     return true;
1069   }
1070   
1071   /**
1072    * Methode executant les actions nécéssaires avant le changement du DN de
1073    * l'objet.
1074    * 
1075    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1076    * pour les objets plus complexe.
1077    * 
1078    * @retval True en cas de cas ce succès, False sinon.
1079    */
1080   function beforeRename() {
1081     return $this -> updateRelationsCache();
1082   }
1083   
1084   /**
1085    * Methode executant les actions nécéssaires après le changement du DN de
1086    * l'objet.
1087    * 
1088    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1089    * pour les objets plus complexe.
1090    * 
1091    * @param[in] $oldDn string L'ancien DN de l'objet
1092    * @param[in] $newDn string Le nouveau DN de l'objet
1093    * 
1094    * @retval True en cas de cas ce succès, False sinon.
1095    */
1096   function afterRename($oldDn,$newDn) {
1097     $error = 0;
1098     if($GLOBALS['LSsession'] -> dn == $oldDn) {
1099       $GLOBALS['LSsession'] -> changeAuthUser($this);
1100     }
1101     
1102     foreach($this -> _relationsCache as $relation_name => $objInfos) {
1103       if ((isset($this->config['relations'][$relation_name]['rename_function']))&&(is_array($objInfos['list']))) {
1104         foreach($objInfos['list'] as $obj) {
1105           $meth = $this->config['relations'][$relation_name]['rename_function'];
1106           if (method_exists($obj,$meth)) {
1107             if (!($obj -> $meth($this,$objInfos['keyvalue']))) {
1108               $error=1;
1109             }
1110           }
1111           else {
1112             $error=1;
1113           }
1114         }
1115       }
1116     }
1117     return !$error;
1118   }
1119   
1120   /**
1121    * Methode executant les actions nécéssaires avant la suppression de
1122    * l'objet.
1123    * 
1124    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1125    * pour les objets plus complexe.
1126    * 
1127    * @retval True en cas de cas ce succès, False sinon.
1128    */
1129   function beforeDelete() {
1130     return $this -> updateRelationsCache();
1131   }
1132   
1133   /**
1134    * Methode executant les actions nécéssaires après la suppression de
1135    * l'objet.
1136    * 
1137    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1138    * pour les objets plus complexe.
1139    * 
1140    * @retval True en cas de cas ce succès, False sinon.
1141    */
1142   function afterDelete() {
1143     $error = 0;
1144     foreach($this -> _relationsCache as $relation_name => $objInfos) {
1145       if ((isset($this->config['relations'][$relation_name]['remove_function']))&&(is_array($objInfos['list']))) {
1146         foreach($objInfos['list'] as $obj) {
1147           $meth = $this->config['relations'][$relation_name]['remove_function'];
1148           if (method_exists($obj,$meth)) {
1149             if (!($obj -> $meth($this))) {
1150               $error=1;
1151             }
1152           }
1153           else {
1154             $error=1;
1155           }
1156         }
1157       }
1158     }
1159     
1160     if (isset($this -> config['after_delete'])) {
1161       if (is_array($this -> config['after_delete'])) {
1162         $config = $this -> config['after_delete'];
1163       }
1164       else {
1165         $config = array($this -> config['after_delete']);
1166       }
1167       foreach($config as $action) {
1168         if(function_exists($action)) {
1169           if(!$action($this)) {
1170             $GLOBALS['LSerror'] -> addErrorCode(305,$action);
1171             $error=true;
1172           }
1173         }
1174         else {
1175           $GLOBALS['LSerror'] -> addErrorCode(304,$action);
1176           $error=true;
1177         }
1178       }
1179     }
1180     
1181     return !$error;
1182   }
1183   
1184   /**
1185    * Methode executant les actions nécéssaires après la création de
1186    * l'objet.
1187    * 
1188    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1189    * pour les objets plus complexe.
1190    * 
1191    * @retval True en cas de cas ce succès, False sinon.
1192    */
1193   function afterCreate() {
1194     LSdebug('after');
1195     $error = 0;
1196     if ($GLOBALS['LSsession'] -> isSubDnLSobject($this -> getType())) {
1197       if (is_array($GLOBALS['LSsession'] -> ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'])) {
1198         foreach($GLOBALS['LSsession'] -> ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'] as $type) {
1199           if ($GLOBALS['LSsession'] -> loadLSobject($type)) {
1200             if (isset($GLOBALS['LSobjects'][$type]['container_auto_create'])&&isset($GLOBALS['LSobjects'][$type]['container_dn'])) {
1201               $dn = $GLOBALS['LSobjects'][$type]['container_dn'].','.$this -> getDn();
1202               if(!$GLOBALS['LSldap'] -> getNewEntry($dn,$GLOBALS['LSobjects'][$type]['container_auto_create']['objectclass'],$GLOBALS['LSobjects'][$type]['container_auto_create']['attrs'],true)) {
1203                 LSdebug("Impossible de créer l'entrée fille : ".print_r(
1204                   array(
1205                     'dn' => $dn,
1206                     'objectClass' => $GLOBALS['LSobjects'][$type]['container_auto_create']['objectclass'],
1207                     'attrs' => $GLOBALS['LSobjects'][$type]['container_auto_create']['attrs']
1208                   )
1209                 ,true));
1210                 $error=1;
1211               }
1212             }
1213           }
1214           else {
1215             $GLOBALS['LSerror'] -> addErrorCode(1004,$type);
1216             $error=1;
1217           }
1218         }
1219       }
1220     }
1221     
1222     if (isset($this -> config['after_create'])) {
1223       if (is_array($this -> config['after_create'])) {
1224         $config = $this -> config['after_create'];
1225       }
1226       else {
1227         $config = array($this -> config['after_create']);
1228       }
1229       foreach($config as $action) {
1230         if(function_exists($action)) {
1231           if(!$action($this)) {
1232             $GLOBALS['LSerror'] -> addErrorCode(303,$action);
1233             $error=true;
1234           }
1235         }
1236         else {
1237           $GLOBALS['LSerror'] -> addErrorCode(302,$action);
1238           $error=true;
1239         }
1240       }
1241     }
1242     
1243     return !$error;
1244   }
1245   
1246   /**
1247    * Retourne la valeur clef d'un objet en relation
1248    * 
1249    * @param[in] $object Un object de type $objectType
1250    * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
1251    * @param[in] $objectType Le type d'objet en relation
1252    * @param[in] $value La valeur que doit avoir l'attribut :
1253    *                      - soit le dn (par defaut)
1254    *                      - soit la valeur [0] d'un attribut
1255    * 
1256    * @retval Mixed La valeur clef d'un objet en relation
1257    **/
1258   function getObjectKeyValueInRelation($object,$attr,$objectType,$attrValue='dn') {
1259     if ((!$attr)||(!$objectType)) {
1260       $GLOBALS['LSerror'] -> addErrorCode(1021,'getObjectKeyValueInRelation');
1261       return;
1262     }
1263     if ($attrValue=='dn') {
1264       $val = $object -> getDn();
1265     }
1266     else {
1267       $val = $object -> getValue($attrValue);
1268       $val = $val[0];
1269     }
1270     return $val;
1271   }
1272   
1273   /**
1274    * Retourne la liste des relations pour l'objet en fonction de sa présence 
1275    * dans un des attributs
1276    * 
1277    * Retourne un tableau de d'objet (type : $objectType) correspondant à la 
1278    * relation entre l'objet $object et les objets de type $objectType. Cette relation
1279    * est établis par la présence de la valeur de référence à l'objet dans 
1280    * l'attribut des objets de type $objectType.
1281    * 
1282    * @param[in] $object Un object de type $objectType
1283    * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
1284    * @param[in] $objectType Le type d'objet en relation
1285    * @param[in] $value La valeur que doit avoir l'attribut :
1286    *                      - soit le dn (par defaut)
1287    *                      - soit la valeur [0] d'un attribut
1288    * 
1289    * @retval Array of $objectType Les objets en relations
1290    **/
1291   function listObjectsInRelation($object,$attr,$objectType,$attrValue='dn') {
1292     if ((!$attr)||(!$objectType)) {
1293       $GLOBALS['LSerror'] -> addErrorCode(1021,'listObjectsInRelation');
1294       return;
1295     }
1296     if ($attrValue=='dn') {
1297       $val = $object -> getDn();
1298     }
1299     else {
1300       $val = $object -> getValue($attrValue);
1301       $val = $val[0];
1302     }
1303     if ($val) {
1304       $filter = $this -> getObjectFilter();
1305       $filter = '(&'.$filter.'('.$attr.'='.$val.'))';
1306       return $this -> listObjects($filter,$GLOBALS['LSsession'] -> ldapServer['ldap_config']['basedn'],array('scope' => 'sub'));
1307     }
1308     return;
1309   }
1310
1311   /**
1312    * Ajoute un objet en relation dans l'attribut $attr de $this
1313    * 
1314    * @param[in] $object Un objet de type $objectType à ajouter
1315    * @param[in] $attr L'attribut dans lequel l'objet doit être ajouté
1316    * @param[in] $objectType Le type d'objet en relation
1317    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1318    *                      - soit le dn (par defaut)
1319    *                      - soit la valeur [0] d'un attribut
1320    * 
1321    * @retval boolean true si l'objet à été ajouté, False sinon
1322    **/  
1323   function addOneObjectInRelation($object,$attr,$objectType,$attrValue='dn') {
1324     if ((!$attr)||(!$objectType)) {
1325       $GLOBALS['LSerror'] -> addErrorCode(1021,'addOneObjectInRelation');
1326       return;
1327     }
1328     if ($object instanceof $objectType) {
1329       if ($this -> attrs[$attr] instanceof LSattribute) {
1330         if ($attrValue=='dn') {
1331           $val = $object -> getDn();
1332         }
1333         else {
1334           $val = $object -> getValue($attrValue);
1335           $val = $val[0];
1336         }
1337         $values = $this -> attrs[$attr] -> getValue();
1338         if ($this -> attrs[$attr] -> config['multiple']) {
1339           if (!is_array($values)) {
1340             $updateData = array($val);
1341           }
1342           else if (!in_array($val,$values)) {
1343             $values[]=$val;
1344             $updateData = $values;
1345           }
1346         }
1347         else {
1348           if (($values[0]!=$val)&&($values!=$val)) {
1349             $updateData = array($val);
1350           }
1351         }
1352         if (isset($updateData)) {
1353           return $GLOBALS['LSldap'] -> update($this -> getType(),$this -> getDn(), array($attr => $updateData));
1354         }
1355         return true;
1356       }
1357     }
1358     return;
1359   }
1360   
1361   /**
1362    * Supprime un objet en relation dans l'attribut $attr de $this
1363    * 
1364    * @param[in] $object Un objet de type $objectType à supprimer
1365    * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1366    * @param[in] $objectType Le type d'objet en relation
1367    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1368    *                      - soit le dn (par defaut)
1369    *                      - soit la valeur [0] d'un attribut
1370    * 
1371    * @retval boolean true si l'objet à été supprimé, False sinon
1372    **/  
1373   function deleteOneObjectInRelation($object,$attr,$objectType,$attrValue='dn') {
1374     if ((!$attr)||(!$objectType)) {
1375       $GLOBALS['LSerror'] -> addErrorCode(1021,'deleteOneObjectInRelation');
1376       return;
1377     }
1378     if ($object instanceof $objectType) {
1379       if ($this -> attrs[$attr] instanceof LSattribute) {
1380         if ($attrValue=='dn') {
1381           $val = $object -> getDn();
1382         }
1383         else {
1384           $val = $object -> getValue($attrValue);
1385           $val = $val[0];
1386         }
1387         $values = $this -> attrs[$attr] -> getValue();
1388         if ((!is_array($values)) && (!empty($values))) {
1389           $values = array($values);
1390         }
1391         if (is_array($values)) {
1392           $updateData=array();
1393           foreach($values as $value) {
1394             if ($value!=$val) {
1395               $updateData[]=$value;
1396             }
1397           }
1398           return $GLOBALS['LSldap'] -> update($this -> getType(),$this -> getDn(), array($attr => $updateData));
1399         }
1400       }
1401     }
1402     return;
1403   }
1404   
1405  /**
1406   * Renome un objet en relation dans l'attribut $attr de $this
1407   * 
1408   * @param[in] $object Un objet de type $objectType à renomer
1409   * @param[in] $oldValue string L'ancienne valeur faisant référence à l'objet
1410   * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1411   * @param[in] $objectType Le type d'objet en relation
1412   * @param[in] $attrValue La valeur que doit avoir l'attribut :
1413   *                      - soit le dn (par defaut)
1414   *                      - soit la valeur [0] d'un attribut
1415   *  
1416   * @retval boolean True en cas de succès, False sinon
1417   */
1418   function renameOneObjectInRelation($object,$oldValue,$attr,$objectType,$attrValue='dn') {
1419     if ((!$attr)||(!$objectType)) {
1420       $GLOBALS['LSerror'] -> addErrorCode(1021,'renameOneObjectInRelation');
1421       return;
1422     }
1423     if ($object instanceof $objectType) {
1424       if ($this -> attrs[$attr] instanceof LSattribute) {
1425         $values = $this -> attrs[$attr] -> getValue();
1426         if ((!is_array($values)) && (!empty($values))) {
1427           $values = array($values);
1428         }
1429         if (is_array($values)) {
1430           $updateData=array();
1431           foreach($values as $value) {
1432             if ($value!=$oldValue) {
1433               $updateData[] = $value;
1434             }
1435             else {
1436               if ($attrValue=='dn') {
1437                 $val = $object -> getDn();
1438               }
1439               else {
1440                 $val = $object -> getValue($attrValue);
1441                 $val = $val[0];
1442               }
1443               $updateData[] = $val;
1444             }
1445           }
1446           return $GLOBALS['LSldap'] -> update($this -> getType(),$this -> getDn(), array($attr => $updateData));
1447         }
1448       }
1449     }
1450     return;
1451   }
1452   
1453   /**
1454    * Met à jour les objets du meme type que $this en relation avec l'objet $object
1455    * de type $objectType en modifiant la valeur de leur attribut $attr des objets
1456    * en relation
1457    * 
1458    * @param[in] $object Mixed Un object (type : $this -> userObjectType) : l'utilisateur
1459    * @param[in] $listDns Array(string) Un tableau des DNs des objets en relation
1460    * @param[in] $attr L'attribut dans lequel l'utilisateur doit apparaitre
1461    * @param[in] $objectType Le type d'objet en relation
1462    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1463    *                      - soit le dn (par defaut)
1464    *                      - soit la valeur [0] d'un attribut
1465    * 
1466    * @retval boolean true si tout c'est bien passé, False sinon
1467    **/  
1468   function updateObjectsInRelation($object,$listDns,$attr,$objectType,$attrValue='dn') {
1469     if ((!$attr)||(!$objectType)) {
1470       $GLOBALS['LSerror'] -> addErrorCode(1021,'updateObjectsInRelation');
1471       return;
1472     }
1473     $currentObjects = $this -> listObjectsInRelation($object,$attr,$objectType,$attrValue);
1474     $type=$this -> getType();
1475     if(is_array($currentObjects)) {
1476       if (is_array($listDns)) {
1477         $values=array();
1478         if ($attrValue!='dn') {
1479           $obj=new $objectType();
1480           foreach ($listDns as $dn) {
1481             $obj -> loadData($dn);
1482             $val = $obj -> getValue($attrValue);
1483             $values[$dn] = $val[0];
1484           }
1485         }
1486         else {
1487           foreach($listDns as $dn) {
1488             $values[$dn] = $dn;
1489           }
1490         }
1491         $dontDelete=array();
1492         $dontAdd=array();
1493         for ($i=0;$i<count($currentObjects);$i++) {
1494           if ($attrValue=='dn') {
1495             $val = $currentObjects[$i] -> getDn();
1496           }
1497           else {
1498             $val = $currentObjects[$i] -> getValue($attrValue);
1499             $val = $val[0];
1500           }
1501           if (in_array($val, $listDns)) {
1502             $dontDelete[$i]=true;
1503             $dontAdd[]=$val;
1504           }
1505         }
1506         
1507         for($i=0;$i<count($currentObjects);$i++) {
1508           if ($dontDelete[$i]) {
1509             continue;
1510           }
1511           else {
1512             if (!$currentObjects[$i] -> deleteOneObjectInRelation($object,$attr,$objectType,$attrValue)) {
1513               return;
1514             }
1515           }
1516         }
1517         
1518         foreach($values as $dn => $val) {
1519           if (in_array($val,$dontAdd)) {
1520             continue;
1521           }
1522           else {
1523             $obj = new $type();
1524             if ($obj -> loadData($dn)) {
1525               if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue)) {
1526                 return;
1527               }
1528             }
1529             else {
1530               return;
1531             }
1532           }
1533         }
1534         return true;
1535       }
1536     }
1537     else {
1538       if(!is_array($listDns)) {
1539         return true;
1540       }
1541       foreach($listDns as $dn) {
1542         $obj = new $type();
1543         if ($obj -> loadData($dn)) {
1544           if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue)) {
1545             return;
1546           }
1547         }
1548         else {
1549           return;
1550         }
1551       }
1552     }
1553   }
1554   
1555 }
1556
1557 ?>