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