Code

Updated pre-events
[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     /* Do not write in plugin base class, this must be done by
308        children, since there are normally additional attribs,
309        lists, etc. */
310     /*
311        $ldap->modify($this->attrs);
312      */
313     if($this->initially_was_account){
314         $this->handle_pre_events('remove');
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);
398     if($this->is_new){
399         $this->handle_pre_events('add');
400     }else{
401         $this->handle_pre_events('modify');
402     }
403   }
406   function cleanup()
407   {
408     foreach ($this->attrs as $index => $value){
409       
410       /* Convert arrays with one element to non arrays, if the saved
411          attributes are no array, too */
412       if (is_array($this->attrs[$index]) && 
413           count ($this->attrs[$index]) == 1 &&
414           isset($this->saved_attributes[$index]) &&
415           !is_array($this->saved_attributes[$index])){
416           
417         $tmp= $this->attrs[$index][0];
418         $this->attrs[$index]= $tmp;
419       }
421       /* Remove emtpy arrays if they do not differ */
422       if (is_array($this->attrs[$index]) &&
423           count($this->attrs[$index]) == 0 &&
424           !isset($this->saved_attributes[$index])){
425           
426         unset ($this->attrs[$index]);
427         continue;
428       }
430       /* Remove single attributes that do not differ */
431       if (!is_array($this->attrs[$index]) &&
432           isset($this->saved_attributes[$index]) &&
433           !is_array($this->saved_attributes[$index]) &&
434           $this->attrs[$index] == $this->saved_attributes[$index]){
436         unset ($this->attrs[$index]);
437         continue;
438       }
440       /* Remove arrays that do not differ */
441       if (is_array($this->attrs[$index]) && 
442           isset($this->saved_attributes[$index]) &&
443           is_array($this->saved_attributes[$index])){
444           
445         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
446           unset ($this->attrs[$index]);
447           continue;
448         }
449       }
450     }
452     /* Update saved attributes and ensure that next cleanups will be successful too */
453     foreach($this->attrs as $name => $value){
454       $this->saved_attributes[$name] = $value;
455     }
456   }
458   /*! \brief Check formular input */
459   function check()
460   {
461     $message= array();
463     /* Skip if we've no config object */
464     if (!isset($this->config) || !is_object($this->config)){
465       return $message;
466     }
468     /* Find hooks entries for this class */
469     $command= $this->config->search(get_class($this), "CHECK", array('menu', 'tabs'));
471     if ($command != ""){
473       if (!check_command($command)){
474         $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
475       } else {
477         /* Generate "ldif" for check hook */
478         $ldif= "dn: $this->dn\n";
479         
480         /* ... objectClasses */
481         foreach ($this->objectclasses as $oc){
482           $ldif.= "objectClass: $oc\n";
483         }
484         
485         /* ... attributes */
486         foreach ($this->attributes as $attr){
487           if ($this->$attr == ""){
488             continue;
489           }
490           if (is_array($this->$attr)){
491             foreach ($this->$attr as $val){
492               $ldif.= "$attr: $val\n";
493             }
494           } else {
495               $ldif.= "$attr: ".$this->$attr."\n";
496           }
497         }
499         /* Append empty line */
500         $ldif.= "\n";
502         /* Feed "ldif" into hook and retrieve result*/
503         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
504         $fh= proc_open($command, $descriptorspec, $pipes);
505         if (is_resource($fh)) {
506           fwrite ($pipes[0], $ldif);
507           fclose($pipes[0]);
508           
509           $result= stream_get_contents($pipes[1]);
510           if ($result != ""){
511             $message[]= $result;
512           }
513           
514           fclose($pipes[1]);
515           fclose($pipes[2]);
516           proc_close($fh);
517         }
518       }
520     }
522     /* Check entryCSN */
523     if($this->CSN_check_active){
524       $current_csn = getEntryCSN($this->dn);
525       if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
526         $this->entryCSN = $current_csn;
527         $message[] = _("The object has changed since opened in GOsa. All changes that may be done by others get lost if you save this entry!");
528       }
529     }
530     return ($message);
531   }
533   /* Adapt from template, using 'dn' */
534   function adapt_from_template($dn, $skip= array())
535   {
536     /* Include global link_info */
537     $ldap= $this->config->get_ldap_link();
539     /* Load requested 'dn' to 'attrs' */
540     $ldap->cat ($dn);
541     $this->attrs= $ldap->fetch();
543     /* Walk through attributes */
544     foreach ($this->attributes as $val){
546       /* Skip the ones in skip list */
547       if (in_array($val, $skip)){
548         continue;
549       }
551       if (isset($this->attrs["$val"][0])){
553         /* If attribute is set, replace dynamic parts: 
554            %sn, %givenName and %uid. Fill these in our local variables. */
555         $value= $this->attrs["$val"][0];
557         foreach (array("sn", "givenName", "uid") as $repl){
558           if (preg_match("/%$repl/i", $value)){
559             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
560           }
561         }
562         $this->$val= $value;
563       }
564     }
566     /* Is Account? */
567     $found= TRUE;
568     foreach ($this->objectclasses as $obj){
569       if (preg_match('/top/i', $obj)){
570         continue;
571       }
572       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
573         $found= FALSE;
574         break;
575       }
576     }
577     if ($found){
578       $this->is_account= TRUE;
579     }
580   }
582   /* \brief Indicate whether a password change is needed or not */
583   function password_change_needed()
584   {
585     return FALSE;
586   }
589   /*! \brief Show header message for tab dialogs */
590   function show_enable_header($button_text, $text, $disabled= FALSE)
591   {
592     if (($disabled == TRUE) || (!$this->acl_is_createable())){
593       $state= "disabled";
594     } else {
595       $state= "";
596     }
597     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
598     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
599       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
601     return($display);
602   }
605   /*! \brief Show header message for tab dialogs */
606   function show_disable_header($button_text, $text, $disabled= FALSE)
607   {
608     if (($disabled == TRUE) || !$this->acl_is_removeable()){
609       $state= "disabled";
610     } else {
611       $state= "";
612     }
613     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
614     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
615       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
617     return($display);
618   }
621   /*! \brief Show header message for tab dialogs */
622   function show_header($button_text, $text, $disabled= FALSE)
623   {
624     echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
625     if ($disabled == TRUE){
626       $state= "disabled";
627     } else {
628       $state= "";
629     }
630     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
631     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
632       ($this->acl_is_createable()?'':'disabled')." ".$state.
633       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
635     return($display);
636   }
638   /*! \brief Executes commands after an object has been created */
639   function postcreate($add_attrs= array())
640   {
641     /* Find postcreate entries for this class */
642     $command= $this->config->search(get_class($this), "POSTCREATE",array('menu', 'tabs'));
644     if ($command != ""){
646       /* Walk through attribute list */
647       foreach ($this->attributes as $attr){
648         if (!is_array($this->$attr)){
649           $add_attrs[$attr] = $this->$attr;
650         }
651       }
652       $ui = get_userinfo();
653       $add_attrs['dn']=$this->dn;
654       $add_attrs['callerDN']=$ui->dn;
655       $add_attrs['location']=$this->config->current['NAME'];
657       $tmp = array();
658       foreach($add_attrs as $name => $value){
659         $tmp[$name] =  strlen($name);
660       }
661       arsort($tmp);
662       
663       /* Additional attributes */
664       foreach ($tmp as $name => $len){
665         $value = $add_attrs[$name];
666         $command= str_replace("%$name", "$value", $command);
667       }
669       if (check_command($command)){
670         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
671             $command, "Execute");
672         exec($command,$arr);
673         foreach($arr as $str){
674           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
675             $command, "Result: ".$str);
676         }
677       } else {
678         $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
679         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
680       }
681     }
682   }
684   /*! \brief Execute commands after an object has been modified */
685   function postmodify($add_attrs= array())
686   {
687     /* Find postcreate entries for this class */
688     $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
690     if ($command != ""){
692       /* Walk through attribute list */
693       foreach ($this->attributes as $attr){
694         if (!is_array($this->$attr)){
695           $add_attrs[$attr] = $this->$attr;
696         }
697       }
698       $ui = get_userinfo();
699       $add_attrs['dn']=$this->dn;
700       $add_attrs['callerDN']=$ui->dn;
701       $add_attrs['location']=$this->config->current['NAME'];
703       $tmp = array();
704       foreach($add_attrs as $name => $value){
705         $tmp[$name] =  strlen($name);
706       }
707       arsort($tmp);
708       
709       /* Additional attributes */
710       foreach ($tmp as $name => $len){
711         $value = $add_attrs[$name];
712         $command= str_replace("%$name", "$value", $command);
713       }
715       if (check_command($command)){
716         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
717         exec($command,$arr);
718         foreach($arr as $str){
719           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
720             $command, "Result: ".$str);
721         }
722       } else {
723         $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
724         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
725       }
726     }
727   }
729   /*! \brief Executes a command after an object has been removed */
730   function postremove($add_attrs= array())
731   {
732     /* Find postremove entries for this class */
733     $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
734     if ($command != ""){
736       /* Walk through attribute list */
737       foreach ($this->attributes as $attr){
738         if (!is_array($this->$attr)){
739           $add_attrs[$attr] = $this->$attr;
740         }
741       }
742       $ui = get_userinfo();
743       $add_attrs['dn']=$this->dn;
744       $add_attrs['callerDN']=$ui->dn;
745       $add_attrs['location']=$this->config->current['NAME'];
747       $tmp = array();
748       foreach($add_attrs as $name => $value){
749         $tmp[$name] =  strlen($name);
750       }
751       arsort($tmp);
752       
753       /* Additional attributes */
754       foreach ($tmp as $name => $len){
755         $value = $add_attrs[$name];
756         $command= str_replace("%$name", "$value", $command);
757       }
759       if (check_command($command)){
760         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
761             $command, "Execute");
763         exec($command,$arr);
764         foreach($arr as $str){
765           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
766             $command, "Result: ".$str);
767         }
768       } else {
769         $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
770         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
771       }
772     }
773   }
776   /* Create unique DN */
777   function create_unique_dn2($data, $base)
778   {
779     $ldap= $this->config->get_ldap_link();
780     $base= preg_replace("/^,*/", "", $base);
782     /* Try to use plain entry first */
783     $dn= "$data,$base";
784     $attribute= preg_replace('/=.*$/', '', $data);
785     $ldap->cat ($dn, array('dn'));
786     if (!$ldap->fetch()){
787       return ($dn);
788     }
790     /* Look for additional attributes */
791     foreach ($this->attributes as $attr){
792       if ($attr == $attribute || $this->$attr == ""){
793         continue;
794       }
796       $dn= "$data+$attr=".$this->$attr.",$base";
797       $ldap->cat ($dn, array('dn'));
798       if (!$ldap->fetch()){
799         return ($dn);
800       }
801     }
803     /* None found */
804     return ("none");
805   }
808   /*! \brief Create unique DN */
809   function create_unique_dn($attribute, $base)
810   {
811     $ldap= $this->config->get_ldap_link();
812     $base= preg_replace("/^,*/", "", $base);
814     /* Try to use plain entry first */
815     $dn= "$attribute=".$this->$attribute.",$base";
816     $ldap->cat ($dn, array('dn'));
817     if (!$ldap->fetch()){
818       return ($dn);
819     }
821     /* Look for additional attributes */
822     foreach ($this->attributes as $attr){
823       if ($attr == $attribute || $this->$attr == ""){
824         continue;
825       }
827       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
828       $ldap->cat ($dn, array('dn'));
829       if (!$ldap->fetch()){
830         return ($dn);
831       }
832     }
834     /* None found */
835     return ("none");
836   }
839   function rebind($ldap, $referral)
840   {
841     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
842     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
843       $this->error = "Success";
844       $this->hascon=true;
845       $this->reconnect= true;
846       return (0);
847     } else {
848       $this->error = "Could not bind to " . $credentials['ADMIN'];
849       return NULL;
850     }
851   }
854   /* Recursively copy ldap object */
855   function _copy($src_dn,$dst_dn)
856   {
857     $ldap=$this->config->get_ldap_link();
858     $ldap->cat($src_dn);
859     $attrs= $ldap->fetch();
861     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
862     $ds= ldap_connect($this->config->current['SERVER']);
863     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
864     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
865       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
866     }
868     $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
869     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
870     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
872     /* Fill data from LDAP */
873     $new= array();
874     if ($sr) {
875       $ei=ldap_first_entry($ds, $sr);
876       if ($ei) {
877         foreach($attrs as $attr => $val){
878           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
879             for ($i= 0; $i<$info['count']; $i++){
880               if ($info['count'] == 1){
881                 $new[$attr]= $info[$i];
882               } else {
883                 $new[$attr][]= $info[$i];
884               }
885             }
886           }
887         }
888       }
889     }
891     /* close conncetion */
892     ldap_unbind($ds);
894     /* Adapt naming attribute */
895     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
896     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
897     $new[$dst_name]= LDAP::fix($dst_val);
899     /* Check if this is a department.
900      * If it is a dep. && there is a , override in his ou 
901      *  change \2C to , again, else this entry can't be saved ...
902      */
903     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
904       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
905     }
907     /* Save copy */
908     $ldap->connect();
909     $ldap->cd($this->config->current['BASE']);
910     
911     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
913     /* FAIvariable=.../..., cn=.. 
914         could not be saved, because the attribute FAIvariable was different to 
915         the dn FAIvariable=..., cn=... */
917     if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
919     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
920       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
921     }
922     $ldap->cd($dst_dn);
923     $ldap->add($new);
925     if (!$ldap->success()){
926       trigger_error("Trying to save $dst_dn failed.",
927           E_USER_WARNING);
928       return(FALSE);
929     }
930     return(TRUE);
931   }
934   /* This is a workaround function. */
935   function copy($src_dn, $dst_dn)
936   {
937     /* Rename dn in possible object groups */
938     $ldap= $this->config->get_ldap_link();
939     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
940         array('cn'));
941     while ($attrs= $ldap->fetch()){
942       $og= new ogroup($this->config, $ldap->getDN());
943       unset($og->member[$src_dn]);
944       $og->member[$dst_dn]= $dst_dn;
945       $og->save ();
946     }
948     $ldap->cat($dst_dn);
949     $attrs= $ldap->fetch();
950     if (count($attrs)){
951       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
952           E_USER_WARNING);
953       return (FALSE);
954     }
956     $ldap->cat($src_dn);
957     $attrs= $ldap->fetch();
958     if (!count($attrs)){
959       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
960           E_USER_WARNING);
961       return (FALSE);
962     }
964     $ldap->cd($src_dn);
965     $ldap->search("objectClass=*",array("dn"));
966     while($attrs = $ldap->fetch()){
967       $src = $attrs['dn'];
968       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
969       $this->_copy($src,$dst);
970     }
971     return (TRUE);
972   }
976   /*! \brief  Rename/Move a given src_dn to the given dest_dn
977    *
978    * Move a given ldap object indentified by $src_dn to the
979    * given destination $dst_dn
980    *
981    * - Ensure that all references are updated (ogroups)
982    * - Update ACLs   
983    * - Update accessTo
984    *
985    * \param  string  'src_dn' the source DN.
986    * \param  string  'dst_dn' the destination DN.
987    * \return boolean TRUE on success else FALSE.
988    */
989   function rename($src_dn, $dst_dn)
990   {
991     $start = microtime(1);
993     /* Try to move the source entry to the destination position */
994     $ldap = $this->config->get_ldap_link();
995     $ldap->cd($this->config->current['BASE']);
996     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
997     if (!$ldap->rename_dn($src_dn,$dst_dn)){
998 #      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
999       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());
1000       @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn  -- TO:  $dst_dn", 
1001           "Ldap Protocol v3 implementation error, falling back to maunal method.");
1002       return(FALSE);
1003     }
1005     /* Get list of users,groups and roles within this tree,
1006         maybe we have to update ACL references.
1007      */
1008     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
1009           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1010     foreach($leaf_objs as $obj){
1011       $new_dn = $obj['dn'];
1012       $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
1013       $this->update_acls($old_dn,$new_dn); 
1014     }
1016     // Migrate objectgroups if needed
1017     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
1018       "ogroups", array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1020     // Walk through all objectGroups
1021     foreach($ogroups as $ogroup){
1022       // Migrate old to new dn
1023       $o_ogroup= new ogroup($this->config,$ogroup['dn']);
1024       if (isset($o_ogroup->member[$src_dn])) {
1025         unset($o_ogroup->member[$src_dn]);
1026       }
1027       $o_ogroup->member[$dst_dn]= $dst_dn;
1028       
1029       // Save object group
1030       $o_ogroup->save();
1031     }
1033     // Migrate rfc groups if needed
1034     $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);
1036     // Walk through all POSIX groups
1037     foreach($groups as $group){
1039       // Migrate old to new dn
1040       $o_group= new group($this->config,$group['dn']);
1041       $o_group->save();
1042     }
1044     /* Update roles to use the new entry dn */
1045     $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);
1047     // Walk through all roles
1048     foreach($roles as $role){
1049       $role = new roleGeneric($this->config,$role['dn']);
1050       $key= array_search($src_dn, $role->roleOccupant);      
1051       if($key !== FALSE){
1052         $role->roleOccupant[$key] = $dst_dn;
1053         $role->save();
1054       }
1055     }
1057     // Update 'manager' attributes from gosaDepartment and inetOrgPerson 
1058     $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
1059     $ocs = $ldap->get_objectclasses();
1060     if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
1061       $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
1062     }
1063     $leaf_deps=  get_list($filter,array("all"),$this->config->current['BASE'], 
1064         array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1065     foreach($leaf_deps as $entry){
1066       $update = array('manager' => $dst_dn);
1067       $ldap->cd($entry['dn']);
1068       $ldap->modify($update);
1069       if(!$ldap->success()){
1070         trigger_error(sprintf("Failed to update manager for '%s', error was '%s'", $entry['dn'], $ldap->get_error()));
1071       }
1072     }
1073  
1074     /* Check if there are gosa departments moved. 
1075        If there were deps moved, the force reload of config->deps.
1076      */
1077     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
1078           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1079   
1080     if(count($leaf_deps)){
1081       $this->config->get_departments();
1082       $this->config->make_idepartments();
1083       session::global_set("config",$this->config);
1084       $ui =get_userinfo();
1085       $ui->reset_acl_cache();
1086     }
1088     return(TRUE); 
1089   }
1092  
1093   function move($src_dn, $dst_dn)
1094   {
1095     /* Do not copy if only upper- lowercase has changed */
1096     if(strtolower($src_dn) == strtolower($dst_dn)){
1097       return(TRUE);
1098     }
1100     
1101     /* Try to move the entry instead of copy & delete
1102      */
1103     if(TRUE){
1105       /* Try to move with ldap routines, if this was not successfull
1106           fall back to the old style copy & remove method 
1107        */
1108       if($this->rename($src_dn, $dst_dn)){
1109         return(TRUE);
1110       }else{
1111         // See code below.
1112       }
1113     }
1115     /* Copy source to destination */
1116     if (!$this->copy($src_dn, $dst_dn)){
1117       return (FALSE);
1118     }
1120     /* Delete source */
1121     $ldap= $this->config->get_ldap_link();
1122     $ldap->rmdir_recursive($src_dn);
1123     if (!$ldap->success()){
1124       trigger_error("Trying to delete $src_dn failed.",
1125           E_USER_WARNING);
1126       return (FALSE);
1127     }
1129     return (TRUE);
1130   }
1133   /* \brief Move/Rename complete trees */
1134   function recursive_move($src_dn, $dst_dn)
1135   {
1136     /* Check if the destination entry exists */
1137     $ldap= $this->config->get_ldap_link();
1139     /* Check if destination exists - abort */
1140     $ldap->cat($dst_dn, array('dn'));
1141     if ($ldap->fetch()){
1142       trigger_error("recursive_move $dst_dn already exists.",
1143           E_USER_WARNING);
1144       return (FALSE);
1145     }
1147     $this->copy($src_dn, $dst_dn);
1149     /* Remove src_dn */
1150     $ldap->cd($src_dn);
1151     $ldap->recursive_remove($src_dn);
1152     return (TRUE);
1153   }
1156   /*! \brief    Forward command execution requests
1157    *             to the hook execution method.
1158    */
1159   function handle_post_events($mode, $addAttrs= array())
1160   {
1161     if(!in_array($mode, array('add','remove','modify'))){
1162       trigger_error(sprintf("Invalid post event type given %s! Valid types are [add,modify,remove].", $mode));
1163       return;
1164     }
1165     switch ($mode){
1166       case "add":
1167         plugin::callHook($this,"POSTCREATE", $addAttrs);
1168       break;
1170       case "modify":
1171         plugin::callHook($this,"POSTMODIFY", $addAttrs);
1172       break;
1174       case "remove":
1175         plugin::callHook($this,"POSTREMOVE", $addAttrs);
1176       break;
1177     }
1178   }
1181   /*! \brief    Forward command execution requests
1182    *             to the hook execution method.
1183    */
1184   function handle_pre_events($mode, $addAttrs= array())
1185   {
1186     if(!in_array($mode, array('add','remove','modify'))){
1187       trigger_error(sprintf("Invalid pre event type given %s! Valid types are [add,modify,remove].", $mode));
1188       return;
1189     }
1190     switch ($mode){
1191       case "add":
1192         plugin::callHook($this,"PRECREATE", $addAttrs);
1193       break;
1195       case "modify":
1196         plugin::callHook($this,"PREMODIFY", $addAttrs);
1197       break;
1199       case "remove":
1200         plugin::callHook($this,"PREREMOVE", $addAttrs);
1201       break;
1202     }
1203   }
1206   /*! \brief    Calls external hooks which are defined for this plugin (gosa.conf)
1207    *            Replaces placeholder by class values of this plugin instance.
1208    *  @param    Allows to a add special replacements.
1209    */
1210   static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(), &$returnCode = NULL)
1211   {
1212       global $config;
1213       $command= $config->search(get_class($plugin), $cmd ,array('menu', 'tabs'));
1214       if ($command != ""){
1216           // Walk trough attributes list and add the plugins attributes.
1217           foreach ($plugin->attributes as $attr){
1218               if (!is_array($plugin->$attr)){
1219                   $addAttrs[$attr] = $plugin->$attr;
1220               }
1221           }
1222           $ui = get_userinfo();
1223           $addAttrs['callerDN']=$ui->dn;
1224           $addAttrs['dn']=$plugin->dn;
1225           $addAttrs['location']=$config->current['NAME'];
1227           // Sort attributes by length, ensures correct replacement
1228           $tmp = array();
1229           foreach($addAttrs as $name => $value){
1230               $tmp[$name] =  strlen($name);
1231           }
1232           arsort($tmp);
1234           // Now replace the placeholder
1235           foreach ($tmp as $name => $len){
1236               $value = $addAttrs[$name];
1237               $command= str_replace("%$name", "$value", $command);
1238           }
1239           // If there are still some %.. in our command, try to fill these with some other class vars
1240           if(preg_match("/%/",$command)){
1241               $attrs = get_object_vars($plugin);
1242               foreach($attrs as $name => $value){
1243                   if(is_array($value)){
1244                       $s = "";
1245                       foreach($value as $val){
1246                           if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
1247                               $s .= '"'.$val.'",';
1248                           }
1249                       }
1250                       $value = '['.trim($s,',').']';
1251                   }
1252                   if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
1253                       continue;
1254                   }
1255                   $command= preg_replace("/%$name/", $value, $command);
1256               }
1257           }
1259           if (check_command($command)){
1261               @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
1262               exec($command, $arr, $returnCode);
1263               $returnOutput = $arr;
1265               if($returnCode != 0){
1266                   $str = implode("\n",$arr);
1267                   @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1268                   $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
1269                   msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1270               }elseif(is_array($arr)){
1271                   $str = implode("\n",$arr);
1272                   @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
1273               }
1274           } else {
1275               $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
1276               msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1277           }
1278       }
1279   }
1282   function saveCopyDialog(){
1283   }
1286   function getCopyDialog(){
1287     return(array("string"=>"","status"=>""));
1288   }
1291   /*! \brief Prepare for Copy & Paste */
1292   function PrepareForCopyPaste($source)
1293   {
1294     $todo = $this->attributes;
1295     if(isset($this->CopyPasteVars)){
1296       $todo = array_merge($todo,$this->CopyPasteVars);
1297     }
1299     if(count($this->objectclasses)){
1300       $this->is_account = TRUE;
1301       foreach($this->objectclasses as $class){
1302         if(!in_array($class,$source['objectClass'])){
1303           $this->is_account = FALSE;
1304         }
1305       }
1306     }
1308     foreach($todo as $var){
1309       if (isset($source[$var])){
1310         if(isset($source[$var]['count'])){
1311           if($source[$var]['count'] > 1){
1312             $tmp= $source[$var];
1313             unset($tmp['count']);
1314             $this->$var = $tmp;
1315           }else{
1316             $this->$var = $source[$var][0];
1317           }
1318         }else{
1319           $this->$var= $source[$var];
1320         }
1321       }
1322     }
1323   }
1325   /*! \brief Get gosaUnitTag for the given DN
1326        If this is called from departmentGeneric, we have to skip this
1327         tagging procedure. 
1328     */
1329   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1330   {
1331     /* Skip tagging? */
1332     if($this->skipTagging){
1333       return;
1334     }
1336     /* No dn? Self-operation... */
1337     if ($dn == ""){
1338       $dn= $this->dn;
1340       /* No tag? Find it yourself... */
1341       if ($tag == ""){
1342         $len= strlen($dn);
1344         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1345         $relevant= array();
1346         foreach ($this->config->adepartments as $key => $ntag){
1348           /* This one is bigger than our dn, its not relevant... */
1349           if ($len < strlen($key)){
1350             continue;
1351           }
1353           /* This one matches with the latter part. Break and don't fix this entry */
1354           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1355             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1356             $relevant[strlen($key)]= $ntag;
1357             continue;
1358           }
1360         }
1362         /* If we've some relevant tags to set, just get the longest one */
1363         if (count($relevant)){
1364           ksort($relevant);
1365           $tmp= array_keys($relevant);
1366           $idx= end($tmp);
1367           $tag= $relevant[$idx];
1368           $this->gosaUnitTag= $tag;
1369         }
1370       }
1371     }
1373   /*! \brief Add unit tag */ 
1374     /* Remove tags that may already be here... */
1375     remove_objectClass("gosaAdministrativeUnitTag", $at);
1376     if (isset($at['gosaUnitTag'])){
1377         unset($at['gosaUnitTag']);
1378     }
1380     /* Set tag? */
1381     if ($tag != ""){
1382       add_objectClass("gosaAdministrativeUnitTag", $at);
1383       $at['gosaUnitTag']= $tag;
1384     }
1386     /* Initially this object was tagged. 
1387        - But now, it is no longer inside a tagged department. 
1388        So force the remove of the tag.
1389        (objectClass was already removed obove)
1390      */
1391     if($tag == "" && $this->gosaUnitTag){
1392       $at['gosaUnitTag'] = array();
1393     }
1394   }
1397   /*! \brief Test for removability of the object
1398    *
1399    * Allows testing of conditions for removal of object. If removal should be aborted
1400    * the function needs to remove an error message.
1401    * */
1402   function allow_remove()
1403   {
1404     $reason= "";
1405     return $reason;
1406   }
1409   /*! \brief Create a snapshot of the current object */
1410   function create_snapshot($type= "snapshot", $description= array())
1411   {
1413     /* Check if snapshot functionality is enabled */
1414     if(!$this->snapshotEnabled()){
1415       return;
1416     }
1418     /* Get configuration from gosa.conf */
1419     $config = $this->config;
1421     /* Create lokal ldap connection */
1422     $ldap= $this->config->get_ldap_link();
1423     $ldap->cd($this->config->current['BASE']);
1425     /* check if there are special server configurations for snapshots */
1426     if($config->get_cfg_value("snapshotURI") == ""){
1428       /* Source and destination server are both the same, just copy source to dest obj */
1429       $ldap_to      = $ldap;
1430       $snapldapbase = $this->config->current['BASE'];
1432     }else{
1433       $server         = $config->get_cfg_value("snapshotURI");
1434       $user           = $config->get_cfg_value("snapshotAdminDn");
1435       $password       = $this->config->get_credentials($config->get_cfg_value("snapshotAdminPassword"));
1436       $snapldapbase   = $config->get_cfg_value("snapshotBase");
1438       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1439       $ldap_to -> cd($snapldapbase);
1441       if (!$ldap_to->success()){
1442         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1443       }
1445     }
1447     /* check if the dn exists */ 
1448     if ($ldap->dn_exists($this->dn)){
1450       /* Extract seconds & mysecs, they are used as entry index */
1451       list($usec, $sec)= explode(" ", microtime());
1453       /* Collect some infos */
1454       $base           = $this->config->current['BASE'];
1455       $snap_base      = $config->get_cfg_value("snapshotBase");
1456       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1457       $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1459       /* Create object */
1460 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1461       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1462       $newName          = str_replace(".", "", $sec."-".$usec);
1463       $target= array();
1464       $target['objectClass']            = array("top", "gosaSnapshotObject");
1465       $target['gosaSnapshotData']       = gzcompress($data, 6);
1466       $target['gosaSnapshotType']       = $type;
1467       $target['gosaSnapshotDN']         = $this->dn;
1468       $target['description']            = $description;
1469       $target['gosaSnapshotTimestamp']  = $newName;
1471       /* Insert the new snapshot 
1472          But we have to check first, if the given gosaSnapshotTimestamp
1473          is already used, in this case we should increment this value till there is 
1474          an unused value. */ 
1475       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1476       $ldap_to->cat($new_dn);
1477       while($ldap_to->count()){
1478         $ldap_to->cat($new_dn);
1479         $newName = str_replace(".", "", $sec."-".($usec++));
1480         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1481         $target['gosaSnapshotTimestamp']  = $newName;
1482       } 
1484       /* Inset this new snapshot */
1485       $ldap_to->cd($snapldapbase);
1486       $ldap_to->create_missing_trees($snapldapbase);
1487       $ldap_to->create_missing_trees($new_base);
1488       $ldap_to->cd($new_dn);
1489       $ldap_to->add($target);
1490       if (!$ldap_to->success()){
1491         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1492       }
1494       if (!$ldap->success()){
1495         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1496       }
1498     }
1499   }
1501   /*! \brief Remove a snapshot */
1502   function remove_snapshot($dn)
1503   {
1504     $ui       = get_userinfo();
1505     $old_dn   = $this->dn; 
1506     $this->dn = $dn;
1507     $ldap = $this->config->get_ldap_link();
1508     $ldap->cd($this->config->current['BASE']);
1509     $ldap->rmdir_recursive($this->dn);
1510     if(!$ldap->success()){
1511       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn));
1512     }
1513     $this->dn = $old_dn;
1514   }
1517   /*! \brief Test if snapshotting is enabled
1518    *
1519    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1520    * if the configuration failed 
1521    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1522    */
1523   function snapshotEnabled()
1524   {
1525     return $this->config->snapshotEnabled();
1526   }
1529   /* \brief Return available snapshots for the given base */
1530   function Available_SnapsShots($dn,$raw = false)
1531   {
1532     if(!$this->snapshotEnabled()) return(array());
1534     /* Create an additional ldap object which
1535        points to our ldap snapshot server */
1536     $ldap= $this->config->get_ldap_link();
1537     $ldap->cd($this->config->current['BASE']);
1538     $cfg= &$this->config->current;
1540     /* check if there are special server configurations for snapshots */
1541     if($this->config->get_cfg_value("snapshotURI") == ""){
1542       $ldap_to      = $ldap;
1543     }else{
1544       $server         = $this->config->get_cfg_value("snapshotURI");
1545       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1546       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1547       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1548       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1549       $ldap_to -> cd($snapldapbase);
1550       if (!$ldap_to->success()){
1551         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1552       }
1553     }
1555     /* Prepare bases and some other infos */
1556     $base           = $this->config->current['BASE'];
1557     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1558     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1559     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1560     $tmp            = array(); 
1562     /* Fetch all objects with  gosaSnapshotDN=$dn */
1563     $ldap_to->cd($new_base);
1564     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1565         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1567     /* Put results into a list and add description if missing */
1568     while($entry = $ldap_to->fetch()){ 
1569       if(!isset($entry['description'][0])){
1570         $entry['description'][0]  = "";
1571       }
1572       $tmp[] = $entry; 
1573     }
1575     /* Return the raw array, or format the result */
1576     if($raw){
1577       return($tmp);
1578     }else{  
1579       $tmp2 = array();
1580       foreach($tmp as $entry){
1581         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1582       }
1583     }
1584     return($tmp2);
1585   }
1588   function getAllDeletedSnapshots($base_of_object,$raw = false)
1589   {
1590     if(!$this->snapshotEnabled()) return(array());
1592     /* Create an additional ldap object which
1593        points to our ldap snapshot server */
1594     $ldap= $this->config->get_ldap_link();
1595     $ldap->cd($this->config->current['BASE']);
1596     $cfg= &$this->config->current;
1598     /* check if there are special server configurations for snapshots */
1599     if($this->config->get_cfg_value("snapshotURI") == ""){
1600       $ldap_to      = $ldap;
1601     }else{
1602       $server         = $this->config->get_cfg_value("snapshotURI");
1603       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1604       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1605       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1606       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1607       $ldap_to -> cd($snapldapbase);
1608       if (!$ldap_to->success()){
1609         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1610       }
1611     }
1613     /* Prepare bases */ 
1614     $base           = $this->config->current['BASE'];
1615     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1616     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1618     /* Fetch all objects and check if they do not exist anymore */
1619     $ui = get_userinfo();
1620     $tmp = array();
1621     $ldap_to->cd($new_base);
1622     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1623     while($entry = $ldap_to->fetch()){
1625       $chk =  str_replace($new_base,"",$entry['dn']);
1626       if(preg_match("/,ou=/",$chk)) continue;
1628       if(!isset($entry['description'][0])){
1629         $entry['description'][0]  = "";
1630       }
1631       $tmp[] = $entry; 
1632     }
1634     /* Check if entry still exists */
1635     foreach($tmp as $key => $entry){
1636       $ldap->cat($entry['gosaSnapshotDN'][0]);
1637       if($ldap->count()){
1638         unset($tmp[$key]);
1639       }
1640     }
1642     /* Format result as requested */
1643     if($raw) {
1644       return($tmp);
1645     }else{
1646       $tmp2 = array();
1647       foreach($tmp as $key => $entry){
1648         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1649       }
1650     }
1651     return($tmp2);
1652   } 
1655   /* \brief Restore selected snapshot */
1656   function restore_snapshot($dn)
1657   {
1658     if(!$this->snapshotEnabled()) return(array());
1660     $ldap= $this->config->get_ldap_link();
1661     $ldap->cd($this->config->current['BASE']);
1662     $cfg= &$this->config->current;
1664     /* check if there are special server configurations for snapshots */
1665     if($this->config->get_cfg_value("snapshotURI") == ""){
1666       $ldap_to      = $ldap;
1667     }else{
1668       $server         = $this->config->get_cfg_value("snapshotURI");
1669       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1670       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1671       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1672       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1673       $ldap_to -> cd($snapldapbase);
1674       if (!$ldap_to->success()){
1675         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1676       }
1677     }
1679     /* Get the snapshot */ 
1680     $ldap_to->cat($dn);
1681     $restoreObject = $ldap_to->fetch();
1683     /* Prepare import string */
1684     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1686     /* Import the given data */
1687     $err = "";
1688     $ldap->import_complete_ldif($data,$err,false,false);
1689     if (!$ldap->success()){
1690       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1691     }
1692   }
1695   function showSnapshotDialog($base,$baseSuffixe,&$parent)
1696   {
1697     $once = true;
1698     $ui = get_userinfo();
1699     $this->parent = $parent;
1701     foreach($_POST as $name => $value){
1703       /* Create a new snapshot, display a dialog */
1704       if(preg_match("/^CreateSnapShotDialog_[^_]*_[xy]$/",$name) && $once){
1706                           $entry = base64_decode(preg_replace("/^CreateSnapShotDialog_([^_]*)_[xy]$/","\\1",$name));
1707         $once = false;
1708         $entry = preg_replace("/^CreateSnapShotDialog_/","",$entry);
1710         if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1711           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1712         }else{
1713           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1714         }
1715       }  
1716   
1717       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1718       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1719         $once = false;
1720         $entry = base64_decode(preg_replace("/^RestoreSnapShotDialog_([^_]*)_[xy]$/i","\\1",$name));
1721         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1722           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1723           $this->snapDialog->display_restore_dialog = true;
1724         }else{
1725           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1726         }
1727       }
1729       /* Restore one of the already deleted objects */
1730       if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot") 
1731           || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1732         $once = false;
1734         if($ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1735           $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1736           $this->snapDialog->set_snapshot_bases($baseSuffixe);
1737           $this->snapDialog->display_restore_dialog      = true;
1738           $this->snapDialog->display_all_removed_objects  = true;
1739         }else{
1740           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1741         }
1742       }
1744       /* Restore selected snapshot */
1745       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1746         $once = false;
1747         $entry = base64_decode(preg_replace("/^RestoreSnapShot_([^_]*)_[xy]$/i","\\1",$name));
1749         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1750           $this->restore_snapshot($entry);
1751           $this->snapDialog = NULL;
1752         }else{
1753           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1754         }
1755       }
1756     }
1758     /* Create a new snapshot requested, check
1759        the given attributes and create the snapshot*/
1760     if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1761       $this->snapDialog->save_object();
1762       $msgs = $this->snapDialog->check();
1763       if(count($msgs)){
1764         foreach($msgs as $msg){
1765           msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1766         }
1767       }else{
1768         $this->dn =  $this->snapDialog->dn;
1769         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1770         $this->snapDialog = NULL;
1771       }
1772     }
1774     /* Restore is requested, restore the object with the posted dn .*/
1775     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1776     }
1778     if(isset($_POST['CancelSnapshot'])){
1779       $this->snapDialog = NULL;
1780     }
1782     if(is_object($this->snapDialog )){
1783       $this->snapDialog->save_object();
1784       return($this->snapDialog->execute());
1785     }
1786   }
1789   /*! \brief Return plugin informations for acl handling */
1790   static function plInfo()
1791   {
1792     return array();
1793   }
1796   function set_acl_base($base)
1797   {
1798     $this->acl_base= $base;
1799   }
1802   function set_acl_category($category)
1803   {
1804     $this->acl_category= "$category/";
1805   }
1808   function acl_is_writeable($attribute,$skip_write = FALSE)
1809   {
1810     if($this->read_only) return(FALSE);
1811     $ui= get_userinfo();
1812     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1813   }
1816   function acl_is_readable($attribute)
1817   {
1818     $ui= get_userinfo();
1819     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1820   }
1823   function acl_is_createable($base ="")
1824   {
1825     if($this->read_only) return(FALSE);
1826     $ui= get_userinfo();
1827     if($base == "") $base = $this->acl_base;
1828     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1829   }
1832   function acl_is_removeable($base ="")
1833   {
1834     if($this->read_only) return(FALSE);
1835     $ui= get_userinfo();
1836     if($base == "") $base = $this->acl_base;
1837     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1838   }
1841   function acl_is_moveable($base = "")
1842   {
1843     if($this->read_only) return(FALSE);
1844     $ui= get_userinfo();
1845     if($base == "") $base = $this->acl_base;
1846     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1847   }
1850   function acl_have_any_permissions()
1851   {
1852   }
1855   function getacl($attribute,$skip_write= FALSE)
1856   {
1857     $ui= get_userinfo();
1858     $skip_write |= $this->read_only;
1859     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1860   }
1863   /*! \brief Returns a list of all available departments for this object.
1864    * 
1865    * If this object is new, all departments we are allowed to create a new user in
1866    * are returned. If this is an existing object, return all deps. 
1867    * We are allowed to move tis object too.
1868    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1869   */
1870   function get_allowed_bases()
1871   {
1872     $ui = get_userinfo();
1873     $deps = array();
1875     /* Is this a new object ? Or just an edited existing object */
1876     if(!$this->initially_was_account && $this->is_account){
1877       $new = true;
1878     }else{
1879       $new = false;
1880     }
1882     foreach($this->config->idepartments as $dn => $name){
1883       if($new && $this->acl_is_createable($dn)){
1884         $deps[$dn] = $name;
1885       }elseif(!$new && $this->acl_is_moveable($dn)){
1886         $deps[$dn] = $name;
1887       }
1888     }
1890     /* Add current base */      
1891     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1892       $deps[$this->base] = $this->config->idepartments[$this->base];
1893     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1895     }else{
1896       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1897     }
1898     return($deps);
1899   }
1902   /* This function updates ACL settings if $old_dn was used.
1903    *  \param string 'old_dn' specifies the actually used dn
1904    *  \param string 'new_dn' specifies the destiantion dn
1905    */
1906   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1907   {
1908     /* Check if old_dn is empty. This should never happen */
1909     if(empty($old_dn) || empty($new_dn)){
1910       trigger_error("Failed to check acl dependencies, wrong dn given.");
1911       return;
1912     }
1914     /* Update userinfo if necessary */
1915     $ui = session::global_get('ui');
1916     if($ui->dn == $old_dn){
1917       $ui->dn = $new_dn;
1918       session::global_set('ui',$ui);
1919       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1920     }
1922     /* Object was moved, ensure that all acls will be moved too */
1923     if($new_dn != $old_dn && $old_dn != "new"){
1925       /* get_ldap configuration */
1926       $update = array();
1927       $ldap = $this->config->get_ldap_link();
1928       $ldap->cd ($this->config->current['BASE']);
1929       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1930       while($attrs = $ldap->fetch()){
1931         $acls = array();
1932         $found = false;
1933         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1934           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1936           /* Roles uses antoher data storage order, members are stored int the third part, 
1937              while the members in direct ACL assignments are stored in the second part.
1938            */
1939           $id = ($acl_parts[1] == "role") ? 3 : 2;
1941           /* Update member entries to use $new_dn instead of old_dn
1942            */
1943           $members = explode(",",$acl_parts[$id]);
1944           foreach($members as $key => $member){
1945             $member = base64_decode($member);
1946             if($member == $old_dn){
1947               $members[$key] = base64_encode($new_dn);
1948               $found = TRUE;
1949             }
1950           } 
1952           /* Check if the selected role has to updated
1953            */
1954           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1955             $acl_parts[2] = base64_encode($new_dn);
1956             $found = TRUE;
1957           }
1959           /* Build new acl string */ 
1960           $acl_parts[$id] = implode($members,",");
1961           $acls[] = implode($acl_parts,":");
1962         }
1964         /* Acls for this object must be adjusted */
1965         if($found){
1967           $debug_info=  _("Changing ACL dn")."&nbsp;:&nbsp;<br>&nbsp;-"._("from")."&nbsp;<b>&nbsp;".
1968             $old_dn."</b><br>&nbsp;-"._("to")."&nbsp;<b>".$new_dn."</b><br>";
1969           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1971           $update[$attrs['dn']] =array();
1972           foreach($acls as $acl){
1973             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1974           }
1975         }
1976       }
1978       /* Write updated acls */
1979       foreach($update as $dn => $attrs){
1980         $ldap->cd($dn);
1981         $ldap->modify($attrs);
1982       }
1983     }
1984   }
1986   
1988   /*! \brief Enable the Serial ID check
1989    *
1990    * This function enables the entry Serial ID check.  If an entry was edited while
1991    * we have edited the entry too, an error message will be shown. 
1992    * To configure this check correctly read the FAQ.
1993    */    
1994   function enable_CSN_check()
1995   {
1996     $this->CSN_check_active =TRUE;
1997     $this->entryCSN = getEntryCSN($this->dn);
1998   }
2001   /*! \brief  Prepares the plugin to be used for multiple edit
2002    *          Update plugin attributes with given array of attribtues.
2003    *  \param  array   Array with attributes that must be updated.
2004    */
2005   function init_multiple_support($attrs,$all)
2006   {
2007     $ldap= $this->config->get_ldap_link();
2008     $this->multi_attrs    = $attrs;
2009     $this->multi_attrs_all= $all;
2011     /* Copy needed attributes */
2012     foreach ($this->attributes as $val){
2013       $found= array_key_ics($val, $this->multi_attrs);
2014  
2015       if ($found != ""){
2016         if(isset($this->multi_attrs["$val"][0])){
2017           $this->$val= $this->multi_attrs["$val"][0];
2018         }
2019       }
2020     }
2021   }
2023  
2024   /*! \brief  Enables multiple support for this plugin
2025    */
2026   function enable_multiple_support()
2027   {
2028     $this->ignore_account = TRUE;
2029     $this->multiple_support_active = TRUE;
2030   }
2033   /*! \brief  Returns all values that have been modfied in multiple edit mode.
2034       \return array Cotaining all modified values. 
2035    */
2036   function get_multi_edit_values()
2037   {
2038     $ret = array();
2039     foreach($this->attributes as $attr){
2040       if(in_array($attr,$this->multi_boxes)){
2041         $ret[$attr] = $this->$attr;
2042       }
2043     }
2044     return($ret);
2045   }
2047   
2048   /*! \brief  Update class variables with values collected by multiple edit.
2049    */
2050   function set_multi_edit_values($attrs)
2051   {
2052     foreach($attrs as $name => $value){
2053       $this->$name = $value;
2054     }
2055   }
2058   /*! \brief Generates the html output for this node for multi edit*/
2059   function multiple_execute()
2060   {
2061     /* This one is empty currently. Fabian - please fill in the docu code */
2062     session::global_set('current_class_for_help',get_class($this));
2064     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
2065     session::set('LOCK_VARS_TO_USE',array());
2066     session::set('LOCK_VARS_USED_GET',array());
2067     session::set('LOCK_VARS_USED_POST',array());
2068     session::set('LOCK_VARS_USED_REQUEST',array());
2069     
2070     return("Multiple edit is currently not implemented for this plugin.");
2071   }
2074   /*! \brief Save HTML posted data to object for multiple edit
2075    */
2076   function multiple_save_object()
2077   {
2078     if(empty($this->entryCSN) && $this->CSN_check_active){
2079       $this->entryCSN = getEntryCSN($this->dn);
2080     }
2082     /* Save values to object */
2083     $this->multi_boxes = array();
2084     foreach ($this->attributes as $val){
2085   
2086       /* Get selected checkboxes from multiple edit */
2087       if(isset($_POST["use_".$val])){
2088         $this->multi_boxes[] = $val;
2089       }
2091       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
2093         /* Check for modifications */
2094         if (get_magic_quotes_gpc()) {
2095           $data= stripcslashes($_POST["$val"]);
2096         } else {
2097           $data= $this->$val = $_POST["$val"];
2098         }
2099         if ($this->$val != $data){
2100           $this->is_modified= TRUE;
2101         }
2102     
2103         /* IE post fix */
2104         if(isset($data[0]) && $data[0] == chr(194)) {
2105           $data = "";  
2106         }
2107         $this->$val= $data;
2108       }
2109     }
2110   }
2113   /*! \brief Returns all attributes of this plugin, 
2114                to be able to detect multiple used attributes 
2115                in multi_plugg::detect_multiple_used_attributes().
2116       @return array Attributes required for intialization of multi_plug
2117    */
2118   public function get_multi_init_values()
2119   {
2120     $attrs = $this->attrs;
2121     return($attrs);
2122   }
2125   /*! \brief  Check given values in multiple edit
2126       \return array Error messages
2127    */
2128   function multiple_check()
2129   {
2130     $message = plugin::check();
2131     return($message);
2132   }
2135   /*! \brief  Returns the snapshot header part for "Actions" menu in management dialogs 
2136       \param  $layer_menu  
2137    */   
2138   function get_snapshot_header($base,$category)
2139   {
2140     $str = "";
2141     $ui = get_userinfo();
2142     if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
2144       $ok = false;
2145       foreach($this->get_used_snapshot_bases() as $base){
2146         $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
2147       }
2149       if($ok){
2150         $str = "..|<img class='center' src='images/lists/restore.png' ".
2151           "alt='"._("Restore")."'>&nbsp;"._("Restore").                       "|RestoreDeletedSnapShot|\n";
2152       }else{
2153         $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;"._("Restore")."||\n";
2154       }
2155     }
2156     return($str);
2157   }
2160   function get_snapshot_action($base,$category)
2161   {
2162     $str= ""; 
2163     $ui = get_userinfo();
2164     if($this->snapshotEnabled()){
2165       if ($ui->allow_snapshot_restore($base,$category)){
2167         if(count($this->Available_SnapsShots($base))){
2168           $str.= "<input class='center' type='image' src='images/lists/restore.png'
2169             alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'>&nbsp;";
2170         } else {
2171           $str = "<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;";
2172         }
2173       }
2174       if($ui->allow_snapshot_create($base,$category)){
2175         $str.= "<input class='center' type='image' src='images/snapshot.png'
2176           alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."' 
2177           title='"._("Create a new snapshot from this object")."'>&nbsp;";
2178       }else{
2179         $str = "<img class='center' src='images/empty.png' alt=' '>&nbsp;";
2180       }
2181     }
2183     return($str);
2184   }
2187   function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2188   {
2189     $ui = get_userinfo();
2190     $action = "";
2191     if($this->CopyPasteHandler){
2192       if($cut){
2193         if($ui->is_cutable($base,$category,$class)){
2194           $action .= "<input class='center' type='image'
2195             src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'>&nbsp;";
2196         }else{
2197           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2198         }
2199       }
2200       if($copy){
2201         if($ui->is_copyable($base,$category,$class)){
2202           $action.= "<input class='center' type='image'
2203             src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'>&nbsp;";
2204         }else{
2205           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2206         }
2207       }
2208     }
2210     return($action); 
2211   }
2214   function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2215   {
2216     $s = "";
2217     $ui =get_userinfo();
2219     if(!is_array($category)){
2220       $category = array($category);
2221     }
2223     /* Check permissions for each category, if there is at least one category which 
2224         support read or paste permissions for the given base, then display the specific actions.
2225      */
2226     $readable = $pasteable = false;
2227     foreach($category as $cat){
2228       $readable= $readable || preg_match('/r/', $ui->get_category_permissions($base, $cat));
2229       $pasteable= $pasteable || $ui->is_pasteable($base, $cat) == 1;
2230     }
2231   
2232     if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2233       if($readable){
2234         $s.= "..|---|\n";
2235         if($copy){
2236           $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2237             "&nbsp;"._("Copy")."|"."multiple_copy_systems|\n";
2238         }
2239         if($cut){
2240           $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2241             "&nbsp;"._("Cut")."|"."multiple_cut_systems|\n";
2242         }
2243       }
2245       if($pasteable){
2246         if($this->CopyPasteHandler->entries_queued()){
2247           $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2248           $s.="..|".$img."&nbsp;"._("Paste")."|editPaste|\n";
2249         }else{
2250           $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2251           $s.="..|".$img."&nbsp;"._("Paste")."\n";
2252         }
2253       }
2254     }
2255     return($s);
2256   }
2259   function get_used_snapshot_bases()
2260   {
2261      return(array());
2262   }
2264   function is_modal_dialog()
2265   {
2266     return(isset($this->dialog) && $this->dialog);
2267   }
2270 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2271 ?>