LSrelation : add possibilty to handle relation with multiple key values
[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] $attrValues La/les valeur(s) que doit/peut avoir l'attribut :
1311    *                        - soit le dn (par defaut)
1312    *                        - soit une des valeurs d'un attribut
1313    * 
1314    * @retval Mixed La valeur clef d'un objet en relation
1315    **/
1316   function getObjectKeyValueInRelation($object,$objectType,$attrValues='dn') {
1317     if (!$objectType) {
1318       LSerror :: addErrorCode('LSrelations_05','getObjectKeyValueInRelation');
1319       return;
1320     }
1321     if (!is_array($attrValues)) $attrValues=array($attrValues);
1322     $keyValues=array();
1323     foreach ($attrValues as $attrValue) {
1324       if ($attrValue=='dn') {
1325         $dn=$object -> getDn();
1326         if (!in_array($dn,$keyValues))
1327           $keyValues[] = $dn;
1328       }
1329       else {
1330         foreach ($object -> getValue($attrValue) as $keyValue)
1331           if (!in_array($keyValue,$keyValues)) $keyValues[]=$keyValue;
1332       }
1333     }
1334     return $keyValues;
1335   }
1336   
1337   /**
1338    * Retourne la liste des relations pour l'objet en fonction de sa présence 
1339    * dans un des attributs
1340    * 
1341    * Retourne un tableau de d'objet (type : $objectType) correspondant à la 
1342    * relation entre l'objet $object et les objets de type $objectType. Cette relation
1343    * est établis par la présence de la valeur de référence à l'objet dans 
1344    * l'attribut des objets de type $objectType.
1345    * 
1346    * @param[in] $object Un object de type $objectType
1347    * @param[in] $attr L'attribut dans lequel l'objet doit apparaitre
1348    * @param[in] $objectType Le type d'objet en relation
1349    * @param[in] $attrValues La/les valeur(s) que doit/peut avoir l'attribut :
1350    *                      - soit le dn (par defaut)
1351    *                      - soit une des valeurs d'un attribut
1352    * 
1353    * @retval Array of $objectType Les objets en relations
1354    **/
1355   function listObjectsInRelation($object,$attr,$objectType,$attrValues='dn') {
1356     if ((!$attr)||(!$objectType)) {
1357       LSerror :: addErrorCode('LSrelations_05','listObjectsInRelation');
1358       return;
1359     }
1360     if (!is_array($attrValues)) $attrValues=array($attrValues);
1361     $keyValues=self :: getObjectKeyValueInRelation($object,$objectType,$attrValues);
1362     if (!empty($keyValues)) {
1363       $keyValuesFilters=array();
1364       foreach($keyValues as $keyValue) {
1365         $keyValuesFilters[] = Net_LDAP2_Filter::create($attr,'equals',$keyValue);
1366       }
1367       $filter = LSldap::combineFilters('or', $keyValuesFilters);
1368       return $this -> listObjects($filter,LSsession :: getRootDn(),array('scope' => 'sub','recursive' => true,'withoutCache'=>true, 'onlyAccessible' => false));
1369     }
1370
1371     return array();
1372   }
1373
1374   /**
1375    * Ajoute un objet en relation dans l'attribut $attr de $this
1376    * 
1377    * @param[in] $object Un objet de type $objectType à ajouter
1378    * @param[in] $attr L'attribut dans lequel l'objet doit être ajouté
1379    * @param[in] $objectType Le type d'objet en relation
1380    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1381    *                      - soit le dn (par defaut)
1382    *                      - soit la valeur [0] d'un attribut
1383    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1384    *                              relation avec l'objet est éditable par le user
1385    * 
1386    * @retval boolean true si l'objet à été ajouté, False sinon
1387    **/  
1388   function addOneObjectInRelation($object,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL) {
1389     if ((!$attr)||(!$objectType)) {
1390       LSerror :: addErrorCode('LSrelations_05','addOneObjectInRelation');
1391       return;
1392     }
1393     if ($object instanceof $objectType) {
1394       if ($canEditFunction) {
1395         if (!$this -> $canEditFunction()) {
1396           LSerror :: addErrorCode('LSsession_11');
1397           return;
1398         }
1399       }
1400       elseif (!LSsession::canEdit($this -> getType(), $this -> getDn(), $attr)) {
1401         LSerror :: addErrorCode('LSsession_11');
1402         return;
1403       }
1404       if ($this -> attrs[$attr] instanceof LSattribute) {
1405         if ($attrValue=='dn') {
1406           $val = $object -> getDn();
1407         }
1408         else {
1409           $val = $object -> getValue($attrValue);
1410           $val = $val[0];
1411         }
1412         $values = $this -> attrs[$attr] -> getValue();
1413         if ($this -> attrs[$attr] -> config['multiple']) {
1414           if (!is_array($values)) {
1415             $updateData = array($val);
1416           }
1417           else if (!in_array($val,$values)) {
1418             $values[]=$val;
1419             $updateData = $values;
1420           }
1421         }
1422         else {
1423           if (($values[0]!=$val)&&($values!=$val)) {
1424             $updateData = array($val);
1425           }
1426         }
1427         if (isset($updateData)) {
1428           return $this -> _updateData(array($attr => $updateData));
1429         }
1430         return true;
1431       }
1432     }
1433     return;
1434   }
1435   
1436   /**
1437    * Supprime un objet en relation dans l'attribut $attr de $this
1438    * 
1439    * @param[in] $object Un objet de type $objectType à supprimer
1440    * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1441    * @param[in] $objectType Le type d'objet en relation
1442    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1443    *                      - soit le dn (par defaut)
1444    *                      - soit la valeur [0] d'un attribut
1445    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1446    *                              relation avec l'objet est éditable par le user
1447    * @param[in] $attrValues L'ensembe des valeurs que peut avoir l'attribut avant mise à jour :
1448    *                        - soit le dn (par defaut)
1449    *                        - soit une des valeurs d'un attribut
1450    * 
1451    * @retval boolean true si l'objet à été supprimé, False sinon
1452    **/  
1453   function deleteOneObjectInRelation($object,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL,$attrValues=null) {
1454     if ((!$attr)||(!$objectType)) {
1455       LSerror :: addErrorCode('LSrelations_05','deleteOneObjectInRelation');
1456       return;
1457     }
1458     if ($object instanceof $objectType) {
1459       if ($canEditFunction) {
1460         if (!$this -> $canEditFunction()) {
1461           LSerror :: addErrorCode('LSsession_11');
1462           return;
1463         }
1464       }
1465       elseif (!LSsession::canEdit($this -> getType(), $this -> getDn(), $attr)) {
1466         LSerror :: addErrorCode('LSsession_11');
1467         return;
1468       }
1469       if ($this -> attrs[$attr] instanceof LSattribute) {
1470         if (!is_array($attrValues)) $attrValues=array($attrValue);
1471         $keyValues=self :: getObjectKeyValueInRelation($object,$objectType,$attrValues);
1472         $values = $this -> attrs[$attr] -> getValue();
1473         if ((!is_array($values)) && (!empty($values))) {
1474           $values = array($values);
1475         }
1476         if (is_array($values)) {
1477           $updateData=array();
1478           foreach($values as $value) {
1479             if (!in_array($value,$keyValues)) {
1480               $updateData[]=$value;
1481             }
1482           }
1483           return $this -> _updateData(array($attr => $updateData));
1484         }
1485       }
1486     }
1487     return;
1488   }
1489   
1490  /**
1491   * Renome un objet en relation dans l'attribut $attr de $this
1492   * 
1493   * @param[in] $object Un objet de type $objectType à renomer
1494   * @param[in] $oldValues array|string Le(s) ancienne(s) valeur(s possible(s)
1495   *                                    faisant référence à l'objet
1496   * @param[in] $attr L'attribut dans lequel l'objet doit être supprimé
1497   * @param[in] $objectType Le type d'objet en relation
1498   * @param[in] $attrValue La valeur que doit avoir l'attribut :
1499   *                      - soit le dn (par defaut)
1500   *                      - soit la valeur [0] d'un attribut
1501   *  
1502   * @retval boolean True en cas de succès, False sinon
1503   */
1504   function renameOneObjectInRelation($object,$oldValues,$attr,$objectType,$attrValue='dn') {
1505     if ((!$attr)||(!$objectType)) {
1506       LSerror :: addErrorCode('LSrelations_05','renameOneObjectInRelation');
1507       return;
1508     }
1509     if (!is_array($oldValues)) $oldValues=array($oldValues);
1510     if ($object instanceof $objectType) {
1511       if ($this -> attrs[$attr] instanceof LSattribute) {
1512         $values = $this -> attrs[$attr] -> getValue();
1513         if ((!is_array($values)) && (!empty($values))) {
1514           $values = array($values);
1515         }
1516         if (is_array($values)) {
1517           $updateData=array();
1518           foreach($values as $value) {
1519             if (!in_array($value,$oldValues)) {
1520               $updateData[] = $value;
1521             }
1522             else {
1523               if ($attrValue=='dn') {
1524                 $val = $object -> getDn();
1525               }
1526               else {
1527                 $val = $object -> getValue($attrValue);
1528                 $val = $val[0];
1529               }
1530               $updateData[] = $val;
1531             }
1532           }
1533           return $this -> _updateData(array($attr => $updateData));
1534         }
1535       }
1536     }
1537     return;
1538   }
1539   
1540   /**
1541    * Met à jour les objets du meme type que $this en relation avec l'objet $object
1542    * de type $objectType en modifiant la valeur de leur attribut $attr des objets
1543    * en relation
1544    * 
1545    * @param[in] $object Mixed Un object (type : $this -> userObjectType) : l'utilisateur
1546    * @param[in] $listDns Array(string) Un tableau des DNs des objets en relation
1547    * @param[in] $attr L'attribut dans lequel l'utilisateur doit apparaitre
1548    * @param[in] $objectType Le type d'objet en relation
1549    * @param[in] $attrValue La valeur que doit avoir l'attribut :
1550    *                      - soit le dn (par defaut)
1551    *                      - soit la valeur [0] d'un attribut
1552    * @param[in] $canEditFunction  Le nom de la fonction pour vérifier que la
1553    *                              relation avec l'objet est éditable par le user
1554    * @param[in] $attrValues L'ensembe des valeurs que peut avoir l'attribut avant mise à jour :
1555    *                        - soit le dn (par defaut)
1556    *                        - soit une des valeurs d'un attribut
1557    * 
1558    * @retval boolean true si tout c'est bien passé, False sinon
1559    **/  
1560   function updateObjectsInRelation($object,$listDns,$attr,$objectType,$attrValue='dn',$canEditFunction=NULL,$attrValues=null) {
1561     if ((!$attr)||(!$objectType)) {
1562       LSerror :: addErrorCode('LSrelations_05','updateObjectsInRelation');
1563       return;
1564     }
1565     if (!is_array($attrValues)) $attrValues=array($attrValue);
1566     $currentDns=array();
1567     $currentObjects = $this -> listObjectsInRelation($object,$attr,$objectType,$attrValues);
1568     if(is_array($currentObjects)) {
1569       for ($i=0;$i<count($currentObjects);$i++) {
1570         $currentDns[]=$currentObjects[$i] -> getDn();
1571       }
1572     }
1573     $dontTouch=array_intersect($listDns,$currentDns);
1574
1575     for($i=0;$i<count($currentObjects);$i++) {
1576       if (in_array($currentObjects[$i] -> getDn(),$dontTouch)) continue;
1577       if (!$currentObjects[$i] -> deleteOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction,$attrValues)) {
1578         return;
1579       }
1580     }
1581
1582     $type=$this -> getType();
1583     foreach($listDns as $dn) {
1584       if (in_array($dn,$dontTouch)) continue;
1585       $obj = new $type();
1586       if ($obj -> loadData($dn)) {
1587         if (!$obj -> addOneObjectInRelation($object,$attr,$objectType,$attrValue,$canEditFunction)) {
1588           return;
1589         }
1590       }
1591       else {
1592         return;
1593       }
1594     }
1595     return true;
1596   }
1597   
1598   /**
1599    * Ajouter une action lors d'un événement
1600    * 
1601    * @param[in] $event string Le nom de l'événement
1602    * @param[in] $fct string Le nom de la fonction à exectuer
1603    * @param[in] $param mixed Paramètre pour le lancement de la fonction
1604    * @param[in] $class Nom de la classe possèdant la méthode $fct à executer
1605    * 
1606    * @retval void
1607    */
1608   function addEvent($event,$fct,$param=NULL,$class=NULL) {
1609     $this -> _events[$event][] = array(
1610       'function'  => $fct,
1611       'param'    => $param,
1612       'class'     => $class
1613     );
1614   }
1615   
1616   /**
1617    * Ajouter une action sur un objet lors d'un événement
1618    * 
1619    * @param[in] $event string Le nom de l'événement
1620    * @param[in] $obj object L'objet dont la méthode doit être executé
1621    * @param[in] $meth string Le nom de la méthode
1622    * @param[in] $param mixed Paramètre d'execution de la méthode
1623    * 
1624    * @retval void
1625    */
1626   function addObjectEvent($event,&$obj,$meth,$param=NULL) {
1627     $this -> _objectEvents[$event][] = array(
1628       'obj'  => &$obj,
1629       'meth'  => $meth,
1630       'param'    => $param
1631     );
1632   }
1633   
1634   /**
1635    * Lance les actions à executer lors d'un événement
1636    * 
1637    * @param[in] $event string Le nom de l'événement
1638    * 
1639    * @retval boolean True si tout c'est bien passé, false sinon
1640    */
1641   function fireEvent($event) {
1642     
1643     // Object event
1644     $return = $this -> fireObjectEvent($event);
1645     
1646     // Config
1647     if(isset($this -> config[$event])) {
1648       if (!is_array($this -> config[$event])) {
1649         $funcs = array($this -> config[$event]);
1650       }
1651       else {
1652         $funcs = $this -> config[$event];
1653       }
1654       foreach($funcs as $func) {
1655         if(function_exists($func)) {
1656           if(!call_user_func_array($func,array(&$this))) {
1657             $return = false;
1658             LSerror :: addErrorCode('LSldapObject_07',array('func' => $func,'event' => $event));
1659           }
1660         }
1661         else {
1662           $return = false;
1663           LSerror :: addErrorCode('LSldapObject_06',array('func' => $func,'event' => $event));
1664         }
1665       }
1666     }
1667     
1668     // Binding via addEvent
1669     if (isset($this -> _events[$event]) && is_array($this -> _events[$event])) {
1670       foreach ($this -> _events[$event] as $e) {
1671         if ($e['class']) {
1672           if (class_exists($e['class'])) {
1673             $obj = new $e['class']();
1674             if (method_exists($obj,$e['fct'])) {
1675               try {
1676                 call_user_func_array(array($obj,$e['fct']),array(&$e['param']));
1677               }
1678               catch(Exception $er) {
1679                 LSerror :: addErrorCode('LSldapObject_10',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1680                 $return = false;
1681               }
1682             }
1683             else {
1684               LSerror :: addErrorCode('LSldapObject_09',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1685               $return = false;
1686             }
1687           }
1688           else {
1689             LSerror :: addErrorCode('LSldapObject_08',array('class' => $e['class'],'meth' => $e['fct'],'event' => $event));
1690             $return = false;
1691           }
1692         }
1693         else {
1694           if (function_exists($e['fct'])) {
1695             try {
1696               call_user_func_array($e['fct'],array(&$e['param']));
1697             }
1698             catch(Exception $er) {
1699               LSerror :: addErrorCode('LSldapObject_27',array('func' => $e['fct'],'event' => $event));
1700               $return = false;
1701             }
1702           }
1703           else {
1704             LSerror :: addErrorCode('LSldapObject_26',array('func' => $e['fct'],'event' => $event));
1705             $return = false;
1706           }
1707         }
1708       }
1709     }
1710     
1711     // Binding via addObjectEvent
1712     if (isset($this -> _objectEvents[$event]) && is_array($this -> _objectEvents[$event])) {
1713       foreach ($this -> _objectEvents[$event] as $e) {
1714         if (method_exists($e['obj'],$e['meth'])) {
1715           try {
1716             call_user_func_array(array($e['obj'], $e['meth']),array(&$e['param']));
1717           }
1718           catch(Exception $er) {
1719             LSerror :: addErrorCode('LSldapObject_29',array('meth' => $e['meth'],'event' => $event));
1720             $return = false;
1721           }
1722         }
1723         else {
1724           LSerror :: addErrorCode('LSldapObject_28',array('meth' => $e['meth'],'event' => $event));
1725           $return = false;
1726         }
1727       }
1728     }
1729     
1730     return $return;
1731   }
1732   
1733   /**
1734    * Lance les actions à executer lors d'un événement sur l'objet lui-même
1735    * 
1736    * @param[in] $event string Le nom de l'événement
1737    * 
1738    * @retval boolean True si tout c'est bien passé, false sinon
1739    **/
1740   function fireObjectEvent($event) {
1741     switch($event) {
1742       case 'after_create':
1743         return $this -> afterCreate();
1744       case 'after_delete':
1745         return $this -> afterDelete();
1746       case 'after_rename':
1747         return $this -> afterRename();
1748       case 'after_modify':
1749         return $this -> afterModify();
1750 /*
1751       case 'before_create':
1752         return $this -> beforeCreate();
1753 */
1754       case 'before_delete':
1755         return $this -> beforeDelete();
1756       case 'before_rename':
1757         return $this -> beforeRename();
1758 /*
1759       case 'before_modify':
1760         return $this -> beforeModify();
1761 */
1762     }
1763     return true;
1764   }
1765   
1766   /**
1767    * Access to infos of the object
1768    * 
1769    * @param[in] $key string The name of the value
1770    * 
1771    * @retval mixed The value
1772    **/
1773   function __get($key) {
1774     if ($key=='subDnValue') {
1775       if (isset($this -> cache['subDnValue'])) {
1776         return $this -> cache['subDnValue'];
1777       }
1778       $this -> cache['subDnValue'] = self :: getSubDnValue($this -> dn);
1779       return $this -> cache['subDnValue'];
1780     }
1781     elseif ($key=='subDnName') {
1782       if ($this -> cache['subDnName']) {
1783         return $this -> cache['subDnName'];
1784       }
1785       $this -> cache['subDnName'] = self :: getSubDnName($this -> dn);
1786       return $this -> cache['subDnName'];
1787     }
1788     elseif ($key=='rdn') {
1789       if ($this -> config['rdn'] && isset($this -> attrs[ $this -> config['rdn'] ])) {
1790         return $this -> attrs[ $this -> config['rdn'] ] -> getValue();
1791       }
1792       return false;
1793     }
1794   }
1795
1796   /**
1797    * List IOformats of this object type
1798    *
1799    * @retval mixed Array of valid IOformats of this object type
1800    **/
1801   function listValidIOformats() {
1802     $ret=array();
1803     if (isset($this -> config['ioFormat']) && is_array($this -> config['ioFormat'])) {
1804       foreach($this -> config['ioFormat'] as $name => $conf) {
1805         $ret[$name]=_((isset($conf['label'])?$conf['label']:$name));
1806       }
1807     }
1808     return $ret;
1809   }
1810
1811   /**
1812    * Check if an IOformat is valid for this object type
1813    *
1814    * @param[in] $f string The IOformat name to check
1815    *
1816    * @retval boolean True if it's a valid IOformat, false otherwise
1817    **/
1818   function isValidIOformat($f) {
1819     if (isset($this -> config['ioFormat']) && is_array($this -> config['ioFormat']) && isset($this -> config['ioFormat'][$f])) {
1820       return True;
1821     }
1822     return False;
1823   }
1824   
1825 }
1826
1827 /**
1828  * Error Codes
1829  **/
1830 LSerror :: defineError('LSldapObject_01',
1831 _("LSldapObject : Object type unknown.")
1832 );
1833 LSerror :: defineError('LSldapObject_02',
1834 _("LSldapObject : Update form is not defined for the object %{obj}.")
1835 );
1836 LSerror :: defineError('LSldapObject_03',
1837 _("LSldapObject : No form exists for the object %{obj}.")
1838 );
1839 LSerror :: defineError('LSldapObject_04',
1840 _("LSldapObject : The function %{func} to validate the attribute %{attr} the object %{obj} is unknow.")
1841 );
1842 LSerror :: defineError('LSldapObject_05',
1843 _("LSldapObject : Configuration data are missing to validate the attribute %{attr} of the object %{obj}.")
1844 );
1845
1846 LSerror :: defineError('LSldapObject_06',
1847 _("LSldapObject : The function %{func} to be executed on the object event %{event} doesn't exist.")
1848 );
1849 LSerror :: defineError('LSldapObject_07',
1850 _("LSldapObject : The %{func} execution on the object event %{event} failed.")
1851 );
1852
1853 LSerror :: defineError('LSldapObject_08',
1854 _("LSldapObject : Class %{class}, which method %{meth} to be executed on the object event %{event}, doesn't exist.")
1855 );
1856 LSerror :: defineError('LSldapObject_09',
1857 _("LSldapObject : Method %{meth} within %{class} class to be executed on object event %{event}, doesn't exist.")
1858 );
1859 LSerror :: defineError('LSldapObject_10',
1860 _("LSldapObject : Error during execute %{meth} method within %{class} class, to be executed on object event %{event}.")
1861 );
1862
1863 LSerror :: defineError('LSldapObject_11',
1864 _("LSldapObject : Some configuration data of the object type %{obj} are missing to generate the DN of the new object.")
1865 );
1866 LSerror :: defineError('LSldapObject_12',
1867 _("LSldapObject : The attibute %{attr} of the object is not yet defined. Can't generate DN.")
1868 );
1869 LSerror :: defineError('LSldapObject_13',
1870 _("LSldapObject : Without DN, the object could not be changed.")
1871 );
1872 LSerror :: defineError('LSldapObject_14',
1873 _("LSldapObject : The attribute %{attr_depend} depending on the attribute %{attr} doesn't exist.")
1874 );
1875 LSerror :: defineError('LSldapObject_15',
1876 _("LSldapObject : Error during deleting the object %{objectname}.")
1877 );
1878
1879 LSerror :: defineError('LSldapObject_16',
1880 _("LSldapObject : Error during actions to be executed before renaming the objet.")
1881 );
1882 LSerror :: defineError('LSldapObject_17',
1883 _("LSldapObject : Error during actions to be executed after renaming the objet.")
1884 );
1885
1886 LSerror :: defineError('LSldapObject_18',
1887 _("LSldapObject : Error during actions to be executed before deleting the objet.")
1888 );
1889 LSerror :: defineError('LSldapObject_19',
1890 _("LSldapObject : Error during actions to be executed after deleting the objet.")
1891 );
1892
1893 LSerror :: defineError('LSldapObject_20',
1894 _("LSldapObject : Error during the actions to be executed before creating the object.")
1895 );
1896 LSerror :: defineError('LSldapObject_21',
1897 _("LSldapObject : Error during the actions to be executed after creating the object. It was created anyway.")
1898 );
1899
1900 LSerror :: defineError('LSldapObject_22',
1901 _("LSldapObject : The function %{func} to be executed before creating the object doesn't exist.")
1902 );
1903 LSerror :: defineError('LSldapObject_23',
1904 _("LSldapObject : Error executing the function %{func} to be execute after deleting the object.")
1905 );
1906 LSerror :: defineError('LSldapObject_24',
1907 _("LSldapObject : The function %{func} to be executed after deleting the object doesn't exist.")
1908 );
1909 LSerror :: defineError('LSldapObject_25',
1910 _("LSldapObject : Error executing the function %{func} to be execute after creating the object.")
1911 );
1912
1913 LSerror :: defineError('LSldapObject_26',
1914 _("LSldapObject : %{func} function, to be executed on object event %{event}, doesn't exist.")
1915 );
1916 LSerror :: defineError('LSldapObject_27',
1917 _("LSldapObject : Error during the execution of %{func} function on object event %{event}.")
1918 );
1919
1920 LSerror :: defineError('LSldapObject_28',
1921 _("LSldapObject : %{meth} method, to be executed on object event %{event}, doesn't exist.")
1922 );
1923 LSerror :: defineError('LSldapObject_29',
1924 _("LSldapObject : Error during execution of %{meth} method on object event %{event}.")
1925 );
1926 LSerror :: defineError('LSldapObject_30',
1927 _("LSldapObject : Error during generate LDAP filter for %{LSobject}.")
1928 );
1929
1930 LSerror :: defineError('LSldapObject_31',
1931 _("LSldapObject : Error during execution of the custom action %{customAction} on %{objectname}.")
1932 );
1933
1934 // LSrelation
1935 LSerror :: defineError('LSrelations_05',
1936 _("LSrelation : Some parameters are missing in the call of methods to handle standard relations (Method : %{meth}).")
1937 );
1938
1939 ?>