- config.inc.php :
[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       debug("les données sont validées");
307       if(isset($this -> config['before_save'])) {
308         if(function_exists($this -> config['before_save'])) {
309           if(!$this -> config['before_save']($this)) {
310             $GLOBALS['LSerror'] -> addErrorCode(28,$this -> config['before_save']);
311             return;
312           }
313         }
314         else {
315           $GLOBALS['LSerror'] -> addErrorCode(27,$this -> config['before_save']);
316           return;
317         }
318       }
319       if ($this -> submitChange($idForm)) {
320         debug('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_save']))&&(!$this -> submitError)) {
329         if(function_exists($this -> config['after_save'])) {
330           if(!$this -> config['after_save']($this)) {
331             $GLOBALS['LSerror'] -> addErrorCode(30,$this -> config['after_save']);
332             return;
333           }
334         }
335         else {
336           $GLOBALS['LSerror'] -> addErrorCode(29,$this -> config['after_save']);
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           debug('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             if (!$this -> afterRename($oldDn,$newDn)) {
533               $GLOBALS['LSerror'] -> addErrorCode(37);
534               return;
535             }
536           }
537           else {
538             return;
539           }
540         }
541         else {
542           $submitData[$attr -> name] = $attr -> getUpdateData();
543         }
544       }
545     }
546     if(!empty($submitData)) {
547       $dn=$this -> getDn();
548       if($dn) {
549         $this -> dn=$dn;
550         debug($submitData);
551         if (!$GLOBALS['LSldap'] -> update($this -> getType(),$dn, $submitData)) {
552           return;
553         }
554         if ($new) {
555           if (!$this -> afterCreate()) {
556             $GLOBALS['LSerror'] -> addErrorCode(40);
557             return;
558           }
559         }
560         return true;
561       }
562       else {
563         $GLOBALS['LSerror'] -> addErrorCode(33);
564         return;
565       }
566     }
567     else {
568       return true;
569     }
570   }
571   
572   /**
573    * Retourne les informations issus d'un DN
574    *
575    * @param[in] $dn Un DN.
576    *
577    * @author Benjamin Renard <brenard@easter-eggs.com>
578    *
579    * @retval array Tableau : 
580    *                  - [0] : le premier paramètre
581    *                  - [1] : les paramètres suivants
582    */ 
583   function getDnInfos($dn) {
584     $infos=ldap_explode_dn($dn,0);
585     if(!$infos)
586       return;
587     $first=true;
588     for($i=1;$i<$infos['count'];$i++)
589       if($first) {
590         $basedn.=$infos[$i];
591         $first=false;
592       }
593       else
594         $basedn.=','.$infos[$i];
595     return array($infos[0],$basedn);
596   }
597   
598   /**
599    * Retourne le filtre correpondants aux objetcClass de l'objet
600    *
601    * @author Benjamin Renard <brenard@easter-eggs.com>
602    *
603    * @retval string le filtre ldap correspondant au type de l'objet
604    */ 
605   function getObjectFilter() {
606     if(!isset($this -> config['objectclass'])) return;
607     foreach ($this -> config['objectclass'] as $class)
608       $filter.='(objectClass='.$class.')';
609     return $filter;
610   }
611   
612   /**
613    * Retourne une liste d'objet du même type.
614    *
615    * Effectue une recherche en fonction des paramètres passé et retourne un
616    * tableau d'objet correspond au resultat de la recherche.
617    *
618    * @author Benjamin Renard <brenard@easter-eggs.com>
619    *
620    * @param[in] $filter array (ou string) Filtre de recherche Ldap / Tableau de filtres de recherche
621    * @param[in] $basedn string DN de base pour la recherche
622    * @param[in] $params array Paramètres de recherche au format Net_LDAP2::search()
623    *
624    * @retval array Tableau d'objets correspondant au resultat de la recherche
625    */ 
626   function listObjects($filter='',$basedn=NULL,$params=array()) {
627     $retInfos=array();
628     $attrs=false;
629     $check_final_dn=false;
630
631     if(!is_array($filter))
632       $filter=array(array('filter' => $filter));
633     
634     $nbFilter=count($filter);
635
636     for($i=0;$i<$nbFilter;$i++) {
637       $new_attrs=array();
638       // Défintion des paramètres de base pour la recherche
639       $sbasedn=$basedn;
640       $sparams=$params;
641       $ret=array();
642       if (isset($filter[$i]['scope']))
643         $sparams["scope"]=$filter[$i]['scope'];
644       
645       // Definition des critères de recherche correspondant au type d'objet à lister
646       if(($nbFilter==1)||(!isset($filter[$i]['attr']))) {
647         // Filtre sur l'objet souhaité
648         $sfilter='(&';
649         $sfilter.=$this -> getObjectFilter();
650         $sfilter_end=')';
651         $check_final_dn=true;
652       }
653       // Initialisation des critères d'une recherche intermédiaire
654       else {
655         if(isset($filter[$i]['object_type'])) {
656           $obj_tmp=new $filter[$i]['object_type']();
657           $obj_filter=$obj_tmp->getObjectFilter();
658           $sfilter='(&'.$obj_filter;
659           $sfilter_end=')';
660         }
661         else {
662           $sfilter='';
663           $sfilter_end='';
664         }
665         if(isset($filter[$i]['scope'])) {
666           $sparams['scope']=$filter[$i]['scope'];
667         }
668         if(isset($filter[$i]['basedn'])) {
669           $sbasedn=$filter[$i]['basedn'];
670         }
671       }
672       // Dans le cas d'une recherche intermédiaire ou finale
673       if($attrs!=false) {
674         // Initialisation des variables
675         $ret_gen=array();
676         $new_attrs=array();
677         
678         // Pour tout les attributs retournés
679         for($ii=0;$ii<count($attrs);$ii++) {
680           $sfilter_for='';
681           // Définition du filtre de recherche à partir des paramètres utilisateurs et
682           // des paramètres de recherche de l'objet à listé (dans le cas d'une recherche finale
683           if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
684             $sfilter_user=getFData($filter[$i]['filter'],$attrs[$ii]);
685             if($sfilter_user[0]=='(')
686               $sfilter_for=$sfilter.$sfilter_user;
687             else
688               $sfilter_for=$sfilter.'('.$sfilter_user.')';
689           }
690           else {
691             $sfilter_for=$sfilter;
692           }
693           
694           if(isset($filter[$i]['basedn'])) {
695             $sbasedn=getFData($filter[$i]['basedn'],$attrs[$ii]);
696             if ((!$this -> isCompatibleDNs($sbasedn,$basedn))&&($check_final_dn)) continue;
697           }
698         
699           // Vérification de la compatibilité du basedn de la recherche et du basedn générale
700           // Finalisation du filtre
701           $sfilter_for.=$sfilter_end;
702         
703         
704           // Execution de la recherche
705           $ret=$GLOBALS['LSldap'] -> search ($sfilter_for,$sbasedn,$sparams);
706           
707           // Si il y un retour
708           if(isset($ret[0])) {
709             // si il ya une suite (recherche intermédiaire)
710             if($filter[$i]['attr']){
711               for($iii=0;$iii<count($ret);$iii++) {
712                 if(isset($ret[$iii]['attrs'][$filter[$i]['attr']])) {
713                   // cas de valeur multiple
714                   if(is_array($ret[$iii]['attrs'][$filter[$i]['attr']])) {
715                     foreach($ret[$iii]['attrs'][$filter[$i]['attr']] as $val_attr) {
716                       $new_attrs[]=$val_attr;
717                     }
718                   }
719                   // cas de valeur unique
720                   else {
721                     $new_attrs[]=$ret[$iii]['attrs'][$filter[$i]['attr']];
722                   }
723                 }
724               }
725             }
726             else {
727               // vérification de la compatibilité de la compatibilité du DN resultant
728               // et du basedn de recherche 
729               if (!$this -> isCompatibleDNs($ret[0]['dn'],$basedn))
730                 continue;
731               // ajout du DN au resultat finale
732               $ret_gen[]=$ret[0]['dn'];
733             }
734           }
735         }
736         // cas du dernier filtre
737         if(!empty($ret_gen)) {
738           // on quitte la boucle des filtres de la conf
739           $ret=$ret_gen;
740           break;
741         }
742         // dans le cas d'une suite prévu mais d'un retour nul de la précédente recherche
743         else if(empty($new_attrs)) {
744             // retour vide et arrêt de la recherche
745             $ret=array();
746             break;
747         }
748         else {
749           $attrs=$new_attrs;
750         }
751       }
752       // Dans le cas de la recherche initiale
753       else {
754         // Déclaration du filtre de recherche
755         if((isset($filter[$i]['filter']))&&(!empty($filter[$i]['filter']))) {
756           if($filter[$i]['filter'][0]=='(') {
757             $sfilter.=$filter[$i]['filter'];
758           }
759           else {
760             $sfilter.='('.$filter[$i]['filter'].')';
761           }
762         }
763         // fermeture du filtre
764         $sfilter.=$sfilter_end;
765         
766         // Lancement de la recherche
767         $ret=$GLOBALS['LSldap'] -> search ($sfilter,$sbasedn,$sparams);
768         
769         //Si filtre multiple => on recupère une liste d'attributs
770         if(isset($filter[$i]['attr'])) {
771           for($ii=0;$ii<count($ret);$ii++) {
772             if(isset($ret[$ii]['attrs'][$filter[$i]['attr']])) {
773               // cas de valeur multiple
774               if(is_array($ret[$ii]['attrs'][$filter[$i]['attr']])) {
775                 foreach($ret[$ii]['attrs'][$filter[$i]['attr']] as $val_attr) {
776                   $attrs[]=$val_attr;
777                 }
778               }
779               // cas de valeur unique
780               else {
781                 $attrs[]=$ret[$ii]['attrs'][$filter[$i]['attr']];
782               }
783             }
784           }
785           
786           // Si aucunne valeur n'est retournées
787           if(empty($attrs)){
788             // arrêt et retour à zéro
789             $ret=array();
790             break;
791           }
792         }
793         // Si recherche unique
794         else {
795           // préparation du retour finale
796           if (is_array($ret)) {
797             $ret_final=array();
798             foreach($ret as $obj)
799               $ret_final[]=$obj['dn'];
800             $ret=$ret_final;
801           }
802           else {
803             $ret=array();
804           }
805           break;
806         }
807       }
808     }
809     
810     // Création d'un tableau d'objet correspondant au valeur retourné
811     for($i=0;$i<count($ret);$i++) {
812       $retInfos[$i] = new $this -> type_name($this -> config);
813       $retInfos[$i] -> loadData($ret[$i]);
814     }
815     
816     return $retInfos;
817   }
818  
819  
820   /**
821    * Recherche un objet à partir de la valeur exact de son RDN
822    * 
823    * @author Benjamin Renard <brenard@easter-eggs.com>
824    * 
825    * @param[in] $name string Valeur de son RDN
826    * @param[in] $basedn string Le DN de base de la recherche
827    * 
828    * @retval array Tableau d'objets correspondant au resultat de la recherche
829    */
830   function searchObject($name,$basedn=NULL) {
831     $filter = $this -> config['rdn'].'='.$name; 
832     return $this -> listObjects($filter,$basedn); 
833   }
834
835   /**
836    * Retourne une valeur de l'objet
837    *
838    * Retourne une valeur en fonction du paramètre. Si la valeur est inconnue, la valeur retourné est ' '.
839    * tableau d'objet correspond au resultat de la recherche.
840    *
841    * Valeurs possibles :
842    * - 'dn' ou '%{dn} : DN de l'objet
843    * - [nom d'un attribut] : valeur de l'attribut
844    * - [clef de $this -> other_values] : valeur de $this -> other_values
845    *
846    * @author Benjamin Renard <brenard@easter-eggs.com>
847    *
848    * @param[in] $val string Le nom de la valeur demandée
849    *
850    * @retval mixed la valeur demandé ou ' ' si celle-ci est inconnue.
851    */ 
852   function getValue($val) {
853     if(($val=='dn')||($val=='%{dn}')) {
854       return $this -> dn;
855     }
856     else if(($val=='rdn')||($val=='%{rdn}')) {
857       return $this -> attrs[ $this -> config['rdn'] ] -> getValue();
858     }
859     else if(($val=='subDn')||($val=='%{subDn}')) {
860       return $this -> getSubDnValue();
861     }
862     else if(($val=='subDnName')||($val=='%{subDnName}')) {
863       return $this -> getSubDnName();
864     }
865     else if(isset($this ->  attrs[$val])){
866       if (method_exists($this ->  attrs[$val],'getValue'))
867         return $this -> attrs[$val] -> getValue();
868       else
869         return ' ';
870     }
871     else if(isset($this -> other_values[$val])){
872       return $this -> other_values[$val];
873     }
874     else {
875       return ' ';
876     }
877   }
878
879   /**
880    * Retourn un tableau pour un select d'un objet du même type
881    * 
882    * @author Benjamin Renard <brenard@easter-eggs.com>
883    *
884    * @retval array('dn' => 'display')
885    */
886   function getSelectArray($topDn=NULL) {
887     $list = $this -> listObjects(NULL,$topDn);
888     $return=array();
889     foreach($list as $object) {
890       $return[$object -> getDn()] = $object -> getDisplayValue(); 
891     }
892     return $return;
893   }
894
895   /**
896    * Retourne le DN de l'objet
897    *
898    * Cette methode retourne le DN de l'objet. Si celui-ci n'existe pas, il le construit à partir de la 
899    * configuration de l'objet et la valeur de son attribut rdn.
900    *
901    * @author Benjamin Renard <brenard@easter-eggs.com>
902    *
903    * @retval string Le DN de l'objet
904    */   
905   function getDn() {
906     if($this -> dn) {
907       return $this -> dn;
908     }
909     else {
910       $rdn_attr=$this -> config['rdn'];
911       if( (isset($this -> config['rdn'])) && (isset($this -> attrs[$rdn_attr])) && (isset($this -> config['container_dn'])) && (isset($GLOBALS['LSsession']->topDn)) ) {
912         $rdn_val=$this -> attrs[$rdn_attr] -> getUpdateData();
913         if (!empty($rdn_val)) {
914           return $rdn_attr.'='.$rdn_val[0].','.$this -> config['container_dn'].','.$GLOBALS['LSsession']->topDn;
915         }
916         else {
917           $GLOBALS['LSerror'] -> addErrorCode(32,$this -> config['rdn']);
918           return;
919         }
920       }
921       else {
922         $GLOBALS['LSerror'] -> addErrorCode(31,$this -> getType());
923         return;
924       }
925     }
926   }
927
928   /**
929    * Retourne le type de l'objet
930    *
931    * @author Benjamin Renard <brenard@easter-eggs.com>
932    * 
933    * @retval string Le type de l'objet ($this -> type_name)
934    */
935   function getType() {
936     return $this -> type_name;
937   }
938   
939   /**
940    * Retourne qui est l'utilisateur par rapport à cet object
941    *
942    * @author Benjamin Renard <brenard@easter-eggs.com>
943    * 
944    * @retval string 'admin'/'self'/'user' pour Admin , l'utilisateur lui même ou un simple utilisateur
945    */
946   function whoami() {
947     if (!$this -> _whoami)
948       $this -> _whoami = $GLOBALS['LSsession'] -> whoami($this -> dn);
949     return $this -> _whoami;
950   }
951   
952   /**
953    * Retourne le label de l'objet
954    *
955    * @author Benjamin Renard <brenard@easter-eggs.com>
956    * 
957    * @retval string Le label de l'objet ($this -> config['label'])
958    */
959   function getLabel() {
960     return $this -> config['label'];
961   }
962   
963   
964   /**
965    * Supprime l'objet dans l'annuaire
966    *
967    * @author Benjamin Renard <brenard@easter-eggs.com>
968    * 
969    * @retval boolean True si l'objet à été supprimé, false sinon
970    */
971   function remove() {
972     if ($this -> beforeDelete()) {
973       if ($GLOBALS['LSldap'] -> remove($this -> getDn())) {
974         if ($this -> afterDelete()) {
975           return true;
976         }
977         $GLOBALS['LSerror'] -> addErrorCode(39);
978       }
979     }
980     else {
981       $GLOBALS['LSerror'] -> addErrorCode(38);
982     }
983     return;
984   }
985   
986   /**
987    * L'objet est-il nouveau
988    * 
989    * @author Benjamin Renard <brenard@easter-eggs.com>
990    * 
991    * @retval boolean True si l'objet est nouveau, false sinon
992    */
993   function isNew() {
994     return (!$this -> dn);
995   }
996
997   /**
998    * Retourne la valeur (DN) du subDn de l'objet  
999    * 
1000    * @return string La valeur du subDn de l'object
1001    */
1002   function getSubDnValue() {
1003     if ($this -> _subDn_value) {
1004       return $this -> _subDn_value;
1005     }
1006     $dn = $this -> getValue('dn');
1007     $subDn_value='';
1008     $subDnLdapServer = $GLOBALS['LSsession'] -> getSortSubDnLdapServer();
1009     foreach ($subDnLdapServer as $subDn => $subDn_name) {
1010       if (isCompatibleDNs($subDn,$dn)&&($subDn!=$dn)) {
1011         $subDn_value=$subDn;
1012         break;
1013       }
1014     }
1015     $this -> _subDn_value = $subDn_value;
1016     return $subDn_value;
1017   }
1018
1019   /**
1020    * Retourne la nom du subDn de l'objet  
1021    * 
1022    * @return string Le nom du subDn de l'object
1023    */
1024   function getSubDnName() {
1025     $subDnLdapServer = $GLOBALS['LSsession'] -> getSortSubDnLdapServer();
1026     return $subDnLdapServer[$this -> getSubDnValue()];
1027   }
1028   
1029   /**
1030    * Methode créant la liste des objets en relations avec l'objet courant et qui
1031    * la met en cache ($this -> _relationsCache)
1032    * 
1033    * @retval True en cas de cas ce succès, False sinon.
1034    */
1035   function updateRelationsCache() {
1036     $this -> _relationsCache=array();
1037     if (is_array($this->config['relations'])) {
1038       foreach($this->config['relations'] as $relation_name => $relation_conf) {
1039         if ( isset($relation_conf['list_function']) ) {
1040           if ($GLOBALS['LSsession'] -> loadLSobject($relation_conf['LSobject'])) {
1041             $obj = new $relation_conf['LSobject']();
1042             if (method_exists($obj,$relation_conf['list_function'])) {
1043               $list = $obj -> $relation_conf['list_function']($this);
1044               if (is_array($list)) {
1045                 $this -> _relationsCache[$relation_name] = $list; 
1046               }
1047               else {
1048                 return;
1049               }
1050             }
1051             else {
1052               return;
1053             }
1054           }
1055           else {
1056             return;
1057           }
1058         }
1059       }
1060     }
1061     return true;
1062   }
1063   
1064   /**
1065    * Methode executant les actions nécéssaires avant le changement du DN de
1066    * l'objet.
1067    * 
1068    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1069    * pour les objets plus complexe.
1070    * 
1071    * @retval True en cas de cas ce succès, False sinon.
1072    */
1073   function beforeRename() {
1074     return $this -> updateRelationsCache();
1075   }
1076   
1077   /**
1078    * Methode executant les actions nécéssaires après le changement du DN de
1079    * l'objet.
1080    * 
1081    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1082    * pour les objets plus complexe.
1083    * 
1084    * @param[in] $oldDn string L'ancien DN de l'objet
1085    * @param[in] $newDn string Le nouveau DN de l'objet
1086    * 
1087    * @retval True en cas de cas ce succès, False sinon.
1088    */
1089   function afterRename($oldDn,$newDn) {
1090     $error = 0;
1091     if($GLOBALS['LSsession'] -> dn == $oldDn) {
1092       $GLOBALS['LSsession'] -> changeAuthUser($this);
1093     }
1094     
1095     foreach($this -> _relationsCache as $relation_name => $objList) {
1096       if (isset($this->config['relations'][$relation_name]['rename_function'])) {
1097         foreach($objList as $obj) {
1098           $meth = $this->config['relations'][$relation_name]['rename_function'];
1099           if (method_exists($obj,$meth)) {
1100             if (!($obj -> $meth($this,$oldDn))) {
1101               $error=1;
1102             }
1103           }
1104           else {
1105             $error=1;
1106           }
1107         }
1108       }
1109     }
1110     return !$error;
1111   }
1112   
1113   /**
1114    * Methode executant les actions nécéssaires avant la suppression de
1115    * l'objet.
1116    * 
1117    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1118    * pour les objets plus complexe.
1119    * 
1120    * @retval True en cas de cas ce succès, False sinon.
1121    */
1122   function beforeDelete() {
1123     return $this -> updateRelationsCache();
1124   }
1125   
1126   /**
1127    * Methode executant les actions nécéssaires après la suppression de
1128    * l'objet.
1129    * 
1130    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1131    * pour les objets plus complexe.
1132    * 
1133    * @retval True en cas de cas ce succès, False sinon.
1134    */
1135   function afterDelete() {
1136     $error = 0;
1137     foreach($this -> _relationsCache as $relation_name => $objList) {
1138       if (isset($this->config['relations'][$relation_name]['remove_function'])) {
1139         foreach($objList as $obj) {
1140           $meth = $this->config['relations'][$relation_name]['remove_function'];
1141           if (method_exists($obj,$meth)) {
1142             if (!($obj -> $meth($this))) {
1143               $error=1;
1144             }
1145           }
1146           else {
1147             $error=1;
1148           }
1149         }
1150       }
1151     }
1152     return !$error;
1153   }
1154   
1155   /**
1156    * Methode executant les actions nécéssaires après la création de
1157    * l'objet.
1158    * 
1159    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1160    * pour les objets plus complexe.
1161    * 
1162    * @retval True en cas de cas ce succès, False sinon.
1163    */
1164   function afterCreate() {
1165     debug('after');
1166     $error = 0;
1167     if ($GLOBALS['LSsession'] -> isSubDnLSobject($this -> getType())) {
1168       if (is_array($GLOBALS['LSsession'] -> ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'])) {
1169         foreach($GLOBALS['LSsession'] -> ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'] as $type) {
1170           if ($GLOBALS['LSsession'] -> loadLSobject($type)) {
1171             if (isset($GLOBALS['LSobjects'][$type]['container_auto_create'])&&isset($GLOBALS['LSobjects'][$type]['container_dn'])) {
1172               $dn = $GLOBALS['LSobjects'][$type]['container_dn'].','.$this -> getDn();
1173               if(!$GLOBALS['LSldap'] -> getNewEntry($dn,$GLOBALS['LSobjects'][$type]['container_auto_create']['objectclass'],$GLOBALS['LSobjects'][$type]['container_auto_create']['attrs'],true)) {
1174                 debug("Impossible de créer l'entrée fille : ".print_r(
1175                   array(
1176                     'dn' => $dn,
1177                     'objectClass' => $GLOBALS['LSobjects'][$type]['container_auto_create']['objectclass'],
1178                     'attrs' => $GLOBALS['LSobjects'][$type]['container_auto_create']['attrs']
1179                   )
1180                 ,true));
1181                 $error=1;
1182               }
1183             }
1184           }
1185           else {
1186             $GLOBALS['LSerror'] -> addErrorCode(1004,$type);
1187             $error=1;
1188           }
1189         }
1190       }
1191     }
1192     return !$error;
1193   }
1194   
1195 }
1196
1197 ?>