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