Code

Do not focus next field while pressing 'Enter'whithin a textarea .
[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= $this->$val = get_post($val);
340         if ($this->$val != $data){
341           $this->is_modified= TRUE;
342         }
343     
344         /* Okay, how can I explain this fix ... 
345          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
346          * So IE posts these 'unselectable' option, with value = chr(194) 
347          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
348          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
349          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
350          */
351         if(isset($data[0]) && $data[0] == chr(194)) {
352           $data = "";  
353         }
354         $this->$val= $data;
355       }
356     }
357   }
360   /*! \brief Save data to LDAP, depending on is_account we save or delete */
361   function save()
362   {
363     /* include global link_info */
364     $ldap= $this->config->get_ldap_link();
366     /* Save all plugins */
367     $this->entryCSN = "";
369     /* Start with empty array */
370     $this->attrs= array();
372     /* Get current objectClasses in order to add the required ones */
373     $ldap->cat($this->dn);
374     
375     $tmp= $ldap->fetch ();
377     $oc= array();
378     if (isset($tmp['objectClass'])){
379       $oc= $tmp["objectClass"];
380       $this->is_new= FALSE;
381       unset($oc['count']);
382     } else {
383       $this->is_new= TRUE;
384     }
386     /* Load (minimum) attributes, add missing ones */
387     $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
389     /* Copy standard attributes */
390     foreach ($this->attributes as $val){
391       if ($this->$val != ""){
392         $this->attrs["$val"]= $this->$val;
393       } elseif (!$this->is_new) {
394         $this->attrs["$val"]= array();
395       }
396     }
398     /* Handle tagging */
399     $this->tag_attrs($this->attrs);
400   }
403   function cleanup()
404   {
405     foreach ($this->attrs as $index => $value){
406       
407       /* Convert arrays with one element to non arrays, if the saved
408          attributes are no array, too */
409       if (is_array($this->attrs[$index]) && 
410           count ($this->attrs[$index]) == 1 &&
411           isset($this->saved_attributes[$index]) &&
412           !is_array($this->saved_attributes[$index])){
413           
414         $tmp= $this->attrs[$index][0];
415         $this->attrs[$index]= $tmp;
416       }
418       /* Remove emtpy arrays if they do not differ */
419       if (is_array($this->attrs[$index]) &&
420           count($this->attrs[$index]) == 0 &&
421           !isset($this->saved_attributes[$index])){
422           
423         unset ($this->attrs[$index]);
424         continue;
425       }
427       /* Remove single attributes that do not differ */
428       if (!is_array($this->attrs[$index]) &&
429           isset($this->saved_attributes[$index]) &&
430           !is_array($this->saved_attributes[$index]) &&
431           $this->attrs[$index] == $this->saved_attributes[$index]){
433         unset ($this->attrs[$index]);
434         continue;
435       }
437       /* Remove arrays that do not differ */
438       if (is_array($this->attrs[$index]) && 
439           isset($this->saved_attributes[$index]) &&
440           is_array($this->saved_attributes[$index])){
441           
442         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
443           unset ($this->attrs[$index]);
444           continue;
445         }
446       }
447     }
449     /* Update saved attributes and ensure that next cleanups will be successful too */
450     foreach($this->attrs as $name => $value){
451       $this->saved_attributes[$name] = $value;
452     }
453   }
455   /*! \brief Check formular input */
456   function check()
457   {
458     $message= array();
460     /* Skip if we've no config object */
461     if (!isset($this->config) || !is_object($this->config)){
462       return $message;
463     }
465     /* Find hooks entries for this class */
466     $command = $this->config->configRegistry->getPropertyValue(get_class($this),"check");
467     if ($command != ""){
469       if (!check_command($command)){
470         $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
471       } else {
473         /* Generate "ldif" for check hook */
474         $ldif= "dn: $this->dn\n";
475         
476         /* ... objectClasses */
477         foreach ($this->objectclasses as $oc){
478           $ldif.= "objectClass: $oc\n";
479         }
480         
481         /* ... attributes */
482         foreach ($this->attributes as $attr){
483           if ($this->$attr == ""){
484             continue;
485           }
486           if (is_array($this->$attr)){
487             foreach ($this->$attr as $val){
488               $ldif.= "$attr: $val\n";
489             }
490           } else {
491               $ldif.= "$attr: ".$this->$attr."\n";
492           }
493         }
495         /* Append empty line */
496         $ldif.= "\n";
498         /* Feed "ldif" into hook and retrieve result*/
499         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
500         $fh= proc_open($command, $descriptorspec, $pipes);
501         if (is_resource($fh)) {
502           fwrite ($pipes[0], $ldif);
503           fclose($pipes[0]);
504           
505           $result= stream_get_contents($pipes[1]);
506           if ($result != ""){
507             $message[]= $result;
508           }
509           
510           fclose($pipes[1]);
511           fclose($pipes[2]);
512           proc_close($fh);
513         }
514       }
516     }
518     /* Check entryCSN */
519     if($this->CSN_check_active){
520       $current_csn = getEntryCSN($this->dn);
521       if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
522         $this->entryCSN = $current_csn;
523         $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!");
524       }
525     }
526     return ($message);
527   }
529   /* Adapt from template, using 'dn' */
530   function adapt_from_template($dn, $skip= array())
531   {
532     /* Include global link_info */
533     $ldap= $this->config->get_ldap_link();
535     /* Load requested 'dn' to 'attrs' */
536     $ldap->cat ($dn);
537     $this->attrs= $ldap->fetch();
539     /* Walk through attributes */
540     foreach ($this->attributes as $val){
542       /* Skip the ones in skip list */
543       if (in_array($val, $skip)){
544         continue;
545       }
547       if (isset($this->attrs["$val"][0])){
549         /* If attribute is set, replace dynamic parts: 
550            %sn, %givenName and %uid. Fill these in our local variables. */
551         $value= $this->attrs["$val"][0];
553         foreach (array("sn", "givenName", "uid") as $repl){
554           if (preg_match("/%$repl/i", $value)){
555             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
556           }
557         }
558         $this->$val= $value;
559       }
560     }
562     /* Is Account? */
563     $found= TRUE;
564     foreach ($this->objectclasses as $obj){
565       if (preg_match('/top/i', $obj)){
566         continue;
567       }
568       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
569         $found= FALSE;
570         break;
571       }
572     }
573     if ($found){
574       $this->is_account= TRUE;
575     }
576   }
578   /* \brief Indicate whether a password change is needed or not */
579   function password_change_needed()
580   {
581     return FALSE;
582   }
585   /*! \brief Show header message for tab dialogs */
586   function show_enable_header($button_text, $text, $disabled= FALSE)
587   {
588     if (($disabled == TRUE) || (!$this->acl_is_createable())){
589       $state= "disabled";
590     } else {
591       $state= "";
592     }
593     $display = "<div class='plugin-enable-header'>\n";
594     $display.= "<p>$text</p>\n";
595     $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
596     $display.= "</div>\n";
598     return($display);
599   }
602   /*! \brief Show header message for tab dialogs */
603   function show_disable_header($button_text, $text, $disabled= FALSE)
604   {
605     if (($disabled == TRUE) || !$this->acl_is_removeable()){
606       $state= "disabled";
607     } else {
608       $state= "";
609     }
610     $display = "<div class='plugin-disable-header'>\n";
611     $display.= "<p>$text</p>\n";
612     $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
613     $display.= "</div>\n";
614     return($display);
615   }
619   /* Create unique DN */
620   function create_unique_dn2($data, $base)
621   {
622     $ldap= $this->config->get_ldap_link();
623     $base= preg_replace("/^,*/", "", $base);
625     /* Try to use plain entry first */
626     $dn= "$data,$base";
627     $attribute= preg_replace('/=.*$/', '', $data);
628     $ldap->cat ($dn, array('dn'));
629     if (!$ldap->fetch()){
630       return ($dn);
631     }
633     /* Look for additional attributes */
634     foreach ($this->attributes as $attr){
635       if ($attr == $attribute || $this->$attr == ""){
636         continue;
637       }
639       $dn= "$data+$attr=".$this->$attr.",$base";
640       $ldap->cat ($dn, array('dn'));
641       if (!$ldap->fetch()){
642         return ($dn);
643       }
644     }
646     /* None found */
647     return ("none");
648   }
651   /*! \brief Create unique DN */
652   function create_unique_dn($attribute, $base)
653   {
654     $ldap= $this->config->get_ldap_link();
655     $base= preg_replace("/^,*/", "", $base);
657     /* Try to use plain entry first */
658     $dn= "$attribute=".$this->$attribute.",$base";
659     $ldap->cat ($dn, array('dn'));
660     if (!$ldap->fetch()){
661       return ($dn);
662     }
664     /* Look for additional attributes */
665     foreach ($this->attributes as $attr){
666       if ($attr == $attribute || $this->$attr == ""){
667         continue;
668       }
670       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
671       $ldap->cat ($dn, array('dn'));
672       if (!$ldap->fetch()){
673         return ($dn);
674       }
675     }
677     /* None found */
678     return ("none");
679   }
682   function rebind($ldap, $referral)
683   {
684     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
685     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
686       $this->error = "Success";
687       $this->hascon=true;
688       $this->reconnect= true;
689       return (0);
690     } else {
691       $this->error = "Could not bind to " . $credentials['ADMIN'];
692       return NULL;
693     }
694   }
697   /* Recursively copy ldap object */
698   function _copy($src_dn,$dst_dn)
699   {
700     $ldap=$this->config->get_ldap_link();
701     $ldap->cat($src_dn);
702     $attrs= $ldap->fetch();
704     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
705     $ds= ldap_connect($this->config->current['SERVER']);
706     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
707     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
708       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
709     }
711     $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
712     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
713     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
715     /* Fill data from LDAP */
716     $new= array();
717     if ($sr) {
718       $ei=ldap_first_entry($ds, $sr);
719       if ($ei) {
720         foreach($attrs as $attr => $val){
721           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
722             for ($i= 0; $i<$info['count']; $i++){
723               if ($info['count'] == 1){
724                 $new[$attr]= $info[$i];
725               } else {
726                 $new[$attr][]= $info[$i];
727               }
728             }
729           }
730         }
731       }
732     }
734     /* close conncetion */
735     ldap_unbind($ds);
737     /* Adapt naming attribute */
738     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
739     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
740     $new[$dst_name]= LDAP::fix($dst_val);
742     /* Check if this is a department.
743      * If it is a dep. && there is a , override in his ou 
744      *  change \2C to , again, else this entry can't be saved ...
745      */
746     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
747       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
748     }
750     /* Save copy */
751     $ldap->connect();
752     $ldap->cd($this->config->current['BASE']);
753     
754     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
756     /* FAIvariable=.../..., cn=.. 
757         could not be saved, because the attribute FAIvariable was different to 
758         the dn FAIvariable=..., cn=... */
760     if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
762     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
763       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
764     }
765     $ldap->cd($dst_dn);
766     $ldap->add($new);
768     if (!$ldap->success()){
769       trigger_error("Trying to save $dst_dn failed.",
770           E_USER_WARNING);
771       return(FALSE);
772     }
773     return(TRUE);
774   }
777   /* This is a workaround function. */
778   function copy($src_dn, $dst_dn)
779   {
780     /* Rename dn in possible object groups */
781     $ldap= $this->config->get_ldap_link();
782     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
783         array('cn'));
784     while ($attrs= $ldap->fetch()){
785       $og= new ogroup($this->config, $ldap->getDN());
786       unset($og->member[$src_dn]);
787       $og->member[$dst_dn]= $dst_dn;
788       $og->save ();
789     }
791     $ldap->cat($dst_dn);
792     $attrs= $ldap->fetch();
793     if (count($attrs)){
794       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
795           E_USER_WARNING);
796       return (FALSE);
797     }
799     $ldap->cat($src_dn);
800     $attrs= $ldap->fetch();
801     if (!count($attrs)){
802       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
803           E_USER_WARNING);
804       return (FALSE);
805     }
807     $ldap->cd($src_dn);
808     $ldap->search("objectClass=*",array("dn"));
809     while($attrs = $ldap->fetch()){
810       $src = $attrs['dn'];
811       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
812       $this->_copy($src,$dst);
813     }
814     return (TRUE);
815   }
819   /*! \brief  Rename/Move a given src_dn to the given dest_dn
820    *
821    * Move a given ldap object indentified by $src_dn to the
822    * given destination $dst_dn
823    *
824    * - Ensure that all references are updated (ogroups)
825    * - Update ACLs   
826    * - Update accessTo
827    *
828    * \param  string  'src_dn' the source DN.
829    * \param  string  'dst_dn' the destination DN.
830    * \return boolean TRUE on success else FALSE.
831    */
832   function rename($src_dn, $dst_dn)
833   {
834     $start = microtime(1);
836     /* Try to move the source entry to the destination position */
837     $ldap = $this->config->get_ldap_link();
838     $ldap->cd($this->config->current['BASE']);
839     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
840     if (!$ldap->rename_dn($src_dn,$dst_dn)){
841       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());
842       @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn  -- TO:  $dst_dn", 
843           "Ldap Protocol v3 implementation error, falling back to maunal method.");
844       return(FALSE);
845     }
847     /* Get list of users,groups and roles within this tree,
848         maybe we have to update ACL references.
849      */
850     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
851           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
852     foreach($leaf_objs as $obj){
853       $new_dn = $obj['dn'];
854       $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
855       $this->update_acls($old_dn,$new_dn); 
856     }
858     // Migrate objectgroups if needed
859     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
860       "ogroups", array(get_ou("group", "ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
862     // Walk through all objectGroups
863     foreach($ogroups as $ogroup){
864       // Migrate old to new dn
865       $o_ogroup= new ogroup($this->config,$ogroup['dn']);
866       if (isset($o_ogroup->member[$src_dn])) {
867         unset($o_ogroup->member[$src_dn]);
868       }
869       $o_ogroup->member[$dst_dn]= $dst_dn;
870       
871       // Save object group
872       $o_ogroup->save();
873     }
875     // Migrate rfc groups if needed
876     $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);
878     // Walk through all POSIX groups
879     foreach($groups as $group){
881       // Migrate old to new dn
882       $o_group= new group($this->config,$group['dn']);
883       $o_group->save();
884     }
886     /* Update roles to use the new entry dn */
887     $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);
889     // Walk through all roles
890     foreach($roles as $role){
891       $role = new roleGeneric($this->config,$role['dn']);
892       $key= array_search($src_dn, $role->roleOccupant);      
893       if($key !== FALSE){
894         $role->roleOccupant[$key] = $dst_dn;
895         $role->save();
896       }
897     }
899     // Update 'manager' attributes from gosaDepartment and inetOrgPerson 
900     $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
901     $ocs = $ldap->get_objectclasses();
902     if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
903       $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
904     }
905     $leaf_deps=  get_list($filter,array("all"),$this->config->current['BASE'], 
906         array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
907     foreach($leaf_deps as $entry){
908       $update = array('manager' => $dst_dn);
909       $ldap->cd($entry['dn']);
910       $ldap->modify($update);
911       if(!$ldap->success()){
912         trigger_error(sprintf("Failed to update manager for %s: %s", bold($entry['dn']), $ldap->get_error()));
913       }
914     }
916     // Migrate 'dyn-groups' here. labeledURIObject
917     if(class_available('DynamicLdapGroup')) {
918         DynamicLdapGroup::moveDynGroup($this->config,$src_dn,$dst_dn);
919     }
920  
921     /* Check if there are gosa departments moved. 
922        If there were deps moved, the force reload of config->deps.
923      */
924     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
925           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
926   
927     if(count($leaf_deps)){
928       $this->config->get_departments();
929       $this->config->make_idepartments();
930       session::global_set("config",$this->config);
931       $ui =get_userinfo();
932       $ui->reset_acl_cache();
933     }
935     return(TRUE); 
936   }
939  
940   function move($src_dn, $dst_dn)
941   {
942     /* Do not copy if only upper- lowercase has changed */
943     if(strtolower($src_dn) == strtolower($dst_dn)){
944       return(TRUE);
945     }
947     
948     /* Try to move the entry instead of copy & delete
949      */
950     if(TRUE){
952       /* Try to move with ldap routines, if this was not successfull
953           fall back to the old style copy & remove method 
954        */
955       if($this->rename($src_dn, $dst_dn)){
956         return(TRUE);
957       }else{
958         // See code below.
959       }
960     }
962     /* Copy source to destination */
963     if (!$this->copy($src_dn, $dst_dn)){
964       return (FALSE);
965     }
967     /* Delete source */
968     $ldap= $this->config->get_ldap_link();
969     $ldap->rmdir_recursive($src_dn);
970     if (!$ldap->success()){
971       trigger_error("Trying to delete $src_dn failed.",
972           E_USER_WARNING);
973       return (FALSE);
974     }
976     return (TRUE);
977   }
980   /* \brief Move/Rename complete trees */
981   function recursive_move($src_dn, $dst_dn)
982   {
983     /* Check if the destination entry exists */
984     $ldap= $this->config->get_ldap_link();
986     /* Check if destination exists - abort */
987     $ldap->cat($dst_dn, array('dn'));
988     if ($ldap->fetch()){
989       trigger_error("recursive_move $dst_dn already exists.",
990           E_USER_WARNING);
991       return (FALSE);
992     }
994     $this->copy($src_dn, $dst_dn);
996     /* Remove src_dn */
997     $ldap->cd($src_dn);
998     $ldap->recursive_remove($src_dn);
999     return (TRUE);
1000   }
1003   function saveCopyDialog(){
1004   }
1007   function getCopyDialog(){
1008     return(array("string"=>"","status"=>""));
1009   }
1012   /*! \brief Prepare for Copy & Paste */
1013   function PrepareForCopyPaste($source)
1014   {
1015     $todo = $this->attributes;
1016     if(isset($this->CopyPasteVars)){
1017       $todo = array_merge($todo,$this->CopyPasteVars);
1018     }
1020     if(count($this->objectclasses)){
1021       $this->is_account = TRUE;
1022       foreach($this->objectclasses as $class){
1023         if(!in_array($class,$source['objectClass'])){
1024           $this->is_account = FALSE;
1025         }
1026       }
1027     }
1029     foreach($todo as $var){
1030       if (isset($source[$var])){
1031         if(isset($source[$var]['count'])){
1032           if($source[$var]['count'] > 1){
1033             $tmp= $source[$var];
1034             unset($tmp['count']);
1035             $this->$var = $tmp;
1036           }else{
1037             $this->$var = $source[$var][0];
1038           }
1039         }else{
1040           $this->$var= $source[$var];
1041         }
1042       }
1043     }
1044   }
1046   /*! \brief Get gosaUnitTag for the given DN
1047        If this is called from departmentGeneric, we have to skip this
1048         tagging procedure. 
1049     */
1050   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1051   {
1052     /* Skip tagging? */
1053     if($this->skipTagging){
1054       return;
1055     }
1057     /* No dn? Self-operation... */
1058     if ($dn == ""){
1059       $dn= $this->dn;
1061       /* No tag? Find it yourself... */
1062       if ($tag == ""){
1063         $len= strlen($dn);
1065         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1066         $relevant= array();
1067         foreach ($this->config->adepartments as $key => $ntag){
1069           /* This one is bigger than our dn, its not relevant... */
1070           if ($len < strlen($key)){
1071             continue;
1072           }
1074           /* This one matches with the latter part. Break and don't fix this entry */
1075           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1076             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1077             $relevant[strlen($key)]= $ntag;
1078             continue;
1079           }
1081         }
1083         /* If we've some relevant tags to set, just get the longest one */
1084         if (count($relevant)){
1085           ksort($relevant);
1086           $tmp= array_keys($relevant);
1087           $idx= end($tmp);
1088           $tag= $relevant[$idx];
1089           $this->gosaUnitTag= $tag;
1090         }
1091       }
1092     }
1094   /*! \brief Add unit tag */ 
1095     /* Remove tags that may already be here... */
1096     remove_objectClass("gosaAdministrativeUnitTag", $at);
1097     if (isset($at['gosaUnitTag'])){
1098         unset($at['gosaUnitTag']);
1099     }
1101     /* Set tag? */
1102     if ($tag != ""){
1103       add_objectClass("gosaAdministrativeUnitTag", $at);
1104       $at['gosaUnitTag']= $tag;
1105     }
1107     /* Initially this object was tagged. 
1108        - But now, it is no longer inside a tagged department. 
1109        So force the remove of the tag.
1110        (objectClass was already removed obove)
1111      */
1112     if($tag == "" && $this->gosaUnitTag){
1113       $at['gosaUnitTag'] = array();
1114     }
1115   }
1118   /*! \brief Test for removability of the object
1119    *
1120    * Allows testing of conditions for removal of object. If removal should be aborted
1121    * the function needs to remove an error message.
1122    * */
1123   function allow_remove()
1124   {
1125     $reason= "";
1126     return $reason;
1127   }
1130   /*! \brief Test if snapshotting is enabled
1131    *
1132    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1133    * if the configuration failed 
1134    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1135    */
1136   function snapshotEnabled()
1137   {
1138       return $this->config->snapshotEnabled();
1139   }
1142   /*! \brief Return plugin informations for acl handling 
1143    *         See class_core.inc for examples.
1144    */
1145   static function plInfo()
1146   {
1147     return array();
1148   }
1151   function set_acl_base($base)
1152   {
1153     @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$base."</b>","<b>ACL-Base:</b> ");
1154     $this->acl_base= $base;
1155   }
1158   function set_acl_category($category)
1159   {
1160     @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$category."</b>(/".get_class($this).")","<b>ACL-Category:</b> ");
1161     $this->acl_category= "$category/";
1162   }
1165   function acl_is_writeable($attribute,$skip_write = FALSE)
1166   {
1167     if($this->read_only) return(FALSE);
1168     $ui= get_userinfo();
1169     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1170   }
1173   function acl_is_readable($attribute)
1174   {
1175     $ui= get_userinfo();
1176     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1177   }
1180   function acl_is_createable($base ="")
1181   {
1182     if($this->read_only) return(FALSE);
1183     $ui= get_userinfo();
1184     if($base == "") $base = $this->acl_base;
1185     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1186   }
1189   function acl_is_removeable($base ="")
1190   {
1191     if($this->read_only) return(FALSE);
1192     $ui= get_userinfo();
1193     if($base == "") $base = $this->acl_base;
1194     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1195   }
1198   function acl_is_moveable($base = "")
1199   {
1200     if($this->read_only) return(FALSE);
1201     $ui= get_userinfo();
1202     if($base == "") $base = $this->acl_base;
1203     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1204   }
1207   function acl_have_any_permissions()
1208   {
1209   }
1212   function getacl($attribute,$skip_write= FALSE)
1213   {
1214     $ui= get_userinfo();
1215     $skip_write |= $this->read_only;
1216     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1217   }
1220   /*! \brief Returns a list of all available departments for this object.
1221    * 
1222    * If this object is new, all departments we are allowed to create a new user in
1223    * are returned. If this is an existing object, return all deps. 
1224    * We are allowed to move tis object too.
1225    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1226   */
1227   function get_allowed_bases()
1228   {
1229     $ui = get_userinfo();
1230     $deps = array();
1232     /* Is this a new object ? Or just an edited existing object */
1233     if(!$this->initially_was_account && $this->is_account){
1234       $new = true;
1235     }else{
1236       $new = false;
1237     }
1239     foreach($this->config->idepartments as $dn => $name){
1240       if($new && $this->acl_is_createable($dn)){
1241         $deps[$dn] = $name;
1242       }elseif(!$new && $this->acl_is_moveable($dn)){
1243         $deps[$dn] = $name;
1244       }
1245     }
1247     /* Add current base */      
1248     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1249       $deps[$this->base] = $this->config->idepartments[$this->base];
1250     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1252     }else{
1253       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1254     }
1255     return($deps);
1256   }
1259   /* This function updates ACL settings if $old_dn was used.
1260    *  \param string 'old_dn' specifies the actually used dn
1261    *  \param string 'new_dn' specifies the destiantion dn
1262    */
1263   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1264   {
1265     /* Check if old_dn is empty. This should never happen */
1266     if(empty($old_dn) || empty($new_dn)){
1267       trigger_error("Failed to check acl dependencies, wrong dn given.");
1268       return;
1269     }
1271     /* Update userinfo if necessary */
1272     $ui = session::global_get('ui');
1273     if($ui->dn == $old_dn){
1274       $ui->dn = $new_dn;
1275       session::global_set('ui',$ui);
1276       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1277     }
1279     /* Object was moved, ensure that all acls will be moved too */
1280     if($new_dn != $old_dn && $old_dn != "new"){
1282       /* get_ldap configuration */
1283       $update = array();
1284       $ldap = $this->config->get_ldap_link();
1285       $ldap->cd ($this->config->current['BASE']);
1286       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1287       while($attrs = $ldap->fetch()){
1288         $acls = array();
1289         $found = false;
1290         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1291           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1293           /* Roles uses antoher data storage order, members are stored int the third part, 
1294              while the members in direct ACL assignments are stored in the second part.
1295            */
1296           $id = ($acl_parts[1] == "role") ? 3 : 2;
1298           /* Update member entries to use $new_dn instead of old_dn
1299            */
1300           $members = explode(",",$acl_parts[$id]);
1301           foreach($members as $key => $member){
1302             $member = base64_decode($member);
1303             if($member == $old_dn){
1304               $members[$key] = base64_encode($new_dn);
1305               $found = TRUE;
1306             }
1307           } 
1309           /* Check if the selected role has to updated
1310            */
1311           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1312             $acl_parts[2] = base64_encode($new_dn);
1313             $found = TRUE;
1314           }
1316           /* Build new acl string */ 
1317           $acl_parts[$id] = implode($members,",");
1318           $acls[] = implode($acl_parts,":");
1319         }
1321         /* Acls for this object must be adjusted */
1322         if($found){
1324           $debug_info= sprintf(_("Changing ACL DN from %s to %s"), bold($old_dn), bold($new_dn));
1325           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1327           $update[$attrs['dn']] =array();
1328           foreach($acls as $acl){
1329             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1330           }
1331         }
1332       }
1334       /* Write updated acls */
1335       foreach($update as $dn => $attrs){
1336         $ldap->cd($dn);
1337         $ldap->modify($attrs);
1338       }
1339     }
1340   }
1342   
1344   /*! \brief Enable the Serial ID check
1345    *
1346    * This function enables the entry Serial ID check.  If an entry was edited while
1347    * we have edited the entry too, an error message will be shown. 
1348    * To configure this check correctly read the FAQ.
1349    */    
1350   function enable_CSN_check()
1351   {
1352     $this->CSN_check_active =TRUE;
1353     $this->entryCSN = getEntryCSN($this->dn);
1354   }
1357   /*! \brief  Prepares the plugin to be used for multiple edit
1358    *          Update plugin attributes with given array of attribtues.
1359    *  \param  array   Array with attributes that must be updated.
1360    */
1361   function init_multiple_support($attrs,$all)
1362   {
1363     $ldap= $this->config->get_ldap_link();
1364     $this->multi_attrs    = $attrs;
1365     $this->multi_attrs_all= $all;
1367     /* Copy needed attributes */
1368     foreach ($this->attributes as $val){
1369       $found= array_key_ics($val, $this->multi_attrs);
1370  
1371       if ($found != ""){
1372         if(isset($this->multi_attrs["$val"][0])){
1373           $this->$val= $this->multi_attrs["$val"][0];
1374         }
1375       }
1376     }
1377   }
1379  
1380   /*! \brief  Enables multiple support for this plugin
1381    */
1382   function enable_multiple_support()
1383   {
1384     $this->ignore_account = TRUE;
1385     $this->multiple_support_active = TRUE;
1386   }
1389   /*! \brief  Returns all values that have been modfied in multiple edit mode.
1390       \return array Cotaining all modified values. 
1391    */
1392   function get_multi_edit_values()
1393   {
1394     $ret = array();
1395     foreach($this->attributes as $attr){
1396       if(in_array($attr,$this->multi_boxes)){
1397         $ret[$attr] = $this->$attr;
1398       }
1399     }
1400     return($ret);
1401   }
1403   
1404   /*! \brief  Update class variables with values collected by multiple edit.
1405    */
1406   function set_multi_edit_values($attrs)
1407   {
1408     foreach($attrs as $name => $value){
1409       $this->$name = $value;
1410     }
1411   }
1414   /*! \brief Generates the html output for this node for multi edit*/
1415   function multiple_execute()
1416   {
1417     /* This one is empty currently. Fabian - please fill in the docu code */
1418     session::global_set('current_class_for_help',get_class($this));
1420     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1421     session::set('LOCK_VARS_TO_USE',array());
1422     session::set('LOCK_VARS_USED_GET',array());
1423     session::set('LOCK_VARS_USED_POST',array());
1424     session::set('LOCK_VARS_USED_REQUEST',array());
1425     
1426     return("Multiple edit is currently not implemented for this plugin.");
1427   }
1430   /*! \brief Save HTML posted data to object for multiple edit
1431    */
1432   function multiple_save_object()
1433   {
1434     if(empty($this->entryCSN) && $this->CSN_check_active){
1435       $this->entryCSN = getEntryCSN($this->dn);
1436     }
1438     /* Save values to object */
1439     $this->multi_boxes = array();
1440     foreach ($this->attributes as $val){
1441   
1442       /* Get selected checkboxes from multiple edit */
1443       if(isset($_POST["use_".$val])){
1444         $this->multi_boxes[] = $val;
1445       }
1447       if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
1449         $data= $this->$val = get_post($val);
1450         if ($this->$val != $data){
1451           $this->is_modified= TRUE;
1452         }
1453     
1454         /* IE post fix */
1455         if(isset($data[0]) && $data[0] == chr(194)) {
1456           $data = "";  
1457         }
1458         $this->$val= $data;
1459       }
1460     }
1461   }
1464   /*! \brief Returns all attributes of this plugin, 
1465                to be able to detect multiple used attributes 
1466                in multi_plugg::detect_multiple_used_attributes().
1467       @return array Attributes required for intialization of multi_plug
1468    */
1469   public function get_multi_init_values()
1470   {
1471     $attrs = $this->attrs;
1472     return($attrs);
1473   }
1476   /*! \brief  Check given values in multiple edit
1477       \return array Error messages
1478    */
1479   function multiple_check()
1480   {
1481     $message = plugin::check();
1482     return($message);
1483   }
1485   function get_used_snapshot_bases()
1486   {
1487      return(array());
1488   }
1490   function is_modal_dialog()
1491   {
1492     return(isset($this->dialog) && $this->dialog);
1493   }
1496   /*! \brief    Forward command execution requests
1497    *             to the hook execution method. 
1498    */
1499   function handle_post_events($mode, $addAttrs= array())
1500   {
1501     if(!in_array($mode, array('add','remove','modify'))){
1502       trigger_error(sprintf("Invalid post event type given %s! Valid types are [add,modify,remove].", bold($mode)));
1503       return;
1504     }
1505     switch ($mode){
1506       case "add":
1507         plugin::callHook($this,"POSTCREATE", $addAttrs);
1508       break;
1510       case "modify":
1511         plugin::callHook($this,"POSTMODIFY", $addAttrs);
1512       break;
1514       case "remove":
1515         plugin::callHook($this,"POSTREMOVE", $addAttrs);
1516       break;
1517     }
1518   }
1521   /*! \brief    Calls external hooks which are defined for this plugin (gosa.conf)
1522    *            Replaces placeholder by class values of this plugin instance.
1523    *  @param    Allows to a add special replacements.
1524    */
1525   static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(), &$returnCode = NULL)
1526   {
1527     global $config;
1528     $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
1529     
1530     if ($command != ""){
1532       // Walk trough attributes list and add the plugins attributes. 
1533       foreach ($plugin->attributes as $attr){
1534         if (!is_array($plugin->$attr)){
1535           $addAttrs[$attr] = $plugin->$attr;
1536         }
1537       }
1538       $ui = get_userinfo();
1539       $addAttrs['callerDN']=$ui->dn;
1540       $addAttrs['dn']=$plugin->dn;
1542       // Sort attributes by length, ensures correct replacement
1543       $tmp = array();
1544       foreach($addAttrs as $name => $value){
1545         $tmp[$name] =  strlen($name);
1546       }
1547       arsort($tmp);
1549       // Now replace the placeholder 
1550       foreach ($tmp as $name => $len){
1551         $value = $addAttrs[$name];
1552         $command= str_replace("%$name", "$value", $command);
1553       }
1555       // If there are still some %.. in our command, try to fill these with some other class vars 
1556       if(preg_match("/%/",$command)){
1557         $attrs = get_object_vars($plugin);
1558         foreach($attrs as $name => $value){
1559           if(is_array($value)){
1560             $s = "";
1561             foreach($value as $val){
1562               if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
1563                 $s .= '"'.$val.'",'; 
1564               }
1565             }
1566             $value = '['.trim($s,',').']';
1567           }
1568           if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
1569             continue;
1570           }
1571           $command= preg_replace("/%$name/", $value, $command);
1572         }
1573       }
1575       if (check_command($command)){
1577         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
1578         exec($command, $arr, $returnCode);
1579         $returnOutput = $arr;
1581         if($returnCode != 0){
1582           $str = implode("\n",$arr);
1583           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1584           $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
1585           msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1586         }elseif(is_array($arr)){
1587           $str = implode("\n",$arr);
1588           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
1589         }
1590       } else {
1591         $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
1592         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1593       }
1594     }
1595   }
1598 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1599 ?>