Code

Fixed alignment of the icon menu
[gosa.git] / gosa-core / include / class_plugin.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  *
6  * ID: $$Id$$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
23 /*! \brief   The plugin base class
24   \author  Cajus Pollmeier <pollmeier@gonicus.de>
25   \version 2.00
26   \date    24.07.2003
28   This is the base class for all plugins. It can be used standalone or
29   can be included by the tabs class. All management should be done 
30   within this class. Extend your plugins from this class.
31  */
33 class plugin
34 {
35   /*!
36     \brief Reference to parent object
38     This variable is used when the plugin is included in tabs
39     and keeps reference to the tab class. Communication to other
40     tabs is possible by 'name'. So the 'fax' plugin can ask the
41     'userinfo' plugin for the fax number.
43     \sa tab
44    */
45   var $parent= NULL;
47   /*!
48     \brief Configuration container
50     Access to global configuration
51    */
52   var $config= NULL;
54   /*!
55     \brief Mark plugin as account
57     Defines whether this plugin is defined as an account or not.
58     This has consequences for the plugin to be saved from tab
59     mode. If it is set to 'FALSE' the tab will call the delete
60     function, else the save function. Should be set to 'TRUE' if
61     the construtor detects a valid LDAP object.
63     \sa plugin::plugin()
64    */
65   var $is_account= FALSE;
66   var $initially_was_account= FALSE;
68   /*!
69     \brief Mark plugin as template
71     Defines whether we are creating a template or a normal object.
72     Has conseqences on the way execute() shows the formular and how
73     save() puts the data to LDAP.
75     \sa plugin::save() plugin::execute()
76    */
77   var $is_template= FALSE;
78   var $ignore_account= FALSE;
79   var $is_modified= FALSE;
81   /*!
82     \brief Represent temporary LDAP data
84     This is only used internally.
85    */
86   var $attrs= array();
88   /* Keep set of conflicting plugins */
89   var $conflicts= array();
91   /* Save unit tags */
92   var $gosaUnitTag= "";
93   var $skipTagging= FALSE;
95   /*!
96     \brief Used standard values
98     dn
99    */
100   var $dn= "";
101   var $uid= "";
102   var $sn= "";
103   var $givenName= "";
104   var $acl= "*none*";
105   var $dialog= FALSE;
106   var $snapDialog = NULL;
108   /* attribute list for save action */
109   var $attributes= array();
110   var $objectclasses= array();
111   var $is_new= TRUE;
112   var $saved_attributes= array();
114   var $acl_base= "";
115   var $acl_category= "";
116   var $read_only = FALSE; // Used when the entry is opened as "readonly" due to locks.
118   /* This can be set to render the tabulators in another stylesheet */
119   var $pl_notify= FALSE;
121   /* Object entry CSN */
122   var $entryCSN         = "";
123   var $CSN_check_active = FALSE;
125   /* This variable indicates that this class can handle multiple dns at once. */
126   var $multiple_support = FALSE;
127   var $multi_attrs      = array();
128   var $multi_attrs_all  = array(); 
130   /* This aviable indicates, that we are currently in multiple edit handle */
131   var $multiple_support_active = FALSE; 
132   var $selected_edit_values = array();
133   var $multi_boxes = array();
135   /*! \brief plugin constructor
137     If 'dn' is set, the node loads the given 'dn' from LDAP
139     \param dn Distinguished name to initialize plugin from
140     \sa plugin()
141    */
142   function plugin (&$config, $dn= NULL, $object= NULL)
143   {
144     /* Configuration is fine, allways */
145     $this->config= &$config;    
146     $this->dn= $dn;
148     // Ensure that we've a valid acl_category set.
149     if(empty($this->acl_category)){
150       $tmp = $this->plInfo();
151       if (isset($tmp['plCategory'])) {
152         $c = key($tmp['plCategory']);
153         if(is_numeric($c)){
154           $c = $tmp['plCategory'][0];
155         }
156         $this->acl_category = $c."/";
157       }
158     }
160     /* Handle new accounts, don't read information from LDAP */
161     if ($dn == "new"){
162       return;
163     }
165     /* Check if this entry was opened in read only mode */
166     if(isset($_POST['open_readonly'])){
167       if(session::global_is_set("LOCK_CACHE")){
168         $cache = &session::get("LOCK_CACHE");
169         if(isset($cache['READ_ONLY'][$this->dn])){
170           $this->read_only = TRUE;
171         }
172       }
173     }
175     /* Save current dn as acl_base */
176     $this->acl_base= $dn;
178     /* Get LDAP descriptor */
179     if ($dn !== NULL){
181       /* Load data to 'attrs' and save 'dn' */
182       if ($object !== NULL){
183         $this->attrs= $object->attrs;
184       } else {
185         $ldap= $this->config->get_ldap_link();
186         $ldap->cat ($dn);
187         $this->attrs= $ldap->fetch();
188       }
190       /* Copy needed attributes */
191       foreach ($this->attributes as $val){
192         $found= array_key_ics($val, $this->attrs);
193         if ($found != ""){
194           $this->$val= $found[0];
195         }
196       }
198       /* gosaUnitTag loading... */
199       if (isset($this->attrs['gosaUnitTag'][0])){
200         $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
201       }
203       /* Set the template flag according to the existence of objectClass
204          gosaUserTemplate */
205       if (isset($this->attrs['objectClass'])){
206         if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
207           $this->is_template= TRUE;
208           @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
209               "found", "Template check");
210         }
211       }
213       /* Is Account? */
214       $found= TRUE;
215       foreach ($this->objectclasses as $obj){
216         if (preg_match('/top/i', $obj)){
217           continue;
218         }
219         if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
220           $found= FALSE;
221           break;
222         }
223       }
224       if ($found){
225         $this->is_account= TRUE;
226         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
227             "found", "Object check");
228       }
230       /* Prepare saved attributes */
231       $this->saved_attributes= $this->attrs;
232       foreach ($this->saved_attributes as $index => $value){
233         if (is_numeric($index)){
234           unset($this->saved_attributes[$index]);
235           continue;
236         }
238         if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
239           unset($this->saved_attributes[$index]);
240           continue;
241         }
243         if (isset($this->saved_attributes[$index][0])){
244           if(!isset($this->saved_attributes[$index]["count"])){
245             $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
246           }
247           if($this->saved_attributes[$index]["count"] == 1){
248             $tmp= $this->saved_attributes[$index][0];
249             unset($this->saved_attributes[$index]);
250             $this->saved_attributes[$index]= $tmp;
251             continue;
252           }
253         }
254         unset($this->saved_attributes["$index"]["count"]);
255       }
257       if(isset($this->attrs['gosaUnitTag'])){
258         $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
259       }
260     }
262     /* Save initial account state */
263     $this->initially_was_account= $this->is_account;
264   }
267   /*! \brief Generates the html output for this node
268    */
269   function execute()
270   {
271     /* This one is empty currently. Fabian - please fill in the docu code */
272     session::global_set('current_class_for_help',get_class($this));
274     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
275     session::set('LOCK_VARS_TO_USE',array());
276     session::set('LOCK_VARS_USED_GET',array());
277     session::set('LOCK_VARS_USED_POST',array());
278     session::set('LOCK_VARS_USED_REQUEST',array());
279   }
281   /*! \brief Removes object from parent
282    */
283   function remove_from_parent()
284   {
285     /* include global link_info */
286     $ldap= $this->config->get_ldap_link();
288     /* Get current objectClasses in order to add the required ones */
289     $ldap->cat($this->dn);
290     $tmp= $ldap->fetch ();
291     $oc= array();
292     if (isset($tmp['objectClass'])){
293       $oc= $tmp['objectClass'];
294       unset($oc['count']);
295     }
297     /* Remove objectClasses from entry */
298     $ldap->cd($this->dn);
299     $this->attrs= array();
300     $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
302     /* Unset attributes from entry */
303     foreach ($this->attributes as $val){
304       $this->attrs["$val"]= array();
305     }
307     /* Unset account info */
308     $this->is_account= FALSE;
310     /* Do not write in plugin base class, this must be done by
311        children, since there are normally additional attribs,
312        lists, etc. */
313     /*
314        $ldap->modify($this->attrs);
315      */
316   }
319   /*! \brief Save HTML posted data to object 
320    */
321   function save_object()
322   {
323     /* Update entry CSN if it is empty. */
324     if(empty($this->entryCSN) && $this->CSN_check_active){
325       $this->entryCSN = getEntryCSN($this->dn);
326     }
328     /* Save values to object */
329     foreach ($this->attributes as $val){
330       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
331         /* Check for modifications */
332         if (get_magic_quotes_gpc()) {
333           $data= stripcslashes($_POST["$val"]);
334         } else {
335           $data= $this->$val = $_POST["$val"];
336         }
337         if ($this->$val != $data){
338           $this->is_modified= TRUE;
339         }
340     
341         /* Okay, how can I explain this fix ... 
342          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
343          * So IE posts these 'unselectable' option, with value = chr(194) 
344          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
345          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
346          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
347          */
348         if(isset($data[0]) && $data[0] == chr(194)) {
349           $data = "";  
350         }
351         $this->$val= $data;
352       }
353     }
354   }
357   /*! \brief Save data to LDAP, depending on is_account we save or delete */
358   function save()
359   {
360     /* include global link_info */
361     $ldap= $this->config->get_ldap_link();
363     /* Save all plugins */
364     $this->entryCSN = "";
366     /* Start with empty array */
367     $this->attrs= array();
369     /* Get current objectClasses in order to add the required ones */
370     $ldap->cat($this->dn);
371     
372     $tmp= $ldap->fetch ();
374     $oc= array();
375     if (isset($tmp['objectClass'])){
376       $oc= $tmp["objectClass"];
377       $this->is_new= FALSE;
378       unset($oc['count']);
379     } else {
380       $this->is_new= TRUE;
381     }
383     /* Load (minimum) attributes, add missing ones */
384     $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
386     /* Copy standard attributes */
387     foreach ($this->attributes as $val){
388       if ($this->$val != ""){
389         $this->attrs["$val"]= $this->$val;
390       } elseif (!$this->is_new) {
391         $this->attrs["$val"]= array();
392       }
393     }
395     /* Handle tagging */
396     $this->tag_attrs($this->attrs);
397   }
400   function cleanup()
401   {
402     foreach ($this->attrs as $index => $value){
403       
404       /* Convert arrays with one element to non arrays, if the saved
405          attributes are no array, too */
406       if (is_array($this->attrs[$index]) && 
407           count ($this->attrs[$index]) == 1 &&
408           isset($this->saved_attributes[$index]) &&
409           !is_array($this->saved_attributes[$index])){
410           
411         $tmp= $this->attrs[$index][0];
412         $this->attrs[$index]= $tmp;
413       }
415       /* Remove emtpy arrays if they do not differ */
416       if (is_array($this->attrs[$index]) &&
417           count($this->attrs[$index]) == 0 &&
418           !isset($this->saved_attributes[$index])){
419           
420         unset ($this->attrs[$index]);
421         continue;
422       }
424       /* Remove single attributes that do not differ */
425       if (!is_array($this->attrs[$index]) &&
426           isset($this->saved_attributes[$index]) &&
427           !is_array($this->saved_attributes[$index]) &&
428           $this->attrs[$index] == $this->saved_attributes[$index]){
430         unset ($this->attrs[$index]);
431         continue;
432       }
434       /* Remove arrays that do not differ */
435       if (is_array($this->attrs[$index]) && 
436           isset($this->saved_attributes[$index]) &&
437           is_array($this->saved_attributes[$index])){
438           
439         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
440           unset ($this->attrs[$index]);
441           continue;
442         }
443       }
444     }
446     /* Update saved attributes and ensure that next cleanups will be successful too */
447     foreach($this->attrs as $name => $value){
448       $this->saved_attributes[$name] = $value;
449     }
450   }
452   /*! \brief Check formular input */
453   function check()
454   {
455     $message= array();
457     /* Skip if we've no config object */
458     if (!isset($this->config) || !is_object($this->config)){
459       return $message;
460     }
462     /* Find hooks entries for this class */
463     $command= $this->config->search(get_class($this), "CHECK", array('menu', 'tabs'));
465     if ($command != ""){
467       if (!check_command($command)){
468         $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
469       } else {
471         /* Generate "ldif" for check hook */
472         $ldif= "dn: $this->dn\n";
473         
474         /* ... objectClasses */
475         foreach ($this->objectclasses as $oc){
476           $ldif.= "objectClass: $oc\n";
477         }
478         
479         /* ... attributes */
480         foreach ($this->attributes as $attr){
481           if ($this->$attr == ""){
482             continue;
483           }
484           if (is_array($this->$attr)){
485             foreach ($this->$attr as $val){
486               $ldif.= "$attr: $val\n";
487             }
488           } else {
489               $ldif.= "$attr: ".$this->$attr."\n";
490           }
491         }
493         /* Append empty line */
494         $ldif.= "\n";
496         /* Feed "ldif" into hook and retrieve result*/
497         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
498         $fh= proc_open($command, $descriptorspec, $pipes);
499         if (is_resource($fh)) {
500           fwrite ($pipes[0], $ldif);
501           fclose($pipes[0]);
502           
503           $result= stream_get_contents($pipes[1]);
504           if ($result != ""){
505             $message[]= $result;
506           }
507           
508           fclose($pipes[1]);
509           fclose($pipes[2]);
510           proc_close($fh);
511         }
512       }
514     }
516     /* Check entryCSN */
517     if($this->CSN_check_active){
518       $current_csn = getEntryCSN($this->dn);
519       if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
520         $this->entryCSN = $current_csn;
521         $message[] = _("The object has changed since opened in GOsa. All changes that may be done by others get lost if you save this entry!");
522       }
523     }
524     return ($message);
525   }
527   /* Adapt from template, using 'dn' */
528   function adapt_from_template($dn, $skip= array())
529   {
530     /* Include global link_info */
531     $ldap= $this->config->get_ldap_link();
533     /* Load requested 'dn' to 'attrs' */
534     $ldap->cat ($dn);
535     $this->attrs= $ldap->fetch();
537     /* Walk through attributes */
538     foreach ($this->attributes as $val){
540       /* Skip the ones in skip list */
541       if (in_array($val, $skip)){
542         continue;
543       }
545       if (isset($this->attrs["$val"][0])){
547         /* If attribute is set, replace dynamic parts: 
548            %sn, %givenName and %uid. Fill these in our local variables. */
549         $value= $this->attrs["$val"][0];
551         foreach (array("sn", "givenName", "uid") as $repl){
552           if (preg_match("/%$repl/i", $value)){
553             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
554           }
555         }
556         $this->$val= $value;
557       }
558     }
560     /* Is Account? */
561     $found= TRUE;
562     foreach ($this->objectclasses as $obj){
563       if (preg_match('/top/i', $obj)){
564         continue;
565       }
566       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
567         $found= FALSE;
568         break;
569       }
570     }
571     if ($found){
572       $this->is_account= TRUE;
573     }
574   }
576   /* \brief Indicate whether a password change is needed or not */
577   function password_change_needed()
578   {
579     return FALSE;
580   }
583   /*! \brief Show header message for tab dialogs */
584   function show_enable_header($button_text, $text, $disabled= FALSE)
585   {
586     if (($disabled == TRUE) || (!$this->acl_is_createable())){
587       $state= "disabled";
588     } else {
589       $state= "";
590     }
591     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
592     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
593       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
595     return($display);
596   }
599   /*! \brief Show header message for tab dialogs */
600   function show_disable_header($button_text, $text, $disabled= FALSE)
601   {
602     if (($disabled == TRUE) || !$this->acl_is_removeable()){
603       $state= "disabled";
604     } else {
605       $state= "";
606     }
607     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
608     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
609       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
611     return($display);
612   }
615   /*! \brief Show header message for tab dialogs */
616   function show_header($button_text, $text, $disabled= FALSE)
617   {
618     echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
619     if ($disabled == TRUE){
620       $state= "disabled";
621     } else {
622       $state= "";
623     }
624     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
625     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
626       ($this->acl_is_createable()?'':'disabled')." ".$state.
627       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
629     return($display);
630   }
632   /*! \brief Executes commands after an object has been created */
633   function postcreate($add_attrs= array())
634   {
635     /* Find postcreate entries for this class */
636     $command= $this->config->search(get_class($this), "POSTCREATE",array('menu', 'tabs'));
638     if ($command != ""){
640       /* Walk through attribute list */
641       foreach ($this->attributes as $attr){
642         if (!is_array($this->$attr)){
643           $add_attrs[$attr] = $this->$attr;
644         }
645       }
646       $add_attrs['dn']=$this->dn;
648       $tmp = array();
649       foreach($add_attrs as $name => $value){
650         $tmp[$name] =  strlen($name);
651       }
652       arsort($tmp);
653       
654       /* Additional attributes */
655       foreach ($tmp as $name => $len){
656         $value = $add_attrs[$name];
657         $command= str_replace("%$name", "$value", $command);
658       }
660       if (check_command($command)){
661         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
662             $command, "Execute");
663         exec($command,$arr);
664         foreach($arr as $str){
665           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
666             $command, "Result: ".$str);
667         }
668       } else {
669         $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
670         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
671       }
672     }
673   }
675   /*! \brief Execute commands after an object has been modified */
676   function postmodify($add_attrs= array())
677   {
678     /* Find postcreate entries for this class */
679     $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
681     if ($command != ""){
683       /* Walk through attribute list */
684       foreach ($this->attributes as $attr){
685         if (!is_array($this->$attr)){
686           $add_attrs[$attr] = $this->$attr;
687         }
688       }
689       $add_attrs['dn']=$this->dn;
691       $tmp = array();
692       foreach($add_attrs as $name => $value){
693         $tmp[$name] =  strlen($name);
694       }
695       arsort($tmp);
696       
697       /* Additional attributes */
698       foreach ($tmp as $name => $len){
699         $value = $add_attrs[$name];
700         $command= str_replace("%$name", "$value", $command);
701       }
703       if (check_command($command)){
704         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
705         exec($command,$arr);
706         foreach($arr as $str){
707           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
708             $command, "Result: ".$str);
709         }
710       } else {
711         $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
712         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
713       }
714     }
715   }
717   /*! \brief Executes a command after an object has been removed */
718   function postremove($add_attrs= array())
719   {
720     /* Find postremove entries for this class */
721     $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
722     if ($command != ""){
724       /* Walk through attribute list */
725       foreach ($this->attributes as $attr){
726         if (!is_array($this->$attr)){
727           $add_attrs[$attr] = $this->$attr;
728         }
729       }
730       $add_attrs['dn']=$this->dn;
732       $tmp = array();
733       foreach($add_attrs as $name => $value){
734         $tmp[$name] =  strlen($name);
735       }
736       arsort($tmp);
737       
738       /* Additional attributes */
739       foreach ($tmp as $name => $len){
740         $value = $add_attrs[$name];
741         $command= str_replace("%$name", "$value", $command);
742       }
744       if (check_command($command)){
745         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
746             $command, "Execute");
748         exec($command,$arr);
749         foreach($arr as $str){
750           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
751             $command, "Result: ".$str);
752         }
753       } else {
754         $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
755         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
756       }
757     }
758   }
761   /* Create unique DN */
762   function create_unique_dn2($data, $base)
763   {
764     $ldap= $this->config->get_ldap_link();
765     $base= preg_replace("/^,*/", "", $base);
767     /* Try to use plain entry first */
768     $dn= "$data,$base";
769     $attribute= preg_replace('/=.*$/', '', $data);
770     $ldap->cat ($dn, array('dn'));
771     if (!$ldap->fetch()){
772       return ($dn);
773     }
775     /* Look for additional attributes */
776     foreach ($this->attributes as $attr){
777       if ($attr == $attribute || $this->$attr == ""){
778         continue;
779       }
781       $dn= "$data+$attr=".$this->$attr.",$base";
782       $ldap->cat ($dn, array('dn'));
783       if (!$ldap->fetch()){
784         return ($dn);
785       }
786     }
788     /* None found */
789     return ("none");
790   }
793   /*! \brief Create unique DN */
794   function create_unique_dn($attribute, $base)
795   {
796     $ldap= $this->config->get_ldap_link();
797     $base= preg_replace("/^,*/", "", $base);
799     /* Try to use plain entry first */
800     $dn= "$attribute=".$this->$attribute.",$base";
801     $ldap->cat ($dn, array('dn'));
802     if (!$ldap->fetch()){
803       return ($dn);
804     }
806     /* Look for additional attributes */
807     foreach ($this->attributes as $attr){
808       if ($attr == $attribute || $this->$attr == ""){
809         continue;
810       }
812       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
813       $ldap->cat ($dn, array('dn'));
814       if (!$ldap->fetch()){
815         return ($dn);
816       }
817     }
819     /* None found */
820     return ("none");
821   }
824   function rebind($ldap, $referral)
825   {
826     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
827     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
828       $this->error = "Success";
829       $this->hascon=true;
830       $this->reconnect= true;
831       return (0);
832     } else {
833       $this->error = "Could not bind to " . $credentials['ADMIN'];
834       return NULL;
835     }
836   }
839   /* Recursively copy ldap object */
840   function _copy($src_dn,$dst_dn)
841   {
842     $ldap=$this->config->get_ldap_link();
843     $ldap->cat($src_dn);
844     $attrs= $ldap->fetch();
846     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
847     $ds= ldap_connect($this->config->current['SERVER']);
848     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
849     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
850       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
851     }
853     $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
854     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
855     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
857     /* Fill data from LDAP */
858     $new= array();
859     if ($sr) {
860       $ei=ldap_first_entry($ds, $sr);
861       if ($ei) {
862         foreach($attrs as $attr => $val){
863           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
864             for ($i= 0; $i<$info['count']; $i++){
865               if ($info['count'] == 1){
866                 $new[$attr]= $info[$i];
867               } else {
868                 $new[$attr][]= $info[$i];
869               }
870             }
871           }
872         }
873       }
874     }
876     /* close conncetion */
877     ldap_unbind($ds);
879     /* Adapt naming attribute */
880     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
881     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
882     $new[$dst_name]= LDAP::fix($dst_val);
884     /* Check if this is a department.
885      * If it is a dep. && there is a , override in his ou 
886      *  change \2C to , again, else this entry can't be saved ...
887      */
888     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
889       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
890     }
892     /* Save copy */
893     $ldap->connect();
894     $ldap->cd($this->config->current['BASE']);
895     
896     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
898     /* FAIvariable=.../..., cn=.. 
899         could not be saved, because the attribute FAIvariable was different to 
900         the dn FAIvariable=..., cn=... */
902     if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
904     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
905       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
906     }
907     $ldap->cd($dst_dn);
908     $ldap->add($new);
910     if (!$ldap->success()){
911       trigger_error("Trying to save $dst_dn failed.",
912           E_USER_WARNING);
913       return(FALSE);
914     }
915     return(TRUE);
916   }
919   /* This is a workaround function. */
920   function copy($src_dn, $dst_dn)
921   {
922     /* Rename dn in possible object groups */
923     $ldap= $this->config->get_ldap_link();
924     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
925         array('cn'));
926     while ($attrs= $ldap->fetch()){
927       $og= new ogroup($this->config, $ldap->getDN());
928       unset($og->member[$src_dn]);
929       $og->member[$dst_dn]= $dst_dn;
930       $og->save ();
931     }
933     $ldap->cat($dst_dn);
934     $attrs= $ldap->fetch();
935     if (count($attrs)){
936       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
937           E_USER_WARNING);
938       return (FALSE);
939     }
941     $ldap->cat($src_dn);
942     $attrs= $ldap->fetch();
943     if (!count($attrs)){
944       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
945           E_USER_WARNING);
946       return (FALSE);
947     }
949     $ldap->cd($src_dn);
950     $ldap->search("objectClass=*",array("dn"));
951     while($attrs = $ldap->fetch()){
952       $src = $attrs['dn'];
953       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
954       $this->_copy($src,$dst);
955     }
956     return (TRUE);
957   }
961   /*! \brief  Rename/Move a given src_dn to the given dest_dn
962    *
963    * Move a given ldap object indentified by $src_dn to the
964    * given destination $dst_dn
965    *
966    * - Ensure that all references are updated (ogroups)
967    * - Update ACLs   
968    * - Update accessTo
969    *
970    * \param  string  'src_dn' the source DN.
971    * \param  string  'dst_dn' the destination DN.
972    * \return boolean TRUE on success else FALSE.
973    */
974   function rename($src_dn, $dst_dn)
975   {
976     $start = microtime(1);
978     /* Try to move the source entry to the destination position */
979     $ldap = $this->config->get_ldap_link();
980     $ldap->cd($this->config->current['BASE']);
981     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
982     if (!$ldap->rename_dn($src_dn,$dst_dn)){
983 #      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
984       new log("debug","Ldap Protocol v3 implementation error, ldap_rename failed, falling back to manual copy.","FROM: $src_dn  -- TO: $dst_dn",array(),$ldap->get_error());
985       @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn  -- TO:  $dst_dn", 
986           "Ldap Protocol v3 implementation error, falling back to maunal method.");
987       return(FALSE);
988     }
990     /* Get list of users,groups and roles within this tree,
991         maybe we have to update ACL references.
992      */
993     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
994           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
995     foreach($leaf_objs as $obj){
996       $new_dn = $obj['dn'];
997       $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
998       $this->update_acls($old_dn,$new_dn); 
999     }
1001     // Migrate objectgroups if needed
1002     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
1003       "ogroups", array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1005     // Walk through all objectGroups
1006     foreach($ogroups as $ogroup){
1007       // Migrate old to new dn
1008       $o_ogroup= new ogroup($this->config,$ogroup['dn']);
1009       if (isset($o_ogroup->member[$src_dn])) {
1010         unset($o_ogroup->member[$src_dn]);
1011       }
1012       $o_ogroup->member[$dst_dn]= $dst_dn;
1013       
1014       // Save object group
1015       $o_ogroup->save();
1016     }
1018     // Migrate rfc groups if needed
1019     $groups = get_sub_list("(&(objectClass=posixGroup)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","groups", array(get_ou("groupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1021     // Walk through all POSIX groups
1022     foreach($groups as $group){
1024       // Migrate old to new dn
1025       $o_group= new group($this->config,$group['dn']);
1026       $o_group->save();
1027     }
1029     /* Update roles to use the new entry dn */
1030     $roles = get_sub_list("(&(objectClass=organizationalRole)(roleOccupant=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","roles", array(get_ou("roleRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1032     // Walk through all roles
1033     foreach($roles as $role){
1034       $role = new roleGeneric($this->config,$role['dn']);
1035       $key= array_search($src_dn, $role->roleOccupant);      
1036       if($key !== FALSE){
1037         $role->roleOccupant[$key] = $dst_dn;
1038         $role->save();
1039       }
1040     }
1042     // Update 'manager' attributes from gosaDepartment and inetOrgPerson 
1043     $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
1044     $ocs = $ldap->get_objectclasses();
1045     if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
1046       $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
1047     }
1048     $leaf_deps=  get_list($filter,array("all"),$this->config->current['BASE'], 
1049         array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1050     foreach($leaf_deps as $entry){
1051       $update = array('manager' => $dst_dn);
1052       $ldap->cd($entry['dn']);
1053       $ldap->modify($update);
1054       if(!$ldap->success()){
1055         trigger_error(sprintf("Failed to update manager for '%s', error was '%s'", $entry['dn'], $ldap->get_error()));
1056       }
1057     }
1058  
1059     /* Check if there are gosa departments moved. 
1060        If there were deps moved, the force reload of config->deps.
1061      */
1062     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
1063           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1064   
1065     if(count($leaf_deps)){
1066       $this->config->get_departments();
1067       $this->config->make_idepartments();
1068       session::global_set("config",$this->config);
1069       $ui =get_userinfo();
1070       $ui->reset_acl_cache();
1071     }
1073     return(TRUE); 
1074   }
1077  
1078   function move($src_dn, $dst_dn)
1079   {
1080     /* Do not copy if only upper- lowercase has changed */
1081     if(strtolower($src_dn) == strtolower($dst_dn)){
1082       return(TRUE);
1083     }
1085     
1086     /* Try to move the entry instead of copy & delete
1087      */
1088     if(TRUE){
1090       /* Try to move with ldap routines, if this was not successfull
1091           fall back to the old style copy & remove method 
1092        */
1093       if($this->rename($src_dn, $dst_dn)){
1094         return(TRUE);
1095       }else{
1096         // See code below.
1097       }
1098     }
1100     /* Copy source to destination */
1101     if (!$this->copy($src_dn, $dst_dn)){
1102       return (FALSE);
1103     }
1105     /* Delete source */
1106     $ldap= $this->config->get_ldap_link();
1107     $ldap->rmdir_recursive($src_dn);
1108     if (!$ldap->success()){
1109       trigger_error("Trying to delete $src_dn failed.",
1110           E_USER_WARNING);
1111       return (FALSE);
1112     }
1114     return (TRUE);
1115   }
1118   /* \brief Move/Rename complete trees */
1119   function recursive_move($src_dn, $dst_dn)
1120   {
1121     /* Check if the destination entry exists */
1122     $ldap= $this->config->get_ldap_link();
1124     /* Check if destination exists - abort */
1125     $ldap->cat($dst_dn, array('dn'));
1126     if ($ldap->fetch()){
1127       trigger_error("recursive_move $dst_dn already exists.",
1128           E_USER_WARNING);
1129       return (FALSE);
1130     }
1132     $this->copy($src_dn, $dst_dn);
1134     /* Remove src_dn */
1135     $ldap->cd($src_dn);
1136     $ldap->recursive_remove($src_dn);
1137     return (TRUE);
1138   }
1141   function handle_post_events($mode, $add_attrs= array())
1142   {
1143     switch ($mode){
1144       case "add":
1145         $this->postcreate($add_attrs);
1146       break;
1148       case "modify":
1149         $this->postmodify($add_attrs);
1150       break;
1152       case "remove":
1153         $this->postremove($add_attrs);
1154       break;
1155     }
1156   }
1159   function saveCopyDialog(){
1160   }
1163   function getCopyDialog(){
1164     return(array("string"=>"","status"=>""));
1165   }
1168   /*! \brief Prepare for Copy & Paste */
1169   function PrepareForCopyPaste($source)
1170   {
1171     $todo = $this->attributes;
1172     if(isset($this->CopyPasteVars)){
1173       $todo = array_merge($todo,$this->CopyPasteVars);
1174     }
1176     if(count($this->objectclasses)){
1177       $this->is_account = TRUE;
1178       foreach($this->objectclasses as $class){
1179         if(!in_array($class,$source['objectClass'])){
1180           $this->is_account = FALSE;
1181         }
1182       }
1183     }
1185     foreach($todo as $var){
1186       if (isset($source[$var])){
1187         if(isset($source[$var]['count'])){
1188           if($source[$var]['count'] > 1){
1189             $tmp= $source[$var];
1190             unset($tmp['count']);
1191             $this->$var = $tmp;
1192           }else{
1193             $this->$var = $source[$var][0];
1194           }
1195         }else{
1196           $this->$var= $source[$var];
1197         }
1198       }
1199     }
1200   }
1202   /*! \brief Get gosaUnitTag for the given DN
1203        If this is called from departmentGeneric, we have to skip this
1204         tagging procedure. 
1205     */
1206   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1207   {
1208     /* Skip tagging? */
1209     if($this->skipTagging){
1210       return;
1211     }
1213     /* No dn? Self-operation... */
1214     if ($dn == ""){
1215       $dn= $this->dn;
1217       /* No tag? Find it yourself... */
1218       if ($tag == ""){
1219         $len= strlen($dn);
1221         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1222         $relevant= array();
1223         foreach ($this->config->adepartments as $key => $ntag){
1225           /* This one is bigger than our dn, its not relevant... */
1226           if ($len < strlen($key)){
1227             continue;
1228           }
1230           /* This one matches with the latter part. Break and don't fix this entry */
1231           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1232             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1233             $relevant[strlen($key)]= $ntag;
1234             continue;
1235           }
1237         }
1239         /* If we've some relevant tags to set, just get the longest one */
1240         if (count($relevant)){
1241           ksort($relevant);
1242           $tmp= array_keys($relevant);
1243           $idx= end($tmp);
1244           $tag= $relevant[$idx];
1245           $this->gosaUnitTag= $tag;
1246         }
1247       }
1248     }
1250   /*! \brief Add unit tag */ 
1251     /* Remove tags that may already be here... */
1252     remove_objectClass("gosaAdministrativeUnitTag", $at);
1253     if (isset($at['gosaUnitTag'])){
1254         unset($at['gosaUnitTag']);
1255     }
1257     /* Set tag? */
1258     if ($tag != ""){
1259       add_objectClass("gosaAdministrativeUnitTag", $at);
1260       $at['gosaUnitTag']= $tag;
1261     }
1263     /* Initially this object was tagged. 
1264        - But now, it is no longer inside a tagged department. 
1265        So force the remove of the tag.
1266        (objectClass was already removed obove)
1267      */
1268     if($tag == "" && $this->gosaUnitTag){
1269       $at['gosaUnitTag'] = array();
1270     }
1271   }
1274   /*! \brief Test for removability of the object
1275    *
1276    * Allows testing of conditions for removal of object. If removal should be aborted
1277    * the function needs to remove an error message.
1278    * */
1279   function allow_remove()
1280   {
1281     $reason= "";
1282     return $reason;
1283   }
1286   /*! \brief Create a snapshot of the current object */
1287   function create_snapshot($type= "snapshot", $description= array())
1288   {
1290     /* Check if snapshot functionality is enabled */
1291     if(!$this->snapshotEnabled()){
1292       return;
1293     }
1295     /* Get configuration from gosa.conf */
1296     $config = $this->config;
1298     /* Create lokal ldap connection */
1299     $ldap= $this->config->get_ldap_link();
1300     $ldap->cd($this->config->current['BASE']);
1302     /* check if there are special server configurations for snapshots */
1303     if($config->get_cfg_value("snapshotURI") == ""){
1305       /* Source and destination server are both the same, just copy source to dest obj */
1306       $ldap_to      = $ldap;
1307       $snapldapbase = $this->config->current['BASE'];
1309     }else{
1310       $server         = $config->get_cfg_value("snapshotURI");
1311       $user           = $config->get_cfg_value("snapshotAdminDn");
1312       $password       = $this->config->get_credentials($config->get_cfg_value("snapshotAdminPassword"));
1313       $snapldapbase   = $config->get_cfg_value("snapshotBase");
1315       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1316       $ldap_to -> cd($snapldapbase);
1318       if (!$ldap_to->success()){
1319         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1320       }
1322     }
1324     /* check if the dn exists */ 
1325     if ($ldap->dn_exists($this->dn)){
1327       /* Extract seconds & mysecs, they are used as entry index */
1328       list($usec, $sec)= explode(" ", microtime());
1330       /* Collect some infos */
1331       $base           = $this->config->current['BASE'];
1332       $snap_base      = $config->get_cfg_value("snapshotBase");
1333       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1334       $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1336       /* Create object */
1337 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1338       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1339       $newName          = str_replace(".", "", $sec."-".$usec);
1340       $target= array();
1341       $target['objectClass']            = array("top", "gosaSnapshotObject");
1342       $target['gosaSnapshotData']       = gzcompress($data, 6);
1343       $target['gosaSnapshotType']       = $type;
1344       $target['gosaSnapshotDN']         = $this->dn;
1345       $target['description']            = $description;
1346       $target['gosaSnapshotTimestamp']  = $newName;
1348       /* Insert the new snapshot 
1349          But we have to check first, if the given gosaSnapshotTimestamp
1350          is already used, in this case we should increment this value till there is 
1351          an unused value. */ 
1352       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1353       $ldap_to->cat($new_dn);
1354       while($ldap_to->count()){
1355         $ldap_to->cat($new_dn);
1356         $newName = str_replace(".", "", $sec."-".($usec++));
1357         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1358         $target['gosaSnapshotTimestamp']  = $newName;
1359       } 
1361       /* Inset this new snapshot */
1362       $ldap_to->cd($snapldapbase);
1363       $ldap_to->create_missing_trees($snapldapbase);
1364       $ldap_to->create_missing_trees($new_base);
1365       $ldap_to->cd($new_dn);
1366       $ldap_to->add($target);
1367       if (!$ldap_to->success()){
1368         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1369       }
1371       if (!$ldap->success()){
1372         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1373       }
1375     }
1376   }
1378   /*! \brief Remove a snapshot */
1379   function remove_snapshot($dn)
1380   {
1381     $ui       = get_userinfo();
1382     $old_dn   = $this->dn; 
1383     $this->dn = $dn;
1384     $ldap = $this->config->get_ldap_link();
1385     $ldap->cd($this->config->current['BASE']);
1386     $ldap->rmdir_recursive($this->dn);
1387     if(!$ldap->success()){
1388       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn));
1389     }
1390     $this->dn = $old_dn;
1391   }
1394   /*! \brief Test if snapshotting is enabled
1395    *
1396    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1397    * if the configuration failed 
1398    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1399    */
1400   function snapshotEnabled()
1401   {
1402     return $this->config->snapshotEnabled();
1403   }
1406   /* \brief Return available snapshots for the given base */
1407   function Available_SnapsShots($dn,$raw = false)
1408   {
1409     if(!$this->snapshotEnabled()) return(array());
1411     /* Create an additional ldap object which
1412        points to our ldap snapshot server */
1413     $ldap= $this->config->get_ldap_link();
1414     $ldap->cd($this->config->current['BASE']);
1415     $cfg= &$this->config->current;
1417     /* check if there are special server configurations for snapshots */
1418     if($this->config->get_cfg_value("snapshotURI") == ""){
1419       $ldap_to      = $ldap;
1420     }else{
1421       $server         = $this->config->get_cfg_value("snapshotURI");
1422       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1423       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1424       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1425       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1426       $ldap_to -> cd($snapldapbase);
1427       if (!$ldap_to->success()){
1428         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1429       }
1430     }
1432     /* Prepare bases and some other infos */
1433     $base           = $this->config->current['BASE'];
1434     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1435     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1436     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1437     $tmp            = array(); 
1439     /* Fetch all objects with  gosaSnapshotDN=$dn */
1440     $ldap_to->cd($new_base);
1441     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1442         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1444     /* Put results into a list and add description if missing */
1445     while($entry = $ldap_to->fetch()){ 
1446       if(!isset($entry['description'][0])){
1447         $entry['description'][0]  = "";
1448       }
1449       $tmp[] = $entry; 
1450     }
1452     /* Return the raw array, or format the result */
1453     if($raw){
1454       return($tmp);
1455     }else{  
1456       $tmp2 = array();
1457       foreach($tmp as $entry){
1458         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1459       }
1460     }
1461     return($tmp2);
1462   }
1465   function getAllDeletedSnapshots($base_of_object,$raw = false)
1466   {
1467     if(!$this->snapshotEnabled()) return(array());
1469     /* Create an additional ldap object which
1470        points to our ldap snapshot server */
1471     $ldap= $this->config->get_ldap_link();
1472     $ldap->cd($this->config->current['BASE']);
1473     $cfg= &$this->config->current;
1475     /* check if there are special server configurations for snapshots */
1476     if($this->config->get_cfg_value("snapshotURI") == ""){
1477       $ldap_to      = $ldap;
1478     }else{
1479       $server         = $this->config->get_cfg_value("snapshotURI");
1480       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1481       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1482       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1483       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1484       $ldap_to -> cd($snapldapbase);
1485       if (!$ldap_to->success()){
1486         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1487       }
1488     }
1490     /* Prepare bases */ 
1491     $base           = $this->config->current['BASE'];
1492     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1493     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1495     /* Fetch all objects and check if they do not exist anymore */
1496     $ui = get_userinfo();
1497     $tmp = array();
1498     $ldap_to->cd($new_base);
1499     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1500     while($entry = $ldap_to->fetch()){
1502       $chk =  str_replace($new_base,"",$entry['dn']);
1503       if(preg_match("/,ou=/",$chk)) continue;
1505       if(!isset($entry['description'][0])){
1506         $entry['description'][0]  = "";
1507       }
1508       $tmp[] = $entry; 
1509     }
1511     /* Check if entry still exists */
1512     foreach($tmp as $key => $entry){
1513       $ldap->cat($entry['gosaSnapshotDN'][0]);
1514       if($ldap->count()){
1515         unset($tmp[$key]);
1516       }
1517     }
1519     /* Format result as requested */
1520     if($raw) {
1521       return($tmp);
1522     }else{
1523       $tmp2 = array();
1524       foreach($tmp as $key => $entry){
1525         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1526       }
1527     }
1528     return($tmp2);
1529   } 
1532   /* \brief Restore selected snapshot */
1533   function restore_snapshot($dn)
1534   {
1535     if(!$this->snapshotEnabled()) return(array());
1537     $ldap= $this->config->get_ldap_link();
1538     $ldap->cd($this->config->current['BASE']);
1539     $cfg= &$this->config->current;
1541     /* check if there are special server configurations for snapshots */
1542     if($this->config->get_cfg_value("snapshotURI") == ""){
1543       $ldap_to      = $ldap;
1544     }else{
1545       $server         = $this->config->get_cfg_value("snapshotURI");
1546       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1547       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1548       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1549       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1550       $ldap_to -> cd($snapldapbase);
1551       if (!$ldap_to->success()){
1552         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1553       }
1554     }
1556     /* Get the snapshot */ 
1557     $ldap_to->cat($dn);
1558     $restoreObject = $ldap_to->fetch();
1560     /* Prepare import string */
1561     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1563     /* Import the given data */
1564     $err = "";
1565     $ldap->import_complete_ldif($data,$err,false,false);
1566     if (!$ldap->success()){
1567       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1568     }
1569   }
1572   function showSnapshotDialog($base,$baseSuffixe,&$parent)
1573   {
1574     $once = true;
1575     $ui = get_userinfo();
1576     $this->parent = $parent;
1578     foreach($_POST as $name => $value){
1580       /* Create a new snapshot, display a dialog */
1581       if(preg_match("/^CreateSnapShotDialog_[^_]*_[xy]$/",$name) && $once){
1583                           $entry = base64_decode(preg_replace("/^CreateSnapShotDialog_([^_]*)_[xy]$/","\\1",$name));
1584         $once = false;
1585         $entry = preg_replace("/^CreateSnapShotDialog_/","",$entry);
1587         if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1588           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1589         }else{
1590           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1591         }
1592       }  
1593   
1594       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1595       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1596         $once = false;
1597         $entry = base64_decode(preg_replace("/^RestoreSnapShotDialog_([^_]*)_[xy]$/i","\\1",$name));
1598         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1599           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1600           $this->snapDialog->display_restore_dialog = true;
1601         }else{
1602           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1603         }
1604       }
1606       /* Restore one of the already deleted objects */
1607       if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot") 
1608           || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1609         $once = false;
1611         if($ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1612           $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1613           $this->snapDialog->set_snapshot_bases($baseSuffixe);
1614           $this->snapDialog->display_restore_dialog      = true;
1615           $this->snapDialog->display_all_removed_objects  = true;
1616         }else{
1617           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1618         }
1619       }
1621       /* Restore selected snapshot */
1622       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1623         $once = false;
1624         $entry = base64_decode(preg_replace("/^RestoreSnapShot_([^_]*)_[xy]$/i","\\1",$name));
1626         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1627           $this->restore_snapshot($entry);
1628           $this->snapDialog = NULL;
1629         }else{
1630           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1631         }
1632       }
1633     }
1635     /* Create a new snapshot requested, check
1636        the given attributes and create the snapshot*/
1637     if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1638       $this->snapDialog->save_object();
1639       $msgs = $this->snapDialog->check();
1640       if(count($msgs)){
1641         foreach($msgs as $msg){
1642           msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1643         }
1644       }else{
1645         $this->dn =  $this->snapDialog->dn;
1646         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1647         $this->snapDialog = NULL;
1648       }
1649     }
1651     /* Restore is requested, restore the object with the posted dn .*/
1652     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1653     }
1655     if(isset($_POST['CancelSnapshot'])){
1656       $this->snapDialog = NULL;
1657     }
1659     if(is_object($this->snapDialog )){
1660       $this->snapDialog->save_object();
1661       return($this->snapDialog->execute());
1662     }
1663   }
1666   /*! \brief Return plugin informations for acl handling */
1667   static function plInfo()
1668   {
1669     return array();
1670   }
1673   function set_acl_base($base)
1674   {
1675     $this->acl_base= $base;
1676   }
1679   function set_acl_category($category)
1680   {
1681     $this->acl_category= "$category/";
1682   }
1685   function acl_is_writeable($attribute,$skip_write = FALSE)
1686   {
1687     if($this->read_only) return(FALSE);
1688     $ui= get_userinfo();
1689     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1690   }
1693   function acl_is_readable($attribute)
1694   {
1695     $ui= get_userinfo();
1696     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1697   }
1700   function acl_is_createable($base ="")
1701   {
1702     if($this->read_only) return(FALSE);
1703     $ui= get_userinfo();
1704     if($base == "") $base = $this->acl_base;
1705     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1706   }
1709   function acl_is_removeable($base ="")
1710   {
1711     if($this->read_only) return(FALSE);
1712     $ui= get_userinfo();
1713     if($base == "") $base = $this->acl_base;
1714     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1715   }
1718   function acl_is_moveable($base = "")
1719   {
1720     if($this->read_only) return(FALSE);
1721     $ui= get_userinfo();
1722     if($base == "") $base = $this->acl_base;
1723     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1724   }
1727   function acl_have_any_permissions()
1728   {
1729   }
1732   function getacl($attribute,$skip_write= FALSE)
1733   {
1734     $ui= get_userinfo();
1735     $skip_write |= $this->read_only;
1736     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1737   }
1740   /*! \brief Returns a list of all available departments for this object.
1741    * 
1742    * If this object is new, all departments we are allowed to create a new user in
1743    * are returned. If this is an existing object, return all deps. 
1744    * We are allowed to move tis object too.
1745    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1746   */
1747   function get_allowed_bases()
1748   {
1749     $ui = get_userinfo();
1750     $deps = array();
1752     /* Is this a new object ? Or just an edited existing object */
1753     if(!$this->initially_was_account && $this->is_account){
1754       $new = true;
1755     }else{
1756       $new = false;
1757     }
1759     foreach($this->config->idepartments as $dn => $name){
1760       if($new && $this->acl_is_createable($dn)){
1761         $deps[$dn] = $name;
1762       }elseif(!$new && $this->acl_is_moveable($dn)){
1763         $deps[$dn] = $name;
1764       }
1765     }
1767     /* Add current base */      
1768     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1769       $deps[$this->base] = $this->config->idepartments[$this->base];
1770     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1772     }else{
1773       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1774     }
1775     return($deps);
1776   }
1779   /* This function updates ACL settings if $old_dn was used.
1780    *  \param string 'old_dn' specifies the actually used dn
1781    *  \param string 'new_dn' specifies the destiantion dn
1782    */
1783   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1784   {
1785     /* Check if old_dn is empty. This should never happen */
1786     if(empty($old_dn) || empty($new_dn)){
1787       trigger_error("Failed to check acl dependencies, wrong dn given.");
1788       return;
1789     }
1791     /* Update userinfo if necessary */
1792     $ui = session::global_get('ui');
1793     if($ui->dn == $old_dn){
1794       $ui->dn = $new_dn;
1795       session::global_set('ui',$ui);
1796       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1797     }
1799     /* Object was moved, ensure that all acls will be moved too */
1800     if($new_dn != $old_dn && $old_dn != "new"){
1802       /* get_ldap configuration */
1803       $update = array();
1804       $ldap = $this->config->get_ldap_link();
1805       $ldap->cd ($this->config->current['BASE']);
1806       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1807       while($attrs = $ldap->fetch()){
1808         $acls = array();
1809         $found = false;
1810         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1811           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1813           /* Roles uses antoher data storage order, members are stored int the third part, 
1814              while the members in direct ACL assignments are stored in the second part.
1815            */
1816           $id = ($acl_parts[1] == "role") ? 3 : 2;
1818           /* Update member entries to use $new_dn instead of old_dn
1819            */
1820           $members = explode(",",$acl_parts[$id]);
1821           foreach($members as $key => $member){
1822             $member = base64_decode($member);
1823             if($member == $old_dn){
1824               $members[$key] = base64_encode($new_dn);
1825               $found = TRUE;
1826             }
1827           } 
1829           /* Check if the selected role has to updated
1830            */
1831           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1832             $acl_parts[2] = base64_encode($new_dn);
1833             $found = TRUE;
1834           }
1836           /* Build new acl string */ 
1837           $acl_parts[$id] = implode($members,",");
1838           $acls[] = implode($acl_parts,":");
1839         }
1841         /* Acls for this object must be adjusted */
1842         if($found){
1844           $debug_info=  _("Changing ACL dn")."&nbsp;:&nbsp;<br>&nbsp;-"._("from")."&nbsp;<b>&nbsp;".
1845             $old_dn."</b><br>&nbsp;-"._("to")."&nbsp;<b>".$new_dn."</b><br>";
1846           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1848           $update[$attrs['dn']] =array();
1849           foreach($acls as $acl){
1850             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1851           }
1852         }
1853       }
1855       /* Write updated acls */
1856       foreach($update as $dn => $attrs){
1857         $ldap->cd($dn);
1858         $ldap->modify($attrs);
1859       }
1860     }
1861   }
1863   
1865   /*! \brief Enable the Serial ID check
1866    *
1867    * This function enables the entry Serial ID check.  If an entry was edited while
1868    * we have edited the entry too, an error message will be shown. 
1869    * To configure this check correctly read the FAQ.
1870    */    
1871   function enable_CSN_check()
1872   {
1873     $this->CSN_check_active =TRUE;
1874     $this->entryCSN = getEntryCSN($this->dn);
1875   }
1878   /*! \brief  Prepares the plugin to be used for multiple edit
1879    *          Update plugin attributes with given array of attribtues.
1880    *  \param  array   Array with attributes that must be updated.
1881    */
1882   function init_multiple_support($attrs,$all)
1883   {
1884     $ldap= $this->config->get_ldap_link();
1885     $this->multi_attrs    = $attrs;
1886     $this->multi_attrs_all= $all;
1888     /* Copy needed attributes */
1889     foreach ($this->attributes as $val){
1890       $found= array_key_ics($val, $this->multi_attrs);
1891  
1892       if ($found != ""){
1893         if(isset($this->multi_attrs["$val"][0])){
1894           $this->$val= $this->multi_attrs["$val"][0];
1895         }
1896       }
1897     }
1898   }
1900  
1901   /*! \brief  Enables multiple support for this plugin
1902    */
1903   function enable_multiple_support()
1904   {
1905     $this->ignore_account = TRUE;
1906     $this->multiple_support_active = TRUE;
1907   }
1910   /*! \brief  Returns all values that have been modfied in multiple edit mode.
1911       \return array Cotaining all modified values. 
1912    */
1913   function get_multi_edit_values()
1914   {
1915     $ret = array();
1916     foreach($this->attributes as $attr){
1917       if(in_array($attr,$this->multi_boxes)){
1918         $ret[$attr] = $this->$attr;
1919       }
1920     }
1921     return($ret);
1922   }
1924   
1925   /*! \brief  Update class variables with values collected by multiple edit.
1926    */
1927   function set_multi_edit_values($attrs)
1928   {
1929     foreach($attrs as $name => $value){
1930       $this->$name = $value;
1931     }
1932   }
1935   /*! \brief Generates the html output for this node for multi edit*/
1936   function multiple_execute()
1937   {
1938     /* This one is empty currently. Fabian - please fill in the docu code */
1939     session::global_set('current_class_for_help',get_class($this));
1941     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1942     session::set('LOCK_VARS_TO_USE',array());
1943     session::set('LOCK_VARS_USED_GET',array());
1944     session::set('LOCK_VARS_USED_POST',array());
1945     session::set('LOCK_VARS_USED_REQUEST',array());
1946     
1947     return("Multiple edit is currently not implemented for this plugin.");
1948   }
1951   /*! \brief Save HTML posted data to object for multiple edit
1952    */
1953   function multiple_save_object()
1954   {
1955     if(empty($this->entryCSN) && $this->CSN_check_active){
1956       $this->entryCSN = getEntryCSN($this->dn);
1957     }
1959     /* Save values to object */
1960     $this->multi_boxes = array();
1961     foreach ($this->attributes as $val){
1962   
1963       /* Get selected checkboxes from multiple edit */
1964       if(isset($_POST["use_".$val])){
1965         $this->multi_boxes[] = $val;
1966       }
1968       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
1970         /* Check for modifications */
1971         if (get_magic_quotes_gpc()) {
1972           $data= stripcslashes($_POST["$val"]);
1973         } else {
1974           $data= $this->$val = $_POST["$val"];
1975         }
1976         if ($this->$val != $data){
1977           $this->is_modified= TRUE;
1978         }
1979     
1980         /* IE post fix */
1981         if(isset($data[0]) && $data[0] == chr(194)) {
1982           $data = "";  
1983         }
1984         $this->$val= $data;
1985       }
1986     }
1987   }
1990   /*! \brief Returns all attributes of this plugin, 
1991                to be able to detect multiple used attributes 
1992                in multi_plugg::detect_multiple_used_attributes().
1993       @return array Attributes required for intialization of multi_plug
1994    */
1995   public function get_multi_init_values()
1996   {
1997     $attrs = $this->attrs;
1998     return($attrs);
1999   }
2002   /*! \brief  Check given values in multiple edit
2003       \return array Error messages
2004    */
2005   function multiple_check()
2006   {
2007     $message = plugin::check();
2008     return($message);
2009   }
2012   /*! \brief  Returns the snapshot header part for "Actions" menu in management dialogs 
2013       \param  $layer_menu  
2014    */   
2015   function get_snapshot_header($base,$category)
2016   {
2017     $str = "";
2018     $ui = get_userinfo();
2019     if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
2021       $ok = false;
2022       foreach($this->get_used_snapshot_bases() as $base){
2023         $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
2024       }
2026       if($ok){
2027         $str = "..|<img class='center' src='images/lists/restore.png' ".
2028           "alt='"._("Restore")."'>&nbsp;"._("Restore").                       "|RestoreDeletedSnapShot|\n";
2029       }else{
2030         $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;"._("Restore")."||\n";
2031       }
2032     }
2033     return($str);
2034   }
2037   function get_snapshot_action($base,$category)
2038   {
2039     $str= ""; 
2040     $ui = get_userinfo();
2041     if($this->snapshotEnabled()){
2042       if ($ui->allow_snapshot_restore($base,$category)){
2044         if(count($this->Available_SnapsShots($base))){
2045           $str.= "<input class='center' type='image' src='images/lists/restore.png'
2046             alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'>&nbsp;";
2047         } else {
2048           $str = "<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;";
2049         }
2050       }
2051       if($ui->allow_snapshot_create($base,$category)){
2052         $str.= "<input class='center' type='image' src='images/snapshot.png'
2053           alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."' 
2054           title='"._("Create a new snapshot from this object")."'>&nbsp;";
2055       }else{
2056         $str = "<img class='center' src='images/empty.png' alt=' '>&nbsp;";
2057       }
2058     }
2060     return($str);
2061   }
2064   function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2065   {
2066     $ui = get_userinfo();
2067     $action = "";
2068     if($this->CopyPasteHandler){
2069       if($cut){
2070         if($ui->is_cutable($base,$category,$class)){
2071           $action .= "<input class='center' type='image'
2072             src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'>&nbsp;";
2073         }else{
2074           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2075         }
2076       }
2077       if($copy){
2078         if($ui->is_copyable($base,$category,$class)){
2079           $action.= "<input class='center' type='image'
2080             src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'>&nbsp;";
2081         }else{
2082           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2083         }
2084       }
2085     }
2087     return($action); 
2088   }
2091   function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2092   {
2093     $s = "";
2094     $ui =get_userinfo();
2096     if(!is_array($category)){
2097       $category = array($category);
2098     }
2100     /* Check permissions for each category, if there is at least one category which 
2101         support read or paste permissions for the given base, then display the specific actions.
2102      */
2103     $readable = $pasteable = false;
2104     foreach($category as $cat){
2105       $readable= $readable || preg_match('/r/', $ui->get_category_permissions($base, $cat));
2106       $pasteable= $pasteable || $ui->is_pasteable($base, $cat) == 1;
2107     }
2108   
2109     if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2110       if($readable){
2111         $s.= "..|---|\n";
2112         if($copy){
2113           $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2114             "&nbsp;"._("Copy")."|"."multiple_copy_systems|\n";
2115         }
2116         if($cut){
2117           $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2118             "&nbsp;"._("Cut")."|"."multiple_cut_systems|\n";
2119         }
2120       }
2122       if($pasteable){
2123         if($this->CopyPasteHandler->entries_queued()){
2124           $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2125           $s.="..|".$img."&nbsp;"._("Paste")."|editPaste|\n";
2126         }else{
2127           $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2128           $s.="..|".$img."&nbsp;"._("Paste")."\n";
2129         }
2130       }
2131     }
2132     return($s);
2133   }
2136   function get_used_snapshot_bases()
2137   {
2138      return(array());
2139   }
2141   function is_modal_dialog()
2142   {
2143     return(isset($this->dialog) && $this->dialog);
2144   }
2147 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2148 ?>