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