Code

a83cb1cea7635d4c1d774db435fadc04323287d4
[gosa.git] / include / class_plugin.inc
1 <?php
2 /*
3    This code is part of GOsa (https://gosa.gonicus.de)
4    Copyright (C) 2003  Cajus Pollmeier
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
21 /*! \brief   The plugin base class
22   \author  Cajus Pollmeier <pollmeier@gonicus.de>
23   \version 2.00
24   \date    24.07.2003
26   This is the base class for all plugins. It can be used standalone or
27   can be included by the tabs class. All management should be done 
28   within this class. Extend your plugins from this class.
29  */
31 class plugin
32 {
33   /*!
34     \brief Reference to parent object
36     This variable is used when the plugin is included in tabs
37     and keeps reference to the tab class. Communication to other
38     tabs is possible by 'name'. So the 'fax' plugin can ask the
39     'userinfo' plugin for the fax number.
41     \sa tab
42    */
43   var $parent= NULL;
45   /*!
46     \brief Configuration container
48     Access to global configuration
49    */
50   var $config= NULL;
52   /*!
53     \brief Mark plugin as account
55     Defines whether this plugin is defined as an account or not.
56     This has consequences for the plugin to be saved from tab
57     mode. If it is set to 'FALSE' the tab will call the delete
58     function, else the save function. Should be set to 'TRUE' if
59     the construtor detects a valid LDAP object.
61     \sa plugin::plugin()
62    */
63   var $is_account= FALSE;
64   var $initially_was_account= FALSE;
66   /*!
67     \brief Mark plugin as template
69     Defines whether we are creating a template or a normal object.
70     Has conseqences on the way execute() shows the formular and how
71     save() puts the data to LDAP.
73     \sa plugin::save() plugin::execute()
74    */
75   var $is_template= FALSE;
76   var $ignore_account= FALSE;
77   var $is_modified= FALSE;
79   /*!
80     \brief Represent temporary LDAP data
82     This is only used internally.
83    */
84   var $attrs= array();
86   /* Keep set of conflicting plugins */
87   var $conflicts= array();
89   /* Save unit tags */
90   var $gosaUnitTag= "";
92   /*!
93     \brief Used standard values
95     dn
96    */
97   var $dn= "";
98   var $uid= "";
99   var $sn= "";
100   var $givenName= "";
101   var $acl= "*none*";
102   var $dialog= FALSE;
103   var $snapDialog = NULL;
105   /* attribute list for save action */
106   var $attributes= array();
107   var $objectclasses= array();
108   var $is_new= TRUE;
109   var $saved_attributes= array();
111   var $acl_base= "";
113   /* Plugin identifier */
114   var $plHeadline= "";
115   var $plDescription= "";
117   /*! \brief plugin constructor
119     If 'dn' is set, the node loads the given 'dn' from LDAP
121     \param dn Distinguished name to initialize plugin from
122     \sa plugin()
123    */
124   function plugin ($config, $dn= NULL)
125   {
126     /* Configuration is fine, allways */
127     $this->config= $config;     
128     $this->dn= $dn;
130     /* Handle new accounts, don't read information from LDAP */
131     if ($dn == "new"){
132       return;
133     }
135     /* Save current dn as acl_base */
136     $this->acl_base= $dn;
138     /* Get LDAP descriptor */
139     $ldap= $this->config->get_ldap_link();
140     if ($dn != NULL){
142       /* Load data to 'attrs' and save 'dn' */
143       $ldap->cat ($dn);
144       $this->attrs= $ldap->fetch();
146       /* Copy needed attributes */
147       foreach ($this->attributes as $val){
148         $found= array_key_ics($val, $this->attrs);
149         if ($found != ""){
150           $this->$val= $this->attrs["$found"][0];
151         }
152       }
154       /* gosaUnitTag loading... */
155       if (isset($this->attrs['gosaUnitTag'][0])){
156         $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
157       }
159       /* Set the template flag according to the existence of objectClass
160          gosaUserTemplate */
161       if (isset($this->attrs['objectClass'])){
162         if (in_array ("gosaUserTemplate", $this->attrs['objectClass'])){
163           $this->is_template= TRUE;
164           @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
165               "found", "Template check");
166         }
167       }
169       /* Is Account? */
170       error_reporting(0);
171       $found= TRUE;
172       foreach ($this->objectclasses as $obj){
173         if (preg_match('/top/i', $obj)){
174           continue;
175         }
176         if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
177           $found= FALSE;
178           break;
179         }
180       }
181       error_reporting(E_ALL);
182       if ($found){
183         $this->is_account= TRUE;
184         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
185             "found", "Object check");
186       }
188       /* Prepare saved attributes */
189       $this->saved_attributes= $this->attrs;
190       foreach ($this->saved_attributes as $index => $value){
191         if (preg_match('/^[0-9]+$/', $index)){
192           unset($this->saved_attributes[$index]);
193           continue;
194         }
195         if (!in_array($index, $this->attributes) && $index != "objectClass"){
196           unset($this->saved_attributes[$index]);
197           continue;
198         }
199         if ($this->saved_attributes[$index]["count"] == 1){
200           $tmp= $this->saved_attributes[$index][0];
201           unset($this->saved_attributes[$index]);
202           $this->saved_attributes[$index]= $tmp;
203           continue;
204         }
206         unset($this->saved_attributes["$index"]["count"]);
207       }
208     }
210     /* Save initial account state */
211     $this->initially_was_account= $this->is_account;
212   }
214   /*! \brief execute plugin
216     Generates the html output for this node
217    */
218   function execute()
219   {
220     /* This one is empty currently. Fabian - please fill in the docu code */
221     $_SESSION['current_class_for_help'] = get_class($this);
222     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
223     $_SESSION['LOCK_VARS_TO_USE'] =array();
224   }
226   /*! \brief execute plugin
227      Removes object from parent
228    */
229   function remove_from_parent()
230   {
231     /* include global link_info */
232     $ldap= $this->config->get_ldap_link();
234     /* Get current objectClasses in order to add the required ones */
235     $ldap->cat($this->dn);
236     $tmp= $ldap->fetch ();
237     if (isset($tmp['objectClass'])){
238       $oc= $tmp['objectClass'];
239     } else {
240       $oc= array("count" => 0);
241     }
243     /* Remove objectClasses from entry */
244     $ldap->cd($this->dn);
245     $this->attrs= array();
246     $this->attrs['objectClass']= array();
247     for ($i= 0; $i<$oc["count"]; $i++){
248       if (!in_array_ics($oc[$i], $this->objectclasses)){
249         $this->attrs['objectClass'][]= $oc[$i];
250       }
251     }
253     /* Unset attributes from entry */
254     foreach ($this->attributes as $val){
255       $this->attrs["$val"]= array();
256     }
258     /* Unset account info */
259     $this->is_account= FALSE;
261     /* Do not write in plugin base class, this must be done by
262        children, since there are normally additional attribs,
263        lists, etc. */
264     /*
265        $ldap->modify($this->attrs);
266      */
267   }
270   /* Save data to object */
271   function save_object()
272   {
273     /* Save values to object */
274     foreach ($this->attributes as $val){
275       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
276         /* Check for modifications */
277         if (get_magic_quotes_gpc()) {
278           $data= stripcslashes($_POST["$val"]);
279         } else {
280           $data= $this->$val = $_POST["$val"];
281         }
282         if ($this->$val != $data){
283           $this->is_modified= TRUE;
284         }
285     
286         /* Okay, how can I explain this fix ... 
287          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
288          * So IE posts these 'unselectable' option, with value = chr(194) 
289          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
290          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
291          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
292          */
293         if(isset($data[0]) && $data[0] == chr(194)) {
294           $data = "";  
295         }
296         $this->$val= $data;
297         //echo "<font color='blue'>".$val."</font><br>";
298       }else{
299         //echo "<font color='red'>".$val."</font><br>";
300       }
301     }
302   }
305   /* Save data to LDAP, depending on is_account we save or delete */
306   function save()
307   {
308     /* include global link_info */
309     $ldap= $this->config->get_ldap_link();
311     /* Start with empty array */
312     $this->attrs= array();
314     /* Get current objectClasses in order to add the required ones */
315     $ldap->cat($this->dn);
316     
317     $tmp= $ldap->fetch ();
318     
319     if (isset($tmp['objectClass'])){
320       $oc= $tmp["objectClass"];
321       $this->is_new= FALSE;
322     } else {
323       $oc= array("count" => 0);
324       $this->is_new= TRUE;
325     }
327     /* Load (minimum) attributes, add missing ones */
328     $this->attrs['objectClass']= $this->objectclasses;
329     for ($i= 0; $i<$oc["count"]; $i++){
330       if (!in_array_ics($oc[$i], $this->objectclasses)){
331         $this->attrs['objectClass'][]= $oc[$i];
332       }
333     }
335     /* Copy standard attributes */
336     foreach ($this->attributes as $val){
337       if ($this->$val != ""){
338         $this->attrs["$val"]= $this->$val;
339       } elseif (!$this->is_new) {
340         $this->attrs["$val"]= array();
341       }
342     }
344   }
347   function cleanup()
348   {
349     foreach ($this->attrs as $index => $value){
351       /* Convert arrays with one element to non arrays, if the saved
352          attributes are no array, too */
353       if (is_array($this->attrs[$index]) && 
354           count ($this->attrs[$index]) == 1 &&
355           isset($this->saved_attributes[$index]) &&
356           !is_array($this->saved_attributes[$index])){
357           
358         $tmp= $this->attrs[$index][0];
359         $this->attrs[$index]= $tmp;
360       }
362       /* Remove emtpy arrays if they do not differ */
363       if (is_array($this->attrs[$index]) &&
364           count($this->attrs[$index]) == 0 &&
365           !isset($this->saved_attributes[$index])){
366           
367         unset ($this->attrs[$index]);
368         continue;
369       }
371       /* Remove single attributes that do not differ */
372       if (!is_array($this->attrs[$index]) &&
373           isset($this->saved_attributes[$index]) &&
374           !is_array($this->saved_attributes[$index]) &&
375           $this->attrs[$index] == $this->saved_attributes[$index]){
377         unset ($this->attrs[$index]);
378         continue;
379       }
381       /* Remove arrays that do not differ */
382       if (is_array($this->attrs[$index]) && 
383           isset($this->saved_attributes[$index]) &&
384           is_array($this->saved_attributes[$index])){
385           
386         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
387           unset ($this->attrs[$index]);
388           continue;
389         }
390       }
391     }
392   }
394   /* Check formular input */
395   function check()
396   {
397     $message= array();
399     /* Skip if we've no config object */
400     if (!isset($this->config)){
401       return $message;
402     }
404     /* Find hooks entries for this class */
405     $command= search_config($this->config->data['MENU'], get_class($this), "CHECK");
406     if ($command == "" && isset($this->config->data['TABS'])){
407       $command= search_config($this->config->data['TABS'], get_class($this), "CHECK");
408     }
410     if ($command != ""){
412       if (!check_command($command)){
413         $message[]= sprintf(_("Command '%s', specified as CHECK hook for plugin '%s' doesn't seem to exist."), $command,
414                             get_class($this));
415       } else {
417         /* Generate "ldif" for check hook */
418         $ldif= "dn: $this->dn\n";
419         
420         /* ... objectClasses */
421         foreach ($this->objectclasses as $oc){
422           $ldif.= "objectClass: $oc\n";
423         }
424         
425         /* ... attributes */
426         foreach ($this->attributes as $attr){
427           if ($this->$attr == ""){
428             continue;
429           }
430           if (is_array($this->$attr)){
431             foreach ($this->$attr as $val){
432               $ldif.= "$attr: $val\n";
433             }
434           } else {
435               $ldif.= "$attr: ".$this->$attr."\n";
436           }
437         }
439         /* Append empty line */
440         $ldif.= "\n";
442         /* Feed "ldif" into hook and retrieve result*/
443         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
444         $fh= proc_open($command, $descriptorspec, $pipes);
445         if (is_resource($fh)) {
446           fwrite ($pipes[0], $ldif);
447           fclose($pipes[0]);
448           
449           $result= stream_get_contents($pipes[1]);
450           if ($result != ""){
451             $message[]= $result;
452           }
453           
454           fclose($pipes[1]);
455           fclose($pipes[2]);
456           proc_close($fh);
457         }
458       }
460     }
462     return ($message);
463   }
465   /* Adapt from template, using 'dn' */
466   function adapt_from_template($dn)
467   {
468     /* Include global link_info */
469     $ldap= $this->config->get_ldap_link();
471     /* Load requested 'dn' to 'attrs' */
472     $ldap->cat ($dn);
473     $this->attrs= $ldap->fetch();
475     /* Walk through attributes */
476     foreach ($this->attributes as $val){
478       if (isset($this->attrs["$val"][0])){
480         /* If attribute is set, replace dynamic parts: 
481            %sn, %givenName and %uid. Fill these in our local variables. */
482         $value= $this->attrs["$val"][0];
484         foreach (array("sn", "givenName", "uid") as $repl){
485           if (preg_match("/%$repl/i", $value)){
486             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
487           }
488         }
489         $this->$val= $value;
490       }
491     }
493     /* Is Account? */
494     $found= TRUE;
495     foreach ($this->objectclasses as $obj){
496       if (preg_match('/top/i', $obj)){
497         continue;
498       }
499       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
500         $found= FALSE;
501         break;
502       }
503     }
504     if ($found){
505       $this->is_account= TRUE;
506     }
507   }
509   /* Indicate whether a password change is needed or not */
510   function password_change_needed()
511   {
512     return FALSE;
513   }
516   /* Show header message for tab dialogs */
517   function show_enable_header($button_text, $text, $disabled= FALSE)
518   {
519     if ($disabled == TRUE){
520       $state= "disabled";
521     } else {
522       $state= "";
523     }
524     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
525     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
526       ($this->acl_is_createable()?'':'disabled')." ".$state.
527       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
529     return($display);
530   }
533   /* Show header message for tab dialogs */
534   function show_disable_header($button_text, $text, $disabled= FALSE)
535   {
536     if ($disabled == TRUE){
537       $state= "disabled";
538     } else {
539       $state= "";
540     }
541     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
542     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
543       ($this->acl_is_removeable()?'':'disabled')." ".$state.
544       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
546     return($display);
547   }
550   /* Show header message for tab dialogs */
551   function show_header($button_text, $text, $disabled= FALSE)
552   {
553     echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
554     if ($disabled == TRUE){
555       $state= "disabled";
556     } else {
557       $state= "";
558     }
559     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
560     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
561       ($this->acl_is_createable()?'':'disabled')." ".$state.
562       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
564     return($display);
565   }
568   function postcreate($add_attrs= array())
569   {
570     /* Find postcreate entries for this class */
571     $command= search_config($this->config->data['MENU'], get_class($this), "POSTCREATE");
572     if ($command == "" && isset($this->config->data['TABS'])){
573       $command= search_config($this->config->data['TABS'], get_class($this), "POSTCREATE");
574     }
576     if ($command != ""){
577       /* Walk through attribute list */
578       foreach ($this->attributes as $attr){
579         if (!is_array($this->$attr)){
580           $command= preg_replace("/%$attr/", $this->$attr, $command);
581         }
582       }
583       $command= preg_replace("/%dn/", $this->dn, $command);
585       /* Additional attributes */
586       foreach ($add_attrs as $name => $value){
587         $command= preg_replace("/%$name/", $value, $command);
588       }
590       if (check_command($command)){
591         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
592             $command, "Execute");
594         exec($command);
595       } else {
596         $message= sprintf(_("Command '%s', specified as POSTCREATE for plugin '%s' doesn't seem to exist."), $command, get_class($this));
597         print_red ($message);
598       }
599     }
600   }
602   function postmodify($add_attrs= array())
603   {
604     /* Find postcreate entries for this class */
605     $command= search_config($this->config->data['MENU'], get_class($this), "POSTMODIFY");
606     if ($command == "" && isset($this->config->data['TABS'])){
607       $command= search_config($this->config->data['TABS'], get_class($this), "POSTMODIFY");
608     }
610     if ($command != ""){
611       /* Walk through attribute list */
612       foreach ($this->attributes as $attr){
613         if (!is_array($this->$attr)){
614           $command= preg_replace("/%$attr/", $this->$attr, $command);
615         }
616       }
617       $command= preg_replace("/%dn/", $this->dn, $command);
619       /* Additional attributes */
620       foreach ($add_attrs as $name => $value){
621         $command= preg_replace("/%$name/", $value, $command);
622       }
624       if (check_command($command)){
625         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
626             $command, "Execute");
628         exec($command);
629       } else {
630         $message= sprintf(_("Command '%s', specified as POSTMODIFY for plugin '%s' doesn't seem to exist."), $command, get_class($this));
631         print_red ($message);
632       }
633     }
634   }
636   function postremove($add_attrs= array())
637   {
638     /* Find postremove entries for this class */
639     $command= search_config($this->config->data['MENU'], get_class($this), "POSTREMOVE");
640     if ($command == "" && isset($this->config->data['TABS'])){
641       $command= search_config($this->config->data['TABS'], get_class($this), "POSTREMOVE");
642     }
644     if ($command != ""){
645       /* Walk through attribute list */
646       foreach ($this->attributes as $attr){
647         if (!is_array($this->$attr)){
648           $command= preg_replace("/%$attr/", $this->$attr, $command);
649         }
650       }
651       $command= preg_replace("/%dn/", $this->dn, $command);
653       /* Additional attributes */
654       foreach ($add_attrs as $name => $value){
655         $command= preg_replace("/%$name/", $value, $command);
656       }
658       if (check_command($command)){
659         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
660             $command, "Execute");
662         exec($command);
663       } else {
664         $message= sprintf(_("Command '%s', specified as POSTREMOVE for plugin '%s' doesn't seem to exist."), $command, get_class($this));
665         print_red ($message);
666       }
667     }
668   }
670   /* Create unique DN */
671   function create_unique_dn($attribute, $base)
672   {
673     $ldap= $this->config->get_ldap_link();
674     $base= preg_replace("/^,*/", "", $base);
676     /* Try to use plain entry first */
677     $dn= "$attribute=".$this->$attribute.",$base";
678     $ldap->cat ($dn, array('dn'));
679     if (!$ldap->fetch()){
680       return ($dn);
681     }
683     /* Look for additional attributes */
684     foreach ($this->attributes as $attr){
685       if ($attr == $attribute || $this->$attr == ""){
686         continue;
687       }
689       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
690       $ldap->cat ($dn, array('dn'));
691       if (!$ldap->fetch()){
692         return ($dn);
693       }
694     }
696     /* None found */
697     return ("none");
698   }
700   function rebind($ldap, $referral)
701   {
702     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
703     if (ldap_bind($ldap, $credentials['ADMIN'], $credentials['PASSWORD'])) {
704       $this->error = "Success";
705       $this->hascon=true;
706       $this->reconnect= true;
707       return (0);
708     } else {
709       $this->error = "Could not bind to " . $credentials['ADMIN'];
710       return NULL;
711     }
712   }
714   /* This is a workaround function. */
715   function copy($src_dn, $dst_dn)
716   {
717     /* Rename dn in possible object groups */
718     $ldap= $this->config->get_ldap_link();
719     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::fix($src_dn).'))',
720         array('cn'));
721     while ($attrs= $ldap->fetch()){
722       $og= new ogroup($this->config, $ldap->getDN());
723       unset($og->member[$src_dn]);
724       $og->member[$dst_dn]= $dst_dn;
725       $og->save ();
726     }
728     $ldap->cat($dst_dn);
729     $attrs= $ldap->fetch();
730     if (count($attrs)){
731       trigger_error("Trying to overwrite ".@LDAP::fix($dst_dn).", which already exists.",
732           E_USER_WARNING);
733       return (FALSE);
734     }
736     $ldap->cat($src_dn);
737     $attrs= $ldap->fetch();
738     if (!count($attrs)){
739       trigger_error("Trying to move ".@LDAP::fix($src_dn).", which does not seem to exist.",
740           E_USER_WARNING);
741       return (FALSE);
742     }
744     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
745     $ds= ldap_connect($this->config->current['SERVER']);
746     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
747     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
748       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
749     }
751     $r=ldap_bind($ds,$this->config->current['ADMIN'], $this->config->current['PASSWORD']);
752     error_reporting (0);
753     $sr=ldap_read($ds, @LDAP::fix($src_dn), "objectClass=*");
755     /* Fill data from LDAP */
756     $new= array();
757     if ($sr) {
758       $ei=ldap_first_entry($ds, $sr);
759       if ($ei) {
760         foreach($attrs as $attr => $val){
761           if ($info = ldap_get_values_len($ds, $ei, $attr)){
762             for ($i= 0; $i<$info['count']; $i++){
763               if ($info['count'] == 1){
764                 $new[$attr]= $info[$i];
765               } else {
766                 $new[$attr][]= $info[$i];
767               }
768             }
769           }
770         }
771       }
772     }
774     /* close conncetion */
775     error_reporting (E_ALL);
776     ldap_unbind($ds);
778     /* Adapt naming attribute */
779     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
780     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
781     $new[$dst_name]= @LDAP::fix($dst_val);
783     /* Check if this is a department.
784      * If it is a dep. && there is a , override in his ou 
785      *  change \2C to , again, else this entry can't be saved ...
786      */
787     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
788       $new['ou'] = preg_replace("/\\\\,/",",",$new['ou']);
789     }
791     /* Save copy */
792     $ldap->connect();
793     $ldap->cd($this->config->current['BASE']);
794     
795     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
797     /* FAIvariable=.../..., cn=.. 
798         could not be saved, because the attribute FAIvariable was different to 
799         the dn FAIvariable=..., cn=... */
800     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
801       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
802     }
803     $ldap->cd($dst_dn);
804     $ldap->add($new);
806     if ($ldap->error != "Success"){
807       trigger_error("Trying to save $dst_dn failed.",
808           E_USER_WARNING);
809       return(FALSE);
810     }
812     return (TRUE);
813   }
816   function move($src_dn, $dst_dn)
817   {
818     /* Copy source to destination */
819     if (!$this->copy($src_dn, $dst_dn)){
820       return (FALSE);
821     }
823     /* Delete source */
824     $ldap= $this->config->get_ldap_link();
825     $ldap->rmdir($src_dn);
826     if ($ldap->error != "Success"){
827       trigger_error("Trying to delete $src_dn failed.",
828           E_USER_WARNING);
829       return (FALSE);
830     }
832     return (TRUE);
833   }
836   /* Move/Rename complete trees */
837   function recursive_move($src_dn, $dst_dn)
838   {
839     /* Check if the destination entry exists */
840     $ldap= $this->config->get_ldap_link();
842     /* Check if destination exists - abort */
843     $ldap->cat($dst_dn, array('dn'));
844     if ($ldap->fetch()){
845       trigger_error("recursive_move $dst_dn already exists.",
846           E_USER_WARNING);
847       return (FALSE);
848     }
850     /* Perform a search for all objects to be moved */
851     $objects= array();
852     $ldap->cd($src_dn);
853     $ldap->search("(objectClass=*)", array("dn"));
854     while($attrs= $ldap->fetch()){
855       $dn= $attrs['dn'];
856       $objects[$dn]= strlen($dn);
857     }
859     /* Sort objects by indent level */
860     asort($objects);
861     reset($objects);
863     /* Copy objects from small to big indent levels by replacing src_dn by dst_dn */
864     foreach ($objects as $object => $len){
865       $src= $object;
866       $dst= preg_replace("/$src_dn$/", "$dst_dn", $object);
867       if (!$this->copy($src, $dst)){
868         return (FALSE);
869       }
870     }
872     /* Remove src_dn */
873     $ldap->cd($src_dn);
874     $ldap->recursive_remove();
875     return (TRUE);
876   }
879   function handle_post_events($mode, $add_attrs= array())
880   {
881     switch ($mode){
882       case "add":
883         $this->postcreate($add_attrs);
884       break;
886       case "modify":
887         $this->postmodify($add_attrs);
888       break;
890       case "remove":
891         $this->postremove($add_attrs);
892       break;
893     }
894   }
897   function saveCopyDialog(){
898   }
901   function getCopyDialog(){
902     return(array("string"=>"","status"=>""));
903   }
906   function PrepareForCopyPaste($source){
907     $todo = $this->attributes;
908     if(isset($this->CopyPasteVars)){
909       $todo = array_merge($todo,$this->CopyPasteVars);
910     }
911     $todo[] = "is_account";
912     foreach($todo as $var){
913       $this->$var = $source->$var;
914     }
915   }
918   function handle_object_tagging($dn= "", $tag= "", $show= false)
919   {
920     //FIXME: How to optimize this? We have at least two
921     //       LDAP accesses per object. It would be a good
922     //       idea to have it integrated.
924     /* No dn? Self-operation... */
925     if ($dn == ""){
926       $dn= $this->dn;
928       /* No tag? Find it yourself... */
929       if ($tag == ""){
930         $len= strlen($dn);
932         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
933         $relevant= array();
934         foreach ($this->config->adepartments as $key => $ntag){
936           /* This one is bigger than our dn, its not relevant... */
937           if ($len <= strlen($key)){
938             continue;
939           }
941           /* This one matches with the latter part. Break and don't fix this entry */
942           if (preg_match('/(^|,)'.normalizePreg($key).'$/', $dn)){
943             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
944             $relevant[strlen($key)]= $ntag;
945             continue;
946           }
948         }
950         /* If we've some relevant tags to set, just get the longest one */
951         if (count($relevant)){
952           ksort($relevant);
953           $tmp= array_keys($relevant);
954           $idx= end($tmp);
955           $tag= $relevant[$idx];
956           $this->gosaUnitTag= $tag;
957         }
958       }
959     }
962     /* Set tag? */
963     if ($tag != ""){
964       /* Set objectclass and attribute */
965       $ldap= $this->config->get_ldap_link();
966       $ldap->cat($dn, array('gosaUnitTag', 'objectClass'));
967       $attrs= $ldap->fetch();
968       if(isset($attrs['gosaUnitTag'][0]) && $attrs['gosaUnitTag'][0] == $tag){
969         if ($show) {
970           echo sprintf(_("Object '%s' is already tagged"), @LDAP::fix($dn))."<br>";
971           flush();
972         }
973         return;
974       }
975       if (count($attrs)){
976         if ($show){
977           echo sprintf(_("Adding tag (%s) to object '%s'"), $tag, @LDAP::fix($dn))."<br>";
978           flush();
979         }
980         $nattrs= array("gosaUnitTag" => $tag);
981         $nattrs['objectClass']= array();
982         for ($i= 0; $i<$attrs['objectClass']['count']; $i++){
983           $oc= $attrs['objectClass'][$i];
984           if ($oc != "gosaAdministrativeUnitTag"){
985             $nattrs['objectClass'][]= $oc;
986           }
987         }
988         $nattrs['objectClass'][]= "gosaAdministrativeUnitTag";
989         $ldap->cd($dn);
990         $ldap->modify($nattrs);
991         show_ldap_error($ldap->get_error(), sprintf(_("Handle object tagging with dn '%s' failed."),$dn));
992       } else {
993         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "Not tagging ($tag) $dn - seems to have moved away", "Tagging");
994       }
996     } else {
997       /* Remove objectclass and attribute */
998       $ldap= $this->config->get_ldap_link();
999       $ldap->cat($dn, array('gosaUnitTag', 'objectClass'));
1000       $attrs= $ldap->fetch();
1001       if (isset($attrs['objectClass']) && !in_array_ics("gosaAdministrativeUnitTag", $attrs['objectClass'])){
1002         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "$dn is not tagged", "Tagging");
1003         return;
1004       }
1005       if (count($attrs)){
1006         if ($show){
1007           echo sprintf(_("Removing tag from object '%s'"), @LDAP::fix($dn))."<br>";
1008           flush();
1009         }
1010         $nattrs= array("gosaUnitTag" => array());
1011         $nattrs['objectClass']= array();
1012         for ($i= 0; $i<$attrs['objectClass']['count']; $i++){
1013           $oc= $attrs['objectClass'][$i];
1014           if ($oc != "gosaAdministrativeUnitTag"){
1015             $nattrs['objectClass'][]= $oc;
1016           }
1017         }
1018         $ldap->cd($dn);
1019         $ldap->modify($nattrs);
1020         show_ldap_error($ldap->get_error(), sprintf(_("Handle object tagging with dn '%s' failed."),$dn));
1021       } else {
1022         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "Not removing tag ($tag) $dn - seems to have moved away", "Tagging");
1023       }
1024     }
1026   }
1029   /* Add possibility to stop remove process */
1030   function allow_remove()
1031   {
1032     $reason= "";
1033     return $reason;
1034   }
1037   /* Create a snapshot of the current object */
1038   function create_snapshot($type= "snapshot", $description= array())
1039   {
1041     /* Check if snapshot functionality is enabled */
1042     if(!$this->snapshotEnabled()){
1043       return;
1044     }
1046     /* Get configuration from gosa.conf */
1047     $tmp = $this->config->current;
1049     /* Create lokal ldap connection */
1050     $ldap= $this->config->get_ldap_link();
1051     $ldap->cd($this->config->current['BASE']);
1053     /* check if there are special server configurations for snapshots */
1054     if(!isset($tmp['SNAPSHOT_SERVER'])){
1056       /* Source and destination server are both the same, just copy source to dest obj */
1057       $ldap_to      = $ldap;
1058       $snapldapbase = $this->config->current['BASE'];
1060     }else{
1061       $server         = $tmp['SNAPSHOT_SERVER'];
1062       $user           = $tmp['SNAPSHOT_USER'];
1063       $password       = $tmp['SNAPSHOT_PASSWORD'];
1064       $snapldapbase   = $tmp['SNAPSHOT_LDAP_BASE'];
1066       $ldap_to        = new LDAP($user,$password, $server);
1067       $ldap_to -> cd($snapldapbase);
1068       show_ldap_error($ldap->get_error(), sprintf(_("Saving object snapshot with dn '%s' failed."),$snapldapbase));
1069     }
1071     /* check if the dn exists */ 
1072     if ($ldap->dn_exists($this->dn)){
1074       /* Extract seconds & mysecs, they are used as entry index */
1075       list($usec, $sec)= explode(" ", microtime());
1077       /* Collect some infos */
1078       $base           = $this->config->current['BASE'];
1079       $snap_base      = $tmp['SNAPSHOT_BASE'];
1080       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1081       $new_base       = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1083       /* Create object */
1084 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1085       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1086       $newName          = preg_replace("/\./", "", $sec."-".$usec);
1087       $target= array();
1088       $target['objectClass']            = array("top", "gosaSnapshotObject");
1089       $target['gosaSnapshotData']       = gzcompress($data, 6);
1090       $target['gosaSnapshotType']       = $type;
1091       $target['gosaSnapshotDN']         = $this->dn;
1092       $target['description']            = $description;
1093       $target['gosaSnapshotTimestamp']  = $newName;
1095       /* Insert the new snapshot 
1096          But we have to check first, if the given gosaSnapshotTimestamp
1097          is already used, in this case we should increment this value till there is 
1098          an unused value. */ 
1099       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1100       $ldap_to->cat($new_dn);
1101       while($ldap_to->count()){
1102         $ldap_to->cat($new_dn);
1103         $newName = preg_replace("/\./", "", $sec."-".($usec++));
1104         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1105         $target['gosaSnapshotTimestamp']  = $newName;
1106       } 
1108       /* Inset this new snapshot */
1109       $ldap_to->cd($snapldapbase);
1110       $ldap_to->create_missing_trees($new_base);
1111       $ldap_to->cd($new_dn);
1112       $ldap_to->add($target);
1114       show_ldap_error($ldap->get_error(), sprintf(_("Saving object snapshot with dn '%s' failed."),$new_base));
1115       show_ldap_error($ldap_to->get_error(), sprintf(_("Saving object snapshot with dn '%s' failed."),$new_base));
1116     }
1117   }
1119   function remove_snapshot($dn)
1120   {
1121 echo "FIXME: remove_snapshot uses old acl's<br>";
1122     $ui   = get_userinfo();
1123     $acl  = get_permissions ($dn, $ui->subtreeACL);
1124     $acl  = get_module_permission($acl, "snapshot", $dn);
1126     if (chkacl($this->acl, "delete") == ""){
1127       $ldap = $this->config->get_ldap_link();
1128       $ldap->cd($this->config->current['BASE']);
1129       $ldap->rmdir_recursive($dn);
1130     }else{
1131       print_red (_("You are not allowed to delete this snapshot!"));
1132     }
1133   }
1136   /* returns true if snapshots are enabled, and false if it is disalbed
1137      There will also be some errors psoted, if the configuration failed */
1138   function snapshotEnabled()
1139   {
1140     $tmp = $this->config->current;
1141     if(isset($tmp['ENABLE_SNAPSHOT'])){
1142       if (preg_match("/^true$/i", $tmp['ENABLE_SNAPSHOT']) || preg_match("/yes/i", $tmp['ENABLE_SNAPSHOT'])){
1144         /* Check if the snapshot_base is defined */
1145         if(!isset($tmp['SNAPSHOT_BASE'])){
1146           print_red(sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not configured in your gosa.conf."),$missing));
1147           return(FALSE);
1148         }
1150         /* check if there are special server configurations for snapshots */
1151         if(isset($tmp['SNAPSHOT_SERVER'])){
1153           /* check if all required vars are available to create a new ldap connection */
1154           $missing = "";
1155           foreach(array("SNAPSHOT_SERVER","SNAPSHOT_USER","SNAPSHOT_PASSWORD","SNAPSHOT_LDAP_BASE") as $var){
1156             if(!isset($tmp[$var])){
1157               $missing .= $var." ";
1158               print_red(sprintf(_("The snapshot functionality is enabled, but the required variable(s) '%s' is not configured in your gosa.conf."),$missing));
1159               return(FALSE);
1160             }
1161           }
1162         }
1163         return(TRUE);
1164       }
1165     }
1166     return(FALSE);
1167   }
1170   /* Return available snapshots for the given base 
1171    */
1172   function Available_SnapsShots($dn,$raw = false)
1173   {
1174     if(!$this->snapshotEnabled()) return(array());
1176     /* Create an additional ldap object which
1177        points to our ldap snapshot server */
1178     $ldap= $this->config->get_ldap_link();
1179     $ldap->cd($this->config->current['BASE']);
1180     $tmp = $this->config->current;
1182     /* check if there are special server configurations for snapshots */
1183     if(isset($tmp['SNAPSHOT_SERVER'])){
1184       $server       = $tmp['SNAPSHOT_SERVER'];
1185       $user         = $tmp['SNAPSHOT_USER'];
1186       $password     = $tmp['SNAPSHOT_PASSWORD'];
1187       $snapldapbase = $tmp['SNAPSHOT_LDAP_BASE'];
1188       $ldap_to      = new LDAP($user,$password, $server);
1189       $ldap_to -> cd ($snapldapbase);
1190       show_ldap_error($ldap->get_error(), sprintf(_("Method get available snapshots with dn '%s' failed."),$this->dn));
1191     }else{
1192       $ldap_to    = $ldap;
1193     }
1195     /* Prepare bases and some other infos */
1196     $base           = $this->config->current['BASE'];
1197     $snap_base      = $tmp['SNAPSHOT_BASE'];
1198     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1199     $new_base       = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1200     $tmp            = array(); 
1202     /* Fetch all objects with  gosaSnapshotDN=$dn */
1203     $ldap_to->cd($new_base);
1204     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1205         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1207     /* Put results into a list and add description if missing */
1208     while($entry = $ldap_to->fetch()){ 
1209       if(!isset($entry['description'][0])){
1210         $entry['description'][0]  = "";
1211       }
1212       $tmp[] = $entry; 
1213     }
1215     /* Return the raw array, or format the result */
1216     if($raw){
1217       return($tmp);
1218     }else{  
1219       $tmp2 = array();
1220       foreach($tmp as $entry){
1221         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1222       }
1223     }
1224     return($tmp2);
1225   }
1228   function getAllDeletedSnapshots($base_of_object,$raw = false)
1229   {
1230     if(!$this->snapshotEnabled()) return(array());
1232     /* Create an additional ldap object which
1233        points to our ldap snapshot server */
1234     $ldap= $this->config->get_ldap_link();
1235     $ldap->cd($this->config->current['BASE']);
1236     $tmp = $this->config->current;
1238     /* check if there are special server configurations for snapshots */
1239     if(isset($tmp['SNAPSHOT_SERVER'])){
1240       $server       = $tmp['SNAPSHOT_SERVER'];
1241       $user         = $tmp['SNAPSHOT_USER'];
1242       $password     = $tmp['SNAPSHOT_PASSWORD'];
1243       $snapldapbase = $tmp['SNAPSHOT_LDAP_BASE'];
1244       $ldap_to      = new LDAP($user,$password, $server);
1245       $ldap_to->cd ($snapldapbase);
1246       show_ldap_error($ldap->get_error(), sprintf(_("Method get deleted snapshots with dn '%s' failed."),$this->dn));
1247     }else{
1248       $ldap_to    = $ldap;
1249     }
1251     /* Prepare bases */ 
1252     $base           = $this->config->current['BASE'];
1253     $snap_base      = $tmp['SNAPSHOT_BASE'];
1254     $new_base       = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1256     /* Fetch all objects and check if they do not exist anymore */
1257     $ui = get_userinfo();
1258     $tmp = array();
1259     $ldap_to->cd($new_base);
1260     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1261     while($entry = $ldap_to->fetch()){
1263       $chk =  str_replace($new_base,"",$entry['dn']);
1264       if(preg_match("/,ou=/",$chk)) continue;
1266       if(!isset($entry['description'][0])){
1267         $entry['description'][0]  = "";
1268       }
1269       $tmp[] = $entry; 
1270     }
1272     /* Check if entry still exists */
1273     foreach($tmp as $key => $entry){
1274       $ldap->cat($entry['gosaSnapshotDN'][0]);
1275       if($ldap->count()){
1276         unset($tmp[$key]);
1277       }
1278     }
1280     /* Format result as requested */
1281     if($raw) {
1282       return($tmp);
1283     }else{
1284       $tmp2 = array();
1285       foreach($tmp as $key => $entry){
1286         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1287       }
1288     }
1289     return($tmp2);
1290   } 
1293   /* Restore selected snapshot */
1294   function restore_snapshot($dn)
1295   {
1296     if(!$this->snapshotEnabled()) return(array());
1298     $ldap= $this->config->get_ldap_link();
1299     $ldap->cd($this->config->current['BASE']);
1300     $tmp = $this->config->current;
1302     /* check if there are special server configurations for snapshots */
1303     if(isset($tmp['SNAPSHOT_SERVER'])){
1304       $server       = $tmp['SNAPSHOT_SERVER'];
1305       $user         = $tmp['SNAPSHOT_USER'];
1306       $password     = $tmp['SNAPSHOT_PASSWORD'];
1307       $snapldapbase = $tmp['SNAPSHOT_LDAP_BASE'];
1308       $ldap_to      = new LDAP($user,$password, $server);
1309       $ldap_to->cd ($snapldapbase);
1310       show_ldap_error($ldap->get_error(), sprintf(_("Restore snapshot with dn '%s' failed."),$snapldapbase));
1311     }else{
1312       $ldap_to    = $ldap;
1313     }
1315     /* Get the snapshot */ 
1316     $ldap_to->cat($dn);
1317     $restoreObject = $ldap_to->fetch();
1319     /* Prepare import string */
1320     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1322     /* Import the given data */
1323     $ldap->import_complete_ldif($data,$err,false,false);
1324     show_ldap_error($ldap->get_error(), sprintf(_("Restore snapshot with dn '%s' failed."),$dn));
1325   }
1328   function showSnapshotDialog($base,$baseSuffixe)
1329   {
1330     $once = true;
1331     foreach($_POST as $name => $value){
1333       /* Create a new snapshot, display a dialog */
1334       if(preg_match("/^CreateSnapShotDialog_/",$name) && $once){
1335         $once = false;
1336         $entry = preg_replace("/^CreateSnapShotDialog_/","",$name);
1337         $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1338         $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1339       }
1341       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1342       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1343         $once = false;
1344         $entry = preg_replace("/^RestoreSnapShotDialog_/","",$name);
1345         $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1346         $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1347         $this->snapDialog->display_restore_dialog = true;
1348       }
1350       /* Restore one of the already deleted objects */
1351       if(preg_match("/^RestoreDeletedSnapShot_/",$name) && $once){
1352         $once = false;
1353         $this->snapDialog = new SnapShotDialog($this->config,$baseSuffixe,$this);
1354         $this->snapDialog->display_restore_dialog      = true;
1355         $this->snapDialog->display_all_removed_objects  = true;
1356       }
1358       /* Restore selected snapshot */
1359       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1360         $once = false;
1361         $entry = preg_replace("/^RestoreSnapShot_/","",$name);
1362         $entry = base64_decode(trim(preg_replace("/_[xy]$/","",$entry)));
1363         if(!empty($entry)){
1364           $this->restore_snapshot($entry);
1365           $this->snapDialog = NULL;
1366         }
1367       }
1368     }
1370     /* Create a new snapshot requested, check
1371        the given attributes and create the snapshot*/
1372     if(isset($_POST['CreateSnapshot'])){
1373       $this->snapDialog->save_object();
1374       $msgs = $this->snapDialog->check();
1375       if(count($msgs)){
1376         foreach($msgs as $msg){
1377           print_red($msg);
1378         }
1379       }else{
1380         $this->dn =  $this->snapDialog->dn;
1381         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1382         $this->snapDialog = NULL;
1383       }
1384     }
1386     /* Restore is requested, restore the object with the posted dn .*/
1387     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1388     }
1390     if(isset($_POST['CancelSnapshot'])){
1391       $this->snapDialog = NULL;
1392     }
1394     if($this->snapDialog){
1395       $this->snapDialog->save_object();
1396       return($this->snapDialog->execute());
1397     }
1398   }
1401   function plInfo()
1402   {
1403     return array();
1404   }
1407   function set_acl_base($base)
1408   {
1409     $this->acl_base= $base;
1410   }
1413   function acl_is_writeable($attribute,$skip_write = FALSE)
1414   {
1415     $ui= get_userinfo();
1416     return preg_match('/w/', $ui->get_permissions($this->acl_base, get_class($this), $attribute,$skip_write));
1417   }
1420   function acl_is_readable($attribute)
1421   {
1422     $ui= get_userinfo();
1423     return preg_match('/r/', $ui->get_permissions($this->acl_base, get_class($this), $attribute));
1424   }
1427   function acl_is_createable()
1428   {
1429     $ui= get_userinfo();
1430     return preg_match('/c/', $ui->get_permissions($this->acl_base, get_class($this), '0'));
1431   }
1434   function acl_is_removeable()
1435   {
1436     $ui= get_userinfo();
1437     return preg_match('/d/', $ui->get_permissions($this->acl_base, get_class($this), '0'));
1438   }
1441   function acl_is_moveable()
1442   {
1443     $ui= get_userinfo();
1444     return preg_match('/m/', $ui->get_permissions($this->acl_base, get_class($this), '0'));
1445   }
1448   function acl_have_any_permissions()
1449   {
1450   }
1453   function getacl($attribute,$skip_write= FALSE)
1454   {
1455     $ui= get_userinfo();
1456     return  $ui->get_permissions($this->acl_base, get_class($this), $attribute,$skip_write);
1457   }
1461 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1462 ?>