Code

Enabled modal dialogs
[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, $parent= NULL)
143   {
144     /* Configuration is fine, allways */
145     $this->config= &$config;    
146     $this->dn= $dn;
148     /* Handle new accounts, don't read information from LDAP */
149     if ($dn == "new"){
150       return;
151     }
153     if(isset($_POST['open_readonly'])){
154       $this->read_only = TRUE;
155     }
157     /* Save current dn as acl_base */
158     $this->acl_base= $dn;
160     /* Get LDAP descriptor */
161     if ($dn !== NULL){
163       /* Load data to 'attrs' and save 'dn' */
164       if ($parent !== NULL){
165         $this->attrs= $parent->attrs;
166       } else {
167         $ldap= $this->config->get_ldap_link();
168         $ldap->cat ($dn);
169         $this->attrs= $ldap->fetch();
170       }
172       /* Copy needed attributes */
173       foreach ($this->attributes as $val){
174         $found= array_key_ics($val, $this->attrs);
175         if ($found != ""){
176           $this->$val= $found[0];
177         }
178       }
180       /* gosaUnitTag loading... */
181       if (isset($this->attrs['gosaUnitTag'][0])){
182         $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
183       }
185       /* Set the template flag according to the existence of objectClass
186          gosaUserTemplate */
187       if (isset($this->attrs['objectClass'])){
188         if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
189           $this->is_template= TRUE;
190           @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
191               "found", "Template check");
192         }
193       }
195       /* Is Account? */
196       $found= TRUE;
197       foreach ($this->objectclasses as $obj){
198         if (preg_match('/top/i', $obj)){
199           continue;
200         }
201         if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
202           $found= FALSE;
203           break;
204         }
205       }
206       if ($found){
207         $this->is_account= TRUE;
208         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
209             "found", "Object check");
210       }
212       /* Prepare saved attributes */
213       $this->saved_attributes= $this->attrs;
214       foreach ($this->saved_attributes as $index => $value){
215         if (is_numeric($index)){
216           unset($this->saved_attributes[$index]);
217           continue;
218         }
220         if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
221           unset($this->saved_attributes[$index]);
222           continue;
223         }
225         if (isset($this->saved_attributes[$index][0])){
226           if(!isset($this->saved_attributes[$index]["count"])){
227             $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
228           }
229           if($this->saved_attributes[$index]["count"] == 1){
230             $tmp= $this->saved_attributes[$index][0];
231             unset($this->saved_attributes[$index]);
232             $this->saved_attributes[$index]= $tmp;
233             continue;
234           }
235         }
236         unset($this->saved_attributes["$index"]["count"]);
237       }
239       if(isset($this->attrs['gosaUnitTag'])){
240         $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
241       }
242     }
244     /* Save initial account state */
245     $this->initially_was_account= $this->is_account;
246   }
249   /*! \brief execute plugin
251     Generates the html output for this node
252    */
253   function execute()
254   {
255     /* This one is empty currently. Fabian - please fill in the docu code */
256     session::set('current_class_for_help',get_class($this));
258     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
259     session::set('LOCK_VARS_TO_USE',array());
260     session::set('LOCK_VARS_USED',array());
261   }
263   /*! \brief execute plugin
264      Removes object from parent
265    */
266   function remove_from_parent()
267   {
268     /* include global link_info */
269     $ldap= $this->config->get_ldap_link();
271     /* Get current objectClasses in order to add the required ones */
272     $ldap->cat($this->dn);
273     $tmp= $ldap->fetch ();
274     $oc= array();
275     if (isset($tmp['objectClass'])){
276       $oc= $tmp['objectClass'];
277       unset($oc['count']);
278     }
280     /* Remove objectClasses from entry */
281     $ldap->cd($this->dn);
282     $this->attrs= array();
283     $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
285     /* Unset attributes from entry */
286     foreach ($this->attributes as $val){
287       $this->attrs["$val"]= array();
288     }
290     /* Unset account info */
291     $this->is_account= FALSE;
293     /* Do not write in plugin base class, this must be done by
294        children, since there are normally additional attribs,
295        lists, etc. */
296     /*
297        $ldap->modify($this->attrs);
298      */
299   }
302   /*! \brief   Save HTML posted data to object 
303    */
304   function save_object()
305   {
306     /* Update entry CSN if it is empty. */
307     if(empty($this->entryCSN) && $this->CSN_check_active){
308       $this->entryCSN = getEntryCSN($this->dn);
309     }
311     /* Save values to object */
312     foreach ($this->attributes as $val){
313       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
314         /* Check for modifications */
315         if (get_magic_quotes_gpc()) {
316           $data= stripcslashes($_POST["$val"]);
317         } else {
318           $data= $this->$val = $_POST["$val"];
319         }
320         if ($this->$val != $data){
321           $this->is_modified= TRUE;
322         }
323     
324         /* Okay, how can I explain this fix ... 
325          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
326          * So IE posts these 'unselectable' option, with value = chr(194) 
327          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
328          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
329          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
330          */
331         if(isset($data[0]) && $data[0] == chr(194)) {
332           $data = "";  
333         }
334         $this->$val= $data;
335       }
336     }
337   }
340   /* Save data to LDAP, depending on is_account we save or delete */
341   function save()
342   {
343     /* include global link_info */
344     $ldap= $this->config->get_ldap_link();
346     /* Save all plugins */
347     $this->entryCSN = "";
349     /* Start with empty array */
350     $this->attrs= array();
352     /* Get current objectClasses in order to add the required ones */
353     $ldap->cat($this->dn);
354     
355     $tmp= $ldap->fetch ();
357     $oc= array();
358     if (isset($tmp['objectClass'])){
359       $oc= $tmp["objectClass"];
360       $this->is_new= FALSE;
361       unset($oc['count']);
362     } else {
363       $this->is_new= TRUE;
364     }
366     /* Load (minimum) attributes, add missing ones */
367     $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
369     /* Copy standard attributes */
370     foreach ($this->attributes as $val){
371       if ($this->$val != ""){
372         $this->attrs["$val"]= $this->$val;
373       } elseif (!$this->is_new) {
374         $this->attrs["$val"]= array();
375       }
376     }
378     /* Handle tagging */
379     $this->tag_attrs($this->attrs);
380   }
383   function cleanup()
384   {
385     foreach ($this->attrs as $index => $value){
386       
387       /* Convert arrays with one element to non arrays, if the saved
388          attributes are no array, too */
389       if (is_array($this->attrs[$index]) && 
390           count ($this->attrs[$index]) == 1 &&
391           isset($this->saved_attributes[$index]) &&
392           !is_array($this->saved_attributes[$index])){
393           
394         $tmp= $this->attrs[$index][0];
395         $this->attrs[$index]= $tmp;
396       }
398       /* Remove emtpy arrays if they do not differ */
399       if (is_array($this->attrs[$index]) &&
400           count($this->attrs[$index]) == 0 &&
401           !isset($this->saved_attributes[$index])){
402           
403         unset ($this->attrs[$index]);
404         continue;
405       }
407       /* Remove single attributes that do not differ */
408       if (!is_array($this->attrs[$index]) &&
409           isset($this->saved_attributes[$index]) &&
410           !is_array($this->saved_attributes[$index]) &&
411           $this->attrs[$index] == $this->saved_attributes[$index]){
413         unset ($this->attrs[$index]);
414         continue;
415       }
417       /* Remove arrays that do not differ */
418       if (is_array($this->attrs[$index]) && 
419           isset($this->saved_attributes[$index]) &&
420           is_array($this->saved_attributes[$index])){
421           
422         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
423           unset ($this->attrs[$index]);
424           continue;
425         }
426       }
427     }
429     /* Update saved attributes and ensure that next cleanups will be successful too */
430     foreach($this->attrs as $name => $value){
431       $this->saved_attributes[$name] = $value;
432     }
433   }
435   /* Check formular input */
436   function check()
437   {
438     $message= array();
440     /* Skip if we've no config object */
441     if (!isset($this->config) || !is_object($this->config)){
442       return $message;
443     }
445     /* Find hooks entries for this class */
446     $command= $this->config->search(get_class($this), "CHECK", array('menu', 'tabs'));
448     if ($command != ""){
450       if (!check_command($command)){
451         $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
452       } else {
454         /* Generate "ldif" for check hook */
455         $ldif= "dn: $this->dn\n";
456         
457         /* ... objectClasses */
458         foreach ($this->objectclasses as $oc){
459           $ldif.= "objectClass: $oc\n";
460         }
461         
462         /* ... attributes */
463         foreach ($this->attributes as $attr){
464           if ($this->$attr == ""){
465             continue;
466           }
467           if (is_array($this->$attr)){
468             foreach ($this->$attr as $val){
469               $ldif.= "$attr: $val\n";
470             }
471           } else {
472               $ldif.= "$attr: ".$this->$attr."\n";
473           }
474         }
476         /* Append empty line */
477         $ldif.= "\n";
479         /* Feed "ldif" into hook and retrieve result*/
480         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
481         $fh= proc_open($command, $descriptorspec, $pipes);
482         if (is_resource($fh)) {
483           fwrite ($pipes[0], $ldif);
484           fclose($pipes[0]);
485           
486           $result= stream_get_contents($pipes[1]);
487           if ($result != ""){
488             $message[]= $result;
489           }
490           
491           fclose($pipes[1]);
492           fclose($pipes[2]);
493           proc_close($fh);
494         }
495       }
497     }
499     /* Check entryCSN */
500     if($this->CSN_check_active){
501       $current_csn = getEntryCSN($this->dn);
502       if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
503         $this->entryCSN = $current_csn;
504         $message[] = _("The object has changed since opened in GOsa. All changes that may be done by others get lost if you save this entry!");
505       }
506     }
507     return ($message);
508   }
510   /* Adapt from template, using 'dn' */
511   function adapt_from_template($dn, $skip= array())
512   {
513     /* Include global link_info */
514     $ldap= $this->config->get_ldap_link();
516     /* Load requested 'dn' to 'attrs' */
517     $ldap->cat ($dn);
518     $this->attrs= $ldap->fetch();
520     /* Walk through attributes */
521     foreach ($this->attributes as $val){
523       /* Skip the ones in skip list */
524       if (in_array($val, $skip)){
525         continue;
526       }
528       if (isset($this->attrs["$val"][0])){
530         /* If attribute is set, replace dynamic parts: 
531            %sn, %givenName and %uid. Fill these in our local variables. */
532         $value= $this->attrs["$val"][0];
534         foreach (array("sn", "givenName", "uid") as $repl){
535           if (preg_match("/%$repl/i", $value)){
536             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
537           }
538         }
539         $this->$val= $value;
540       }
541     }
543     /* Is Account? */
544     $found= TRUE;
545     foreach ($this->objectclasses as $obj){
546       if (preg_match('/top/i', $obj)){
547         continue;
548       }
549       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
550         $found= FALSE;
551         break;
552       }
553     }
554     if ($found){
555       $this->is_account= TRUE;
556     }
557   }
559   /* Indicate whether a password change is needed or not */
560   function password_change_needed()
561   {
562     return FALSE;
563   }
566   /* Show header message for tab dialogs */
567   function show_enable_header($button_text, $text, $disabled= FALSE)
568   {
569     if (($disabled == TRUE) || (!$this->acl_is_createable())){
570       $state= "disabled";
571     } else {
572       $state= "";
573     }
574     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
575     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
576       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
578     return($display);
579   }
582   /* Show header message for tab dialogs */
583   function show_disable_header($button_text, $text, $disabled= FALSE)
584   {
585     if (($disabled == TRUE) || !$this->acl_is_removeable()){
586       $state= "disabled";
587     } else {
588       $state= "";
589     }
590     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
591     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
592       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
594     return($display);
595   }
598   /* Show header message for tab dialogs */
599   function show_header($button_text, $text, $disabled= FALSE)
600   {
601     echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
602     if ($disabled == TRUE){
603       $state= "disabled";
604     } else {
605       $state= "";
606     }
607     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
608     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
609       ($this->acl_is_createable()?'':'disabled')." ".$state.
610       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
612     return($display);
613   }
616   function postcreate($add_attrs= array())
617   {
618     /* Find postcreate entries for this class */
619     $command= $this->config->search(get_class($this), "POSTCREATE",array('menu', 'tabs'));
621     if ($command != ""){
623       /* Walk through attribute list */
624       foreach ($this->attributes as $attr){
625         if (!is_array($this->$attr)){
626           $add_attrs[$attr] = $this->$attr;
627         }
628       }
629       $add_attrs['dn']=$this->dn;
631       $tmp = array();
632       foreach($add_attrs as $name => $value){
633         $tmp[$name] =  strlen($name);
634       }
635       arsort($tmp);
636       
637       /* Additional attributes */
638       foreach ($tmp as $name => $len){
639         $value = $add_attrs[$name];
640         $command= str_replace("%$name", "$value", $command);
641       }
643       if (check_command($command)){
644         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
645             $command, "Execute");
646         exec($command,$arr);
647         foreach($arr as $str){
648           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
649             $command, "Result: ".$str);
650         }
651       } else {
652         $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
653         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
654       }
655     }
656   }
658   function postmodify($add_attrs= array())
659   {
660     /* Find postcreate entries for this class */
661     $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
663     if ($command != ""){
665       /* Walk through attribute list */
666       foreach ($this->attributes as $attr){
667         if (!is_array($this->$attr)){
668           $add_attrs[$attr] = $this->$attr;
669         }
670       }
671       $add_attrs['dn']=$this->dn;
673       $tmp = array();
674       foreach($add_attrs as $name => $value){
675         $tmp[$name] =  strlen($name);
676       }
677       arsort($tmp);
678       
679       /* Additional attributes */
680       foreach ($tmp as $name => $len){
681         $value = $add_attrs[$name];
682         $command= str_replace("%$name", "$value", $command);
683       }
685       if (check_command($command)){
686         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
687         exec($command,$arr);
688         foreach($arr as $str){
689           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
690             $command, "Result: ".$str);
691         }
692       } else {
693         $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
694         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
695       }
696     }
697   }
699   function postremove($add_attrs= array())
700   {
701     /* Find postremove entries for this class */
702     $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
703     if ($command != ""){
705       /* Walk through attribute list */
706       foreach ($this->attributes as $attr){
707         if (!is_array($this->$attr)){
708           $add_attrs[$attr] = $this->$attr;
709         }
710       }
711       $add_attrs['dn']=$this->dn;
713       $tmp = array();
714       foreach($add_attrs as $name => $value){
715         $tmp[$name] =  strlen($name);
716       }
717       arsort($tmp);
718       
719       /* Additional attributes */
720       foreach ($tmp as $name => $len){
721         $value = $add_attrs[$name];
722         $command= str_replace("%$name", "$value", $command);
723       }
725       if (check_command($command)){
726         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
727             $command, "Execute");
729         exec($command,$arr);
730         foreach($arr as $str){
731           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
732             $command, "Result: ".$str);
733         }
734       } else {
735         $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
736         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
737       }
738     }
739   }
741   /* Create unique DN */
742   function create_unique_dn($attribute, $base)
743   {
744     $ldap= $this->config->get_ldap_link();
745     $base= preg_replace("/^,*/", "", $base);
747     /* Try to use plain entry first */
748     $dn= "$attribute=".$this->$attribute.",$base";
749     $ldap->cat ($dn, array('dn'));
750     if (!$ldap->fetch()){
751       return ($dn);
752     }
754     /* Look for additional attributes */
755     foreach ($this->attributes as $attr){
756       if ($attr == $attribute || $this->$attr == ""){
757         continue;
758       }
760       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
761       $ldap->cat ($dn, array('dn'));
762       if (!$ldap->fetch()){
763         return ($dn);
764       }
765     }
767     /* None found */
768     return ("none");
769   }
771   function rebind($ldap, $referral)
772   {
773     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
774     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
775       $this->error = "Success";
776       $this->hascon=true;
777       $this->reconnect= true;
778       return (0);
779     } else {
780       $this->error = "Could not bind to " . $credentials['ADMIN'];
781       return NULL;
782     }
783   }
786   /* Recursively copy ldap object */
787   function _copy($src_dn,$dst_dn)
788   {
789     $ldap=$this->config->get_ldap_link();
790     $ldap->cat($src_dn);
791     $attrs= $ldap->fetch();
793     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
794     $ds= ldap_connect($this->config->current['SERVER']);
795     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
796     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
797       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
798     }
800     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $this->config->current['ADMINPASSWORD']);
801     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
803     /* Fill data from LDAP */
804     $new= array();
805     if ($sr) {
806       $ei=ldap_first_entry($ds, $sr);
807       if ($ei) {
808         foreach($attrs as $attr => $val){
809           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
810             for ($i= 0; $i<$info['count']; $i++){
811               if ($info['count'] == 1){
812                 $new[$attr]= $info[$i];
813               } else {
814                 $new[$attr][]= $info[$i];
815               }
816             }
817           }
818         }
819       }
820     }
822     /* close conncetion */
823     ldap_unbind($ds);
825     /* Adapt naming attribute */
826     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
827     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
828     $new[$dst_name]= LDAP::fix($dst_val);
830     /* Check if this is a department.
831      * If it is a dep. && there is a , override in his ou 
832      *  change \2C to , again, else this entry can't be saved ...
833      */
834     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
835       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
836     }
838     /* Save copy */
839     $ldap->connect();
840     $ldap->cd($this->config->current['BASE']);
841     
842     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
844     /* FAIvariable=.../..., cn=.. 
845         could not be saved, because the attribute FAIvariable was different to 
846         the dn FAIvariable=..., cn=... */
847     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
848       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
849     }
850     $ldap->cd($dst_dn);
851     $ldap->add($new);
853     if (!$ldap->success()){
854       trigger_error("Trying to save $dst_dn failed.",
855           E_USER_WARNING);
856       return(FALSE);
857     }
858     return(TRUE);
859   }
862   /* This is a workaround function. */
863   function copy($src_dn, $dst_dn)
864   {
865     /* Rename dn in possible object groups */
866     $ldap= $this->config->get_ldap_link();
867     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
868         array('cn'));
869     while ($attrs= $ldap->fetch()){
870       $og= new ogroup($this->config, $ldap->getDN());
871       unset($og->member[$src_dn]);
872       $og->member[$dst_dn]= $dst_dn;
873       $og->save ();
874     }
876     $ldap->cat($dst_dn);
877     $attrs= $ldap->fetch();
878     if (count($attrs)){
879       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
880           E_USER_WARNING);
881       return (FALSE);
882     }
884     $ldap->cat($src_dn);
885     $attrs= $ldap->fetch();
886     if (!count($attrs)){
887       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
888           E_USER_WARNING);
889       return (FALSE);
890     }
892     $ldap->cd($src_dn);
893     $ldap->search("objectClass=*",array("dn"));
894     while($attrs = $ldap->fetch()){
895       $src = $attrs['dn'];
896       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
897       $this->_copy($src,$dst);
898     }
899     return (TRUE);
900   }
904   /*! \brief  Move a given ldap object indentified by $src_dn   \
905                to the given destination $dst_dn   \
906               * Ensure that all references are updated (ogroups) \
907               * Update ACLs   \
908               * Update accessTo   \
909       @param  String  The source dn.
910       @param  String  The destination dn.
911       @return Boolean TRUE on success else FALSE.
912    */
913   function rename($src_dn, $dst_dn)
914   {
915     $start = microtime(1);
917     /* Try to move the source entry to the destination position */
918     $ldap = $this->config->get_ldap_link();
919     $ldap->cd($this->config->current['BASE']);
920     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
921     if (!$ldap->rename_dn($src_dn,$dst_dn)){
922       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
923       return(FALSE);
924     }
926     /* Get list of users,groups and roles within this tree,
927         maybe we have to update ACL references.
928      */
929     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
930           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
931     foreach($leaf_objs as $obj){
932       $new_dn = $obj['dn'];
933       $old_dn = preg_replace("/".preg_quote($dst_dn, '/')."$/i",$src_dn,$new_dn);
934       $this->update_acls($old_dn,$new_dn); 
935     }
937     /* Get all objectGroups defined in this database. 
938         and check if there is an entry matching the source dn,
939         if this is the case, then update this objectgroup to use the new dn.
940      */
941     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=*))","ogroups",
942         array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("member"),
943         GL_SUBSEARCH | GL_NO_ACL_CHECK) ;
945     /* Walk through all objectGroups and check if there are 
946         members matching the source dn 
947      */
948     foreach($ogroups as $ogroup){
949       if(isset($ogroup['member'])){
951         /* Reset class object, this will be initialized with class_ogroup on demand 
952          */
953         $o_ogroup = NULL; 
954         for($i = 0 ; $i < $ogroup['member']['count'] ; $i ++){
956           $c_mem = $ogroup['member'][$i];
957   
958           if(preg_match("/".preg_quote($src_dn, '/')."$/i",$c_mem)){
959  
960             $d_mem = preg_replace("/".preg_quote($src_dn, '/')."$/i",$dst_dn,$ogroup['member'][$i]);
962             if($o_ogroup == NULL){
963               $o_ogroup = new ogroup($this->config,$ogroup['dn']);
964             }              
966             unset($o_ogroup->member[$c_mem]);
967             $o_ogroup->member[$d_mem]= $d_mem;
968           }
969         }
970        
971         /* Save object group if there were changes made on the membership */ 
972         if($o_ogroup != NULL){
973           $o_ogroup->save();
974         }
975       }
976     }
977  
978     /* Check if there are gosa departments moved. 
979        If there were deps moved, the force reload of config->deps.
980      */
981     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
982           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
983   
984     if(count($leaf_deps)){
985       $this->config->get_departments();
986       $this->config->make_idepartments();
987       session::set("config",$this->config);
988       $ui =get_userinfo();
989       $ui->reset_acl_cache();
990     }
992     return(TRUE); 
993   }
997   function move($src_dn, $dst_dn)
998   {
999     /* Do not copy if only upper- lowercase has changed */
1000     if(strtolower($src_dn) == strtolower($dst_dn)){
1001       return(TRUE);
1002     }
1004     
1005     /* Try to move the entry instead of copy & delete
1006      */
1007     if(TRUE){
1009       /* Try to move with ldap routines, if this was not successfull
1010           fall back to the old style copy & remove method 
1011        */
1012       if($this->rename($src_dn, $dst_dn)){
1013         return(TRUE);
1014       }else{
1015         // See code below.
1016       }
1017     }
1019     /* Copy source to destination */
1020     if (!$this->copy($src_dn, $dst_dn)){
1021       return (FALSE);
1022     }
1024     /* Delete source */
1025     $ldap= $this->config->get_ldap_link();
1026     $ldap->rmdir_recursive($src_dn);
1027     if (!$ldap->success()){
1028       trigger_error("Trying to delete $src_dn failed.",
1029           E_USER_WARNING);
1030       return (FALSE);
1031     }
1033     return (TRUE);
1034   }
1037   /* Move/Rename complete trees */
1038   function recursive_move($src_dn, $dst_dn)
1039   {
1040     /* Check if the destination entry exists */
1041     $ldap= $this->config->get_ldap_link();
1043     /* Check if destination exists - abort */
1044     $ldap->cat($dst_dn, array('dn'));
1045     if ($ldap->fetch()){
1046       trigger_error("recursive_move $dst_dn already exists.",
1047           E_USER_WARNING);
1048       return (FALSE);
1049     }
1051     $this->copy($src_dn, $dst_dn);
1053     /* Remove src_dn */
1054     $ldap->cd($src_dn);
1055     $ldap->recursive_remove($src_dn);
1056     return (TRUE);
1057   }
1060   function handle_post_events($mode, $add_attrs= array())
1061   {
1062     switch ($mode){
1063       case "add":
1064         $this->postcreate($add_attrs);
1065       break;
1067       case "modify":
1068         $this->postmodify($add_attrs);
1069       break;
1071       case "remove":
1072         $this->postremove($add_attrs);
1073       break;
1074     }
1075   }
1078   function saveCopyDialog(){
1079   }
1082   function getCopyDialog(){
1083     return(array("string"=>"","status"=>""));
1084   }
1087   function PrepareForCopyPaste($source)
1088   {
1089     $todo = $this->attributes;
1090     if(isset($this->CopyPasteVars)){
1091       $todo = array_merge($todo,$this->CopyPasteVars);
1092     }
1094     if(count($this->objectclasses)){
1095       $this->is_account = TRUE;
1096       foreach($this->objectclasses as $class){
1097         if(!in_array($class,$source['objectClass'])){
1098           $this->is_account = FALSE;
1099         }
1100       }
1101     }
1103     foreach($todo as $var){
1104       if (isset($source[$var])){
1105         if(isset($source[$var]['count'])){
1106           if($source[$var]['count'] > 1){
1107             $this->$var = array();
1108             $tmp = array();
1109             for($i = 0 ; $i < $source[$var]['count']; $i++){
1110               $tmp = $source[$var][$i];
1111             }
1112             $this->$var = $tmp;
1113           }else{
1114             $this->$var = $source[$var][0];
1115           }
1116         }else{
1117           $this->$var= $source[$var];
1118         }
1119       }
1120     }
1121   }
1123   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1124   {
1125     /* Skip tagging? 
1126        If this is called from departmentGeneric, we have to skip this
1127         tagging procedure. 
1128      */
1129     if($this->skipTagging){
1130       return;
1131     }
1133     /* No dn? Self-operation... */
1134     if ($dn == ""){
1135       $dn= $this->dn;
1137       /* No tag? Find it yourself... */
1138       if ($tag == ""){
1139         $len= strlen($dn);
1141         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1142         $relevant= array();
1143         foreach ($this->config->adepartments as $key => $ntag){
1145           /* This one is bigger than our dn, its not relevant... */
1146           if ($len < strlen($key)){
1147             continue;
1148           }
1150           /* This one matches with the latter part. Break and don't fix this entry */
1151           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1152             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1153             $relevant[strlen($key)]= $ntag;
1154             continue;
1155           }
1157         }
1159         /* If we've some relevant tags to set, just get the longest one */
1160         if (count($relevant)){
1161           ksort($relevant);
1162           $tmp= array_keys($relevant);
1163           $idx= end($tmp);
1164           $tag= $relevant[$idx];
1165           $this->gosaUnitTag= $tag;
1166         }
1167       }
1168     }
1169   
1170     /* Remove tags that may already be here... */
1171     remove_objectClass("gosaAdministrativeUnitTag", $at);
1172     if (isset($at['gosaUnitTag'])){
1173         unset($at['gosaUnitTag']);
1174     }
1176     /* Set tag? */
1177     if ($tag != ""){
1178       add_objectClass("gosaAdministrativeUnitTag", $at);
1179       $at['gosaUnitTag']= $tag;
1180     }
1182     /* Initially this object was tagged. 
1183        - But now, it is no longer inside a tagged department. 
1184        So force the remove of the tag.
1185        (objectClass was already removed obove)
1186      */
1187     if($tag == "" && $this->gosaUnitTag){
1188       $at['gosaUnitTag'] = array();
1189     }
1190   }
1193   /* Add possibility to stop remove process */
1194   function allow_remove()
1195   {
1196     $reason= "";
1197     return $reason;
1198   }
1201   /* Create a snapshot of the current object */
1202   function create_snapshot($type= "snapshot", $description= array())
1203   {
1205     /* Check if snapshot functionality is enabled */
1206     if(!$this->snapshotEnabled()){
1207       return;
1208     }
1210     /* Get configuration from gosa.conf */
1211     $config = $this->config;
1213     /* Create lokal ldap connection */
1214     $ldap= $this->config->get_ldap_link();
1215     $ldap->cd($this->config->current['BASE']);
1217     /* check if there are special server configurations for snapshots */
1218     if($config->get_cfg_value("snapshotURI") == ""){
1220       /* Source and destination server are both the same, just copy source to dest obj */
1221       $ldap_to      = $ldap;
1222       $snapldapbase = $this->config->current['BASE'];
1224     }else{
1225       $server         = $config->get_cfg_value("snapshotURI");
1226       $user           = $config->get_cfg_value("snapshotAdminDn");
1227       $password       = $config->get_cfg_value("snapshotAdminPassword");
1228       $snapldapbase   = $config->get_cfg_value("snapshotBase");
1230       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1231       $ldap_to -> cd($snapldapbase);
1233       if (!$ldap_to->success()){
1234         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1235       }
1237     }
1239     /* check if the dn exists */ 
1240     if ($ldap->dn_exists($this->dn)){
1242       /* Extract seconds & mysecs, they are used as entry index */
1243       list($usec, $sec)= explode(" ", microtime());
1245       /* Collect some infos */
1246       $base           = $this->config->current['BASE'];
1247       $snap_base      = $config->get_cfg_value("snapshotBase");
1248       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1249       $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1251       /* Create object */
1252 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1253       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1254       $newName          = str_replace(".", "", $sec."-".$usec);
1255       $target= array();
1256       $target['objectClass']            = array("top", "gosaSnapshotObject");
1257       $target['gosaSnapshotData']       = gzcompress($data, 6);
1258       $target['gosaSnapshotType']       = $type;
1259       $target['gosaSnapshotDN']         = $this->dn;
1260       $target['description']            = $description;
1261       $target['gosaSnapshotTimestamp']  = $newName;
1263       /* Insert the new snapshot 
1264          But we have to check first, if the given gosaSnapshotTimestamp
1265          is already used, in this case we should increment this value till there is 
1266          an unused value. */ 
1267       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1268       $ldap_to->cat($new_dn);
1269       while($ldap_to->count()){
1270         $ldap_to->cat($new_dn);
1271         $newName = str_replace(".", "", $sec."-".($usec++));
1272         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1273         $target['gosaSnapshotTimestamp']  = $newName;
1274       } 
1276       /* Inset this new snapshot */
1277       $ldap_to->cd($snapldapbase);
1278       $ldap_to->create_missing_trees($snapldapbase);
1279       $ldap_to->create_missing_trees($new_base);
1280       $ldap_to->cd($new_dn);
1281       $ldap_to->add($target);
1282       if (!$ldap_to->success()){
1283         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1284       }
1286       if (!$ldap->success()){
1287         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1288       }
1290     }
1291   }
1293   function remove_snapshot($dn)
1294   {
1295     $ui       = get_userinfo();
1296     $old_dn   = $this->dn; 
1297     $this->dn = $dn;
1298     $ldap = $this->config->get_ldap_link();
1299     $ldap->cd($this->config->current['BASE']);
1300     $ldap->rmdir_recursive($dn);
1301     $this->dn = $old_dn;
1302   }
1305   /* returns true if snapshots are enabled, and false if it is disalbed
1306      There will also be some errors psoted, if the configuration failed */
1307   function snapshotEnabled()
1308   {
1309     $config = $this->config;
1310     if($config->get_cfg_value("enableSnapshots") == "true"){
1311             /* Check if the snapshot_base is defined */
1312             if ($config->get_cfg_value("snapshotBase") == ""){
1313                     msg_dialog::display(_("Configuration error"), sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not set."),"snapshotBase"), ERROR_DIALOG);
1314                     return(FALSE);
1315             }
1317             /* check if there are special server configurations for snapshots */
1318             if ($config->get_cfg_value("snapshotURI") != ""){
1320                     /* check if all required vars are available to create a new ldap connection */
1321                     $missing = "";
1322                     foreach(array("snapshotURI","snapshotAdminDn","snapshotAdminPassword","snapshotBase") as $var){
1323                             if($config->get_cfg_value($var) == ""){
1324                                     $missing .= $var." ";
1325                                     msg_dialog::display(_("Configuration error"), sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not set."), $missing), ERROR_DIALOG);
1326                                     return(FALSE);
1327                             }
1328                     }
1329             }
1330             return(TRUE);
1331     }
1332     return(FALSE);
1333   }
1336   /* Return available snapshots for the given base 
1337    */
1338   function Available_SnapsShots($dn,$raw = false)
1339   {
1340     if(!$this->snapshotEnabled()) return(array());
1342     /* Create an additional ldap object which
1343        points to our ldap snapshot server */
1344     $ldap= $this->config->get_ldap_link();
1345     $ldap->cd($this->config->current['BASE']);
1346     $cfg= &$this->config->current;
1348     /* check if there are special server configurations for snapshots */
1349     if($this->config->get_cfg_value("snapshotURI") == ""){
1350       $ldap_to      = $ldap;
1351     }else{
1352       $server         = $this->config->get_cfg_value("snapshotURI");
1353       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1354       $password       = $this->config->get_cfg_value("snapshotAdminPassword");
1355       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1356       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1357       $ldap_to -> cd($snapldapbase);
1358       if (!$ldap_to->success()){
1359         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1360       }
1361     }
1363     /* Prepare bases and some other infos */
1364     $base           = $this->config->current['BASE'];
1365     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1366     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1367     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1368     $tmp            = array(); 
1370     /* Fetch all objects with  gosaSnapshotDN=$dn */
1371     $ldap_to->cd($new_base);
1372     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1373         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1375     /* Put results into a list and add description if missing */
1376     while($entry = $ldap_to->fetch()){ 
1377       if(!isset($entry['description'][0])){
1378         $entry['description'][0]  = "";
1379       }
1380       $tmp[] = $entry; 
1381     }
1383     /* Return the raw array, or format the result */
1384     if($raw){
1385       return($tmp);
1386     }else{  
1387       $tmp2 = array();
1388       foreach($tmp as $entry){
1389         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1390       }
1391     }
1392     return($tmp2);
1393   }
1396   function getAllDeletedSnapshots($base_of_object,$raw = false)
1397   {
1398     if(!$this->snapshotEnabled()) return(array());
1400     /* Create an additional ldap object which
1401        points to our ldap snapshot server */
1402     $ldap= $this->config->get_ldap_link();
1403     $ldap->cd($this->config->current['BASE']);
1404     $cfg= &$this->config->current;
1406     /* check if there are special server configurations for snapshots */
1407     if($this->config->get_cfg_value("snapshotURI") == ""){
1408       $ldap_to      = $ldap;
1409     }else{
1410       $server         = $this->config->get_cfg_value("snapshotURI");
1411       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1412       $password       = $this->config->get_cfg_value("snapshotAdminPassword");
1413       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1414       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1415       $ldap_to -> cd($snapldapbase);
1416       if (!$ldap_to->success()){
1417         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1418       }
1419     }
1421     /* Prepare bases */ 
1422     $base           = $this->config->current['BASE'];
1423     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1424     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1426     /* Fetch all objects and check if they do not exist anymore */
1427     $ui = get_userinfo();
1428     $tmp = array();
1429     $ldap_to->cd($new_base);
1430     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1431     while($entry = $ldap_to->fetch()){
1433       $chk =  str_replace($new_base,"",$entry['dn']);
1434       if(preg_match("/,ou=/",$chk)) continue;
1436       if(!isset($entry['description'][0])){
1437         $entry['description'][0]  = "";
1438       }
1439       $tmp[] = $entry; 
1440     }
1442     /* Check if entry still exists */
1443     foreach($tmp as $key => $entry){
1444       $ldap->cat($entry['gosaSnapshotDN'][0]);
1445       if($ldap->count()){
1446         unset($tmp[$key]);
1447       }
1448     }
1450     /* Format result as requested */
1451     if($raw) {
1452       return($tmp);
1453     }else{
1454       $tmp2 = array();
1455       foreach($tmp as $key => $entry){
1456         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1457       }
1458     }
1459     return($tmp2);
1460   } 
1463   /* Restore selected snapshot */
1464   function restore_snapshot($dn)
1465   {
1466     if(!$this->snapshotEnabled()) return(array());
1468     $ldap= $this->config->get_ldap_link();
1469     $ldap->cd($this->config->current['BASE']);
1470     $cfg= &$this->config->current;
1472     /* check if there are special server configurations for snapshots */
1473     if($this->config->get_cfg_value("snapshotURI") == ""){
1474       $ldap_to      = $ldap;
1475     }else{
1476       $server         = $this->config->get_cfg_value("snapshotURI");
1477       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1478       $password       = $this->config->get_cfg_value("snapshotAdminPassword");
1479       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1480       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1481       $ldap_to -> cd($snapldapbase);
1482       if (!$ldap_to->success()){
1483         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1484       }
1485     }
1487     /* Get the snapshot */ 
1488     $ldap_to->cat($dn);
1489     $restoreObject = $ldap_to->fetch();
1491     /* Prepare import string */
1492     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1494     /* Import the given data */
1495     $err = "";
1496     $ldap->import_complete_ldif($data,$err,false,false);
1497     if (!$ldap->success()){
1498       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1499     }
1500   }
1503   function showSnapshotDialog($base,$baseSuffixe,&$parent)
1504   {
1505     $once = true;
1506     $ui = get_userinfo();
1507     $this->parent = $parent;
1509     foreach($_POST as $name => $value){
1510                         $entry = base64_decode(preg_replace("/_[xy]$/","",$name));
1512       /* Create a new snapshot, display a dialog */
1513       if(preg_match("/^CreateSnapShotDialog_/",$name) && $once){
1514         $once = false;
1515         $entry = preg_replace("/^CreateSnapShotDialog_/","",$entry);
1517         if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1518           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1519         }else{
1520           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1521         }
1522       }  
1523   
1524       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1525       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1526         $once = false;
1527         $entry = preg_replace("/^RestoreSnapShotDialog_/","",$entry);
1528         if(!empty($entry) && $ui->allow_snapshot_restore($entry,$this->parent->acl_module)){
1529           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1530           $this->snapDialog->display_restore_dialog = true;
1531         }else{
1532           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1533         }
1534       }
1536       /* Restore one of the already deleted objects */
1537       if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot") 
1538           || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1539         $once = false;
1541         if($ui->allow_snapshot_restore($base,$this->parent->acl_module)){
1542           $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1543           $this->snapDialog->set_snapshot_bases($baseSuffixe);
1544           $this->snapDialog->display_restore_dialog      = true;
1545           $this->snapDialog->display_all_removed_objects  = true;
1546         }else{
1547           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1548         }
1549       }
1551       /* Restore selected snapshot */
1552       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1553         $once = false;
1554         $entry = preg_replace("/^RestoreSnapShot_/","",$entry);
1555         if(!empty($entry) && $ui->allow_snapshot_restore($entry,$this->parent->acl_module)){
1556           $this->restore_snapshot($entry);
1557           $this->snapDialog = NULL;
1558         }else{
1559           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1560         }
1561       }
1562     }
1564     /* Create a new snapshot requested, check
1565        the given attributes and create the snapshot*/
1566     if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1567       $this->snapDialog->save_object();
1568       $msgs = $this->snapDialog->check();
1569       if(count($msgs)){
1570         foreach($msgs as $msg){
1571           msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1572         }
1573       }else{
1574         $this->dn =  $this->snapDialog->dn;
1575         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1576         $this->snapDialog = NULL;
1577       }
1578     }
1580     /* Restore is requested, restore the object with the posted dn .*/
1581     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1582     }
1584     if(isset($_POST['CancelSnapshot'])){
1585       $this->snapDialog = NULL;
1586     }
1588     if(is_object($this->snapDialog )){
1589       $this->snapDialog->save_object();
1590       return($this->snapDialog->execute());
1591     }
1592   }
1595   static function plInfo()
1596   {
1597     return array();
1598   }
1601   function set_acl_base($base)
1602   {
1603     $this->acl_base= $base;
1604   }
1607   function set_acl_category($category)
1608   {
1609     $this->acl_category= "$category/";
1610   }
1613   function acl_is_writeable($attribute,$skip_write = FALSE)
1614   {
1615     if($this->read_only) return(FALSE);
1616     $ui= get_userinfo();
1617     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1618   }
1621   function acl_is_readable($attribute)
1622   {
1623     $ui= get_userinfo();
1624     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1625   }
1628   function acl_is_createable($base ="")
1629   {
1630     if($this->read_only) return(FALSE);
1631     $ui= get_userinfo();
1632     if($base == "") $base = $this->acl_base;
1633     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1634   }
1637   function acl_is_removeable($base ="")
1638   {
1639     if($this->read_only) return(FALSE);
1640     $ui= get_userinfo();
1641     if($base == "") $base = $this->acl_base;
1642     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1643   }
1646   function acl_is_moveable($base = "")
1647   {
1648     if($this->read_only) return(FALSE);
1649     $ui= get_userinfo();
1650     if($base == "") $base = $this->acl_base;
1651     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1652   }
1655   function acl_have_any_permissions()
1656   {
1657   }
1660   function getacl($attribute,$skip_write= FALSE)
1661   {
1662     $ui= get_userinfo();
1663     $skip_write |= $this->read_only;
1664     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1665   }
1668   /*! \brief    Returns a list of all available departments for this object.  
1669                 If this object is new, all departments we are allowed to create a new user in are returned.
1670                 If this is an existing object, return all deps. we are allowed to move tis object too.
1672       @return   Array [dn] => "..name"  // All deps. we are allowed to act on.
1673   */
1674   function get_allowed_bases()
1675   {
1676     $ui = get_userinfo();
1677     $deps = array();
1679     /* Is this a new object ? Or just an edited existing object */
1680     if(!$this->initially_was_account && $this->is_account){
1681       $new = true;
1682     }else{
1683       $new = false;
1684     }
1686     foreach($this->config->idepartments as $dn => $name){
1687       if($new && $this->acl_is_createable($dn)){
1688         $deps[$dn] = $name;
1689       }elseif(!$new && $this->acl_is_moveable($dn)){
1690         $deps[$dn] = $name;
1691       }
1692     }
1694     /* Add current base */      
1695     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1696       $deps[$this->base] = $this->config->idepartments[$this->base];
1697     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1699     }else{
1700       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1701     }
1702     return($deps);
1703   }
1706   /* This function updates ACL settings if $old_dn was used.
1707    *  $old_dn   specifies the actually used dn
1708    *  $new_dn   specifies the destiantion dn
1709    */
1710   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1711   {
1712     /* Check if old_dn is empty. This should never happen */
1713     if(empty($old_dn) || empty($new_dn)){
1714       trigger_error("Failed to check acl dependencies, wrong dn given.");
1715       return;
1716     }
1718     /* Update userinfo if necessary */
1719     $ui = session::get('ui');
1720     if($ui->dn == $old_dn){
1721       $ui->dn = $new_dn;
1722       session::set('ui',$ui);
1723       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1724     }
1726     /* Object was moved, ensure that all acls will be moved too */
1727     if($new_dn != $old_dn && $old_dn != "new"){
1729       /* get_ldap configuration */
1730       $update = array();
1731       $ldap = $this->config->get_ldap_link();
1732       $ldap->cd ($this->config->current['BASE']);
1733       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1734       while($attrs = $ldap->fetch()){
1735         $acls = array();
1736         $found = false;
1737         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1738           $acl_parts = split(":",$attrs['gosaAclEntry'][$i]);
1740           /* Roles uses antoher data storage order, members are stored int the third part, 
1741              while the members in direct ACL assignments are stored in the second part.
1742            */
1743           $id = ($acl_parts[1] == "role") ? 3 : 2;
1745           /* Update member entries to use $new_dn instead of old_dn
1746            */
1747           $members = explode(",",$acl_parts[$id]);
1748           foreach($members as $key => $member){
1749             $member = base64_decode($member);
1750             if($member == $old_dn){
1751               $members[$key] = base64_encode($new_dn);
1752               $found = TRUE;
1753             }
1754           } 
1756           /* Check if the selected role has to updated
1757            */
1758           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1759             $acl_parts[2] = base64_encode($new_dn);
1760             $found = TRUE;
1761           }
1763           /* Build new acl string */ 
1764           $acl_parts[$id] = implode($members,",");
1765           $acls[] = implode($acl_parts,":");
1766         }
1768         /* Acls for this object must be adjusted */
1769         if($found){
1771           $debug_info=  _("Changing ACL dn")."&nbsp;:&nbsp;<br>&nbsp;-"._("from")."&nbsp;<b>&nbsp;".
1772             $old_dn."</b><br>&nbsp;-"._("to")."&nbsp;<b>".$new_dn."</b><br>";
1773           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1775           $update[$attrs['dn']] =array();
1776           foreach($acls as $acl){
1777             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1778           }
1779         }
1780       }
1782       /* Write updated acls */
1783       foreach($update as $dn => $attrs){
1784         $ldap->cd($dn);
1785         $ldap->modify($attrs);
1786       }
1787     }
1788   }
1790   
1792   /* This function enables the entry Serial ID check.
1793    * If an entry was edited while we have edited the entry too,
1794    *  an error message will be shown. 
1795    * To configure this check correctly read the FAQ.
1796    */    
1797   function enable_CSN_check()
1798   {
1799     $this->CSN_check_active =TRUE;
1800     $this->entryCSN = getEntryCSN($this->dn);
1801   }
1804   /*! \brief  Prepares the plugin to be used for multiple edit
1805    *          Update plugin attributes with given array of attribtues.
1806    *  @param  array   Array with attributes that must be updated.
1807    */
1808   function init_multiple_support($attrs,$all)
1809   {
1810     $ldap= $this->config->get_ldap_link();
1811     $this->multi_attrs    = $attrs;
1812     $this->multi_attrs_all= $all;
1814     /* Copy needed attributes */
1815     foreach ($this->attributes as $val){
1816       $found= array_key_ics($val, $this->multi_attrs);
1817       if ($found != ""){
1818         if(isset($this->multi_attrs["$found"][0])){
1819           $this->$val= $this->multi_attrs["$found"][0];
1820         }
1821       }
1822     }
1823   }
1825  
1826   /*! \brief  Enables multiple support for this plugin
1827    */
1828   function enable_multiple_support()
1829   {
1830     $this->ignore_account = TRUE;
1831     $this->multiple_support_active = TRUE;
1832   }
1835   /*! \brief  Returns all values that have been modfied in multiple edit mode.
1836       @return array Cotaining all mdofied values. 
1837    */
1838   function get_multi_edit_values()
1839   {
1840     $ret = array();
1841     foreach($this->attributes as $attr){
1842       if(in_array($attr,$this->multi_boxes)){
1843         $ret[$attr] = $this->$attr;
1844       }
1845     }
1846     return($ret);
1847   }
1849   
1850   /*! \brief  Update class variables with values collected by multiple edit.
1851    */
1852   function set_multi_edit_values($attrs)
1853   {
1854     foreach($attrs as $name => $value){
1855       $this->$name = $value;
1856     }
1857   }
1860   /*! \brief execute plugin
1862     Generates the html output for this node
1863    */
1864   function multiple_execute()
1865   {
1866     /* This one is empty currently. Fabian - please fill in the docu code */
1867     session::set('current_class_for_help',get_class($this));
1869     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1870     session::set('LOCK_VARS_TO_USE',array());
1871     session::set('LOCK_VARS_USED',array());
1872     
1873     return("Multiple edit is currently not implemented for this plugin.");
1874   }
1877   /*! \brief   Save HTML posted data to object for multiple edit
1878    */
1879   function multiple_save_object()
1880   {
1881     if(empty($this->entryCSN) && $this->CSN_check_active){
1882       $this->entryCSN = getEntryCSN($this->dn);
1883     }
1885     /* Save values to object */
1886     $this->multi_boxes = array();
1887     foreach ($this->attributes as $val){
1888   
1889       /* Get selected checkboxes from multiple edit */
1890       if(isset($_POST["use_".$val])){
1891         $this->multi_boxes[] = $val;
1892       }
1894       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
1896         /* Check for modifications */
1897         if (get_magic_quotes_gpc()) {
1898           $data= stripcslashes($_POST["$val"]);
1899         } else {
1900           $data= $this->$val = $_POST["$val"];
1901         }
1902         if ($this->$val != $data){
1903           $this->is_modified= TRUE;
1904         }
1905     
1906         /* IE post fix */
1907         if(isset($data[0]) && $data[0] == chr(194)) {
1908           $data = "";  
1909         }
1910         $this->$val= $data;
1911       }
1912     }
1913   }
1916   /*! \brief  Returns all attributes of this plugin, 
1917                to be able to detect multiple used attributes 
1918                in multi_plugg::detect_multiple_used_attributes().
1919       @return array Attributes required for intialization of multi_plug
1920    */
1921   public function get_multi_init_values()
1922   {
1923     $attrs = $this->attrs;
1924     return($attrs);
1925   }
1928   /*! \brief  Check given values in multiple edit
1929       @return array Error messages
1930    */
1931   function multiple_check()
1932   {
1933     $message = plugin::check();
1934     return($message);
1935   }
1938   /*! \brief  Returns the snapshot header part for "Actions" menu in management dialogs 
1939       @param  $layer_menu  
1940    */   
1941   function get_snapshot_header($base,$category)
1942   {
1943     $str = "";
1944     $ui = get_userinfo();
1945     if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
1947       $ok = false;
1948       foreach($this->get_used_snapshot_bases() as $base){
1949         $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
1950       }
1952       if($ok){
1953         $str = "..|<img class='center' src='images/lists/restore.png' ".
1954           "alt='"._("Restore")."'>&nbsp;"._("Restore").                       "|RestoreDeletedSnapShot|\n";
1955       }else{
1956         $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;"._("Restore")."||\n";
1957       }
1958     }
1959     return($str);
1960   }
1963   function get_snapshot_action($base,$category)
1964   {
1965     $str= ""; 
1966     $ui = get_userinfo();
1967     if($this->snapshotEnabled()){
1968       if ($ui->allow_snapshot_restore($base,$category)){
1970         if(count($this->Available_SnapsShots($base))){
1971           $str.= "<input class='center' type='image' src='images/lists/restore.png'
1972             alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'>&nbsp;";
1973         } else {
1974           $str = "<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;";
1975         }
1976       }
1977       if($ui->allow_snapshot_create($base,$category)){
1978         $str.= "<input class='center' type='image' src='images/snapshot.png'
1979           alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."' 
1980           title='"._("Create a new snapshot from this object")."'>&nbsp;";
1981       }else{
1982         $str = "<img class='center' src='images/empty.png' alt=' '>&nbsp;";
1983       }
1984     }
1986     return($str);
1987   }
1990   function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
1991   {
1992     $ui = get_userinfo();
1993     $action = "";
1994     if($this->CopyPasteHandler){
1995       if($cut){
1996         if($ui->is_cutable($base,$category,$class)){
1997           $action .= "<input class='center' type='image'
1998             src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'>&nbsp;";
1999         }else{
2000           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2001         }
2002       }
2003       if($copy){
2004         if($ui->is_copyable($base,$category,$class)){
2005           $action.= "<input class='center' type='image'
2006             src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'>&nbsp;";
2007         }else{
2008           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2009         }
2010       }
2011     }
2013     return($action); 
2014   }
2017   function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2018   {
2019     $s = "";
2020     $ui =get_userinfo();
2022     if(!is_array($category)){
2023       $category = array($category);
2024     }
2026     /* Check permissions for each category, if there is at least one category which 
2027         support read or paste permissions for the given base, then display the specific actions.
2028      */
2029     $readable = $pasteable = TRUE;
2030     foreach($category as $cat){
2031       $readable |= $ui->get_category_permissions($base,$cat);
2032       $pasteable|= $ui->is_pasteable($base,$cat);
2033     }
2034   
2035     if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2036       if($readable){
2037         $s.= "..|---|\n";
2038         if($copy){
2039           $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2040             "&nbsp;"._("Copy")."|"."multiple_copy_systems|\n";
2041         }
2042         if($cut){
2043           $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2044             "&nbsp;"._("Cut")."|"."multiple_cut_systems|\n";
2045         }
2046       }
2048       if($pasteable){
2049         if($this->CopyPasteHandler->entries_queued()){
2050           $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2051           $s.="..|".$img."&nbsp;"._("Paste")."|editPaste|\n";
2052         }else{
2053           $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2054           $s.="..|".$img."&nbsp;"._("Paste")."\n";
2055         }
2056       }
2057     }
2058     return($s);
2059   }
2062   function get_used_snapshot_bases()
2063   {
2064      return(array());
2065   }
2067   function is_modal_dialog()
2068   {
2069     return(isset($this->dialog) && $this->dialog);
2070   }
2073 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2074 ?>