51fd5c790f9708a3ea31cc14d947d68345c3a8d5
[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_array($test['function'],array(&$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']) && LSsession :: loadLSclass('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         $relation = new LSrelation($me, $relation_name);
1115         $list = $relation -> listRelatedObjects();
1116         if (is_array($list)) {
1117           $this -> _LSrelationsCache[$relation_name] = array(
1118             'list' => $list,
1119             'keyvalue' => $relation -> getRelatedKeyValue()
1120           );
1121         }
1122         else {
1123           LSdebug('Problème durant la mise en cache de la relation '.$relation_name);
1124           return;
1125         }
1126       }
1127     }
1128     return true;
1129   }
1130   
1131   /**
1132    * Methode executant les actions nécéssaires avant le changement du DN de
1133    * l'objet.
1134    * 
1135    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1136    * pour les objets plus complexe.
1137    * 
1138    * @retval True en cas de cas ce succès, False sinon.
1139    */
1140   function beforeRename() {
1141     // LSrelations
1142     return $this -> updateLSrelationsCache();
1143   }
1144   
1145   /**
1146    * Methode executant les actions nécéssaires après le changement du DN de
1147    * l'objet.
1148    * 
1149    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1150    * pour les objets plus complexe.
1151    * 
1152    * @retval True en cas de cas ce succès, False sinon.
1153    */
1154   function afterRename() {
1155     $error = 0;
1156     
1157     // Change LSsession -> userObject Dn
1158     if(LSsession :: getLSuserObjectDn() == $this -> oldDn) {
1159       LSsession :: changeAuthUser($this);
1160     }
1161     
1162     // LSrelations
1163     foreach($this -> _LSrelationsCache as $relation_name => $objInfos) {
1164       $relation = new LSrelation($this, $relation_name);
1165       if (is_array($objInfos['list'])) {
1166         foreach($objInfos['list'] as $obj) {
1167           if (!$relation -> renameRelationWithObject($obj, $objInfos['keyvalue'])) {
1168             $error=1;
1169           }
1170         }
1171       }
1172     }
1173     return !$error;
1174   }
1175   
1176   /**
1177    * Methode executant les actions nécéssaires avant la suppression de
1178    * l'objet.
1179    * 
1180    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1181    * pour les objets plus complexe.
1182    * 
1183    * @retval True en cas de cas ce succès, False sinon.
1184    */
1185   function beforeDelete() {
1186     $return = $this -> updateLSrelationsCache();
1187     
1188     foreach(array_keys($this -> attrs) as $attr_name) {
1189       if (!$this -> attrs[$attr_name] -> fireEvent('before_delete')) {
1190         $return = false;
1191       }
1192     }
1193     
1194     return $return;
1195   }
1196   
1197   /**
1198    * Methode executant les actions nécéssaires après 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 afterDelete() {
1207     $error = 0;
1208     
1209     // LSrelations
1210     foreach($this -> _LSrelationsCache as $relation_name => $objInfos) {
1211       $relation = new LSrelation($this, $relation_name);
1212       if (is_array($objInfos['list'])) {
1213         foreach($objInfos['list'] as $obj) {
1214           if (!$relation -> canEditRelationWithObject($obj)) {
1215             LSerror :: addErrorCode('LSsession_11');
1216           }
1217           elseif (!$relation -> removeRelationWithObject($obj)) $error=1;
1218         }
1219       }
1220     }
1221     
1222     // Binding LSattributes
1223     foreach(array_keys($this -> attrs) as $attr_name) {
1224       if (!$this -> attrs[$attr_name] -> fireEvent('after_delete')) {
1225         $error = true;
1226       }
1227     }
1228     
1229     // LSsearch : Purge LSobject cache
1230     if (LSsession :: loadLSclass('LSsearch')) {
1231       LSsearch :: purgeCache($this -> type_name);
1232     }
1233     
1234     return !$error;
1235   }
1236   
1237   /**
1238    * Methode executant les actions nécéssaires après la création de
1239    * l'objet.
1240    * 
1241    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1242    * pour les objets plus complexe.
1243    * 
1244    * @retval True en cas de cas ce succès, False sinon.
1245    */
1246   function afterCreate() {
1247     LSdebug('after');
1248     $error = 0;
1249     
1250     // container_auto_create
1251     if (LSsession :: isSubDnLSobject($this -> getType())) {
1252       if (is_array(LSsession :: $ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'])) {
1253         foreach(LSsession :: $ldapServer['subDn']['LSobject'][$this -> getType()]['LSobjects'] as $type) {
1254           if (LSsession :: loadLSobject($type)) {
1255             $conf_type=LSconfig :: get("LSobjects.$type");
1256             if (isset($conf_type['container_auto_create'])&&isset($conf_type['container_dn'])) {
1257               $dn = $conf_type['container_dn'].','.$this -> getDn();
1258               if(!LSldap :: getNewEntry($dn,$conf_type['container_auto_create']['objectclass'],$conf_type['container_auto_create']['attrs'],true)) {
1259                 LSdebug("Impossible de créer l'entrée fille : ".print_r(
1260                   array(
1261                     'dn' => $dn,
1262                     'objectClass' => $conf_type['container_auto_create']['objectclass'],
1263                     'attrs' => $conf_type['container_auto_create']['attrs']
1264                   )
1265                 ,true));
1266                 $error=1;
1267               }
1268             }
1269           }
1270           else {
1271             $error=1;
1272           }
1273         }
1274       }
1275     }
1276     
1277     // LSsearch : Purge LSobject cache
1278     if (LSsession :: loadLSclass('LSsearch')) {
1279       LSsearch :: purgeCache($this -> type_name);
1280     }
1281     
1282     return !$error;
1283   }
1284   
1285   /**
1286    * Methode executant les actions nécéssaires après la modification de
1287    * l'objet.
1288    * 
1289    * Cette méthode n'est qu'un exemple et elle doit être certainement réécrite
1290    * pour les objets plus complexe.
1291    * 
1292    * @retval True en cas de cas ce succès, False sinon.
1293    */
1294   function afterModify() {
1295     $error = 0;
1296     
1297     // LSsearch : Purge LSobject cache
1298     if (LSsession :: loadLSclass('LSsearch')) {
1299       LSsearch :: purgeCache($this -> type_name);
1300     }
1301     
1302     return !$error;
1303   }
1304   
1305   /**
1306    * Retourne la valeur clef d'un objet en relation
1307    * 
1308    * @param[in] $object Un object de type $objectType
1309    * @param[in] $objectType Le type d'objet en relation
1310    * @param[in] $value La valeur que doit avoir l'attribut :
1311    *                      - soit le dn (par defaut)
1312    *                      - soit la valeur [0] d'un attribut
1313    * 
1314    * @retval Mixed La valeur clef d'un objet en relation
1315    **/
1316   function getObjectKeyValueInRelation($object,$objectType,$attrValue='dn') {
1317     if (!$objectType) {
1318       LSerror :: addErrorCode('LSrelations_05','getObjectKeyValueInRelation');
1319       return;
1320     }
1321     if ($attrValue=='dn') {
1322       $val = $object -> getDn();
1323     }
1324     else {
1325       $val = $object -> getValue($attrValue);
1326       $val = $val[0];
1327     }
1328     return $val;
1329   }
1330   
1331   /**
1332    * Retourne la liste des relations pour l'objet en fonction de sa présence 
1333    * dans un des attributs
1334    * 
1335    * Retourne un tableau de d'objet (type : $objectType) correspondant à la 
1336    * relation entre l'objet $object et les objets de type $objectType. Cette relation
1337    * est établis par la présence de la valeur de référence à l'objet dans 
1338    * l'attribut des objets de type $objectType.
1339    * 
1340    * @param[in] $object Un object de type $objectType
1341    * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
1342    * @param[in] $objectType Le type d'objet en relation
1343    * @param[in] $value La valeur que doit avoir l'attribut :
1344    *                      - soit le dn (par defaut)
1345    *                      - soit la valeur [0] d'un attribut
1346    * 
1347    * @retval Array of $objectType Les objets en relations
1348    **/
1349   function listObjectsInRelation($object,$attr,$objectType,$attrValue='dn') {
1350     if ((!$attr)||(!$objectType)) {
1351       LSerror :: addErrorCode('LSrelations_05','listObjectsInRelation');
1352       return;
1353     }
1354     if ($attrValue=='dn') {
1355       $val = $object -> getDn();
1356     }
1357     else {
1358       $val = $object -> getValue($attrValue);
1359       $val = $val[0];
1360     }
1361     if ($val) {
1362       $filter = Net_LDAP2_Filter::create($attr,'equals',$val);
1363       return $this -> listObjects($filter,LSsession :: getRootDn(),array('scope' => 'sub','recursive' => true,'withoutCache'=>true, 'onlyAccessible' => false));
1364     }
1365     return;
1366   }
1367
1368   /**
1369    * Ajoute un objet en relation dans l'attribut $attr de $this
1370    * 
1371    * @param[in] $object Un objet de type $objectType à ajouter
1372    * @param[in] $attr L'attribut dans lequel l'objet doit être ajouté
1373    * @param[in] $objectType Le type d'objet en relation
1374    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1375    *                      - soit le dn (par defaut)
1376    *                      - soit la valeur [0] d'un attribut
1377    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1378    *                              relation avec l'objet est éditable par le user
1379    * 
1380    * @retval boolean true si l'objet à été ajouté, False sinon
1381    **/  
1382   function addOneObjectInRelation($object,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1383     if ((!$attr)||(!$objectType)) {
1384       LSerror :: addErrorCode('LSrelations_05','addOneObjectInRelation');
1385       return;
1386     }
1387     if ($object instanceof $objectType) {
1388       if ($canEditFunction) {
1389         if (!$this -> $canEditFunction()) {
1390           LSerror :: addErrorCode('LSsession_11');
1391           return;
1392         }
1393       }
1394       elseif (!LSsession::canEdit($this -> getType(), $this -> getDn(), $attr)) {
1395         LSerror :: addErrorCode('LSsession_11');
1396         return;
1397       }
1398       if ($this -> attrs[$attr] instanceof LSattribute) {
1399         if ($attrValue=='dn') {
1400           $val = $object -> getDn();
1401         }
1402         else {
1403           $val = $object -> getValue($attrValue);
1404           $val = $val[0];
1405         }
1406         $values = $this -> attrs[$attr] -> getValue();
1407         if ($this -> attrs[$attr] -> config['multiple']) {
1408           if (!is_array($values)) {
1409             $updateData = array($val);
1410           }
1411           else if (!in_array($val,$values)) {
1412             $values[]=$val;
1413             $updateData = $values;
1414           }
1415         }
1416         else {
1417           if (($values[0]!=$val)&&($values!=$val)) {
1418             $updateData = array($val);
1419           }
1420         }
1421         if (isset($updateData)) {
1422           return $this -> _updateData(array($attr => $updateData));
1423         }
1424         return true;
1425       }
1426     }
1427     return;
1428   }
1429   
1430   /**
1431    * Supprime un objet en relation dans l'attribut $attr de $this
1432    * 
1433    * @param[in] $object Un objet de type $objectType à supprimer
1434    * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1435    * @param[in] $objectType Le type d'objet en relation
1436    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1437    *                      - soit le dn (par defaut)
1438    *                      - soit la valeur [0] d'un attribut
1439    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1440    *                              relation avec l'objet est éditable par le user
1441    * 
1442    * @retval boolean true si l'objet à été supprimé, False sinon
1443    **/  
1444   function deleteOneObjectInRelation($object,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1445     if ((!$attr)||(!$objectType)) {
1446       LSerror :: addErrorCode('LSrelations_05','deleteOneObjectInRelation');
1447       return;
1448     }
1449     if ($object instanceof $objectType) {
1450       if ($canEditFunction) {
1451         if (!$this -> $canEditFunction()) {
1452           LSerror :: addErrorCode('LSsession_11');
1453           return;
1454         }
1455       }
1456       elseif (!LSsession::canEdit($this -> getType(), $this -> getDn(), $attr)) {
1457         LSerror :: addErrorCode('LSsession_11');
1458         return;
1459       }
1460       if ($this -> attrs[$attr] instanceof LSattribute) {
1461         if ($attrValue=='dn') {
1462           $val = $object -> getDn();
1463         }
1464         else {
1465           $val = $object -> getValue($attrValue);
1466           $val = $val[0];
1467         }
1468         $values = $this -> attrs[$attr] -> getValue();
1469         if ((!is_array($values)) && (!empty($values))) {
1470           $values = array($values);
1471         }
1472         if (is_array($values)) {
1473           $updateData=array();
1474           foreach($values as $value) {
1475             if ($value!=$val) {
1476               $updateData[]=$value;
1477             }
1478           }
1479           return $this -> _updateData(array($attr => $updateData));
1480         }
1481       }
1482     }
1483     return;
1484   }
1485   
1486  /**
1487   * Renome un objet en relation dans l'attribut $attr de $this
1488   * 
1489   * @param[in] $object Un objet de type $objectType à renomer
1490   * @param[in] $oldValue string L'ancienne valeur faisant référence à l'objet
1491   * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1492   * @param[in] $objectType Le type d'objet en relation
1493   * @param[in] $attrValue La valeur que doit avoir l'attribut :
1494   *                      - soit le dn (par defaut)
1495   *                      - soit la valeur [0] d'un attribut
1496   *  
1497   * @retval boolean True en cas de succès, False sinon
1498   */
1499   function renameOneObjectInRelation($object,$oldValue,$attr,$objectType,$attrValue='dn') {
1500     if ((!$attr)||(!$objectType)) {
1501       LSerror :: addErrorCode('LSrelations_05','renameOneObjectInRelation');
1502       return;
1503     }
1504     if ($object instanceof $objectType) {
1505       if ($this -> attrs[$attr] instanceof LSattribute) {
1506         $values = $this -> attrs[$attr] -> getValue();
1507         if ((!is_array($values)) && (!empty($values))) {
1508           $values = array($values);
1509         }
1510         if (is_array($values)) {
1511           $updateData=array();
1512           foreach($values as $value) {
1513             if ($value!=$oldValue) {
1514               $updateData[] = $value;
1515             }
1516             else {
1517               if ($attrValue=='dn') {
1518                 $val = $object -> getDn();
1519               }
1520               else {
1521                 $val = $object -> getValue($attrValue);
1522                 $val = $val[0];
1523               }
1524               $updateData[] = $val;
1525             }
1526           }
1527           return $this -> _updateData(array($attr => $updateData));
1528         }
1529       }
1530     }
1531     return;
1532   }
1533   
1534   /**
1535    * Met à jour les objets du meme type que $this en relation avec l'objet $object
1536    * de type $objectType en modifiant la valeur de leur attribut $attr des objets
1537    * en relation
1538    * 
1539    * @param[in] $object Mixed Un object (type : $this -> userObjectType) : l'utilisateur
1540    * @param[in] $listDns Array(string) Un tableau des DNs des objets en relation
1541    * @param[in] $attr L'attribut dans lequel l'utilisateur doit apparaitre
1542    * @param[in] $objectType Le type d'objet en relation
1543    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1544    *                      - soit le dn (par defaut)
1545    *                      - soit la valeur [0] d'un attribut
1546    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1547    *                              relation avec l'objet est éditable par le user
1548    * 
1549    * @retval boolean true si tout c'est bien passé, False sinon
1550    **/  
1551   function updateObjectsInRelation($object,$listDns,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1552     if ((!$attr)||(!$objectType)) {
1553       LSerror :: addErrorCode('LSrelations_05','updateObjectsInRelation');
1554       return;
1555     }
1556     $currentObjects = $this -> listObjectsInRelation($object,$attr,$objectType,$attrValue);
1557     $type=$this -> getType();
1558     if(is_array($currentObjects)) {
1559       if (is_array($listDns)) {
1560         $values=array();
1561         if ($attrValue!='dn') {
1562           $obj=new $objectType();
1563           foreach ($listDns as $dn) {
1564             $obj -> loadData($dn);
1565             $val = $obj -> getValue($attrValue);
1566             $values[$dn] = $val[0];
1567           }
1568         }
1569         else {
1570           foreach($listDns as $dn) {
1571             $values[$dn] = $dn;
1572           }
1573         }
1574         $dontDelete=array();
1575         $dontAdd=array();
1576         for ($i=0;$i<count($currentObjects);$i++) {
1577           if ($attrValue=='dn') {
1578             $val = $currentObjects[$i] -> getDn();
1579           }
1580           else {
1581             $val = $currentObjects[$i] -> getValue($attrValue);
1582             $val = $val[0];
1583           }
1584           if (in_array($val, $listDns)) {
1585             $dontDelete[$i]=true;
1586             $dontAdd[]=$val;
1587           }
1588         }
1589         
1590         for($i=0;$i<count($currentObjects);$i++) {
1591           if ($dontDelete[$i]) {
1592             continue;
1593           }
1594           else {
1595             if (!$currentObjects[$i] -> deleteOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1596               return;
1597             }
1598           }
1599         }
1600         
1601         foreach($values as $dn => $val) {
1602           if (in_array($val,$dontAdd)) {
1603             continue;
1604           }
1605           else {
1606             $obj = new $type();
1607             if ($obj -> loadData($dn)) {
1608               if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1609                 return;
1610               }
1611             }
1612             else {
1613               return;
1614             }
1615           }
1616         }
1617         return true;
1618       }
1619     }
1620     else {
1621       if(!is_array($listDns)) {
1622         return true;
1623       }
1624       foreach($listDns as $dn) {
1625         $obj = new $type();
1626         if ($obj -> loadData($dn)) {
1627           if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1628             return;
1629           }
1630         }
1631         else {
1632           return;
1633         }
1634       }
1635     }
1636   }
1637   
1638   /**
1639    * Ajouter une action lors d'un événement
1640    * 
1641    * @param[in] $event string Le nom de l'événement
1642    * @param[in] $fct string Le nom de la fonction à exectuer
1643    * @param[in] $param mixed Paramètre pour le lancement de la fonction
1644    * @param[in] $class Nom de la classe possèdant la méthode $fct à executer
1645    * 
1646    * @retval void
1647    */
1648   function addEvent($event,$fct,$param=NULL,$class=NULL) {
1649     $this -> _events[$event][] = array(
1650       'function'  => $fct,
1651       'param'    => $param,
1652       'class'     => $class
1653     );
1654   }
1655   
1656   /**
1657    * Ajouter une action sur un objet lors d'un événement
1658    * 
1659    * @param[in] $event string Le nom de l'événement
1660    * @param[in] $obj object L'objet dont la méthode doit être executé
1661    * @param[in] $meth string Le nom de la méthode
1662    * @param[in] $param mixed Paramètre d'execution de la méthode
1663    * 
1664    * @retval void
1665    */
1666   function addObjectEvent($event,&$obj,$meth,$param=NULL) {
1667     $this -> _objectEvents[$event][] = array(
1668       'obj'  => &$obj,
1669       'meth'  => $meth,
1670       'param'    => $param
1671     );
1672   }
1673   
1674   /**
1675    * Lance les actions à executer lors d'un événement
1676    * 
1677    * @param[in] $event string Le nom de l'événement
1678    * 
1679    * @retval boolean True si tout c'est bien passé, false sinon
1680    */
1681   function fireEvent($event) {
1682     
1683     // Object event
1684     $return = $this -> fireObjectEvent($event);
1685     
1686     // Config
1687     if(isset($this -> config[$event])) {
1688       if (!is_array($this -> config[$event])) {
1689         $funcs = array($this -> config[$event]);
1690       }
1691       else {
1692         $funcs = $this -> config[$event];
1693       }
1694       foreach($funcs as $func) {
1695         if(function_exists($func)) {
1696           if(!call_user_func_array($func,array(&$this))) {
1697             $return = false;
1698             LSerror :: addErrorCode('LSldapObject_07',array('func' => $func,'event' => $event));
1699           }
1700         }
1701         else {
1702           $return = false;
1703           LSerror :: addErrorCode('LSldapObject_06',array('func' => $func,'event' => $event));
1704         }
1705       }
1706     }
1707     
1708     // Binding via addEvent
1709     if (isset($this -> _events[$event]) && is_array($this -> _events[$event])) {
1710       foreach ($this -> _events[$event] as $e) {
1711         if ($e['class']) {
1712           if (class_exists($e['class'])) {
1713             $obj = new $e['class']();
1714             if (method_exists($obj,$e['fct'])) {
1715               try {
1716                 call_user_func_array(array($obj,$e['fct']),array(&$e['param']));
1717               }
1718               catch(Exception $er) {
1719                 LSerror :: addErrorCode('LSldapObject_10',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1720                 $return = false;
1721               }
1722             }
1723             else {
1724               LSerror :: addErrorCode('LSldapObject_09',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1725               $return = false;
1726             }
1727           }
1728           else {
1729             LSerror :: addErrorCode('LSldapObject_08',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1730             $return = false;
1731           }
1732         }
1733         else {
1734           if (function_exists($e['fct'])) {
1735             try {
1736               call_user_func_array($e['fct'],array(&$e['param']));
1737             }
1738             catch(Exception $er) {
1739               LSerror :: addErrorCode('LSldapObject_27',array('func' => $e['fct'],'event' => $event));
1740               $return = false;
1741             }
1742           }
1743           else {
1744             LSerror :: addErrorCode('LSldapObject_26',array('func' => $e['fct'],'event' => $event));
1745             $return = false;
1746           }
1747         }
1748       }
1749     }
1750     
1751     // Binding via addObjectEvent
1752     if (isset($this -> _objectEvents[$event]) && is_array($this -> _objectEvents[$event])) {
1753       foreach ($this -> _objectEvents[$event] as $e) {
1754         if (method_exists($e['obj'],$e['meth'])) {
1755           try {
1756             call_user_func_array(array($e['obj'], $e['meth']),array(&$e['param']));
1757           }
1758           catch(Exception $er) {
1759             LSerror :: addErrorCode('LSldapObject_29',array('meth' => $e['meth'],'event' => $event));
1760             $return = false;
1761           }
1762         }
1763         else {
1764           LSerror :: addErrorCode('LSldapObject_28',array('meth' => $e['meth'],'event' => $event));
1765           $return = false;
1766         }
1767       }
1768     }
1769     
1770     return $return;
1771   }
1772   
1773   /**
1774    * Lance les actions à executer lors d'un événement sur l'objet lui-même
1775    * 
1776    * @param[in] $event string Le nom de l'événement
1777    * 
1778    * @retval boolean True si tout c'est bien passé, false sinon
1779    **/
1780   function fireObjectEvent($event) {
1781     switch($event) {
1782       case 'after_create':
1783         return $this -> afterCreate();
1784       case 'after_delete':
1785         return $this -> afterDelete();
1786       case 'after_rename':
1787         return $this -> afterRename();
1788       case 'after_modify':
1789         return $this -> afterModify();
1790 /*
1791       case 'before_create':
1792         return $this -> beforeCreate();
1793 */
1794       case 'before_delete':
1795         return $this -> beforeDelete();
1796       case 'before_rename':
1797         return $this -> beforeRename();
1798 /*
1799       case 'before_modify':
1800         return $this -> beforeModify();
1801 */
1802     }
1803     return true;
1804   }
1805   
1806   /**
1807    * Access to infos of the object
1808    * 
1809    * @param[in] $key string The name of the value
1810    * 
1811    * @retval mixed The value
1812    **/
1813   function __get($key) {
1814     if ($key=='subDnValue') {
1815       if (isset($this -> cache['subDnValue'])) {
1816         return $this -> cache['subDnValue'];
1817       }
1818       $this -> cache['subDnValue'] = self :: getSubDnValue($this -> dn);
1819       return $this -> cache['subDnValue'];
1820     }
1821     elseif ($key=='subDnName') {
1822       if ($this -> cache['subDnName']) {
1823         return $this -> cache['subDnName'];
1824       }
1825       $this -> cache['subDnName'] = self :: getSubDnName($this -> dn);
1826       return $this -> cache['subDnName'];
1827     }
1828     elseif ($key=='rdn') {
1829       if ($this -> config['rdn'] && isset($this -> attrs[ $this -> config['rdn'] ])) {
1830         return $this -> attrs[ $this -> config['rdn'] ] -> getValue();
1831       }
1832       return false;
1833     }
1834   }
1835
1836   /**
1837    * List IOformats of this object type
1838    *
1839    * @retval mixed Array of valid IOformats of this object type
1840    **/
1841   function listValidIOformats() {
1842     $ret=array();
1843     if (isset($this -> config['ioFormat']) && is_array($this -> config['ioFormat'])) {
1844       foreach($this -> config['ioFormat'] as $name => $conf) {
1845         $ret[$name]=_((isset($conf['label'])?$conf['label']:$name));
1846       }
1847     }
1848     return $ret;
1849   }
1850
1851   /**
1852    * Check if an IOformat is valid for this object type
1853    *
1854    * @param[in] $f string The IOformat name to check
1855    *
1856    * @retval boolean True if it's a valid IOformat, false otherwise
1857    **/
1858   function isValidIOformat($f) {
1859     if (isset($this -> config['ioFormat']) && is_array($this -> config['ioFormat']) && isset($this -> config['ioFormat'][$f])) {
1860       return True;
1861     }
1862     return False;
1863   }
1864   
1865 }
1866
1867 /**
1868  * Error Codes
1869  **/
1870 LSerror :: defineError('LSldapObject_01',
1871 _("LSldapObject : Object type unknown.")
1872 );
1873 LSerror :: defineError('LSldapObject_02',
1874 _("LSldapObject : Update form is not defined for the object %{obj}.")
1875 );
1876 LSerror :: defineError('LSldapObject_03',
1877 _("LSldapObject : No form exists for the object %{obj}.")
1878 );
1879 LSerror :: defineError('LSldapObject_04',
1880 _("LSldapObject : The function %{func} to validate the attribute %{attr} the object %{obj} is unknow.")
1881 );
1882 LSerror :: defineError('LSldapObject_05',
1883 _("LSldapObject : Configuration data are missing to validate the attribute %{attr} of the object %{obj}.")
1884 );
1885
1886 LSerror :: defineError('LSldapObject_06',
1887 _("LSldapObject : The function %{func} to be executed on the object event %{event} doesn't exist.")
1888 );
1889 LSerror :: defineError('LSldapObject_07',
1890 _("LSldapObject : The %{func} execution on the object event %{event} failed.")
1891 );
1892
1893 LSerror :: defineError('LSldapObject_08',
1894 _("LSldapObject : Class %{class}, which method %{meth} to be executed on the object event %{event}, doesn't exist.")
1895 );
1896 LSerror :: defineError('LSldapObject_09',
1897 _("LSldapObject : Method %{meth} within %{class} class to be executed on object event %{event}, doesn't exist.")
1898 );
1899 LSerror :: defineError('LSldapObject_10',
1900 _("LSldapObject : Error during execute %{meth} method within %{class} class, to be executed on object event %{event}.")
1901 );
1902
1903 LSerror :: defineError('LSldapObject_11',
1904 _("LSldapObject : Some configuration data of the object type %{obj} are missing to generate the DN of the new object.")
1905 );
1906 LSerror :: defineError('LSldapObject_12',
1907 _("LSldapObject : The attibute %{attr} of the object is not yet defined. Can't generate DN.")
1908 );
1909 LSerror :: defineError('LSldapObject_13',
1910 _("LSldapObject : Without DN, the object could not be changed.")
1911 );
1912 LSerror :: defineError('LSldapObject_14',
1913 _("LSldapObject : The attribute %{attr_depend} depending on the attribute %{attr} doesn't exist.")
1914 );
1915 LSerror :: defineError('LSldapObject_15',
1916 _("LSldapObject : Error during deleting the object %{objectname}.")
1917 );
1918
1919 LSerror :: defineError('LSldapObject_16',
1920 _("LSldapObject : Error during actions to be executed before renaming the objet.")
1921 );
1922 LSerror :: defineError('LSldapObject_17',
1923 _("LSldapObject : Error during actions to be executed after renaming the objet.")
1924 );
1925
1926 LSerror :: defineError('LSldapObject_18',
1927 _("LSldapObject : Error during actions to be executed before deleting the objet.")
1928 );
1929 LSerror :: defineError('LSldapObject_19',
1930 _("LSldapObject : Error during actions to be executed after deleting the objet.")
1931 );
1932
1933 LSerror :: defineError('LSldapObject_20',
1934 _("LSldapObject : Error during the actions to be executed before creating the object.")
1935 );
1936 LSerror :: defineError('LSldapObject_21',
1937 _("LSldapObject : Error during the actions to be executed after creating the object. It was created anyway.")
1938 );
1939
1940 LSerror :: defineError('LSldapObject_22',
1941 _("LSldapObject : The function %{func} to be executed before creating the object doesn't exist.")
1942 );
1943 LSerror :: defineError('LSldapObject_23',
1944 _("LSldapObject : Error executing the function %{func} to be execute after deleting the object.")
1945 );
1946 LSerror :: defineError('LSldapObject_24',
1947 _("LSldapObject : The function %{func} to be executed after deleting the object doesn't exist.")
1948 );
1949 LSerror :: defineError('LSldapObject_25',
1950 _("LSldapObject : Error executing the function %{func} to be execute after creating the object.")
1951 );
1952
1953 LSerror :: defineError('LSldapObject_26',
1954 _("LSldapObject : %{func} function, to be executed on object event %{event}, doesn't exist.")
1955 );
1956 LSerror :: defineError('LSldapObject_27',
1957 _("LSldapObject : Error during the execution of %{func} function on object event %{event}.")
1958 );
1959
1960 LSerror :: defineError('LSldapObject_28',
1961 _("LSldapObject : %{meth} method, to be executed on object event %{event}, doesn't exist.")
1962 );
1963 LSerror :: defineError('LSldapObject_29',
1964 _("LSldapObject : Error during execution of %{meth} method on object event %{event}.")
1965 );
1966 LSerror :: defineError('LSldapObject_30',
1967 _("LSldapObject : Error during generate LDAP filter for %{LSobject}.")
1968 );
1969
1970 LSerror :: defineError('LSldapObject_31',
1971 _("LSldapObject : Error during execution of the custom action %{customAction} on %{objectname}.")
1972 );
1973
1974 // LSrelation
1975 LSerror :: defineError('LSrelations_05',
1976 _("LSrelation : Some parameters are missing in the call of methods to handle standard relations (Method : %{meth}).")
1977 );
1978
1979 ?>