Code

Updated property handling
[gosa.git] / gosa-core / include / class_configRegistry.inc
1 <?php
3 class configRegistry{
5     public $config = NULL;
6     public $properties = array();
7     public $ldapStoredProperties = array(); 
8     public $fileStoredProperties = array(); 
9     public $classToName = array(); 
11     public $status = 'none';
13     // Excludes property values defined in ldap 
14     public $ignoreLdapProperties = FALSE;
16     // Contains all classes with plInfo
17     public $classesWithInfo = array();
19     function __construct($config)
20     {
21         $this->config = &$config;
23         // Detect classes that have a plInfo method 
24         global $class_mapping;
25         foreach ($class_mapping as $cname => $path){
26             $cmethods = get_class_methods($cname);
27             if (is_array($cmethods) && in_array_ics('plInfo',$cmethods)){
29                 // Get plugin definitions
30                 $def = call_user_func(array($cname, 'plInfo'));;
32                 // Register Post Events (postmodfiy,postcreate,postremove,checkhook)
33                 if(count($def)){
34                     $this->classesWithInfo[$cname] = $def;
35                 }
36             }
37         }
39         // (Re)Load properties
40         $this->reload();
41     }
43     function reload($force = FALSE)
44     {
45         // Do not reload the properties everytime, once we have  
46         //  everything loaded and registrered skip the reload.
47         // Status is 'finished' once we had a ldap connection (logged in)
48         if(!$force && $this->status == 'finished') return;
50         // Reset everything
51         $this->ldapStoredProperties = array();
52         $this->fileStoredProperties = array();
53         $this->properties = array();
54         $this->mapByName = array();
56         // Search for config flags defined in the config file (TAB section)
57         foreach($this->config->data['TABS'] as $tabname => $tabdefs){
58             foreach($tabdefs as $info){
60                 // Check if the info is valid
61                 if(isset($info['NAME']) && isset($info['CLASS'])){
63                     // Check if there is nore than just the plugin definition
64                     if(count($info) > 2){
65                         foreach($info as $name => $value){
66                             
67                             if(!in_array($name, array('CLASS','NAME'))){
68                                 $class= $info['CLASS'];    
69                                 $this->fileStoredProperties[$class][strtolower($name)] = $value;
70                             }
71                         }
72                     }
73                 }
74             }
75         }
77         // Search for config flags defined in the config file (MENU section)
78         foreach($this->config->data['MENU'] as $section => $entries){
79             foreach($entries as $entry){
80                 if(count($entry) > 2 && isset($entry['CLASS'])){
81                     $class = $entry['CLASS'];
82                     foreach($entry as $name => $value){
83                         if(!in_array($name, array('CLASS','ACL'))){
84                             $this->fileStoredProperties[strtolower($class)][strtolower($name)] = $value;
85                         }
86                     }
87                 }
88             }
89         }
91         // Search for config flags defined in the config file (MAIN section)
92         foreach($this->config->data['MAIN'] as $name => $value){
93             $this->fileStoredProperties['core'][strtolower($name)] = $value;
94         }
96         // Search for config flags defined in the config file (Current LOCATION section)
97         if(isset($this->config->current)){
98             foreach($this->config->current as $name => $value){
99                 $this->fileStoredProperties['core'][strtolower($name)] = $value;
100             }
101         }
103         // Skip searching for LDAP defined properties if 'ignoreLdapProperties' is set to 'true'
104         //  in the config. 
105         $this->ignoreLdapProperties = FALSE;
106         if(isset($this->fileStoredProperties['core'][strtolower('ignoreLdapProperties')]) && 
107             preg_match("/(true|on)/i", $this->fileStoredProperties['core'][strtolower('ignoreLdapProperties')])){
108             $this->ignoreLdapProperties = TRUE;
109         }
111         // Search for all config flags defined in the LDAP - BUT only if we ARE logged in. 
112         if(!empty($this->config->current['CONFIG'])){
113             $ldap = $this->config->get_ldap_link();
114             $ldap->cd($this->config->current['CONFIG']);
115             $ldap->search('(&(objectClass=gosaConfig)(gosaSetting=*))', array('cn','gosaSetting'));
116             while($attrs = $ldap->fetch()){
117                 $class = $attrs['cn'][0];
118                 for($i=0; $i<$attrs['gosaSetting']['count']; $i++){
119                     list($name,$value) = preg_split("/:/",$attrs['gosaSetting'][$i],2);
120                     $this->ldapStoredProperties[$class][$name] = $value;
121                 }
122             }
123             $this->status = 'finished';
124         }
126         // Register plugin properties.
127         foreach ($this->classesWithInfo as $cname => $def){
129             // Detect class name
130             $name = $cname;
131             $name = (isset($def['plShortName'])) ? $def['plShortName'] : $cname;
132             $name = (isset($def['plDescription'])) ? $def['plDescription'] : $cname;
134             // Register post events
135             $this->classToName[$cname] = $name;
136             $data = array('name' => 'postcreate','type' => 'command');
137             $this->register($cname, $data);    
138             $data = array('name' => 'postremove','type' => 'command');
139             $this->register($cname, $data);    
140             $data = array('name' => 'postmodify','type' => 'command');
141             $this->register($cname, $data);    
142             $data = array('name' => 'check', 'type' => 'command');
143             $this->register($cname, $data);    
145             // Register properties 
146             if(isset($def['plProperties'])){
147                 foreach($def['plProperties'] as $property){
148                     $this->register($cname, $property);
149                 }
150             }
151         }
152     }
154     function register($class,$data)
155     {
156         $id = count($this->properties);
157         $this->properties[$id] = new gosaProperty($this,$class,$data);
158         $p = strtolower("{$class}::{$data['name']}");
159         $this->mapByName[$p] = $id;
160     }
162     public function getAllProperties()
163     {
164         return($this->properties);
165     }
167     function propertyExists($class,$name)
168     {       
169         $p = strtolower("{$class}::{$name}");
170         return(isset($this->mapByName[$p]));
171     }
173     private function getId($class,$name)
174     {
175         $p = strtolower("{$class}::{$name}");
176         if(!isset($this->mapByName[$p])){
177             return(-1);
178         }       
179         return($this->mapByName[$p]);    
180     }
182     function getProperty($class,$name)
183     {
184         if($this->propertyExists($class,$name)){
185             return($this->properties[$this->getId($class,$name)]);
186         }
187         return(NULL); 
188     }
190     function getPropertyValue($class,$name)
191     {   
192         if($this->propertyExists($class,$name)){
193             $tmp = $this->getProperty($class,$name);
194             return($tmp->getValue());
195         }
196         return("");
197     }
199     function setPropertyValue($class,$name, $value)
200     {   
201         if($this->propertyExists($class,$name)){
202             $tmp = $this->getProperty($class,$name);
203             return($tmp->setValue($value));
204         }
205         return("");
206     }
208     function saveChanges()
209     {
210         $migrate = array();
211         foreach($this->properties as $prop){
213             // Is this property modified
214             if(in_array($prop->getStatus(),array('modified','removed'))){
216                 // Check if we've to migrate something before we can make the changes effective. 
217                 if($prop->migrationRequired()){
218                     $migrate[] = $prop;
219                 }else{
220                     $prop->save();
221                 }
222             }
223         }
224         return($migrate);
225     }
229 class gosaProperty
231     protected $name = "";
232     protected $class = "";
233     protected $value = "";
234     protected $tmp_value = "";  // Used when modified but not saved 
235     protected $type = "string";
236     protected $default = "";
237     protected $defaults = "";
238     protected $description = "";
239     protected $check = "";
240     protected $migrate = "";
241     protected $mandatory = FALSE;
242     protected $group = "default";
243     protected $parent = NULL;
244     protected $data = array();
246     protected $migrationClass = NULL;
248     /*!  The current property status
249      *     'ldap'       Property is stored in ldap 
250      *     'file'       Property is stored in the config file
251      *     'undefined'  Property is currently not stored anywhere
252      *     'modified'   Property has been modified (should be saved)
253      */
254     protected $status = 'undefined';
256     protected $attributes = array('name','type','default','description','check',
257             'migrate','mandatory','group','defaults');
262     function __construct($parent,$classname,$data)
263     {
264         // Set some basic infos 
265         $this->parent = &$parent;
266         $this->class = $classname;
267         $this->data  = $data;
269         // Get all relevant information from the data array (comes from plInfo)    
270         foreach($this->attributes as $aName){
271             if(isset($data[$aName])){
272                 $this->$aName = $data[$aName];
273             }
274         }      
276         // Initialize with the current value
277         $this->_restoreCurrentValue(); 
279     }
281     function migrationRequired()
282     {
283         // Instantiate migration class 
284         if(!empty($this->migrate) && $this->migrationClass == NULL){
285             if(!class_available($this->migrate)){
286                 trigger_error("Cannot start migration for gosaProperty::'{$this->getName()}' class not found ({$this->migrate})!");
287             }else{
288                 $class = $this->migrate;
289                 $tmp = new $class($this->parent->config,$this);
290                 if(! $tmp instanceof propertyMigration){ 
291                     trigger_error("Cannot start migration for gosaProperty::'{$this->getName()}' doesn't implement propertyMigration!");
292                 }else{
293                     $this->migrationClass = $tmp;
294                 }
295             }
296         }
297         if(empty($this->migrate) || $this->migrationClass == NULL){
298             return(FALSE);
299         }
300         return($this->migrationClass->checkForIssues());
301     }
303     function getMigrationClass()
304     {
305         return($this->migrationClass);
306     }
308     function check()
309     {
310         $val = $this->getValue(TRUE);
311         $return = TRUE;
312         if($this->mandatory && empty($val)){
313             $return = FALSE;
314         }
316         $check = $this->getCheck();
317         if(!empty($val) && !empty($check)){
318             $res = call_user_func(preg_split("/::/", $this->check),$messages=TRUE, $this->class,$this->name,$val, $this->type);
319             if(!$res){
320                 $return = FALSE;
321             }
322         }
323         return($return);
324     }
326     static function isBool($message,$class,$name,$value, $type)
327     {
328         $match = in_array($value,array('true','false',''));
330         // Display the reason for failing this check.         
331         if($message && ! $match){
332             msg_dialog::display(_("Warning"), 
333                     sprintf(_("The value '%s' specified for '%s:%s' is invalid. A bool value is required here!"), 
334                         bold($value),bold($class),bold($name)), 
335                     WARNING_DIALOG);
336         }
337     
338         return($match);
339     }
341     static function isString($message,$class,$name,$value, $type)
342     {
343         $match = TRUE;
344     
345         // Display the reason for failing this check.         
346         if($message && ! $match){
347             msg_dialog::display(_("Warning"), 
348                     sprintf(_("The value '%s' specified for '%s:%s' is invalid. A string value is required here!"), 
349                         bold($value),bold($class),bold($name)), 
350                     WARNING_DIALOG);
351         }
353         return($match);
354     }
356     static function isInteger($message,$class,$name,$value, $type)
357     {
358         $match = is_numeric($value) && !preg_match("/[^0-9]/", $value);
360         // Display the reason for failing this check.         
361         if($message && ! $match){
362             msg_dialog::display(_("Warning"), 
363                     sprintf(_("The value '%s' specified for '%s:%s' is invalid. A numeric value is required here!"), 
364                         bold($value),bold($class),bold($name)), 
365                     WARNING_DIALOG);
366         }
368         return($match);
369     }
371     static function isPath($message,$class,$name,$value, $type)
372     {
373         $match = preg_match("#^(/[^/]*/){1}#", $value);
374     
375         // Display the reason for failing this check.         
376         if($message && ! $match){
377             msg_dialog::display(_("Warning"), 
378                     sprintf(_("The path '%s' specified for '%s:%s' is invalid!"), 
379                         bold($value),bold($class),bold($name)), 
380                     WARNING_DIALOG);
381         }
383         return($match);
384     }
386     static function isReadablePath($message,$class,$name,$value, $type)
387     {
388         $match = !empty($value)&&is_dir($value)&&is_writeable($value);
389    
390         // Display the reason for failing this check.         
391         if($message && ! $match){
392             if(!is_dir($value)){
393                 msg_dialog::display(_("Warning"), 
394                         sprintf(_("The folder '%s' specified for '%s:%s' does not exists!"), 
395                             bold($value),bold($class),bold($name)), 
396                         WARNING_DIALOG);
397             }elseif(!is_readable($value)){
398                 msg_dialog::display(_("Warning"), 
399                         sprintf(_("The folder '%s' specified for '%s:%s' cannot be used for reading!"), 
400                             bold($value),bold($class),bold($name)), 
401                         WARNING_DIALOG);
402             }
403         }
405         return($match);
406     }
408     static function isWriteablePath($message,$class,$name,$value, $type)
409     {
410         $match = !empty($value)&&is_dir($value)&&is_writeable($value);
411    
412         // Display the reason for failing this check.         
413         if($message && ! $match){
414             if(!is_dir($value)){
415                 msg_dialog::display(_("Warning"), 
416                         sprintf(_("The folder '%s' specified for '%s:%s' does not exists!"), 
417                             bold($value),bold($class),bold($name)), 
418                         WARNING_DIALOG);
419             }elseif(!is_writeable($value)){
420                 msg_dialog::display(_("Warning"), 
421                         sprintf(_("The folder '%s' specified for '%s:%s' cannot be used for writing!"), 
422                             bold($value),bold($class),bold($name)), 
423                         WARNING_DIALOG);
424             }
425         }
427         return($match);
428     }
430     static function isReadableFile($message,$class,$name,$value, $type)
431     {
432         $match = !empty($value) && is_readable($value) && is_file($value);
434         // Display the reason for failing this check.         
435         if($message && ! $match){
436                 
437             if(!is_file($value)){
438                 msg_dialog::display(_("Warning"), 
439                         sprintf(_("The file '%s' specified for '%s:%s' does not exists!"), 
440                             bold($value),bold($class),bold($name)), 
441                         WARNING_DIALOG);
442             }elseif(!is_readable($value)){
443                 msg_dialog::display(_("Warning"), 
444                         sprintf(_("The file '%s' specified for '%s:%s' cannot be read!"), 
445                             bold($value),bold($class),bold($name)), 
446                         WARNING_DIALOG);
447             }
448         }
450         return($match);
451     }
453     static function isCommand($message,$class,$name,$value, $type)
454     {
455         $match = TRUE;
457         // Display the reason for failing this check.         
458         if($message && ! $match){
459             msg_dialog::display(_("Warning"), 
460                     sprintf(_("The command '%s' specified for '%s:%s' is invalid!"), 
461                         bold($value),bold($class),bold($name)), 
462                     WARNING_DIALOG);
463         }
464         
465         return($match);
466     }
468     static function isDn($message,$class,$name,$value, $type)
469     {
470         $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=]*$/i", $value);
472         // Display the reason for failing this check.         
473         if($message && ! $match){
474             msg_dialog::display(_("Warning"), 
475                     sprintf(_("The dn '%s' specified for '%s:%s' is invalid!"), 
476                         bold($value),bold($class),bold($name)), 
477                     WARNING_DIALOG);
478         }
479         
480         return($match);
481     }
483     static function isRdn($message,$class,$name,$value, $type)
484     {
485         $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=,]*,?$/i", $value);
487         // Display the reason for failing this check.         
488         if($message && ! $match){
489             msg_dialog::display(_("Warning"), 
490                     sprintf(_("The rdn '%s' specified for '%s:%s' is invalid!"), 
491                         bold($value),bold($class),bold($name)), 
492                     WARNING_DIALOG);
493         }
494         
495         return($match);
496     }
498     private function _restoreCurrentValue()
499     {
500         // First check for values in the LDAP Database.
501         if(isset($this->parent->ldapStoredProperties[$this->class][$this->name])){
502             $this->setStatus('ldap');
503             $this->value = $this->parent->ldapStoredProperties[$this->class][$this->name];
504             return;
505         }
507         // Second check for values in the config file.
508         if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
509             $this->setStatus('file');
510             $this->value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
511             return;
512         }
514         // If there still wasn't found anything then fallback to the default.
515         if($this->getStatus() == 'undefined'){
516             $this->value = $this->getDefault();
517         }
518     }
520     function getMigrate() { return($this->migrate); }
521     function getCheck() { return($this->check); }
522     function getName() { return($this->name); }
523     function getClass() { return($this->class); }
524     function getGroup() { return($this->group); }
525     function getType() { return($this->type); }
526     function getDescription() { return($this->description); }
527     function getDefault() { return($this->default); }
528     function getDefaults() { return($this->defaults); }
529     function getStatus() { return($this->status); }
530     function isMandatory() { return($this->mandatory); }
532     function setValue($str) 
533     {
534         if(in_array($this->getStatus(), array('modified'))){
535             $this->tmp_value = $str; 
536         }elseif($this->value != $str){
537             $this->setStatus('modified'); 
538             $this->tmp_value = $str; 
539         }
540     }
542     function getValue($temporary = FALSE) 
543     { 
544         if($temporary){
545             if(in_array($this->getStatus(), array('modified','removed'))){
546                 return($this->tmp_value); 
547             }else{
548                 return($this->value); 
549             }
550         }else{ 
552             // Do not return ldap values if we've to ignore them.
553             if($this->parent->ignoreLdapProperties){
554                 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
555                     return($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)]);
556                 }else{
557                     return($this->getDefault());
558                 }
559             }else{
560                 return($this->value); 
561             }
562         }
563     }
565     function restoreDefault() 
566     {
567         if(in_array($this->getStatus(),array('ldap'))){
568             $this->setStatus('removed'); 
570             // Second check for values in the config file.
571             if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
572                 $this->tmp_value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
573             }else{
574                 $this->tmp_value = $this->getDefault();
575             }
576         }
577     }
579     function save()
580     {
581         if($this->getStatus() == 'modified'){
582             $ldap = $this->parent->config->get_ldap_link();
583             $ldap->cd($this->parent->config->current['BASE']);
584             $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
585             $ldap->cat($dn);
586             if(!$ldap->count()){
587                 $ldap->cd($dn);
588                 $data = array(
589                         'cn' => $this->class, 
590                         'objectClass' => array('top','gosaConfig'),
591                         'gosaSetting' => $this->name.":".$this->tmp_value);
593                 $ldap->add($data);
594                 if(!$ldap->success()){
595                     echo $ldap->get_error();
596                 }
598             }else{
599                 $attrs = $ldap->fetch();
600                 $data = array();
601                 $found = false;
602                 if(isset($attrs['gosaSetting']['count'])){
603                     for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
604                         $set = $attrs['gosaSetting'][$i];
605                         if(preg_match("/^{$this->name}:/", $set)){
606                             $set = "{$this->name}:{$this->tmp_value}";
607                             $found = true;
608                         }
609                         $data['gosaSetting'][] = $set;
610                     }
611                 }
612                 if(!$found) $data['gosaSetting'][] = "{$this->name}:{$this->tmp_value}";
613                 $ldap->cd($dn);
614                 $ldap->modify($data);
615                 if(!$ldap->success()){
616                     echo $ldap->get_error();
617                 }
618             }
619             $this->value = $this->tmp_value;
620             $this->setStatus('ldap'); 
621         }elseif($this->getStatus() == 'removed'){
622             $ldap = $this->parent->config->get_ldap_link();
623             $ldap->cd($this->parent->config->current['BASE']);
624             $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
625             $ldap->cat($dn);
626             $attrs = $ldap->fetch();
627             $data = array('gosaSetting' => array());
628             for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
629                 $set = $attrs['gosaSetting'][$i];
630                 if(preg_match("/^{$this->name}:/", $set)){
631                     continue;
632                 }
633                 $data['gosaSetting'][] = $set;
634             }
635             $ldap->cd($dn);
636             $ldap->modify($data);
637             if(!$ldap->success()){
638                 echo $ldap->get_error();
639             }
640             $this->_restoreCurrentValue();
641         }
642     }
644     private function setStatus($state) 
645     {
646         if(!in_array($state, array('ldap','file','undefined','modified','removed'))) {
647             trigger_error("Unknown property status given '{$state}' for {$this->class}:{$this->name}!");
648         }else{
649             $this->status = $state; 
650         }
651     }
653     function isValid() 
654     { 
655         return(TRUE);    
656     }
661 interface propertyMigration
663     function __construct($config,$property);
667 ?>