Code

Added 'location' to command execution
[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       $ui = get_userinfo();
647       $add_attrs['dn']=$this->dn;
648       $add_attrs['callerDN']=$ui->dn;
649       $add_attrs['location']=$this->config->current['NAME'];
651       $tmp = array();
652       foreach($add_attrs as $name => $value){
653         $tmp[$name] =  strlen($name);
654       }
655       arsort($tmp);
656       
657       /* Additional attributes */
658       foreach ($tmp as $name => $len){
659         $value = $add_attrs[$name];
660         $command= str_replace("%$name", "$value", $command);
661       }
663       if (check_command($command)){
664         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
665             $command, "Execute");
666         exec($command,$arr);
667         foreach($arr as $str){
668           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
669             $command, "Result: ".$str);
670         }
671       } else {
672         $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
673         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
674       }
675     }
676   }
678   /*! \brief Execute commands after an object has been modified */
679   function postmodify($add_attrs= array())
680   {
681     /* Find postcreate entries for this class */
682     $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
684     if ($command != ""){
686       /* Walk through attribute list */
687       foreach ($this->attributes as $attr){
688         if (!is_array($this->$attr)){
689           $add_attrs[$attr] = $this->$attr;
690         }
691       }
692       $ui = get_userinfo();
693       $add_attrs['dn']=$this->dn;
694       $add_attrs['callerDN']=$ui->dn;
695       $add_attrs['location']=$this->config->current['NAME'];
697       $tmp = array();
698       foreach($add_attrs as $name => $value){
699         $tmp[$name] =  strlen($name);
700       }
701       arsort($tmp);
702       
703       /* Additional attributes */
704       foreach ($tmp as $name => $len){
705         $value = $add_attrs[$name];
706         $command= str_replace("%$name", "$value", $command);
707       }
709       if (check_command($command)){
710         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
711         exec($command,$arr);
712         foreach($arr as $str){
713           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
714             $command, "Result: ".$str);
715         }
716       } else {
717         $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
718         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
719       }
720     }
721   }
723   /*! \brief Executes a command after an object has been removed */
724   function postremove($add_attrs= array())
725   {
726     /* Find postremove entries for this class */
727     $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
728     if ($command != ""){
730       /* Walk through attribute list */
731       foreach ($this->attributes as $attr){
732         if (!is_array($this->$attr)){
733           $add_attrs[$attr] = $this->$attr;
734         }
735       }
736       $ui = get_userinfo();
737       $add_attrs['dn']=$this->dn;
738       $add_attrs['callerDN']=$ui->dn;
739       $add_attrs['location']=$this->config->current['NAME'];
741       $tmp = array();
742       foreach($add_attrs as $name => $value){
743         $tmp[$name] =  strlen($name);
744       }
745       arsort($tmp);
746       
747       /* Additional attributes */
748       foreach ($tmp as $name => $len){
749         $value = $add_attrs[$name];
750         $command= str_replace("%$name", "$value", $command);
751       }
753       if (check_command($command)){
754         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
755             $command, "Execute");
757         exec($command,$arr);
758         foreach($arr as $str){
759           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
760             $command, "Result: ".$str);
761         }
762       } else {
763         $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
764         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
765       }
766     }
767   }
770   /* Create unique DN */
771   function create_unique_dn2($data, $base)
772   {
773     $ldap= $this->config->get_ldap_link();
774     $base= preg_replace("/^,*/", "", $base);
776     /* Try to use plain entry first */
777     $dn= "$data,$base";
778     $attribute= preg_replace('/=.*$/', '', $data);
779     $ldap->cat ($dn, array('dn'));
780     if (!$ldap->fetch()){
781       return ($dn);
782     }
784     /* Look for additional attributes */
785     foreach ($this->attributes as $attr){
786       if ($attr == $attribute || $this->$attr == ""){
787         continue;
788       }
790       $dn= "$data+$attr=".$this->$attr.",$base";
791       $ldap->cat ($dn, array('dn'));
792       if (!$ldap->fetch()){
793         return ($dn);
794       }
795     }
797     /* None found */
798     return ("none");
799   }
802   /*! \brief Create unique DN */
803   function create_unique_dn($attribute, $base)
804   {
805     $ldap= $this->config->get_ldap_link();
806     $base= preg_replace("/^,*/", "", $base);
808     /* Try to use plain entry first */
809     $dn= "$attribute=".$this->$attribute.",$base";
810     $ldap->cat ($dn, array('dn'));
811     if (!$ldap->fetch()){
812       return ($dn);
813     }
815     /* Look for additional attributes */
816     foreach ($this->attributes as $attr){
817       if ($attr == $attribute || $this->$attr == ""){
818         continue;
819       }
821       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
822       $ldap->cat ($dn, array('dn'));
823       if (!$ldap->fetch()){
824         return ($dn);
825       }
826     }
828     /* None found */
829     return ("none");
830   }
833   function rebind($ldap, $referral)
834   {
835     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
836     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
837       $this->error = "Success";
838       $this->hascon=true;
839       $this->reconnect= true;
840       return (0);
841     } else {
842       $this->error = "Could not bind to " . $credentials['ADMIN'];
843       return NULL;
844     }
845   }
848   /* Recursively copy ldap object */
849   function _copy($src_dn,$dst_dn)
850   {
851     $ldap=$this->config->get_ldap_link();
852     $ldap->cat($src_dn);
853     $attrs= $ldap->fetch();
855     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
856     $ds= ldap_connect($this->config->current['SERVER']);
857     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
858     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
859       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
860     }
862     $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
863     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
864     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
866     /* Fill data from LDAP */
867     $new= array();
868     if ($sr) {
869       $ei=ldap_first_entry($ds, $sr);
870       if ($ei) {
871         foreach($attrs as $attr => $val){
872           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
873             for ($i= 0; $i<$info['count']; $i++){
874               if ($info['count'] == 1){
875                 $new[$attr]= $info[$i];
876               } else {
877                 $new[$attr][]= $info[$i];
878               }
879             }
880           }
881         }
882       }
883     }
885     /* close conncetion */
886     ldap_unbind($ds);
888     /* Adapt naming attribute */
889     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
890     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
891     $new[$dst_name]= LDAP::fix($dst_val);
893     /* Check if this is a department.
894      * If it is a dep. && there is a , override in his ou 
895      *  change \2C to , again, else this entry can't be saved ...
896      */
897     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
898       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
899     }
901     /* Save copy */
902     $ldap->connect();
903     $ldap->cd($this->config->current['BASE']);
904     
905     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
907     /* FAIvariable=.../..., cn=.. 
908         could not be saved, because the attribute FAIvariable was different to 
909         the dn FAIvariable=..., cn=... */
911     if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
913     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
914       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
915     }
916     $ldap->cd($dst_dn);
917     $ldap->add($new);
919     if (!$ldap->success()){
920       trigger_error("Trying to save $dst_dn failed.",
921           E_USER_WARNING);
922       return(FALSE);
923     }
924     return(TRUE);
925   }
928   /* This is a workaround function. */
929   function copy($src_dn, $dst_dn)
930   {
931     /* Rename dn in possible object groups */
932     $ldap= $this->config->get_ldap_link();
933     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
934         array('cn'));
935     while ($attrs= $ldap->fetch()){
936       $og= new ogroup($this->config, $ldap->getDN());
937       unset($og->member[$src_dn]);
938       $og->member[$dst_dn]= $dst_dn;
939       $og->save ();
940     }
942     $ldap->cat($dst_dn);
943     $attrs= $ldap->fetch();
944     if (count($attrs)){
945       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
946           E_USER_WARNING);
947       return (FALSE);
948     }
950     $ldap->cat($src_dn);
951     $attrs= $ldap->fetch();
952     if (!count($attrs)){
953       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
954           E_USER_WARNING);
955       return (FALSE);
956     }
958     $ldap->cd($src_dn);
959     $ldap->search("objectClass=*",array("dn"));
960     while($attrs = $ldap->fetch()){
961       $src = $attrs['dn'];
962       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
963       $this->_copy($src,$dst);
964     }
965     return (TRUE);
966   }
970   /*! \brief  Rename/Move a given src_dn to the given dest_dn
971    *
972    * Move a given ldap object indentified by $src_dn to the
973    * given destination $dst_dn
974    *
975    * - Ensure that all references are updated (ogroups)
976    * - Update ACLs   
977    * - Update accessTo
978    *
979    * \param  string  'src_dn' the source DN.
980    * \param  string  'dst_dn' the destination DN.
981    * \return boolean TRUE on success else FALSE.
982    */
983   function rename($src_dn, $dst_dn)
984   {
985     $start = microtime(1);
987     /* Try to move the source entry to the destination position */
988     $ldap = $this->config->get_ldap_link();
989     $ldap->cd($this->config->current['BASE']);
990     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
991     if (!$ldap->rename_dn($src_dn,$dst_dn)){
992 #      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
993       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());
994       @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn  -- TO:  $dst_dn", 
995           "Ldap Protocol v3 implementation error, falling back to maunal method.");
996       return(FALSE);
997     }
999     /* Get list of users,groups and roles within this tree,
1000         maybe we have to update ACL references.
1001      */
1002     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
1003           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1004     foreach($leaf_objs as $obj){
1005       $new_dn = $obj['dn'];
1006       $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
1007       $this->update_acls($old_dn,$new_dn); 
1008     }
1010     // Migrate objectgroups if needed
1011     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
1012       "ogroups", array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1014     // Walk through all objectGroups
1015     foreach($ogroups as $ogroup){
1016       // Migrate old to new dn
1017       $o_ogroup= new ogroup($this->config,$ogroup['dn']);
1018       if (isset($o_ogroup->member[$src_dn])) {
1019         unset($o_ogroup->member[$src_dn]);
1020       }
1021       $o_ogroup->member[$dst_dn]= $dst_dn;
1022       
1023       // Save object group
1024       $o_ogroup->save();
1025     }
1027     // Migrate rfc groups if needed
1028     $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);
1030     // Walk through all POSIX groups
1031     foreach($groups as $group){
1033       // Migrate old to new dn
1034       $o_group= new group($this->config,$group['dn']);
1035       $o_group->save();
1036     }
1038     /* Update roles to use the new entry dn */
1039     $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);
1041     // Walk through all roles
1042     foreach($roles as $role){
1043       $role = new roleGeneric($this->config,$role['dn']);
1044       $key= array_search($src_dn, $role->roleOccupant);      
1045       if($key !== FALSE){
1046         $role->roleOccupant[$key] = $dst_dn;
1047         $role->save();
1048       }
1049     }
1051     // Update 'manager' attributes from gosaDepartment and inetOrgPerson 
1052     $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
1053     $ocs = $ldap->get_objectclasses();
1054     if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
1055       $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
1056     }
1057     $leaf_deps=  get_list($filter,array("all"),$this->config->current['BASE'], 
1058         array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1059     foreach($leaf_deps as $entry){
1060       $update = array('manager' => $dst_dn);
1061       $ldap->cd($entry['dn']);
1062       $ldap->modify($update);
1063       if(!$ldap->success()){
1064         trigger_error(sprintf("Failed to update manager for '%s', error was '%s'", $entry['dn'], $ldap->get_error()));
1065       }
1066     }
1067  
1068     /* Check if there are gosa departments moved. 
1069        If there were deps moved, the force reload of config->deps.
1070      */
1071     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
1072           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1073   
1074     if(count($leaf_deps)){
1075       $this->config->get_departments();
1076       $this->config->make_idepartments();
1077       session::global_set("config",$this->config);
1078       $ui =get_userinfo();
1079       $ui->reset_acl_cache();
1080     }
1082     return(TRUE); 
1083   }
1086  
1087   function move($src_dn, $dst_dn)
1088   {
1089     /* Do not copy if only upper- lowercase has changed */
1090     if(strtolower($src_dn) == strtolower($dst_dn)){
1091       return(TRUE);
1092     }
1094     
1095     /* Try to move the entry instead of copy & delete
1096      */
1097     if(TRUE){
1099       /* Try to move with ldap routines, if this was not successfull
1100           fall back to the old style copy & remove method 
1101        */
1102       if($this->rename($src_dn, $dst_dn)){
1103         return(TRUE);
1104       }else{
1105         // See code below.
1106       }
1107     }
1109     /* Copy source to destination */
1110     if (!$this->copy($src_dn, $dst_dn)){
1111       return (FALSE);
1112     }
1114     /* Delete source */
1115     $ldap= $this->config->get_ldap_link();
1116     $ldap->rmdir_recursive($src_dn);
1117     if (!$ldap->success()){
1118       trigger_error("Trying to delete $src_dn failed.",
1119           E_USER_WARNING);
1120       return (FALSE);
1121     }
1123     return (TRUE);
1124   }
1127   /* \brief Move/Rename complete trees */
1128   function recursive_move($src_dn, $dst_dn)
1129   {
1130     /* Check if the destination entry exists */
1131     $ldap= $this->config->get_ldap_link();
1133     /* Check if destination exists - abort */
1134     $ldap->cat($dst_dn, array('dn'));
1135     if ($ldap->fetch()){
1136       trigger_error("recursive_move $dst_dn already exists.",
1137           E_USER_WARNING);
1138       return (FALSE);
1139     }
1141     $this->copy($src_dn, $dst_dn);
1143     /* Remove src_dn */
1144     $ldap->cd($src_dn);
1145     $ldap->recursive_remove($src_dn);
1146     return (TRUE);
1147   }
1150   function handle_post_events($mode, $add_attrs= array())
1151   {
1152     switch ($mode){
1153       case "add":
1154         $this->postcreate($add_attrs);
1155       break;
1157       case "modify":
1158         $this->postmodify($add_attrs);
1159       break;
1161       case "remove":
1162         $this->postremove($add_attrs);
1163       break;
1164     }
1165   }
1168   function saveCopyDialog(){
1169   }
1172   function getCopyDialog(){
1173     return(array("string"=>"","status"=>""));
1174   }
1177   /*! \brief Prepare for Copy & Paste */
1178   function PrepareForCopyPaste($source)
1179   {
1180     $todo = $this->attributes;
1181     if(isset($this->CopyPasteVars)){
1182       $todo = array_merge($todo,$this->CopyPasteVars);
1183     }
1185     if(count($this->objectclasses)){
1186       $this->is_account = TRUE;
1187       foreach($this->objectclasses as $class){
1188         if(!in_array($class,$source['objectClass'])){
1189           $this->is_account = FALSE;
1190         }
1191       }
1192     }
1194     foreach($todo as $var){
1195       if (isset($source[$var])){
1196         if(isset($source[$var]['count'])){
1197           if($source[$var]['count'] > 1){
1198             $tmp= $source[$var];
1199             unset($tmp['count']);
1200             $this->$var = $tmp;
1201           }else{
1202             $this->$var = $source[$var][0];
1203           }
1204         }else{
1205           $this->$var= $source[$var];
1206         }
1207       }
1208     }
1209   }
1211   /*! \brief Get gosaUnitTag for the given DN
1212        If this is called from departmentGeneric, we have to skip this
1213         tagging procedure. 
1214     */
1215   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1216   {
1217     /* Skip tagging? */
1218     if($this->skipTagging){
1219       return;
1220     }
1222     /* No dn? Self-operation... */
1223     if ($dn == ""){
1224       $dn= $this->dn;
1226       /* No tag? Find it yourself... */
1227       if ($tag == ""){
1228         $len= strlen($dn);
1230         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1231         $relevant= array();
1232         foreach ($this->config->adepartments as $key => $ntag){
1234           /* This one is bigger than our dn, its not relevant... */
1235           if ($len < strlen($key)){
1236             continue;
1237           }
1239           /* This one matches with the latter part. Break and don't fix this entry */
1240           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1241             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1242             $relevant[strlen($key)]= $ntag;
1243             continue;
1244           }
1246         }
1248         /* If we've some relevant tags to set, just get the longest one */
1249         if (count($relevant)){
1250           ksort($relevant);
1251           $tmp= array_keys($relevant);
1252           $idx= end($tmp);
1253           $tag= $relevant[$idx];
1254           $this->gosaUnitTag= $tag;
1255         }
1256       }
1257     }
1259   /*! \brief Add unit tag */ 
1260     /* Remove tags that may already be here... */
1261     remove_objectClass("gosaAdministrativeUnitTag", $at);
1262     if (isset($at['gosaUnitTag'])){
1263         unset($at['gosaUnitTag']);
1264     }
1266     /* Set tag? */
1267     if ($tag != ""){
1268       add_objectClass("gosaAdministrativeUnitTag", $at);
1269       $at['gosaUnitTag']= $tag;
1270     }
1272     /* Initially this object was tagged. 
1273        - But now, it is no longer inside a tagged department. 
1274        So force the remove of the tag.
1275        (objectClass was already removed obove)
1276      */
1277     if($tag == "" && $this->gosaUnitTag){
1278       $at['gosaUnitTag'] = array();
1279     }
1280   }
1283   /*! \brief Test for removability of the object
1284    *
1285    * Allows testing of conditions for removal of object. If removal should be aborted
1286    * the function needs to remove an error message.
1287    * */
1288   function allow_remove()
1289   {
1290     $reason= "";
1291     return $reason;
1292   }
1295   /*! \brief Create a snapshot of the current object */
1296   function create_snapshot($type= "snapshot", $description= array())
1297   {
1299     /* Check if snapshot functionality is enabled */
1300     if(!$this->snapshotEnabled()){
1301       return;
1302     }
1304     /* Get configuration from gosa.conf */
1305     $config = $this->config;
1307     /* Create lokal ldap connection */
1308     $ldap= $this->config->get_ldap_link();
1309     $ldap->cd($this->config->current['BASE']);
1311     /* check if there are special server configurations for snapshots */
1312     if($config->get_cfg_value("snapshotURI") == ""){
1314       /* Source and destination server are both the same, just copy source to dest obj */
1315       $ldap_to      = $ldap;
1316       $snapldapbase = $this->config->current['BASE'];
1318     }else{
1319       $server         = $config->get_cfg_value("snapshotURI");
1320       $user           = $config->get_cfg_value("snapshotAdminDn");
1321       $password       = $this->config->get_credentials($config->get_cfg_value("snapshotAdminPassword"));
1322       $snapldapbase   = $config->get_cfg_value("snapshotBase");
1324       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1325       $ldap_to -> cd($snapldapbase);
1327       if (!$ldap_to->success()){
1328         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1329       }
1331     }
1333     /* check if the dn exists */ 
1334     if ($ldap->dn_exists($this->dn)){
1336       /* Extract seconds & mysecs, they are used as entry index */
1337       list($usec, $sec)= explode(" ", microtime());
1339       /* Collect some infos */
1340       $base           = $this->config->current['BASE'];
1341       $snap_base      = $config->get_cfg_value("snapshotBase");
1342       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1343       $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1345       /* Create object */
1346 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1347       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1348       $newName          = str_replace(".", "", $sec."-".$usec);
1349       $target= array();
1350       $target['objectClass']            = array("top", "gosaSnapshotObject");
1351       $target['gosaSnapshotData']       = gzcompress($data, 6);
1352       $target['gosaSnapshotType']       = $type;
1353       $target['gosaSnapshotDN']         = $this->dn;
1354       $target['description']            = $description;
1355       $target['gosaSnapshotTimestamp']  = $newName;
1357       /* Insert the new snapshot 
1358          But we have to check first, if the given gosaSnapshotTimestamp
1359          is already used, in this case we should increment this value till there is 
1360          an unused value. */ 
1361       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1362       $ldap_to->cat($new_dn);
1363       while($ldap_to->count()){
1364         $ldap_to->cat($new_dn);
1365         $newName = str_replace(".", "", $sec."-".($usec++));
1366         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1367         $target['gosaSnapshotTimestamp']  = $newName;
1368       } 
1370       /* Inset this new snapshot */
1371       $ldap_to->cd($snapldapbase);
1372       $ldap_to->create_missing_trees($snapldapbase);
1373       $ldap_to->create_missing_trees($new_base);
1374       $ldap_to->cd($new_dn);
1375       $ldap_to->add($target);
1376       if (!$ldap_to->success()){
1377         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1378       }
1380       if (!$ldap->success()){
1381         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1382       }
1384     }
1385   }
1387   /*! \brief Remove a snapshot */
1388   function remove_snapshot($dn)
1389   {
1390     $ui       = get_userinfo();
1391     $old_dn   = $this->dn; 
1392     $this->dn = $dn;
1393     $ldap = $this->config->get_ldap_link();
1394     $ldap->cd($this->config->current['BASE']);
1395     $ldap->rmdir_recursive($this->dn);
1396     if(!$ldap->success()){
1397       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn));
1398     }
1399     $this->dn = $old_dn;
1400   }
1403   /*! \brief Test if snapshotting is enabled
1404    *
1405    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1406    * if the configuration failed 
1407    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1408    */
1409   function snapshotEnabled()
1410   {
1411     return $this->config->snapshotEnabled();
1412   }
1415   /* \brief Return available snapshots for the given base */
1416   function Available_SnapsShots($dn,$raw = false)
1417   {
1418     if(!$this->snapshotEnabled()) return(array());
1420     /* Create an additional ldap object which
1421        points to our ldap snapshot server */
1422     $ldap= $this->config->get_ldap_link();
1423     $ldap->cd($this->config->current['BASE']);
1424     $cfg= &$this->config->current;
1426     /* check if there are special server configurations for snapshots */
1427     if($this->config->get_cfg_value("snapshotURI") == ""){
1428       $ldap_to      = $ldap;
1429     }else{
1430       $server         = $this->config->get_cfg_value("snapshotURI");
1431       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1432       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1433       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1434       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1435       $ldap_to -> cd($snapldapbase);
1436       if (!$ldap_to->success()){
1437         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1438       }
1439     }
1441     /* Prepare bases and some other infos */
1442     $base           = $this->config->current['BASE'];
1443     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1444     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1445     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1446     $tmp            = array(); 
1448     /* Fetch all objects with  gosaSnapshotDN=$dn */
1449     $ldap_to->cd($new_base);
1450     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1451         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1453     /* Put results into a list and add description if missing */
1454     while($entry = $ldap_to->fetch()){ 
1455       if(!isset($entry['description'][0])){
1456         $entry['description'][0]  = "";
1457       }
1458       $tmp[] = $entry; 
1459     }
1461     /* Return the raw array, or format the result */
1462     if($raw){
1463       return($tmp);
1464     }else{  
1465       $tmp2 = array();
1466       foreach($tmp as $entry){
1467         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1468       }
1469     }
1470     return($tmp2);
1471   }
1474   function getAllDeletedSnapshots($base_of_object,$raw = false)
1475   {
1476     if(!$this->snapshotEnabled()) return(array());
1478     /* Create an additional ldap object which
1479        points to our ldap snapshot server */
1480     $ldap= $this->config->get_ldap_link();
1481     $ldap->cd($this->config->current['BASE']);
1482     $cfg= &$this->config->current;
1484     /* check if there are special server configurations for snapshots */
1485     if($this->config->get_cfg_value("snapshotURI") == ""){
1486       $ldap_to      = $ldap;
1487     }else{
1488       $server         = $this->config->get_cfg_value("snapshotURI");
1489       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1490       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1491       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1492       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1493       $ldap_to -> cd($snapldapbase);
1494       if (!$ldap_to->success()){
1495         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1496       }
1497     }
1499     /* Prepare bases */ 
1500     $base           = $this->config->current['BASE'];
1501     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1502     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1504     /* Fetch all objects and check if they do not exist anymore */
1505     $ui = get_userinfo();
1506     $tmp = array();
1507     $ldap_to->cd($new_base);
1508     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1509     while($entry = $ldap_to->fetch()){
1511       $chk =  str_replace($new_base,"",$entry['dn']);
1512       if(preg_match("/,ou=/",$chk)) continue;
1514       if(!isset($entry['description'][0])){
1515         $entry['description'][0]  = "";
1516       }
1517       $tmp[] = $entry; 
1518     }
1520     /* Check if entry still exists */
1521     foreach($tmp as $key => $entry){
1522       $ldap->cat($entry['gosaSnapshotDN'][0]);
1523       if($ldap->count()){
1524         unset($tmp[$key]);
1525       }
1526     }
1528     /* Format result as requested */
1529     if($raw) {
1530       return($tmp);
1531     }else{
1532       $tmp2 = array();
1533       foreach($tmp as $key => $entry){
1534         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1535       }
1536     }
1537     return($tmp2);
1538   } 
1541   /* \brief Restore selected snapshot */
1542   function restore_snapshot($dn)
1543   {
1544     if(!$this->snapshotEnabled()) return(array());
1546     $ldap= $this->config->get_ldap_link();
1547     $ldap->cd($this->config->current['BASE']);
1548     $cfg= &$this->config->current;
1550     /* check if there are special server configurations for snapshots */
1551     if($this->config->get_cfg_value("snapshotURI") == ""){
1552       $ldap_to      = $ldap;
1553     }else{
1554       $server         = $this->config->get_cfg_value("snapshotURI");
1555       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1556       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1557       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1558       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1559       $ldap_to -> cd($snapldapbase);
1560       if (!$ldap_to->success()){
1561         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1562       }
1563     }
1565     /* Get the snapshot */ 
1566     $ldap_to->cat($dn);
1567     $restoreObject = $ldap_to->fetch();
1569     /* Prepare import string */
1570     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1572     /* Import the given data */
1573     $err = "";
1574     $ldap->import_complete_ldif($data,$err,false,false);
1575     if (!$ldap->success()){
1576       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1577     }
1578   }
1581   function showSnapshotDialog($base,$baseSuffixe,&$parent)
1582   {
1583     $once = true;
1584     $ui = get_userinfo();
1585     $this->parent = $parent;
1587     foreach($_POST as $name => $value){
1589       /* Create a new snapshot, display a dialog */
1590       if(preg_match("/^CreateSnapShotDialog_[^_]*_[xy]$/",$name) && $once){
1592                           $entry = base64_decode(preg_replace("/^CreateSnapShotDialog_([^_]*)_[xy]$/","\\1",$name));
1593         $once = false;
1594         $entry = preg_replace("/^CreateSnapShotDialog_/","",$entry);
1596         if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1597           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1598         }else{
1599           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1600         }
1601       }  
1602   
1603       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1604       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1605         $once = false;
1606         $entry = base64_decode(preg_replace("/^RestoreSnapShotDialog_([^_]*)_[xy]$/i","\\1",$name));
1607         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1608           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1609           $this->snapDialog->display_restore_dialog = true;
1610         }else{
1611           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1612         }
1613       }
1615       /* Restore one of the already deleted objects */
1616       if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot") 
1617           || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1618         $once = false;
1620         if($ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1621           $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1622           $this->snapDialog->set_snapshot_bases($baseSuffixe);
1623           $this->snapDialog->display_restore_dialog      = true;
1624           $this->snapDialog->display_all_removed_objects  = true;
1625         }else{
1626           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1627         }
1628       }
1630       /* Restore selected snapshot */
1631       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1632         $once = false;
1633         $entry = base64_decode(preg_replace("/^RestoreSnapShot_([^_]*)_[xy]$/i","\\1",$name));
1635         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1636           $this->restore_snapshot($entry);
1637           $this->snapDialog = NULL;
1638         }else{
1639           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1640         }
1641       }
1642     }
1644     /* Create a new snapshot requested, check
1645        the given attributes and create the snapshot*/
1646     if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1647       $this->snapDialog->save_object();
1648       $msgs = $this->snapDialog->check();
1649       if(count($msgs)){
1650         foreach($msgs as $msg){
1651           msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1652         }
1653       }else{
1654         $this->dn =  $this->snapDialog->dn;
1655         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1656         $this->snapDialog = NULL;
1657       }
1658     }
1660     /* Restore is requested, restore the object with the posted dn .*/
1661     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1662     }
1664     if(isset($_POST['CancelSnapshot'])){
1665       $this->snapDialog = NULL;
1666     }
1668     if(is_object($this->snapDialog )){
1669       $this->snapDialog->save_object();
1670       return($this->snapDialog->execute());
1671     }
1672   }
1675   /*! \brief Return plugin informations for acl handling */
1676   static function plInfo()
1677   {
1678     return array();
1679   }
1682   function set_acl_base($base)
1683   {
1684     $this->acl_base= $base;
1685   }
1688   function set_acl_category($category)
1689   {
1690     $this->acl_category= "$category/";
1691   }
1694   function acl_is_writeable($attribute,$skip_write = FALSE)
1695   {
1696     if($this->read_only) return(FALSE);
1697     $ui= get_userinfo();
1698     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1699   }
1702   function acl_is_readable($attribute)
1703   {
1704     $ui= get_userinfo();
1705     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1706   }
1709   function acl_is_createable($base ="")
1710   {
1711     if($this->read_only) return(FALSE);
1712     $ui= get_userinfo();
1713     if($base == "") $base = $this->acl_base;
1714     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1715   }
1718   function acl_is_removeable($base ="")
1719   {
1720     if($this->read_only) return(FALSE);
1721     $ui= get_userinfo();
1722     if($base == "") $base = $this->acl_base;
1723     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1724   }
1727   function acl_is_moveable($base = "")
1728   {
1729     if($this->read_only) return(FALSE);
1730     $ui= get_userinfo();
1731     if($base == "") $base = $this->acl_base;
1732     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1733   }
1736   function acl_have_any_permissions()
1737   {
1738   }
1741   function getacl($attribute,$skip_write= FALSE)
1742   {
1743     $ui= get_userinfo();
1744     $skip_write |= $this->read_only;
1745     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1746   }
1749   /*! \brief Returns a list of all available departments for this object.
1750    * 
1751    * If this object is new, all departments we are allowed to create a new user in
1752    * are returned. If this is an existing object, return all deps. 
1753    * We are allowed to move tis object too.
1754    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1755   */
1756   function get_allowed_bases()
1757   {
1758     $ui = get_userinfo();
1759     $deps = array();
1761     /* Is this a new object ? Or just an edited existing object */
1762     if(!$this->initially_was_account && $this->is_account){
1763       $new = true;
1764     }else{
1765       $new = false;
1766     }
1768     foreach($this->config->idepartments as $dn => $name){
1769       if($new && $this->acl_is_createable($dn)){
1770         $deps[$dn] = $name;
1771       }elseif(!$new && $this->acl_is_moveable($dn)){
1772         $deps[$dn] = $name;
1773       }
1774     }
1776     /* Add current base */      
1777     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1778       $deps[$this->base] = $this->config->idepartments[$this->base];
1779     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1781     }else{
1782       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1783     }
1784     return($deps);
1785   }
1788   /* This function updates ACL settings if $old_dn was used.
1789    *  \param string 'old_dn' specifies the actually used dn
1790    *  \param string 'new_dn' specifies the destiantion dn
1791    */
1792   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1793   {
1794     /* Check if old_dn is empty. This should never happen */
1795     if(empty($old_dn) || empty($new_dn)){
1796       trigger_error("Failed to check acl dependencies, wrong dn given.");
1797       return;
1798     }
1800     /* Update userinfo if necessary */
1801     $ui = session::global_get('ui');
1802     if($ui->dn == $old_dn){
1803       $ui->dn = $new_dn;
1804       session::global_set('ui',$ui);
1805       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1806     }
1808     /* Object was moved, ensure that all acls will be moved too */
1809     if($new_dn != $old_dn && $old_dn != "new"){
1811       /* get_ldap configuration */
1812       $update = array();
1813       $ldap = $this->config->get_ldap_link();
1814       $ldap->cd ($this->config->current['BASE']);
1815       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1816       while($attrs = $ldap->fetch()){
1817         $acls = array();
1818         $found = false;
1819         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1820           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1822           /* Roles uses antoher data storage order, members are stored int the third part, 
1823              while the members in direct ACL assignments are stored in the second part.
1824            */
1825           $id = ($acl_parts[1] == "role") ? 3 : 2;
1827           /* Update member entries to use $new_dn instead of old_dn
1828            */
1829           $members = explode(",",$acl_parts[$id]);
1830           foreach($members as $key => $member){
1831             $member = base64_decode($member);
1832             if($member == $old_dn){
1833               $members[$key] = base64_encode($new_dn);
1834               $found = TRUE;
1835             }
1836           } 
1838           /* Check if the selected role has to updated
1839            */
1840           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1841             $acl_parts[2] = base64_encode($new_dn);
1842             $found = TRUE;
1843           }
1845           /* Build new acl string */ 
1846           $acl_parts[$id] = implode($members,",");
1847           $acls[] = implode($acl_parts,":");
1848         }
1850         /* Acls for this object must be adjusted */
1851         if($found){
1853           $debug_info=  _("Changing ACL dn")."&nbsp;:&nbsp;<br>&nbsp;-"._("from")."&nbsp;<b>&nbsp;".
1854             $old_dn."</b><br>&nbsp;-"._("to")."&nbsp;<b>".$new_dn."</b><br>";
1855           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1857           $update[$attrs['dn']] =array();
1858           foreach($acls as $acl){
1859             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1860           }
1861         }
1862       }
1864       /* Write updated acls */
1865       foreach($update as $dn => $attrs){
1866         $ldap->cd($dn);
1867         $ldap->modify($attrs);
1868       }
1869     }
1870   }
1872   
1874   /*! \brief Enable the Serial ID check
1875    *
1876    * This function enables the entry Serial ID check.  If an entry was edited while
1877    * we have edited the entry too, an error message will be shown. 
1878    * To configure this check correctly read the FAQ.
1879    */    
1880   function enable_CSN_check()
1881   {
1882     $this->CSN_check_active =TRUE;
1883     $this->entryCSN = getEntryCSN($this->dn);
1884   }
1887   /*! \brief  Prepares the plugin to be used for multiple edit
1888    *          Update plugin attributes with given array of attribtues.
1889    *  \param  array   Array with attributes that must be updated.
1890    */
1891   function init_multiple_support($attrs,$all)
1892   {
1893     $ldap= $this->config->get_ldap_link();
1894     $this->multi_attrs    = $attrs;
1895     $this->multi_attrs_all= $all;
1897     /* Copy needed attributes */
1898     foreach ($this->attributes as $val){
1899       $found= array_key_ics($val, $this->multi_attrs);
1900  
1901       if ($found != ""){
1902         if(isset($this->multi_attrs["$val"][0])){
1903           $this->$val= $this->multi_attrs["$val"][0];
1904         }
1905       }
1906     }
1907   }
1909  
1910   /*! \brief  Enables multiple support for this plugin
1911    */
1912   function enable_multiple_support()
1913   {
1914     $this->ignore_account = TRUE;
1915     $this->multiple_support_active = TRUE;
1916   }
1919   /*! \brief  Returns all values that have been modfied in multiple edit mode.
1920       \return array Cotaining all modified values. 
1921    */
1922   function get_multi_edit_values()
1923   {
1924     $ret = array();
1925     foreach($this->attributes as $attr){
1926       if(in_array($attr,$this->multi_boxes)){
1927         $ret[$attr] = $this->$attr;
1928       }
1929     }
1930     return($ret);
1931   }
1933   
1934   /*! \brief  Update class variables with values collected by multiple edit.
1935    */
1936   function set_multi_edit_values($attrs)
1937   {
1938     foreach($attrs as $name => $value){
1939       $this->$name = $value;
1940     }
1941   }
1944   /*! \brief Generates the html output for this node for multi edit*/
1945   function multiple_execute()
1946   {
1947     /* This one is empty currently. Fabian - please fill in the docu code */
1948     session::global_set('current_class_for_help',get_class($this));
1950     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1951     session::set('LOCK_VARS_TO_USE',array());
1952     session::set('LOCK_VARS_USED_GET',array());
1953     session::set('LOCK_VARS_USED_POST',array());
1954     session::set('LOCK_VARS_USED_REQUEST',array());
1955     
1956     return("Multiple edit is currently not implemented for this plugin.");
1957   }
1960   /*! \brief Save HTML posted data to object for multiple edit
1961    */
1962   function multiple_save_object()
1963   {
1964     if(empty($this->entryCSN) && $this->CSN_check_active){
1965       $this->entryCSN = getEntryCSN($this->dn);
1966     }
1968     /* Save values to object */
1969     $this->multi_boxes = array();
1970     foreach ($this->attributes as $val){
1971   
1972       /* Get selected checkboxes from multiple edit */
1973       if(isset($_POST["use_".$val])){
1974         $this->multi_boxes[] = $val;
1975       }
1977       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
1979         /* Check for modifications */
1980         if (get_magic_quotes_gpc()) {
1981           $data= stripcslashes($_POST["$val"]);
1982         } else {
1983           $data= $this->$val = $_POST["$val"];
1984         }
1985         if ($this->$val != $data){
1986           $this->is_modified= TRUE;
1987         }
1988     
1989         /* IE post fix */
1990         if(isset($data[0]) && $data[0] == chr(194)) {
1991           $data = "";  
1992         }
1993         $this->$val= $data;
1994       }
1995     }
1996   }
1999   /*! \brief Returns all attributes of this plugin, 
2000                to be able to detect multiple used attributes 
2001                in multi_plugg::detect_multiple_used_attributes().
2002       @return array Attributes required for intialization of multi_plug
2003    */
2004   public function get_multi_init_values()
2005   {
2006     $attrs = $this->attrs;
2007     return($attrs);
2008   }
2011   /*! \brief  Check given values in multiple edit
2012       \return array Error messages
2013    */
2014   function multiple_check()
2015   {
2016     $message = plugin::check();
2017     return($message);
2018   }
2021   /*! \brief  Returns the snapshot header part for "Actions" menu in management dialogs 
2022       \param  $layer_menu  
2023    */   
2024   function get_snapshot_header($base,$category)
2025   {
2026     $str = "";
2027     $ui = get_userinfo();
2028     if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
2030       $ok = false;
2031       foreach($this->get_used_snapshot_bases() as $base){
2032         $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
2033       }
2035       if($ok){
2036         $str = "..|<img class='center' src='images/lists/restore.png' ".
2037           "alt='"._("Restore")."'>&nbsp;"._("Restore").                       "|RestoreDeletedSnapShot|\n";
2038       }else{
2039         $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;"._("Restore")."||\n";
2040       }
2041     }
2042     return($str);
2043   }
2046   function get_snapshot_action($base,$category)
2047   {
2048     $str= ""; 
2049     $ui = get_userinfo();
2050     if($this->snapshotEnabled()){
2051       if ($ui->allow_snapshot_restore($base,$category)){
2053         if(count($this->Available_SnapsShots($base))){
2054           $str.= "<input class='center' type='image' src='images/lists/restore.png'
2055             alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'>&nbsp;";
2056         } else {
2057           $str = "<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;";
2058         }
2059       }
2060       if($ui->allow_snapshot_create($base,$category)){
2061         $str.= "<input class='center' type='image' src='images/snapshot.png'
2062           alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."' 
2063           title='"._("Create a new snapshot from this object")."'>&nbsp;";
2064       }else{
2065         $str = "<img class='center' src='images/empty.png' alt=' '>&nbsp;";
2066       }
2067     }
2069     return($str);
2070   }
2073   function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2074   {
2075     $ui = get_userinfo();
2076     $action = "";
2077     if($this->CopyPasteHandler){
2078       if($cut){
2079         if($ui->is_cutable($base,$category,$class)){
2080           $action .= "<input class='center' type='image'
2081             src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'>&nbsp;";
2082         }else{
2083           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2084         }
2085       }
2086       if($copy){
2087         if($ui->is_copyable($base,$category,$class)){
2088           $action.= "<input class='center' type='image'
2089             src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'>&nbsp;";
2090         }else{
2091           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2092         }
2093       }
2094     }
2096     return($action); 
2097   }
2100   function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2101   {
2102     $s = "";
2103     $ui =get_userinfo();
2105     if(!is_array($category)){
2106       $category = array($category);
2107     }
2109     /* Check permissions for each category, if there is at least one category which 
2110         support read or paste permissions for the given base, then display the specific actions.
2111      */
2112     $readable = $pasteable = false;
2113     foreach($category as $cat){
2114       $readable= $readable || preg_match('/r/', $ui->get_category_permissions($base, $cat));
2115       $pasteable= $pasteable || $ui->is_pasteable($base, $cat) == 1;
2116     }
2117   
2118     if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2119       if($readable){
2120         $s.= "..|---|\n";
2121         if($copy){
2122           $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2123             "&nbsp;"._("Copy")."|"."multiple_copy_systems|\n";
2124         }
2125         if($cut){
2126           $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2127             "&nbsp;"._("Cut")."|"."multiple_cut_systems|\n";
2128         }
2129       }
2131       if($pasteable){
2132         if($this->CopyPasteHandler->entries_queued()){
2133           $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2134           $s.="..|".$img."&nbsp;"._("Paste")."|editPaste|\n";
2135         }else{
2136           $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2137           $s.="..|".$img."&nbsp;"._("Paste")."\n";
2138         }
2139       }
2140     }
2141     return($s);
2142   }
2145   function get_used_snapshot_bases()
2146   {
2147      return(array());
2148   }
2150   function is_modal_dialog()
2151   {
2152     return(isset($this->dialog) && $this->dialog);
2153   }
2156 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2157 ?>