Code

42e2a4422d4e681d823eac55fb15557aa98c0126
[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", escapeshellarg($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", escapeshellarg($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", escapeshellarg($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/", escapeshellarg($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             = $ldap->generateLdif(LDAP::fix($dn),
1461               "(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))",array(),'base');
1463       $newName          = str_replace(".", "", $sec."-".$usec);
1464       $target= array();
1465       $target['objectClass']            = array("top", "gosaSnapshotObject");
1466       $target['gosaSnapshotData']       = gzcompress($data, 6);
1467       $target['gosaSnapshotType']       = $type;
1468       $target['gosaSnapshotDN']         = $this->dn;
1469       $target['description']            = $description;
1470       $target['gosaSnapshotTimestamp']  = $newName;
1472       /* Insert the new snapshot 
1473          But we have to check first, if the given gosaSnapshotTimestamp
1474          is already used, in this case we should increment this value till there is 
1475          an unused value. */ 
1476       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1477       $ldap_to->cat($new_dn);
1478       while($ldap_to->count()){
1479         $ldap_to->cat($new_dn);
1480         $newName = str_replace(".", "", $sec."-".($usec++));
1481         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1482         $target['gosaSnapshotTimestamp']  = $newName;
1483       } 
1485       /* Inset this new snapshot */
1486       $ldap_to->cd($snapldapbase);
1487       $ldap_to->create_missing_trees($snapldapbase);
1488       $ldap_to->create_missing_trees($new_base);
1489       $ldap_to->cd($new_dn);
1490       $ldap_to->add($target);
1491       if (!$ldap_to->success()){
1492         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1493       }
1495       if (!$ldap->success()){
1496         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1497       }
1499     }
1500   }
1502   /*! \brief Remove a snapshot */
1503   function remove_snapshot($dn)
1504   {
1505     $ui       = get_userinfo();
1506     $old_dn   = $this->dn; 
1507     $this->dn = $dn;
1508     $ldap = $this->config->get_ldap_link();
1509     $ldap->cd($this->config->current['BASE']);
1510     $ldap->rmdir_recursive($this->dn);
1511     if(!$ldap->success()){
1512       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn));
1513     }
1514     $this->dn = $old_dn;
1515   }
1518   /*! \brief Test if snapshotting is enabled
1519    *
1520    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1521    * if the configuration failed 
1522    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1523    */
1524   function snapshotEnabled()
1525   {
1526     return $this->config->snapshotEnabled();
1527   }
1530   /* \brief Return available snapshots for the given base */
1531   function Available_SnapsShots($dn,$raw = false)
1532   {
1533     if(!$this->snapshotEnabled()) return(array());
1535     /* Create an additional ldap object which
1536        points to our ldap snapshot server */
1537     $ldap= $this->config->get_ldap_link();
1538     $ldap->cd($this->config->current['BASE']);
1539     $cfg= &$this->config->current;
1541     /* check if there are special server configurations for snapshots */
1542     if($this->config->get_cfg_value("snapshotURI") == ""){
1543       $ldap_to      = $ldap;
1544     }else{
1545       $server         = $this->config->get_cfg_value("snapshotURI");
1546       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1547       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1548       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1549       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1550       $ldap_to -> cd($snapldapbase);
1551       if (!$ldap_to->success()){
1552         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1553       }
1554     }
1556     /* Prepare bases and some other infos */
1557     $base           = $this->config->current['BASE'];
1558     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1559     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1560     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1561     $tmp            = array(); 
1563     /* Fetch all objects with  gosaSnapshotDN=$dn */
1564     $ldap_to->cd($new_base);
1565     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1566         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1568     /* Put results into a list and add description if missing */
1569     while($entry = $ldap_to->fetch()){ 
1570       if(!isset($entry['description'][0])){
1571         $entry['description'][0]  = "";
1572       }
1573       $tmp[] = $entry; 
1574     }
1576     /* Return the raw array, or format the result */
1577     if($raw){
1578       return($tmp);
1579     }else{  
1580       $tmp2 = array();
1581       foreach($tmp as $entry){
1582         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1583       }
1584     }
1585     return($tmp2);
1586   }
1589   function getAllDeletedSnapshots($base_of_object,$raw = false)
1590   {
1591     if(!$this->snapshotEnabled()) return(array());
1593     /* Create an additional ldap object which
1594        points to our ldap snapshot server */
1595     $ldap= $this->config->get_ldap_link();
1596     $ldap->cd($this->config->current['BASE']);
1597     $cfg= &$this->config->current;
1599     /* check if there are special server configurations for snapshots */
1600     if($this->config->get_cfg_value("snapshotURI") == ""){
1601       $ldap_to      = $ldap;
1602     }else{
1603       $server         = $this->config->get_cfg_value("snapshotURI");
1604       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1605       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1606       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1607       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1608       $ldap_to -> cd($snapldapbase);
1609       if (!$ldap_to->success()){
1610         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1611       }
1612     }
1614     /* Prepare bases */ 
1615     $base           = $this->config->current['BASE'];
1616     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1617     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1619     /* Fetch all objects and check if they do not exist anymore */
1620     $ui = get_userinfo();
1621     $tmp = array();
1622     $ldap_to->cd($new_base);
1623     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1624     while($entry = $ldap_to->fetch()){
1626       $chk =  str_replace($new_base,"",$entry['dn']);
1627       if(preg_match("/,ou=/",$chk)) continue;
1629       if(!isset($entry['description'][0])){
1630         $entry['description'][0]  = "";
1631       }
1632       $tmp[] = $entry; 
1633     }
1635     /* Check if entry still exists */
1636     foreach($tmp as $key => $entry){
1637       $ldap->cat($entry['gosaSnapshotDN'][0]);
1638       if($ldap->count()){
1639         unset($tmp[$key]);
1640       }
1641     }
1643     /* Format result as requested */
1644     if($raw) {
1645       return($tmp);
1646     }else{
1647       $tmp2 = array();
1648       foreach($tmp as $key => $entry){
1649         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1650       }
1651     }
1652     return($tmp2);
1653   } 
1656   /* \brief Restore selected snapshot */
1657   function restore_snapshot($dn)
1658   {
1659     if(!$this->snapshotEnabled()) return(array());
1661     $ldap= $this->config->get_ldap_link();
1662     $ldap->cd($this->config->current['BASE']);
1663     $cfg= &$this->config->current;
1665     /* check if there are special server configurations for snapshots */
1666     if($this->config->get_cfg_value("snapshotURI") == ""){
1667       $ldap_to      = $ldap;
1668     }else{
1669       $server         = $this->config->get_cfg_value("snapshotURI");
1670       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1671       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1672       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1673       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1674       $ldap_to -> cd($snapldapbase);
1675       if (!$ldap_to->success()){
1676         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1677       }
1678     }
1680     /* Get the snapshot */ 
1681     $ldap_to->cat($dn);
1682     $restoreObject = $ldap_to->fetch();
1684     /* Prepare import string */
1685     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1687     /* Import the given data */
1688     $err = "";
1689     $ldap->import_complete_ldif($data,$err,false,false);
1690     if (!$ldap->success()){
1691       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1692     }
1693   }
1696   function showSnapshotDialog($base,$baseSuffixe,&$parent)
1697   {
1698     $once = true;
1699     $ui = get_userinfo();
1700     $this->parent = $parent;
1702     foreach($_POST as $name => $value){
1704       /* Create a new snapshot, display a dialog */
1705       if(preg_match("/^CreateSnapShotDialog_[^_]*_[xy]$/",$name) && $once){
1707                           $entry = base64_decode(preg_replace("/^CreateSnapShotDialog_([^_]*)_[xy]$/","\\1",$name));
1708         $once = false;
1709         $entry = preg_replace("/^CreateSnapShotDialog_/","",$entry);
1711         if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1712           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1713         }else{
1714           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1715         }
1716       }  
1717   
1718       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1719       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1720         $once = false;
1721         $entry = base64_decode(preg_replace("/^RestoreSnapShotDialog_([^_]*)_[xy]$/i","\\1",$name));
1722         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1723           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1724           $this->snapDialog->display_restore_dialog = true;
1725         }else{
1726           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1727         }
1728       }
1730       /* Restore one of the already deleted objects */
1731       if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot") 
1732           || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1733         $once = false;
1735         if($ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1736           $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1737           $this->snapDialog->set_snapshot_bases($baseSuffixe);
1738           $this->snapDialog->display_restore_dialog      = true;
1739           $this->snapDialog->display_all_removed_objects  = true;
1740         }else{
1741           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1742         }
1743       }
1745       /* Restore selected snapshot */
1746       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1747         $once = false;
1748         $entry = base64_decode(preg_replace("/^RestoreSnapShot_([^_]*)_[xy]$/i","\\1",$name));
1750         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1751           $this->restore_snapshot($entry);
1752           $this->snapDialog = NULL;
1753         }else{
1754           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1755         }
1756       }
1757     }
1759     /* Create a new snapshot requested, check
1760        the given attributes and create the snapshot*/
1761     if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1762       $this->snapDialog->save_object();
1763       $msgs = $this->snapDialog->check();
1764       if(count($msgs)){
1765         foreach($msgs as $msg){
1766           msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1767         }
1768       }else{
1769         $this->dn =  $this->snapDialog->dn;
1770         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1771         $this->snapDialog = NULL;
1772       }
1773     }
1775     /* Restore is requested, restore the object with the posted dn .*/
1776     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1777     }
1779     if(isset($_POST['CancelSnapshot'])){
1780       $this->snapDialog = NULL;
1781     }
1783     if(is_object($this->snapDialog )){
1784       $this->snapDialog->save_object();
1785       return($this->snapDialog->execute());
1786     }
1787   }
1790   /*! \brief Return plugin informations for acl handling */
1791   static function plInfo()
1792   {
1793     return array();
1794   }
1797   function set_acl_base($base)
1798   {
1799     $this->acl_base= $base;
1800   }
1803   function set_acl_category($category)
1804   {
1805     $this->acl_category= "$category/";
1806   }
1809   function acl_is_writeable($attribute,$skip_write = FALSE)
1810   {
1811     if($this->read_only) return(FALSE);
1812     $ui= get_userinfo();
1813     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1814   }
1817   function acl_is_readable($attribute)
1818   {
1819     $ui= get_userinfo();
1820     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1821   }
1824   function acl_is_createable($base ="")
1825   {
1826     if($this->read_only) return(FALSE);
1827     $ui= get_userinfo();
1828     if($base == "") $base = $this->acl_base;
1829     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1830   }
1833   function acl_is_removeable($base ="")
1834   {
1835     if($this->read_only) return(FALSE);
1836     $ui= get_userinfo();
1837     if($base == "") $base = $this->acl_base;
1838     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1839   }
1842   function acl_is_moveable($base = "")
1843   {
1844     if($this->read_only) return(FALSE);
1845     $ui= get_userinfo();
1846     if($base == "") $base = $this->acl_base;
1847     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1848   }
1851   function acl_have_any_permissions()
1852   {
1853   }
1856   function getacl($attribute,$skip_write= FALSE)
1857   {
1858     $ui= get_userinfo();
1859     $skip_write |= $this->read_only;
1860     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1861   }
1864   /*! \brief Returns a list of all available departments for this object.
1865    * 
1866    * If this object is new, all departments we are allowed to create a new user in
1867    * are returned. If this is an existing object, return all deps. 
1868    * We are allowed to move tis object too.
1869    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1870   */
1871   function get_allowed_bases()
1872   {
1873     $ui = get_userinfo();
1874     $deps = array();
1876     /* Is this a new object ? Or just an edited existing object */
1877     if(!$this->initially_was_account && $this->is_account){
1878       $new = true;
1879     }else{
1880       $new = false;
1881     }
1883     foreach($this->config->idepartments as $dn => $name){
1884       if($new && $this->acl_is_createable($dn)){
1885         $deps[$dn] = $name;
1886       }elseif(!$new && $this->acl_is_moveable($dn)){
1887         $deps[$dn] = $name;
1888       }
1889     }
1891     /* Add current base */      
1892     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1893       $deps[$this->base] = $this->config->idepartments[$this->base];
1894     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1896     }else{
1897       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1898     }
1899     return($deps);
1900   }
1903   /* This function updates ACL settings if $old_dn was used.
1904    *  \param string 'old_dn' specifies the actually used dn
1905    *  \param string 'new_dn' specifies the destiantion dn
1906    */
1907   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1908   {
1909     /* Check if old_dn is empty. This should never happen */
1910     if(empty($old_dn) || empty($new_dn)){
1911       trigger_error("Failed to check acl dependencies, wrong dn given.");
1912       return;
1913     }
1915     /* Update userinfo if necessary */
1916     $ui = session::global_get('ui');
1917     if($ui->dn == $old_dn){
1918       $ui->dn = $new_dn;
1919       session::global_set('ui',$ui);
1920       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1921     }
1923     /* Object was moved, ensure that all acls will be moved too */
1924     if($new_dn != $old_dn && $old_dn != "new"){
1926       /* get_ldap configuration */
1927       $update = array();
1928       $ldap = $this->config->get_ldap_link();
1929       $ldap->cd ($this->config->current['BASE']);
1930       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1931       while($attrs = $ldap->fetch()){
1932         $acls = array();
1933         $found = false;
1934         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1935           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1937           /* Roles uses antoher data storage order, members are stored int the third part, 
1938              while the members in direct ACL assignments are stored in the second part.
1939            */
1940           $id = ($acl_parts[1] == "role") ? 3 : 2;
1942           /* Update member entries to use $new_dn instead of old_dn
1943            */
1944           $members = explode(",",$acl_parts[$id]);
1945           foreach($members as $key => $member){
1946             $member = base64_decode($member);
1947             if($member == $old_dn){
1948               $members[$key] = base64_encode($new_dn);
1949               $found = TRUE;
1950             }
1951           } 
1953           /* Check if the selected role has to updated
1954            */
1955           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1956             $acl_parts[2] = base64_encode($new_dn);
1957             $found = TRUE;
1958           }
1960           /* Build new acl string */ 
1961           $acl_parts[$id] = implode($members,",");
1962           $acls[] = implode($acl_parts,":");
1963         }
1965         /* Acls for this object must be adjusted */
1966         if($found){
1968           $debug_info=  _("Changing ACL dn")."&nbsp;:&nbsp;<br>&nbsp;-"._("from")."&nbsp;<b>&nbsp;".
1969             $old_dn."</b><br>&nbsp;-"._("to")."&nbsp;<b>".$new_dn."</b><br>";
1970           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1972           $update[$attrs['dn']] =array();
1973           foreach($acls as $acl){
1974             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1975           }
1976         }
1977       }
1979       /* Write updated acls */
1980       foreach($update as $dn => $attrs){
1981         $ldap->cd($dn);
1982         $ldap->modify($attrs);
1983       }
1984     }
1985   }
1987   
1989   /*! \brief Enable the Serial ID check
1990    *
1991    * This function enables the entry Serial ID check.  If an entry was edited while
1992    * we have edited the entry too, an error message will be shown. 
1993    * To configure this check correctly read the FAQ.
1994    */    
1995   function enable_CSN_check()
1996   {
1997     $this->CSN_check_active =TRUE;
1998     $this->entryCSN = getEntryCSN($this->dn);
1999   }
2002   /*! \brief  Prepares the plugin to be used for multiple edit
2003    *          Update plugin attributes with given array of attribtues.
2004    *  \param  array   Array with attributes that must be updated.
2005    */
2006   function init_multiple_support($attrs,$all)
2007   {
2008     $ldap= $this->config->get_ldap_link();
2009     $this->multi_attrs    = $attrs;
2010     $this->multi_attrs_all= $all;
2012     /* Copy needed attributes */
2013     foreach ($this->attributes as $val){
2014       $found= array_key_ics($val, $this->multi_attrs);
2015  
2016       if ($found != ""){
2017         if(isset($this->multi_attrs["$val"][0])){
2018           $this->$val= $this->multi_attrs["$val"][0];
2019         }
2020       }
2021     }
2022   }
2024  
2025   /*! \brief  Enables multiple support for this plugin
2026    */
2027   function enable_multiple_support()
2028   {
2029     $this->ignore_account = TRUE;
2030     $this->multiple_support_active = TRUE;
2031   }
2034   /*! \brief  Returns all values that have been modfied in multiple edit mode.
2035       \return array Cotaining all modified values. 
2036    */
2037   function get_multi_edit_values()
2038   {
2039     $ret = array();
2040     foreach($this->attributes as $attr){
2041       if(in_array($attr,$this->multi_boxes)){
2042         $ret[$attr] = $this->$attr;
2043       }
2044     }
2045     return($ret);
2046   }
2048   
2049   /*! \brief  Update class variables with values collected by multiple edit.
2050    */
2051   function set_multi_edit_values($attrs)
2052   {
2053     foreach($attrs as $name => $value){
2054       $this->$name = $value;
2055     }
2056   }
2059   /*! \brief Generates the html output for this node for multi edit*/
2060   function multiple_execute()
2061   {
2062     /* This one is empty currently. Fabian - please fill in the docu code */
2063     session::global_set('current_class_for_help',get_class($this));
2065     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
2066     session::set('LOCK_VARS_TO_USE',array());
2067     session::set('LOCK_VARS_USED_GET',array());
2068     session::set('LOCK_VARS_USED_POST',array());
2069     session::set('LOCK_VARS_USED_REQUEST',array());
2070     
2071     return("Multiple edit is currently not implemented for this plugin.");
2072   }
2075   /*! \brief Save HTML posted data to object for multiple edit
2076    */
2077   function multiple_save_object()
2078   {
2079     if(empty($this->entryCSN) && $this->CSN_check_active){
2080       $this->entryCSN = getEntryCSN($this->dn);
2081     }
2083     /* Save values to object */
2084     $this->multi_boxes = array();
2085     foreach ($this->attributes as $val){
2086   
2087       /* Get selected checkboxes from multiple edit */
2088       if(isset($_POST["use_".$val])){
2089         $this->multi_boxes[] = $val;
2090       }
2092       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
2094         /* Check for modifications */
2095         if (get_magic_quotes_gpc()) {
2096           $data= stripcslashes($_POST["$val"]);
2097         } else {
2098           $data= $this->$val = $_POST["$val"];
2099         }
2100         if ($this->$val != $data){
2101           $this->is_modified= TRUE;
2102         }
2103     
2104         /* IE post fix */
2105         if(isset($data[0]) && $data[0] == chr(194)) {
2106           $data = "";  
2107         }
2108         $this->$val= $data;
2109       }
2110     }
2111   }
2114   /*! \brief Returns all attributes of this plugin, 
2115                to be able to detect multiple used attributes 
2116                in multi_plugg::detect_multiple_used_attributes().
2117       @return array Attributes required for intialization of multi_plug
2118    */
2119   public function get_multi_init_values()
2120   {
2121     $attrs = $this->attrs;
2122     return($attrs);
2123   }
2126   /*! \brief  Check given values in multiple edit
2127       \return array Error messages
2128    */
2129   function multiple_check()
2130   {
2131     $message = plugin::check();
2132     return($message);
2133   }
2136   /*! \brief  Returns the snapshot header part for "Actions" menu in management dialogs 
2137       \param  $layer_menu  
2138    */   
2139   function get_snapshot_header($base,$category)
2140   {
2141     $str = "";
2142     $ui = get_userinfo();
2143     if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
2145       $ok = false;
2146       foreach($this->get_used_snapshot_bases() as $base){
2147         $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
2148       }
2150       if($ok){
2151         $str = "..|<img class='center' src='images/lists/restore.png' ".
2152           "alt='"._("Restore")."'>&nbsp;"._("Restore").                       "|RestoreDeletedSnapShot|\n";
2153       }else{
2154         $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;"._("Restore")."||\n";
2155       }
2156     }
2157     return($str);
2158   }
2161   function get_snapshot_action($base,$category)
2162   {
2163     $str= ""; 
2164     $ui = get_userinfo();
2165     if($this->snapshotEnabled()){
2166       if ($ui->allow_snapshot_restore($base,$category)){
2168         if(count($this->Available_SnapsShots($base))){
2169           $str.= "<input class='center' type='image' src='images/lists/restore.png'
2170             alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'>&nbsp;";
2171         } else {
2172           $str = "<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;";
2173         }
2174       }
2175       if($ui->allow_snapshot_create($base,$category)){
2176         $str.= "<input class='center' type='image' src='images/snapshot.png'
2177           alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."' 
2178           title='"._("Create a new snapshot from this object")."'>&nbsp;";
2179       }else{
2180         $str = "<img class='center' src='images/empty.png' alt=' '>&nbsp;";
2181       }
2182     }
2184     return($str);
2185   }
2188   function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2189   {
2190     $ui = get_userinfo();
2191     $action = "";
2192     if($this->CopyPasteHandler){
2193       if($cut){
2194         if($ui->is_cutable($base,$category,$class)){
2195           $action .= "<input class='center' type='image'
2196             src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'>&nbsp;";
2197         }else{
2198           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2199         }
2200       }
2201       if($copy){
2202         if($ui->is_copyable($base,$category,$class)){
2203           $action.= "<input class='center' type='image'
2204             src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'>&nbsp;";
2205         }else{
2206           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2207         }
2208       }
2209     }
2211     return($action); 
2212   }
2215   function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2216   {
2217     $s = "";
2218     $ui =get_userinfo();
2220     if(!is_array($category)){
2221       $category = array($category);
2222     }
2224     /* Check permissions for each category, if there is at least one category which 
2225         support read or paste permissions for the given base, then display the specific actions.
2226      */
2227     $readable = $pasteable = false;
2228     foreach($category as $cat){
2229       $readable= $readable || preg_match('/r/', $ui->get_category_permissions($base, $cat));
2230       $pasteable= $pasteable || $ui->is_pasteable($base, $cat) == 1;
2231     }
2232   
2233     if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2234       if($readable){
2235         $s.= "..|---|\n";
2236         if($copy){
2237           $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2238             "&nbsp;"._("Copy")."|"."multiple_copy_systems|\n";
2239         }
2240         if($cut){
2241           $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2242             "&nbsp;"._("Cut")."|"."multiple_cut_systems|\n";
2243         }
2244       }
2246       if($pasteable){
2247         if($this->CopyPasteHandler->entries_queued()){
2248           $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2249           $s.="..|".$img."&nbsp;"._("Paste")."|editPaste|\n";
2250         }else{
2251           $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2252           $s.="..|".$img."&nbsp;"._("Paste")."\n";
2253         }
2254       }
2255     }
2256     return($s);
2257   }
2260   function get_used_snapshot_bases()
2261   {
2262      return(array());
2263   }
2265   function is_modal_dialog()
2266   {
2267     return(isset($this->dialog) && $this->dialog);
2268   }
2271 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2272 ?>