Code

32b4b61e2e30526714d9a300989ed3e1a9033f0a
[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();
18     public $categoryRequirements  = array();
19     public $categoryToClass  = array();
21     public $objectClasses = array();
23     function __construct($config)
24     {
25         $this->config = &$config;
27         // Detect classes that have a plInfo method 
28         global $class_mapping;
29         foreach ($class_mapping as $cname => $path){
30             $cmethods = get_class_methods($cname);
31             if (is_array($cmethods) && in_array_ics('plInfo',$cmethods)){
33                 // Get plugin definitions
34                 $def = call_user_func(array($cname, 'plInfo'));;
36                 // Register Post Events (postmodfiy,postcreate,postremove,checkhook)
37                 if(count($def)){
38                     $this->classesWithInfo[$cname] = $def;
39                 }
40             }
41         }
43         // (Re)Load properties
44         $this->reload();
45     }
48     function validateSchemata($disableIncompatiblePlugins = FALSE,$displayMessage = FALSE)
49     {
50         // We can check the schemata only with a valid LDAP connection
51         if(empty($this->config->current['CONFIG'])){
52             return(TRUE); 
53         }
55         // Read objectClasses from ldap
56         if(!count($this->objectClasses)){
57             $ldap = $this->config->get_ldap_link();
58             $ldap->cd($this->config->current['BASE']);
59             $this->objectClasses = $ldap->get_objectclasses();
60         }
62         // Collect required schema infos
63         $this->categoryRequirements = array();
64         $this->categoryToClass = array();
65         foreach($this->classesWithInfo as $cname => $defs){
66             if(isset($defs['plCategory'])){
67                 foreach($defs['plCategory'] as $name => $data){
68                     if(!is_numeric($name)){
69                         $this->categoryToClass[$name]['BASE'] = $cname;
70                         if(isset($data['objectClass'])){
71                             $this->categoryRequirements[$name] = $data['objectClass'];
72                         }
73                     }else{
74                         $this->categoryToClass[$data]['SUB'][] = $cname;
75                     }
76                 }
77             }
78         }
80         // Check schema requirements now
81         foreach($this->categoryRequirements as $cat => $requirements){
83             if(!is_string($requirements)){
84             }else{
85                 if(!$this->ocAvailable($requirements)){
87                     if($displayMessage){
88                         $classes = "<ul>";
89                         $name = $this->categoryToClass[$cat]['BASE'];
90                         $classes.= "<li>".$this->classToName[$name]."</li>";
91                         if(isset($this->categoryToClass[$cat]['SUB'])){
92                             foreach($this->categoryToClass[$cat]['SUB'] as $name){
93                                 $classes.= "<li>".$this->classToName[$name]."</li>";
94                             }
95                         }
96                         $classes.= "</ul>";
97                         msg_dialog::display(_("Schema validation error"), 
98                                 sprintf(_("The objectClass '%s' which is required for plugin '%s' is not availabe!"),
99                                     bold($requirements),bold($cat)), 
100                                 ERROR_DIALOG);
101                     }
102                 }else{
103                      #äprint_a($this->objectClasses[$requirements]);   
104                 }
105             }
106         }
107     }
110     // check wheter an objectClass is installed or not.
111     function ocAvailable($name)
112     {
113         return(isset($this->objectClasses[$name]));
114     }
117     function reload($force = FALSE)
118     {
119         // Do not reload the properties everytime, once we have  
120         //  everything loaded and registrered skip the reload.
121         // Status is 'finished' once we had a ldap connection (logged in)
122         if(!$force && $this->status == 'finished') return;
124         // Reset everything
125         $this->ldapStoredProperties = array();
126         $this->fileStoredProperties = array();
127         $this->properties = array();
128         $this->mapByName = array();
130         // Search for config flags defined in the config file (TAB section)
131         foreach($this->config->data['TABS'] as $tabname => $tabdefs){
132             foreach($tabdefs as $info){
134                 // Check if the info is valid
135                 if(isset($info['NAME']) && isset($info['CLASS'])){
137                     // Check if there is nore than just the plugin definition
138                     if(count($info) > 2){
139                         foreach($info as $name => $value){
140                             
141                             if(!in_array($name, array('CLASS','NAME'))){
142                                 $class= $info['CLASS'];    
143                                 $this->fileStoredProperties[$class][strtolower($name)] = $value;
144                             }
145                         }
146                     }
147                 }
148             }
149         }
151         // Search for config flags defined in the config file (MENU section)
152         foreach($this->config->data['MENU'] as $section => $entries){
153             foreach($entries as $entry){
154                 if(count($entry) > 2 && isset($entry['CLASS'])){
155                     $class = $entry['CLASS'];
156                     foreach($entry as $name => $value){
157                         if(!in_array($name, array('CLASS','ACL'))){
158                             $this->fileStoredProperties[strtolower($class)][strtolower($name)] = $value;
159                         }
160                     }
161                 }
162             }
163         }
165         // Search for config flags defined in the config file (MAIN section)
166         foreach($this->config->data['MAIN'] as $name => $value){
167             $this->fileStoredProperties['core'][strtolower($name)] = $value;
168         }
170         // Search for config flags defined in the config file (Current LOCATION section)
171         if(isset($this->config->current)){
172             foreach($this->config->current as $name => $value){
173                 $this->fileStoredProperties['core'][strtolower($name)] = $value;
174             }
175         }
177         // Skip searching for LDAP defined properties if 'ignoreLdapProperties' is set to 'true'
178         //  in the config. 
179         $this->ignoreLdapProperties = FALSE;
180         if(isset($this->fileStoredProperties['core'][strtolower('ignoreLdapProperties')]) && 
181             preg_match("/(true|on)/i", $this->fileStoredProperties['core'][strtolower('ignoreLdapProperties')])){
182             $this->ignoreLdapProperties = TRUE;
183         }
185         // Search for all config flags defined in the LDAP - BUT only if we ARE logged in. 
186         if(!empty($this->config->current['CONFIG'])){
187             $ldap = $this->config->get_ldap_link();
188             $ldap->cd($this->config->current['CONFIG']);
189             $ldap->search('(&(objectClass=gosaConfig)(gosaSetting=*))', array('cn','gosaSetting'));
190             while($attrs = $ldap->fetch()){
191                 $class = $attrs['cn'][0];
192                 for($i=0; $i<$attrs['gosaSetting']['count']; $i++){
193                     list($name,$value) = preg_split("/:/",$attrs['gosaSetting'][$i],2);
194                     $this->ldapStoredProperties[$class][$name] = $value;
195                 }
196             }
197             $this->status = 'finished';
198         }
200         // Register plugin properties.
201         foreach ($this->classesWithInfo as $cname => $def){
203             // Detect class name
204             $name = $cname;
205             $name = (isset($def['plShortName'])) ? $def['plShortName'] : $cname;
206             $name = (isset($def['plDescription'])) ? $def['plDescription'] : $cname;
208             // Register post events
209             $this->classToName[$cname] = $name;
210             $data = array('name' => 'postcreate','type' => 'command');
211             $this->register($cname, $data);    
212             $data = array('name' => 'postremove','type' => 'command');
213             $this->register($cname, $data);    
214             $data = array('name' => 'postmodify','type' => 'command');
215             $this->register($cname, $data);    
216             $data = array('name' => 'check', 'type' => 'command');
217             $this->register($cname, $data);    
219             // Register properties 
220             if(isset($def['plProperties'])){
221                 foreach($def['plProperties'] as $property){
222                     $this->register($cname, $property);
223                 }
224             }
225         }
226     }
228     function register($class,$data)
229     {
230         $id = count($this->properties);
231         $this->properties[$id] = new gosaProperty($this,$class,$data);
232         $p = strtolower("{$class}::{$data['name']}");
233         $this->mapByName[$p] = $id;
234     }
236     public function getAllProperties()
237     {
238         return($this->properties);
239     }
241     function propertyExists($class,$name)
242     {       
243         $p = strtolower("{$class}::{$name}");
244         return(isset($this->mapByName[$p]));
245     }
247     private function getId($class,$name)
248     {
249         $p = strtolower("{$class}::{$name}");
250         if(!isset($this->mapByName[$p])){
251             return(-1);
252         }       
253         return($this->mapByName[$p]);    
254     }
256     function getProperty($class,$name)
257     {
258         if($this->propertyExists($class,$name)){
259             return($this->properties[$this->getId($class,$name)]);
260         }
261         return(NULL); 
262     }
264     function getPropertyValue($class,$name)
265     {   
266         if($this->propertyExists($class,$name)){
267             $tmp = $this->getProperty($class,$name);
268             return($tmp->getValue());
269         }
270         return("");
271     }
273     function setPropertyValue($class,$name, $value)
274     {   
275         if($this->propertyExists($class,$name)){
276             $tmp = $this->getProperty($class,$name);
277             return($tmp->setValue($value));
278         }
279         return("");
280     }
282     function saveChanges()
283     {
284         $migrate = array();
285         foreach($this->properties as $prop){
287             // Is this property modified
288             if(in_array($prop->getStatus(),array('modified','removed'))){
290                 // Check if we've to migrate something before we can make the changes effective. 
291                 if($prop->migrationRequired()){
292                     $migrate[] = $prop;
293                 }else{
294                     $prop->save();
295                 }
296             }
297         }
298         return($migrate);
299     }
303 class gosaProperty
305     protected $name = "";
306     protected $class = "";
307     protected $value = "";
308     protected $tmp_value = "";  // Used when modified but not saved 
309     protected $type = "string";
310     protected $default = "";
311     protected $defaults = "";
312     protected $description = "";
313     protected $check = "";
314     protected $migrate = "";
315     protected $mandatory = FALSE;
316     protected $group = "default";
317     protected $parent = NULL;
318     protected $data = array();
320     protected $migrationClass = NULL;
322     /*!  The current property status
323      *     'ldap'       Property is stored in ldap 
324      *     'file'       Property is stored in the config file
325      *     'undefined'  Property is currently not stored anywhere
326      *     'modified'   Property has been modified (should be saved)
327      */
328     protected $status = 'undefined';
330     protected $attributes = array('name','type','default','description','check',
331             'migrate','mandatory','group','defaults');
336     function __construct($parent,$classname,$data)
337     {
338         // Set some basic infos 
339         $this->parent = &$parent;
340         $this->class = $classname;
341         $this->data  = $data;
343         // Get all relevant information from the data array (comes from plInfo)    
344         foreach($this->attributes as $aName){
345             if(isset($data[$aName])){
346                 $this->$aName = $data[$aName];
347             }
348         }      
350         // Initialize with the current value
351         $this->_restoreCurrentValue(); 
353     }
355     function migrationRequired()
356     {
357         // Instantiate migration class 
358         if(!empty($this->migrate) && $this->migrationClass == NULL){
359             if(!class_available($this->migrate)){
360                 trigger_error("Cannot start migration for gosaProperty::'{$this->getName()}' class not found ({$this->migrate})!");
361             }else{
362                 $class = $this->migrate;
363                 $tmp = new $class($this->parent->config,$this);
364                 if(! $tmp instanceof propertyMigration){ 
365                     trigger_error("Cannot start migration for gosaProperty::'{$this->getName()}' doesn't implement propertyMigration!");
366                 }else{
367                     $this->migrationClass = $tmp;
368                 }
369             }
370         }
371         if(empty($this->migrate) || $this->migrationClass == NULL){
372             return(FALSE);
373         }
374         return($this->migrationClass->checkForIssues());
375     }
377     function getMigrationClass()
378     {
379         return($this->migrationClass);
380     }
382     function check()
383     {
384         $val = $this->getValue(TRUE);
385         $return = TRUE;
386         if($this->mandatory && empty($val)){
387             $return = FALSE;
388         }
390         $check = $this->getCheck();
391         if(!empty($val) && !empty($check)){
392             $res = call_user_func(preg_split("/::/", $this->check),$messages=TRUE, $this->class,$this->name,$val, $this->type);
393             if(!$res){
394                 $return = FALSE;
395             }
396         }
397         return($return);
398     }
400     static function isBool($message,$class,$name,$value, $type)
401     {
402         $match = in_array($value,array('true','false',''));
404         // Display the reason for failing this check.         
405         if($message && ! $match){
406             msg_dialog::display(_("Warning"), 
407                     sprintf(_("The value '%s' specified for '%s:%s' is invalid. A bool value is required here!"), 
408                         bold($value),bold($class),bold($name)), 
409                     WARNING_DIALOG);
410         }
411     
412         return($match);
413     }
415     static function isString($message,$class,$name,$value, $type)
416     {
417         $match = TRUE;
418     
419         // Display the reason for failing this check.         
420         if($message && ! $match){
421             msg_dialog::display(_("Warning"), 
422                     sprintf(_("The value '%s' specified for '%s:%s' is invalid. A string value is required here!"), 
423                         bold($value),bold($class),bold($name)), 
424                     WARNING_DIALOG);
425         }
427         return($match);
428     }
430     static function isInteger($message,$class,$name,$value, $type)
431     {
432         $match = is_numeric($value) && !preg_match("/[^0-9]/", $value);
434         // Display the reason for failing this check.         
435         if($message && ! $match){
436             msg_dialog::display(_("Warning"), 
437                     sprintf(_("The value '%s' specified for '%s:%s' is invalid. A numeric value is required here!"), 
438                         bold($value),bold($class),bold($name)), 
439                     WARNING_DIALOG);
440         }
442         return($match);
443     }
445     static function isPath($message,$class,$name,$value, $type)
446     {
447         $match = preg_match("#^(/[^/]*/){1}#", $value);
448     
449         // Display the reason for failing this check.         
450         if($message && ! $match){
451             msg_dialog::display(_("Warning"), 
452                     sprintf(_("The path '%s' specified for '%s:%s' is invalid!"), 
453                         bold($value),bold($class),bold($name)), 
454                     WARNING_DIALOG);
455         }
457         return($match);
458     }
460     static function isReadablePath($message,$class,$name,$value, $type)
461     {
462         $match = !empty($value)&&is_dir($value)&&is_writeable($value);
463    
464         // Display the reason for failing this check.         
465         if($message && ! $match){
466             if(!is_dir($value)){
467                 msg_dialog::display(_("Warning"), 
468                         sprintf(_("The folder '%s' specified for '%s:%s' does not exists!"), 
469                             bold($value),bold($class),bold($name)), 
470                         WARNING_DIALOG);
471             }elseif(!is_readable($value)){
472                 msg_dialog::display(_("Warning"), 
473                         sprintf(_("The folder '%s' specified for '%s:%s' cannot be used for reading!"), 
474                             bold($value),bold($class),bold($name)), 
475                         WARNING_DIALOG);
476             }
477         }
479         return($match);
480     }
482     static function isWriteablePath($message,$class,$name,$value, $type)
483     {
484         $match = !empty($value)&&is_dir($value)&&is_writeable($value);
485    
486         // Display the reason for failing this check.         
487         if($message && ! $match){
488             if(!is_dir($value)){
489                 msg_dialog::display(_("Warning"), 
490                         sprintf(_("The folder '%s' specified for '%s:%s' does not exists!"), 
491                             bold($value),bold($class),bold($name)), 
492                         WARNING_DIALOG);
493             }elseif(!is_writeable($value)){
494                 msg_dialog::display(_("Warning"), 
495                         sprintf(_("The folder '%s' specified for '%s:%s' cannot be used for writing!"), 
496                             bold($value),bold($class),bold($name)), 
497                         WARNING_DIALOG);
498             }
499         }
501         return($match);
502     }
504     static function isReadableFile($message,$class,$name,$value, $type)
505     {
506         $match = !empty($value) && is_readable($value) && is_file($value);
508         // Display the reason for failing this check.         
509         if($message && ! $match){
510                 
511             if(!is_file($value)){
512                 msg_dialog::display(_("Warning"), 
513                         sprintf(_("The file '%s' specified for '%s:%s' does not exists!"), 
514                             bold($value),bold($class),bold($name)), 
515                         WARNING_DIALOG);
516             }elseif(!is_readable($value)){
517                 msg_dialog::display(_("Warning"), 
518                         sprintf(_("The file '%s' specified for '%s:%s' cannot be read!"), 
519                             bold($value),bold($class),bold($name)), 
520                         WARNING_DIALOG);
521             }
522         }
524         return($match);
525     }
527     static function isCommand($message,$class,$name,$value, $type)
528     {
529         $match = TRUE;
531         // Display the reason for failing this check.         
532         if($message && ! $match){
533             msg_dialog::display(_("Warning"), 
534                     sprintf(_("The command '%s' specified for '%s:%s' is invalid!"), 
535                         bold($value),bold($class),bold($name)), 
536                     WARNING_DIALOG);
537         }
538         
539         return($match);
540     }
542     static function isDn($message,$class,$name,$value, $type)
543     {
544         $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=]*$/i", $value);
546         // Display the reason for failing this check.         
547         if($message && ! $match){
548             msg_dialog::display(_("Warning"), 
549                     sprintf(_("The dn '%s' specified for '%s:%s' is invalid!"), 
550                         bold($value),bold($class),bold($name)), 
551                     WARNING_DIALOG);
552         }
553         
554         return($match);
555     }
557     static function isRdn($message,$class,$name,$value, $type)
558     {
559         $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=,]*,?$/i", $value);
561         // Display the reason for failing this check.         
562         if($message && ! $match){
563             msg_dialog::display(_("Warning"), 
564                     sprintf(_("The rdn '%s' specified for '%s:%s' is invalid!"), 
565                         bold($value),bold($class),bold($name)), 
566                     WARNING_DIALOG);
567         }
568         
569         return($match);
570     }
572     private function _restoreCurrentValue()
573     {
574         // First check for values in the LDAP Database.
575         if(isset($this->parent->ldapStoredProperties[$this->class][$this->name])){
576             $this->setStatus('ldap');
577             $this->value = $this->parent->ldapStoredProperties[$this->class][$this->name];
578             return;
579         }
581         // Second check for values in the config file.
582         if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
583             $this->setStatus('file');
584             $this->value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
585             return;
586         }
588         // If there still wasn't found anything then fallback to the default.
589         if($this->getStatus() == 'undefined'){
590             $this->value = $this->getDefault();
591         }
592     }
594     function getMigrate() { return($this->migrate); }
595     function getCheck() { return($this->check); }
596     function getName() { return($this->name); }
597     function getClass() { return($this->class); }
598     function getGroup() { return($this->group); }
599     function getType() { return($this->type); }
600     function getDescription() { return($this->description); }
601     function getDefault() { return($this->default); }
602     function getDefaults() { return($this->defaults); }
603     function getStatus() { return($this->status); }
604     function isMandatory() { return($this->mandatory); }
606     function setValue($str) 
607     {
608         if(in_array($this->getStatus(), array('modified'))){
609             $this->tmp_value = $str; 
610         }elseif($this->value != $str){
611             $this->setStatus('modified'); 
612             $this->tmp_value = $str; 
613         }
614     }
616     function getValue($temporary = FALSE) 
617     { 
618         if($temporary){
619             if(in_array($this->getStatus(), array('modified','removed'))){
620                 return($this->tmp_value); 
621             }else{
622                 return($this->value); 
623             }
624         }else{ 
626             // Do not return ldap values if we've to ignore them.
627             if($this->parent->ignoreLdapProperties){
628                 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
629                     return($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)]);
630                 }else{
631                     return($this->getDefault());
632                 }
633             }else{
634                 return($this->value); 
635             }
636         }
637     }
639     function restoreDefault() 
640     {
641         if(in_array($this->getStatus(),array('ldap'))){
642             $this->setStatus('removed'); 
644             // Second check for values in the config file.
645             if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
646                 $this->tmp_value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
647             }else{
648                 $this->tmp_value = $this->getDefault();
649             }
650         }
651     }
653     function save()
654     {
655         if($this->getStatus() == 'modified'){
656             $ldap = $this->parent->config->get_ldap_link();
657             $ldap->cd($this->parent->config->current['BASE']);
658             $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
659             $ldap->cat($dn);
660             if(!$ldap->count()){
661                 $ldap->cd($dn);
662                 $data = array(
663                         'cn' => $this->class, 
664                         'objectClass' => array('top','gosaConfig'),
665                         'gosaSetting' => $this->name.":".$this->tmp_value);
667                 $ldap->add($data);
668                 if(!$ldap->success()){
669                     echo $ldap->get_error();
670                 }
672             }else{
673                 $attrs = $ldap->fetch();
674                 $data = array();
675                 $found = false;
676                 if(isset($attrs['gosaSetting']['count'])){
677                     for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
678                         $set = $attrs['gosaSetting'][$i];
679                         if(preg_match("/^{$this->name}:/", $set)){
680                             $set = "{$this->name}:{$this->tmp_value}";
681                             $found = true;
682                         }
683                         $data['gosaSetting'][] = $set;
684                     }
685                 }
686                 if(!$found) $data['gosaSetting'][] = "{$this->name}:{$this->tmp_value}";
687                 $ldap->cd($dn);
688                 $ldap->modify($data);
689                 if(!$ldap->success()){
690                     echo $ldap->get_error();
691                 }
692             }
693             $this->value = $this->tmp_value;
694             $this->setStatus('ldap'); 
695         }elseif($this->getStatus() == 'removed'){
696             $ldap = $this->parent->config->get_ldap_link();
697             $ldap->cd($this->parent->config->current['BASE']);
698             $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
699             $ldap->cat($dn);
700             $attrs = $ldap->fetch();
701             $data = array('gosaSetting' => array());
702             for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
703                 $set = $attrs['gosaSetting'][$i];
704                 if(preg_match("/^{$this->name}:/", $set)){
705                     continue;
706                 }
707                 $data['gosaSetting'][] = $set;
708             }
709             $ldap->cd($dn);
710             $ldap->modify($data);
711             if(!$ldap->success()){
712                 echo $ldap->get_error();
713             }
714             $this->_restoreCurrentValue();
715         }
716     }
718     private function setStatus($state) 
719     {
720         if(!in_array($state, array('ldap','file','undefined','modified','removed'))) {
721             trigger_error("Unknown property status given '{$state}' for {$this->class}:{$this->name}!");
722         }else{
723             $this->status = $state; 
724         }
725     }
727     function isValid() 
728     { 
729         return(TRUE);    
730     }
735 interface propertyMigration
737     function __construct($config,$property);
741 ?>