Code

Updated class plugin
[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   /*! \brief    The title shown in path menu while this plugin is visible.
36    */
37   var $pathTitle = "";
39   /*!
40     \brief Reference to parent object
42     This variable is used when the plugin is included in tabs
43     and keeps reference to the tab class. Communication to other
44     tabs is possible by 'name'. So the 'fax' plugin can ask the
45     'userinfo' plugin for the fax number.
47     \sa tab
48    */
49   var $parent= NULL;
51   /*!
52     \brief Configuration container
54     Access to global configuration
55    */
56   var $config= NULL;
58   /*!
59     \brief Mark plugin as account
61     Defines whether this plugin is defined as an account or not.
62     This has consequences for the plugin to be saved from tab
63     mode. If it is set to 'FALSE' the tab will call the delete
64     function, else the save function. Should be set to 'TRUE' if
65     the construtor detects a valid LDAP object.
67     \sa plugin::plugin()
68    */
69   var $is_account= FALSE;
70   var $initially_was_account= FALSE;
72   /*!
73     \brief Mark plugin as template
75     Defines whether we are creating a template or a normal object.
76     Has conseqences on the way execute() shows the formular and how
77     save() puts the data to LDAP.
79     \sa plugin::save() plugin::execute()
80    */
81   var $is_template= FALSE;
82   var $ignore_account= FALSE;
83   var $is_modified= FALSE;
85   /*!
86     \brief Represent temporary LDAP data
88     This is only used internally.
89    */
90   var $attrs= array();
92   /* Keep set of conflicting plugins */
93   var $conflicts= array();
95   /* Save unit tags */
96   var $gosaUnitTag= "";
97   var $skipTagging= FALSE;
99   /*!
100     \brief Used standard values
102     dn
103    */
104   var $dn= "";
105   var $uid= "";
106   var $sn= "";
107   var $givenName= "";
108   var $acl= "*none*";
109   var $dialog= FALSE;
110   var $snapDialog = NULL;
112   /* attribute list for save action */
113   var $attributes= array();
114   var $objectclasses= array();
115   var $is_new= TRUE;
116   var $saved_attributes= array();
118   var $acl_base= "";
119   var $acl_category= "";
120   var $read_only = FALSE; // Used when the entry is opened as "readonly" due to locks.
122   /* This can be set to render the tabulators in another stylesheet */
123   var $pl_notify= FALSE;
125   /* Object entry CSN */
126   var $entryCSN         = "";
127   var $CSN_check_active = FALSE;
129   /* This variable indicates that this class can handle multiple dns at once. */
130   var $multiple_support = FALSE;
131   var $multi_attrs      = array();
132   var $multi_attrs_all  = array(); 
134   /* This aviable indicates, that we are currently in multiple edit handle */
135   var $multiple_support_active = FALSE; 
136   var $selected_edit_values = array();
137   var $multi_boxes = array();
139   /*! \brief plugin constructor
141     If 'dn' is set, the node loads the given 'dn' from LDAP
143     \param dn Distinguished name to initialize plugin from
144     \sa plugin()
145    */
146   function plugin (&$config, $dn= NULL, $object= NULL)
147   {
148     /* Configuration is fine, allways */
149     $this->config= &$config;    
150     $this->dn= $dn;
152     // Ensure that we've a valid acl_category set.
153     if(empty($this->acl_category)){
154       $tmp = $this->plInfo();
155       if (isset($tmp['plCategory'])) {
156         $c = key($tmp['plCategory']);
157         if(is_numeric($c)){
158           $c = $tmp['plCategory'][0];
159         }
160         $this->acl_category = $c."/";
161       }
162     }
164     /* Handle new accounts, don't read information from LDAP */
165     if ($dn == "new"){
166       return;
167     }
169     /* Check if this entry was opened in read only mode */
170     if(isset($_POST['open_readonly'])){
171       if(session::global_is_set("LOCK_CACHE")){
172         $cache = &session::get("LOCK_CACHE");
173         if(isset($cache['READ_ONLY'][$this->dn])){
174           $this->read_only = TRUE;
175         }
176       }
177     }
179     /* Save current dn as acl_base */
180     $this->acl_base= $dn;
182     /* Get LDAP descriptor */
183     if ($dn !== NULL){
185       /* Load data to 'attrs' and save 'dn' */
186       if ($object !== NULL){
187         $this->attrs= $object->attrs;
188       } else {
189         $ldap= $this->config->get_ldap_link();
190         $ldap->cat ($dn);
191         $this->attrs= $ldap->fetch();
192       }
194       /* Copy needed attributes */
195       foreach ($this->attributes as $val){
196         $found= array_key_ics($val, $this->attrs);
197         if ($found != ""){
198           $this->$val= $found[0];
199         }
200       }
202       /* gosaUnitTag loading... */
203       if (isset($this->attrs['gosaUnitTag'][0])){
204         $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
205       }
207       /* Set the template flag according to the existence of objectClass
208          gosaUserTemplate */
209       if (isset($this->attrs['objectClass'])){
210         if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
211           $this->is_template= TRUE;
212           @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
213               "found", "Template check");
214         }
215       }
217       /* Is Account? */
218       $found= TRUE;
219       foreach ($this->objectclasses as $obj){
220         if (preg_match('/top/i', $obj)){
221           continue;
222         }
223         if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
224           $found= FALSE;
225           break;
226         }
227       }
228       if ($found){
229         $this->is_account= TRUE;
230         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
231             "found", "Object check");
232       }
234       /* Prepare saved attributes */
235       $this->saved_attributes= $this->attrs;
236       foreach ($this->saved_attributes as $index => $value){
237         if (is_numeric($index)){
238           unset($this->saved_attributes[$index]);
239           continue;
240         }
242         if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
243           unset($this->saved_attributes[$index]);
244           continue;
245         }
247         if (isset($this->saved_attributes[$index][0])){
248           if(!isset($this->saved_attributes[$index]["count"])){
249             $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
250           }
251           if($this->saved_attributes[$index]["count"] == 1){
252             $tmp= $this->saved_attributes[$index][0];
253             unset($this->saved_attributes[$index]);
254             $this->saved_attributes[$index]= $tmp;
255             continue;
256           }
257         }
258         unset($this->saved_attributes["$index"]["count"]);
259       }
261       if(isset($this->attrs['gosaUnitTag'])){
262         $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
263       }
264     }
266     /* Save initial account state */
267     $this->initially_was_account= $this->is_account;
268   }
271   /*! \brief Generates the html output for this node
272    */
273   function execute()
274   {
275     /* This one is empty currently. Fabian - please fill in the docu code */
276     session::global_set('current_class_for_help',get_class($this));
278     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
279     session::set('LOCK_VARS_TO_USE',array());
280     session::set('LOCK_VARS_USED_GET',array());
281     session::set('LOCK_VARS_USED_POST',array());
282     session::set('LOCK_VARS_USED_REQUEST',array());
284     pathNavigator::registerPlugin($this);
285   }
287   /*! \brief Removes object from parent
288    */
289   function remove_from_parent()
290   {
291     /* include global link_info */
292     $ldap= $this->config->get_ldap_link();
294     /* Get current objectClasses in order to add the required ones */
295     $ldap->cat($this->dn);
296     $tmp= $ldap->fetch ();
297     $oc= array();
298     if (isset($tmp['objectClass'])){
299       $oc= $tmp['objectClass'];
300       unset($oc['count']);
301     }
303     /* Remove objectClasses from entry */
304     $ldap->cd($this->dn);
305     $this->attrs= array();
306     $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
308     /* Unset attributes from entry */
309     foreach ($this->attributes as $val){
310       $this->attrs["$val"]= array();
311     }
313     /* Unset account info */
314     $this->is_account= FALSE;
316     /* Do not write in plugin base class, this must be done by
317        children, since there are normally additional attribs,
318        lists, etc. */
319     /*
320        $ldap->modify($this->attrs);
321      */
322   }
325   /*! \brief Save HTML posted data to object 
326    */
327   function save_object()
328   {
329     /* Update entry CSN if it is empty. */
330     if(empty($this->entryCSN) && $this->CSN_check_active){
331       $this->entryCSN = getEntryCSN($this->dn);
332     }
334     /* Save values to object */
335     foreach ($this->attributes as $val){
336       if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
337   
338         /* Check for modifications */
339         $data= get_post($val);
340         if ($this->$val != $data){
341           $this->is_modified= TRUE;
342         }
343         $this->$val = $data;
344     
345         /* Okay, how can I explain this fix ... 
346          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
347          * So IE posts these 'unselectable' option, with value = chr(194) 
348          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
349          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
350          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
351          */
352         if(isset($data[0]) && $data[0] == chr(194)) {
353           $data = "";  
354         }
355         $this->$val= $data;
356       }
357     }
358   }
361   /*! \brief Save data to LDAP, depending on is_account we save or delete */
362   function save()
363   {
364     /* include global link_info */
365     $ldap= $this->config->get_ldap_link();
367     /* Save all plugins */
368     $this->entryCSN = "";
370     /* Start with empty array */
371     $this->attrs= array();
373     /* Get current objectClasses in order to add the required ones */
374     $ldap->cat($this->dn);
375     
376     $tmp= $ldap->fetch ();
378     $oc= array();
379     if (isset($tmp['objectClass'])){
380       $oc= $tmp["objectClass"];
381       $this->is_new= FALSE;
382       unset($oc['count']);
383     } else {
384       $this->is_new= TRUE;
385     }
387     /* Load (minimum) attributes, add missing ones */
388     $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
390     /* Copy standard attributes */
391     foreach ($this->attributes as $val){
392       if ($this->$val != ""){
393         $this->attrs["$val"]= $this->$val;
394       } elseif (!$this->is_new) {
395         $this->attrs["$val"]= array();
396       }
397     }
399     /* Handle tagging */
400     $this->tag_attrs($this->attrs);
401   }
404   function cleanup()
405   {
406     foreach ($this->attrs as $index => $value){
407       
408       /* Convert arrays with one element to non arrays, if the saved
409          attributes are no array, too */
410       if (is_array($this->attrs[$index]) && 
411           count ($this->attrs[$index]) == 1 &&
412           isset($this->saved_attributes[$index]) &&
413           !is_array($this->saved_attributes[$index])){
414           
415         $tmp= $this->attrs[$index][0];
416         $this->attrs[$index]= $tmp;
417       }
419       /* Remove emtpy arrays if they do not differ */
420       if (is_array($this->attrs[$index]) &&
421           count($this->attrs[$index]) == 0 &&
422           !isset($this->saved_attributes[$index])){
423           
424         unset ($this->attrs[$index]);
425         continue;
426       }
428       /* Remove single attributes that do not differ */
429       if (!is_array($this->attrs[$index]) &&
430           isset($this->saved_attributes[$index]) &&
431           !is_array($this->saved_attributes[$index]) &&
432           $this->attrs[$index] == $this->saved_attributes[$index]){
434         unset ($this->attrs[$index]);
435         continue;
436       }
438       /* Remove arrays that do not differ */
439       if (is_array($this->attrs[$index]) && 
440           isset($this->saved_attributes[$index]) &&
441           is_array($this->saved_attributes[$index])){
442           
443         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
444           unset ($this->attrs[$index]);
445           continue;
446         }
447       }
448     }
450     /* Update saved attributes and ensure that next cleanups will be successful too */
451     foreach($this->attrs as $name => $value){
452       $this->saved_attributes[$name] = $value;
453     }
454   }
456   /*! \brief Check formular input */
457   function check()
458   {
459     $message= array();
461     /* Skip if we've no config object */
462     if (!isset($this->config) || !is_object($this->config)){
463       return $message;
464     }
466     /* Find hooks entries for this class */
467     $command = $this->config->configRegistry->getPropertyValue(get_class($this),"check");
468     if ($command != ""){
470       if (!check_command($command)){
471         $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
472       } else {
474         /* Generate "ldif" for check hook */
475         $ldif= "dn: $this->dn\n";
476         
477         /* ... objectClasses */
478         foreach ($this->objectclasses as $oc){
479           $ldif.= "objectClass: $oc\n";
480         }
481         
482         /* ... attributes */
483         foreach ($this->attributes as $attr){
484           if ($this->$attr == ""){
485             continue;
486           }
487           if (is_array($this->$attr)){
488             foreach ($this->$attr as $val){
489               $ldif.= "$attr: $val\n";
490             }
491           } else {
492               $ldif.= "$attr: ".$this->$attr."\n";
493           }
494         }
496         /* Append empty line */
497         $ldif.= "\n";
499         /* Feed "ldif" into hook and retrieve result*/
500         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
501         $fh= proc_open($command, $descriptorspec, $pipes);
502         if (is_resource($fh)) {
503           fwrite ($pipes[0], $ldif);
504           fclose($pipes[0]);
505           
506           $result= stream_get_contents($pipes[1]);
507           if ($result != ""){
508             $message[]= $result;
509           }
510           
511           fclose($pipes[1]);
512           fclose($pipes[2]);
513           proc_close($fh);
514         }
515       }
517     }
519     /* Check entryCSN */
520     if($this->CSN_check_active){
521       $current_csn = getEntryCSN($this->dn);
522       if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
523         $this->entryCSN = $current_csn;
524         $message[] = _("The current object has been altered while beeing edited. If you save this entry, changes that have been made by others will be discarded!");
525       }
526     }
527     return ($message);
528   }
530   /* Adapt from template, using 'dn' */
531   function adapt_from_template($dn, $skip= array())
532   {
533     /* Include global link_info */
534     $ldap= $this->config->get_ldap_link();
536     /* Load requested 'dn' to 'attrs' */
537     $ldap->cat ($dn);
538     $this->attrs= $ldap->fetch();
540     /* Walk through attributes */
541     foreach ($this->attributes as $val){
543       /* Skip the ones in skip list */
544       if (in_array($val, $skip)){
545         continue;
546       }
548       if (isset($this->attrs["$val"][0])){
550         /* If attribute is set, replace dynamic parts: 
551            %sn, %givenName and %uid. Fill these in our local variables. */
552         $value= $this->attrs["$val"][0];
554         foreach (array("sn", "givenName", "uid") as $repl){
555           if (preg_match("/%$repl/i", $value)){
556             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
557           }
558         }
559         $this->$val= $value;
560       }
561     }
563     /* Is Account? */
564     $found= TRUE;
565     foreach ($this->objectclasses as $obj){
566       if (preg_match('/top/i', $obj)){
567         continue;
568       }
569       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
570         $found= FALSE;
571         break;
572       }
573     }
574     if ($found){
575       $this->is_account= TRUE;
576     }
577   }
579   /* \brief Indicate whether a password change is needed or not */
580   function password_change_needed()
581   {
582     return FALSE;
583   }
586   /*! \brief Show header message for tab dialogs */
587   function show_enable_header($button_text, $text, $disabled= FALSE)
588   {
589     if (($disabled == TRUE) || (!$this->acl_is_createable())){
590       $state= "disabled";
591     } else {
592       $state= "";
593     }
594     $display = "<div class='plugin-enable-header'>\n";
595     $display.= "<p>$text</p>\n";
596     $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
597     $display.= "</div>\n";
599     return($display);
600   }
603   /*! \brief Show header message for tab dialogs */
604   function show_disable_header($button_text, $text, $disabled= FALSE)
605   {
606     if (($disabled == TRUE) || !$this->acl_is_removeable()){
607       $state= "disabled";
608     } else {
609       $state= "";
610     }
611     $display = "<div class='plugin-disable-header'>\n";
612     $display.= "<p>$text</p>\n";
613     $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
614     $display.= "</div>\n";
615     return($display);
616   }
620   /* Create unique DN */
621   function create_unique_dn2($data, $base)
622   {
623     $ldap= $this->config->get_ldap_link();
624     $base= preg_replace("/^,*/", "", $base);
626     /* Try to use plain entry first */
627     $dn= "$data,$base";
628     $attribute= preg_replace('/=.*$/', '', $data);
629     $ldap->cat ($dn, array('dn'));
630     if (!$ldap->fetch()){
631       return ($dn);
632     }
634     /* Look for additional attributes */
635     foreach ($this->attributes as $attr){
636       if ($attr == $attribute || $this->$attr == ""){
637         continue;
638       }
640       $dn= "$data+$attr=".$this->$attr.",$base";
641       $ldap->cat ($dn, array('dn'));
642       if (!$ldap->fetch()){
643         return ($dn);
644       }
645     }
647     /* None found */
648     return ("none");
649   }
652   /*! \brief Create unique DN */
653   function create_unique_dn($attribute, $base)
654   {
655     $ldap= $this->config->get_ldap_link();
656     $base= preg_replace("/^,*/", "", $base);
658     /* Try to use plain entry first */
659     $dn= "$attribute=".$this->$attribute.",$base";
660     $ldap->cat ($dn, array('dn'));
661     if (!$ldap->fetch()){
662       return ($dn);
663     }
665     /* Look for additional attributes */
666     foreach ($this->attributes as $attr){
667       if ($attr == $attribute || $this->$attr == ""){
668         continue;
669       }
671       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
672       $ldap->cat ($dn, array('dn'));
673       if (!$ldap->fetch()){
674         return ($dn);
675       }
676     }
678     /* None found */
679     return ("none");
680   }
683   function rebind($ldap, $referral)
684   {
685     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
686     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
687       $this->error = "Success";
688       $this->hascon=true;
689       $this->reconnect= true;
690       return (0);
691     } else {
692       $this->error = "Could not bind to " . $credentials['ADMIN'];
693       return NULL;
694     }
695   }
698   /* Recursively copy ldap object */
699   function _copy($src_dn,$dst_dn)
700   {
701     $ldap=$this->config->get_ldap_link();
702     $ldap->cat($src_dn);
703     $attrs= $ldap->fetch();
705     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
706     $ds= ldap_connect($this->config->current['SERVER']);
707     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
708     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
709       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
710     }
712     $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
713     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
714     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
716     /* Fill data from LDAP */
717     $new= array();
718     if ($sr) {
719       $ei=ldap_first_entry($ds, $sr);
720       if ($ei) {
721         foreach($attrs as $attr => $val){
722           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
723             for ($i= 0; $i<$info['count']; $i++){
724               if ($info['count'] == 1){
725                 $new[$attr]= $info[$i];
726               } else {
727                 $new[$attr][]= $info[$i];
728               }
729             }
730           }
731         }
732       }
733     }
735     /* close conncetion */
736     ldap_unbind($ds);
738     /* Adapt naming attribute */
739     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
740     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
741     $new[$dst_name]= LDAP::fix($dst_val);
743     /* Check if this is a department.
744      * If it is a dep. && there is a , override in his ou 
745      *  change \2C to , again, else this entry can't be saved ...
746      */
747     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
748       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
749     }
751     /* Save copy */
752     $ldap->connect();
753     $ldap->cd($this->config->current['BASE']);
754     
755     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
757     /* FAIvariable=.../..., cn=.. 
758         could not be saved, because the attribute FAIvariable was different to 
759         the dn FAIvariable=..., cn=... */
761     if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
763     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
764       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
765     }
766     $ldap->cd($dst_dn);
767     $ldap->add($new);
769     if (!$ldap->success()){
770       trigger_error("Trying to save $dst_dn failed.",
771           E_USER_WARNING);
772       return(FALSE);
773     }
774     return(TRUE);
775   }
778   /* This is a workaround function. */
779   function copy($src_dn, $dst_dn)
780   {
781     /* Rename dn in possible object groups */
782     $ldap= $this->config->get_ldap_link();
783     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
784         array('cn'));
785     while ($attrs= $ldap->fetch()){
786       $og= new ogroup($this->config, $ldap->getDN());
787       unset($og->member[$src_dn]);
788       $og->member[$dst_dn]= $dst_dn;
789       $og->save ();
790     }
792     $ldap->cat($dst_dn);
793     $attrs= $ldap->fetch();
794     if (count($attrs)){
795       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
796           E_USER_WARNING);
797       return (FALSE);
798     }
800     $ldap->cat($src_dn);
801     $attrs= $ldap->fetch();
802     if (!count($attrs)){
803       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
804           E_USER_WARNING);
805       return (FALSE);
806     }
808     $ldap->cd($src_dn);
809     $ldap->search("objectClass=*",array("dn"));
810     while($attrs = $ldap->fetch()){
811       $src = $attrs['dn'];
812       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
813       $this->_copy($src,$dst);
814     }
815     return (TRUE);
816   }
820   /*! \brief  Rename/Move a given src_dn to the given dest_dn
821    *
822    * Move a given ldap object indentified by $src_dn to the
823    * given destination $dst_dn
824    *
825    * - Ensure that all references are updated (ogroups)
826    * - Update ACLs   
827    * - Update accessTo
828    *
829    * \param  string  'src_dn' the source DN.
830    * \param  string  'dst_dn' the destination DN.
831    * \return boolean TRUE on success else FALSE.
832    */
833   function rename($src_dn, $dst_dn)
834   {
835     $start = microtime(1);
837     /* Try to move the source entry to the destination position */
838     $ldap = $this->config->get_ldap_link();
839     $ldap->cd($this->config->current['BASE']);
840     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
841     if (!$ldap->rename_dn($src_dn,$dst_dn)){
842       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());
843       @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn  -- TO:  $dst_dn", 
844           "Ldap Protocol v3 implementation error, falling back to maunal method.");
845       return(FALSE);
846     }
848     /* Get list of users,groups and roles within this tree,
849         maybe we have to update ACL references.
850      */
851     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
852           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
853     foreach($leaf_objs as $obj){
854       $new_dn = $obj['dn'];
855       $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
856       $this->update_acls($old_dn,$new_dn); 
857     }
859     // Migrate objectgroups if needed
860     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
861       "ogroups", array(get_ou("group", "ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
863     // Walk through all objectGroups
864     foreach($ogroups as $ogroup){
865       // Migrate old to new dn
866       $o_ogroup= new ogroup($this->config,$ogroup['dn']);
867       if (isset($o_ogroup->member[$src_dn])) {
868         unset($o_ogroup->member[$src_dn]);
869       }
870       $o_ogroup->member[$dst_dn]= $dst_dn;
871       
872       // Save object group
873       $o_ogroup->save();
874     }
876     // Migrate rfc groups if needed
877     $groups = get_sub_list("(&(objectClass=posixGroup)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","groups", array(get_ou("core", "groupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
879     // Walk through all POSIX groups
880     foreach($groups as $group){
882       // Migrate old to new dn
883       $o_group= new group($this->config,$group['dn']);
884       $o_group->save();
885     }
887     /* Update roles to use the new entry dn */
888     $roles = get_sub_list("(&(objectClass=organizationalRole)(roleOccupant=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","roles", array(get_ou("roleGeneric", "roleRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
890     // Walk through all roles
891     foreach($roles as $role){
892       $role = new roleGeneric($this->config,$role['dn']);
893       $key= array_search($src_dn, $role->roleOccupant);      
894       if($key !== FALSE){
895         $role->roleOccupant[$key] = $dst_dn;
896         $role->save();
897       }
898     }
900     // Update 'manager' attributes from gosaDepartment and inetOrgPerson 
901     $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
902     $ocs = $ldap->get_objectclasses();
903     if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
904       $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
905     }
906     $leaf_deps=  get_list($filter,array("all"),$this->config->current['BASE'], 
907         array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
908     foreach($leaf_deps as $entry){
909       $update = array('manager' => $dst_dn);
910       $ldap->cd($entry['dn']);
911       $ldap->modify($update);
912       if(!$ldap->success()){
913         trigger_error(sprintf("Failed to update manager for %s: %s", bold($entry['dn']), $ldap->get_error()));
914       }
915     }
917     // Migrate 'dyn-groups' here. labeledURIObject
918     if(class_available('DynamicLdapGroup')) {
919         DynamicLdapGroup::moveDynGroup($this->config,$src_dn,$dst_dn);
920     }
921  
922     /* Check if there are gosa departments moved. 
923        If there were deps moved, the force reload of config->deps.
924      */
925     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
926           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
927   
928     if(count($leaf_deps)){
929       $this->config->get_departments();
930       $this->config->make_idepartments();
931       session::global_set("config",$this->config);
932       $ui =get_userinfo();
933       $ui->reset_acl_cache();
934     }
936     return(TRUE); 
937   }
940  
941   function move($src_dn, $dst_dn)
942   {
943     /* Do not copy if only upper- lowercase has changed */
944     if(strtolower($src_dn) == strtolower($dst_dn)){
945       return(TRUE);
946     }
948     
949     /* Try to move the entry instead of copy & delete
950      */
951     if(TRUE){
953       /* Try to move with ldap routines, if this was not successfull
954           fall back to the old style copy & remove method 
955        */
956       if($this->rename($src_dn, $dst_dn)){
957         return(TRUE);
958       }else{
959         // See code below.
960       }
961     }
963     /* Copy source to destination */
964     if (!$this->copy($src_dn, $dst_dn)){
965       return (FALSE);
966     }
968     /* Delete source */
969     $ldap= $this->config->get_ldap_link();
970     $ldap->rmdir_recursive($src_dn);
971     if (!$ldap->success()){
972       trigger_error("Trying to delete $src_dn failed.",
973           E_USER_WARNING);
974       return (FALSE);
975     }
977     return (TRUE);
978   }
981   /* \brief Move/Rename complete trees */
982   function recursive_move($src_dn, $dst_dn)
983   {
984     /* Check if the destination entry exists */
985     $ldap= $this->config->get_ldap_link();
987     /* Check if destination exists - abort */
988     $ldap->cat($dst_dn, array('dn'));
989     if ($ldap->fetch()){
990       trigger_error("recursive_move $dst_dn already exists.",
991           E_USER_WARNING);
992       return (FALSE);
993     }
995     $this->copy($src_dn, $dst_dn);
997     /* Remove src_dn */
998     $ldap->cd($src_dn);
999     $ldap->recursive_remove($src_dn);
1000     return (TRUE);
1001   }
1004   function saveCopyDialog(){
1005   }
1008   function getCopyDialog(){
1009     return(array("string"=>"","status"=>""));
1010   }
1013   /*! \brief Prepare for Copy & Paste */
1014   function PrepareForCopyPaste($source)
1015   {
1016     $todo = $this->attributes;
1017     if(isset($this->CopyPasteVars)){
1018       $todo = array_merge($todo,$this->CopyPasteVars);
1019     }
1021     if(count($this->objectclasses)){
1022       $this->is_account = TRUE;
1023       foreach($this->objectclasses as $class){
1024         if(!in_array($class,$source['objectClass'])){
1025           $this->is_account = FALSE;
1026         }
1027       }
1028     }
1030     foreach($todo as $var){
1031       if (isset($source[$var])){
1032         if(isset($source[$var]['count'])){
1033           if($source[$var]['count'] > 1){
1034             $tmp= $source[$var];
1035             unset($tmp['count']);
1036             $this->$var = $tmp;
1037           }else{
1038             $this->$var = $source[$var][0];
1039           }
1040         }else{
1041           $this->$var= $source[$var];
1042         }
1043       }
1044     }
1045   }
1047   /*! \brief Get gosaUnitTag for the given DN
1048        If this is called from departmentGeneric, we have to skip this
1049         tagging procedure. 
1050     */
1051   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1052   {
1053     /* Skip tagging? */
1054     if($this->skipTagging){
1055       return;
1056     }
1058     /* No dn? Self-operation... */
1059     if ($dn == ""){
1060       $dn= $this->dn;
1062       /* No tag? Find it yourself... */
1063       if ($tag == ""){
1064         $len= strlen($dn);
1066         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1067         $relevant= array();
1068         foreach ($this->config->adepartments as $key => $ntag){
1070           /* This one is bigger than our dn, its not relevant... */
1071           if ($len < strlen($key)){
1072             continue;
1073           }
1075           /* This one matches with the latter part. Break and don't fix this entry */
1076           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1077             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1078             $relevant[strlen($key)]= $ntag;
1079             continue;
1080           }
1082         }
1084         /* If we've some relevant tags to set, just get the longest one */
1085         if (count($relevant)){
1086           ksort($relevant);
1087           $tmp= array_keys($relevant);
1088           $idx= end($tmp);
1089           $tag= $relevant[$idx];
1090           $this->gosaUnitTag= $tag;
1091         }
1092       }
1093     }
1095   /*! \brief Add unit tag */ 
1096     /* Remove tags that may already be here... */
1097     remove_objectClass("gosaAdministrativeUnitTag", $at);
1098     if (isset($at['gosaUnitTag'])){
1099         unset($at['gosaUnitTag']);
1100     }
1102     /* Set tag? */
1103     if ($tag != ""){
1104       add_objectClass("gosaAdministrativeUnitTag", $at);
1105       $at['gosaUnitTag']= $tag;
1106     }
1108     /* Initially this object was tagged. 
1109        - But now, it is no longer inside a tagged department. 
1110        So force the remove of the tag.
1111        (objectClass was already removed obove)
1112      */
1113     if($tag == "" && $this->gosaUnitTag){
1114       $at['gosaUnitTag'] = array();
1115     }
1116   }
1119   /*! \brief Test for removability of the object
1120    *
1121    * Allows testing of conditions for removal of object. If removal should be aborted
1122    * the function needs to remove an error message.
1123    * */
1124   function allow_remove()
1125   {
1126     $reason= "";
1127     return $reason;
1128   }
1131   /*! \brief Test if snapshotting is enabled
1132    *
1133    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1134    * if the configuration failed 
1135    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1136    */
1137   function snapshotEnabled()
1138   {
1139       return $this->config->snapshotEnabled();
1140   }
1143   /*! \brief Return plugin informations for acl handling 
1144    *         See class_core.inc for examples.
1145    */
1146   static function plInfo()
1147   {
1148     return array();
1149   }
1152   function set_acl_base($base)
1153   {
1154     @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$base."</b>","<b>ACL-Base:</b> ");
1155     $this->acl_base= $base;
1156   }
1159   function set_acl_category($category)
1160   {
1161     @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$category."</b>(/".get_class($this).")","<b>ACL-Category:</b> ");
1162     $this->acl_category= "$category/";
1163   }
1166   function acl_is_writeable($attribute,$skip_write = FALSE)
1167   {
1168     if($this->read_only) return(FALSE);
1169     $ui= get_userinfo();
1170     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1171   }
1174   function acl_is_readable($attribute)
1175   {
1176     $ui= get_userinfo();
1177     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1178   }
1181   function acl_is_createable($base ="")
1182   {
1183     if($this->read_only) return(FALSE);
1184     $ui= get_userinfo();
1185     if($base == "") $base = $this->acl_base;
1186     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1187   }
1190   function acl_is_removeable($base ="")
1191   {
1192     if($this->read_only) return(FALSE);
1193     $ui= get_userinfo();
1194     if($base == "") $base = $this->acl_base;
1195     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1196   }
1199   function acl_is_moveable($base = "")
1200   {
1201     if($this->read_only) return(FALSE);
1202     $ui= get_userinfo();
1203     if($base == "") $base = $this->acl_base;
1204     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1205   }
1208   function acl_have_any_permissions()
1209   {
1210   }
1213   function getacl($attribute,$skip_write= FALSE)
1214   {
1215     $ui= get_userinfo();
1216     $skip_write |= $this->read_only;
1217     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1218   }
1221   /*! \brief Returns a list of all available departments for this object.
1222    * 
1223    * If this object is new, all departments we are allowed to create a new user in
1224    * are returned. If this is an existing object, return all deps. 
1225    * We are allowed to move tis object too.
1226    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1227   */
1228   function get_allowed_bases()
1229   {
1230     $ui = get_userinfo();
1231     $deps = array();
1233     /* Is this a new object ? Or just an edited existing object */
1234     if(!$this->initially_was_account && $this->is_account){
1235       $new = true;
1236     }else{
1237       $new = false;
1238     }
1240     foreach($this->config->idepartments as $dn => $name){
1241       if($new && $this->acl_is_createable($dn)){
1242         $deps[$dn] = $name;
1243       }elseif(!$new && $this->acl_is_moveable($dn)){
1244         $deps[$dn] = $name;
1245       }
1246     }
1248     /* Add current base */      
1249     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1250       $deps[$this->base] = $this->config->idepartments[$this->base];
1251     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1253     }else{
1254       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1255     }
1256     return($deps);
1257   }
1260   /* This function updates ACL settings if $old_dn was used.
1261    *  \param string 'old_dn' specifies the actually used dn
1262    *  \param string 'new_dn' specifies the destiantion dn
1263    */
1264   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1265   {
1266     /* Check if old_dn is empty. This should never happen */
1267     if(empty($old_dn) || empty($new_dn)){
1268       trigger_error("Failed to check acl dependencies, wrong dn given.");
1269       return;
1270     }
1272     /* Update userinfo if necessary */
1273     $ui = session::global_get('ui');
1274     if($ui->dn == $old_dn){
1275       $ui->dn = $new_dn;
1276       session::global_set('ui',$ui);
1277       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1278     }
1280     /* Object was moved, ensure that all acls will be moved too */
1281     if($new_dn != $old_dn && $old_dn != "new"){
1283       /* get_ldap configuration */
1284       $update = array();
1285       $ldap = $this->config->get_ldap_link();
1286       $ldap->cd ($this->config->current['BASE']);
1287       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1288       while($attrs = $ldap->fetch()){
1289         $acls = array();
1290         $found = false;
1291         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1292           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1294           /* Roles uses antoher data storage order, members are stored int the third part, 
1295              while the members in direct ACL assignments are stored in the second part.
1296            */
1297           $id = ($acl_parts[1] == "role") ? 3 : 2;
1299           /* Update member entries to use $new_dn instead of old_dn
1300            */
1301           $members = explode(",",$acl_parts[$id]);
1302           foreach($members as $key => $member){
1303             $member = base64_decode($member);
1304             if($member == $old_dn){
1305               $members[$key] = base64_encode($new_dn);
1306               $found = TRUE;
1307             }
1308           } 
1310           /* Check if the selected role has to updated
1311            */
1312           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1313             $acl_parts[2] = base64_encode($new_dn);
1314             $found = TRUE;
1315           }
1317           /* Build new acl string */ 
1318           $acl_parts[$id] = implode($members,",");
1319           $acls[] = implode($acl_parts,":");
1320         }
1322         /* Acls for this object must be adjusted */
1323         if($found){
1325           $debug_info= sprintf(_("Changing ACL DN from %s to %s"), bold($old_dn), bold($new_dn));
1326           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1328           $update[$attrs['dn']] =array();
1329           foreach($acls as $acl){
1330             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1331           }
1332         }
1333       }
1335       /* Write updated acls */
1336       foreach($update as $dn => $attrs){
1337         $ldap->cd($dn);
1338         $ldap->modify($attrs);
1339       }
1340     }
1341   }
1343   
1345   /*! \brief Enable the Serial ID check
1346    *
1347    * This function enables the entry Serial ID check.  If an entry was edited while
1348    * we have edited the entry too, an error message will be shown. 
1349    * To configure this check correctly read the FAQ.
1350    */    
1351   function enable_CSN_check()
1352   {
1353     $this->CSN_check_active =TRUE;
1354     $this->entryCSN = getEntryCSN($this->dn);
1355   }
1358   /*! \brief  Prepares the plugin to be used for multiple edit
1359    *          Update plugin attributes with given array of attribtues.
1360    *  \param  array   Array with attributes that must be updated.
1361    */
1362   function init_multiple_support($attrs,$all)
1363   {
1364     $ldap= $this->config->get_ldap_link();
1365     $this->multi_attrs    = $attrs;
1366     $this->multi_attrs_all= $all;
1368     /* Copy needed attributes */
1369     foreach ($this->attributes as $val){
1370       $found= array_key_ics($val, $this->multi_attrs);
1371  
1372       if ($found != ""){
1373         if(isset($this->multi_attrs["$val"][0])){
1374           $this->$val= $this->multi_attrs["$val"][0];
1375         }
1376       }
1377     }
1378   }
1380  
1381   /*! \brief  Enables multiple support for this plugin
1382    */
1383   function enable_multiple_support()
1384   {
1385     $this->ignore_account = TRUE;
1386     $this->multiple_support_active = TRUE;
1387   }
1390   /*! \brief  Returns all values that have been modfied in multiple edit mode.
1391       \return array Cotaining all modified values. 
1392    */
1393   function get_multi_edit_values()
1394   {
1395     $ret = array();
1396     foreach($this->attributes as $attr){
1397       if(in_array($attr,$this->multi_boxes)){
1398         $ret[$attr] = $this->$attr;
1399       }
1400     }
1401     return($ret);
1402   }
1404   
1405   /*! \brief  Update class variables with values collected by multiple edit.
1406    */
1407   function set_multi_edit_values($attrs)
1408   {
1409     foreach($attrs as $name => $value){
1410       $this->$name = $value;
1411     }
1412   }
1415   /*! \brief Generates the html output for this node for multi edit*/
1416   function multiple_execute()
1417   {
1418     /* This one is empty currently. Fabian - please fill in the docu code */
1419     session::global_set('current_class_for_help',get_class($this));
1421     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1422     session::set('LOCK_VARS_TO_USE',array());
1423     session::set('LOCK_VARS_USED_GET',array());
1424     session::set('LOCK_VARS_USED_POST',array());
1425     session::set('LOCK_VARS_USED_REQUEST',array());
1426     
1427     return("Multiple edit is currently not implemented for this plugin.");
1428   }
1431   /*! \brief Save HTML posted data to object for multiple edit
1432    */
1433   function multiple_save_object()
1434   {
1435     if(empty($this->entryCSN) && $this->CSN_check_active){
1436       $this->entryCSN = getEntryCSN($this->dn);
1437     }
1439     /* Save values to object */
1440     $this->multi_boxes = array();
1441     foreach ($this->attributes as $val){
1442   
1443       /* Get selected checkboxes from multiple edit */
1444       if(isset($_POST["use_".$val])){
1445         $this->multi_boxes[] = $val;
1446       }
1448       if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
1450         $data= $this->$val = get_post($val);
1451         if ($this->$val != $data){
1452           $this->is_modified= TRUE;
1453         }
1454     
1455         /* IE post fix */
1456         if(isset($data[0]) && $data[0] == chr(194)) {
1457           $data = "";  
1458         }
1459         $this->$val= $data;
1460       }
1461     }
1462   }
1465   /*! \brief Returns all attributes of this plugin, 
1466                to be able to detect multiple used attributes 
1467                in multi_plugg::detect_multiple_used_attributes().
1468       @return array Attributes required for intialization of multi_plug
1469    */
1470   public function get_multi_init_values()
1471   {
1472     $attrs = $this->attrs;
1473     return($attrs);
1474   }
1477   /*! \brief  Check given values in multiple edit
1478       \return array Error messages
1479    */
1480   function multiple_check()
1481   {
1482     $message = plugin::check();
1483     return($message);
1484   }
1486   function get_used_snapshot_bases()
1487   {
1488      return(array());
1489   }
1491   function is_modal_dialog()
1492   {
1493     return(isset($this->dialog) && $this->dialog);
1494   }
1497   /*! \brief    Forward command execution requests
1498    *             to the hook execution method. 
1499    */
1500   function handle_post_events($mode, $addAttrs= array())
1501   {
1502     if(!in_array($mode, array('add','remove','modify'))){
1503       trigger_error(sprintf("Invalid post event type given %s! Valid types are [add,modify,remove].", bold($mode)));
1504       return;
1505     }
1506     switch ($mode){
1507       case "add":
1508         plugin::callHook($this,"POSTCREATE", $addAttrs);
1509       break;
1511       case "modify":
1512         plugin::callHook($this,"POSTMODIFY", $addAttrs);
1513       break;
1515       case "remove":
1516         plugin::callHook($this,"POSTREMOVE", $addAttrs);
1517       break;
1518     }
1519   }
1522   /*! \brief    Calls external hooks which are defined for this plugin (gosa.conf)
1523    *            Replaces placeholder by class values of this plugin instance.
1524    *  @param    Allows to a add special replacements.
1525    */
1526   static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(), &$returnCode = NULL)
1527   {
1528     global $config;
1529     $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
1530    
1531     if ($command != ""){
1533       // Walk trough attributes list and add the plugins attributes. 
1534       foreach ($plugin->attributes as $attr){
1535         if (!is_array($plugin->$attr)){
1536           $addAttrs[$attr] = $plugin->$attr;
1537         }
1538       }
1539       $ui = get_userinfo();
1540       $addAttrs['callerDN']=$ui->dn;
1541       $addAttrs['dn']=$plugin->dn;
1542       $addAttrs['location']=$config->current['NAME'];
1544       // Sort attributes by length, ensures correct replacement
1545       $tmp = array();
1546       foreach($addAttrs as $name => $value){
1547         $tmp[$name] =  strlen($name);
1548       }
1549       arsort($tmp);
1551       // Now replace the placeholder 
1552       foreach ($tmp as $name => $len){
1553         $value = $addAttrs[$name];
1554         $command= str_replace("%$name", "$value", $command);
1555       }
1557       // If there are still some %.. in our command, try to fill these with some other class vars 
1558       if(preg_match("/%/",$command)){
1559         $attrs = get_object_vars($plugin);
1560         foreach($attrs as $name => $value){
1561           if(is_array($value)){
1562             $s = "";
1563             foreach($value as $val){
1564               if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
1565                 $s .= '"'.$val.'",'; 
1566               }
1567             }
1568             $value = '['.trim($s,',').']';
1569           }
1570           if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
1571             continue;
1572           }
1573           $command= preg_replace("/%$name/", $value, $command);
1574         }
1575       }
1577       if (check_command($command)){
1579         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
1580         exec($command, $arr, $returnCode);
1581         $returnOutput = $arr;
1583         if($returnCode != 0){
1584           $str = implode("\n",$arr);
1585           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1586           $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
1587           msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1588         }elseif(is_array($arr)){
1589           $str = implode("\n",$arr);
1590           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
1591         }
1592       } else {
1593         $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
1594         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1595       }
1596     }
1597   }
1600 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1601 ?>