bdb9c7aca00cf17dbf717a42babebd706f0365b7
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 {
149 $this->initTime = microtime(TRUE);
150 stats::log('plugin', $class = get_class($this), $action = 'open', $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
152 /* Configuration is fine, allways */
153 $this->config= &$config;
154 $this->dn= $dn;
156 // Ensure that we've a valid acl_category set.
157 if(empty($this->acl_category)){
158 $tmp = $this->plInfo();
159 if (isset($tmp['plCategory'])) {
160 $c = key($tmp['plCategory']);
161 if(is_numeric($c)){
162 $c = $tmp['plCategory'][0];
163 }
164 $this->acl_category = $c."/";
165 }
166 }
168 /* Handle new accounts, don't read information from LDAP */
169 if ($dn == "new"){
170 return;
171 }
173 /* Check if this entry was opened in read only mode */
174 if(isset($_POST['open_readonly'])){
175 if(session::global_is_set("LOCK_CACHE")){
176 $cache = &session::get("LOCK_CACHE");
177 if(isset($cache['READ_ONLY'][$this->dn])){
178 $this->read_only = TRUE;
179 }
180 }
181 }
183 /* Save current dn as acl_base */
184 $this->acl_base= $dn;
186 /* Get LDAP descriptor */
187 if ($dn !== NULL){
189 /* Load data to 'attrs' and save 'dn' */
190 if ($object !== NULL){
191 $this->attrs= $object->attrs;
192 } else {
193 $ldap= $this->config->get_ldap_link();
194 $ldap->cat ($dn);
195 $this->attrs= $ldap->fetch();
196 }
198 /* Copy needed attributes */
199 foreach ($this->attributes as $val){
200 $found= array_key_ics($val, $this->attrs);
201 if ($found != ""){
202 $this->$val= $found[0];
203 }
204 }
206 /* gosaUnitTag loading... */
207 if (isset($this->attrs['gosaUnitTag'][0])){
208 $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
209 }
211 /* Set the template flag according to the existence of objectClass
212 gosaUserTemplate */
213 if (isset($this->attrs['objectClass'])){
214 if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
215 $this->is_template= TRUE;
216 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
217 "found", "Template check");
218 }
219 }
221 /* Is Account? */
222 $found= TRUE;
223 foreach ($this->objectclasses as $obj){
224 if (preg_match('/top/i', $obj)){
225 continue;
226 }
227 if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
228 $found= FALSE;
229 break;
230 }
231 }
232 if ($found){
233 $this->is_account= TRUE;
234 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
235 "found", "Object check");
236 }
238 /* Prepare saved attributes */
239 $this->saved_attributes= $this->attrs;
240 foreach ($this->saved_attributes as $index => $value){
241 if (is_numeric($index)){
242 unset($this->saved_attributes[$index]);
243 continue;
244 }
246 if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
247 unset($this->saved_attributes[$index]);
248 continue;
249 }
251 if (isset($this->saved_attributes[$index][0])){
252 if(!isset($this->saved_attributes[$index]["count"])){
253 $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
254 }
255 if($this->saved_attributes[$index]["count"] == 1){
256 $tmp= $this->saved_attributes[$index][0];
257 unset($this->saved_attributes[$index]);
258 $this->saved_attributes[$index]= $tmp;
259 continue;
260 }
261 }
262 unset($this->saved_attributes["$index"]["count"]);
263 }
265 if(isset($this->attrs['gosaUnitTag'])){
266 $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
267 }
268 }
270 /* Save initial account state */
271 $this->initially_was_account= $this->is_account;
272 }
275 /*! \brief Generates the html output for this node
276 */
277 function execute()
278 {
279 /* This one is empty currently. Fabian - please fill in the docu code */
280 session::global_set('current_class_for_help',get_class($this));
282 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
283 session::set('LOCK_VARS_TO_USE',array());
284 session::set('LOCK_VARS_USED_GET',array());
285 session::set('LOCK_VARS_USED_POST',array());
286 session::set('LOCK_VARS_USED_REQUEST',array());
288 pathNavigator::registerPlugin($this);
289 stats::log('plugin', $class = get_class($this), $action = 'view', $amount = 1, $duration = microtime(TRUE) - $this->initTime);
290 }
292 /*! \brief Removes object from parent
293 */
294 function remove_from_parent()
295 {
296 /* include global link_info */
297 $ldap= $this->config->get_ldap_link();
299 /* Get current objectClasses in order to add the required ones */
300 $ldap->cat($this->dn);
301 $tmp= $ldap->fetch ();
302 $oc= array();
303 if (isset($tmp['objectClass'])){
304 $oc= $tmp['objectClass'];
305 unset($oc['count']);
306 }
308 /* Remove objectClasses from entry */
309 $ldap->cd($this->dn);
310 $this->attrs= array();
311 $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
313 /* Unset attributes from entry */
314 foreach ($this->attributes as $val){
315 $this->attrs["$val"]= array();
316 }
318 /* Unset account info */
319 $this->is_account= FALSE;
321 /* Do not write in plugin base class, this must be done by
322 children, since there are normally additional attribs,
323 lists, etc. */
324 /*
325 $ldap->modify($this->attrs);
326 */
327 if($this->initially_was_account){
328 $this->handle_pre_events('remove');
329 stats::log('plugin', $class = get_class($this), $action = 'remove', $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
330 }
331 }
334 /*! \brief Save HTML posted data to object
335 */
336 function save_object()
337 {
338 /* Update entry CSN if it is empty. */
339 if(empty($this->entryCSN) && $this->CSN_check_active){
340 $this->entryCSN = getEntryCSN($this->dn);
341 }
343 /* Save values to object */
344 foreach ($this->attributes as $val){
345 if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
347 /* Check for modifications */
348 $data= get_post($val);
349 if ($this->$val != $data){
350 $this->is_modified= TRUE;
351 }
352 $this->$val = $data;
354 /* Okay, how can I explain this fix ...
355 * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds.
356 * So IE posts these 'unselectable' option, with value = chr(194)
357 * chr(194) seems to be the in between the ...option> </option.. because there is no value=".." specified in these option fields
358 * This was added for W3c compliance, but now causes these ... ldap errors ...
359 * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
360 */
361 if(isset($data[0]) && $data[0] == chr(194)) {
362 $data = "";
363 }
364 $this->$val= $data;
365 }
366 }
367 }
370 /*! \brief Save data to LDAP, depending on is_account we save or delete */
371 function save()
372 {
373 /* include global link_info */
374 $ldap= $this->config->get_ldap_link();
376 /* Save all plugins */
377 $this->entryCSN = "";
379 /* Start with empty array */
380 $this->attrs= array();
382 /* Get current objectClasses in order to add the required ones */
383 $ldap->cat($this->dn);
385 $tmp= $ldap->fetch ();
387 $oc= array();
388 if (isset($tmp['objectClass'])){
389 $oc= $tmp["objectClass"];
390 $this->is_new= FALSE;
391 unset($oc['count']);
392 } else {
393 $this->is_new= TRUE;
394 }
396 /* Load (minimum) attributes, add missing ones */
397 $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
399 /* Copy standard attributes */
400 foreach ($this->attributes as $val){
401 if ($this->$val != ""){
402 $this->attrs["$val"]= $this->$val;
403 } elseif (!$this->is_new) {
404 $this->attrs["$val"]= array();
405 }
406 }
408 /* Handle tagging */
409 $this->tag_attrs($this->attrs);
411 if($this->is_new){
412 $this->handle_pre_events('add');
413 stats::log('plugin', $class = get_class($this), $action = 'create', $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
414 }else{
415 $this->handle_pre_events('modify');
416 stats::log('plugin', $class = get_class($this), $action = 'modify', $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
417 }
418 }
421 /*! \brief Forward command execution requests
422 * to the hook execution method.
423 */
424 function handle_pre_events($mode, $addAttrs= array())
425 {
426 if(!in_array($mode, array('add','remove','modify'))){
427 trigger_error(sprintf("Invalid pre event type given %s! Valid types are [add,modify,remove].", $mode));
428 return;
429 }
430 switch ($mode){
431 case "add":
432 plugin::callHook($this,"PRECREATE", $addAttrs);
433 break;
435 case "modify":
436 plugin::callHook($this,"PREMODIFY", $addAttrs);
437 break;
439 case "remove":
440 plugin::callHook($this,"PREREMOVE", $addAttrs);
441 break;
442 }
443 }
446 function cleanup()
447 {
448 foreach ($this->attrs as $index => $value){
450 /* Convert arrays with one element to non arrays, if the saved
451 attributes are no array, too */
452 if (is_array($this->attrs[$index]) &&
453 count ($this->attrs[$index]) == 1 &&
454 isset($this->saved_attributes[$index]) &&
455 !is_array($this->saved_attributes[$index])){
457 $tmp= $this->attrs[$index][0];
458 $this->attrs[$index]= $tmp;
459 }
461 /* Remove emtpy arrays if they do not differ */
462 if (is_array($this->attrs[$index]) &&
463 count($this->attrs[$index]) == 0 &&
464 !isset($this->saved_attributes[$index])){
466 unset ($this->attrs[$index]);
467 continue;
468 }
470 /* Remove single attributes that do not differ */
471 if (!is_array($this->attrs[$index]) &&
472 isset($this->saved_attributes[$index]) &&
473 !is_array($this->saved_attributes[$index]) &&
474 $this->attrs[$index] == $this->saved_attributes[$index]){
476 unset ($this->attrs[$index]);
477 continue;
478 }
480 /* Remove arrays that do not differ */
481 if (is_array($this->attrs[$index]) &&
482 isset($this->saved_attributes[$index]) &&
483 is_array($this->saved_attributes[$index])){
485 if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
486 unset ($this->attrs[$index]);
487 continue;
488 }
489 }
490 }
492 /* Update saved attributes and ensure that next cleanups will be successful too */
493 foreach($this->attrs as $name => $value){
494 $this->saved_attributes[$name] = $value;
495 }
496 }
498 /*! \brief Check formular input */
499 function check()
500 {
501 $message= array();
503 /* Skip if we've no config object */
504 if (!isset($this->config) || !is_object($this->config)){
505 return $message;
506 }
508 /* Find hooks entries for this class */
509 $command = $this->config->configRegistry->getPropertyValue(get_class($this),"check");
510 if ($command != ""){
512 if (!check_command($command)){
513 $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
514 } else {
516 /* Generate "ldif" for check hook */
517 $ldif= "dn: $this->dn\n";
519 /* ... objectClasses */
520 foreach ($this->objectclasses as $oc){
521 $ldif.= "objectClass: $oc\n";
522 }
524 /* ... attributes */
525 foreach ($this->attributes as $attr){
526 if ($this->$attr == ""){
527 continue;
528 }
529 if (is_array($this->$attr)){
530 foreach ($this->$attr as $val){
531 $ldif.= "$attr: $val\n";
532 }
533 } else {
534 $ldif.= "$attr: ".$this->$attr."\n";
535 }
536 }
538 /* Append empty line */
539 $ldif.= "\n";
541 /* Feed "ldif" into hook and retrieve result*/
542 $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
543 $fh= proc_open($command, $descriptorspec, $pipes);
544 if (is_resource($fh)) {
545 fwrite ($pipes[0], $ldif);
546 fclose($pipes[0]);
548 $result= stream_get_contents($pipes[1]);
549 if ($result != ""){
550 $message[]= $result;
551 }
553 fclose($pipes[1]);
554 fclose($pipes[2]);
555 proc_close($fh);
556 }
557 }
559 }
561 /* Check entryCSN */
562 if($this->CSN_check_active){
563 $current_csn = getEntryCSN($this->dn);
564 if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
565 $this->entryCSN = $current_csn;
566 $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!");
567 }
568 }
569 return ($message);
570 }
572 /* Adapt from template, using 'dn' */
573 function adapt_from_template($dn, $skip= array())
574 {
575 /* Include global link_info */
576 $ldap= $this->config->get_ldap_link();
578 /* Load requested 'dn' to 'attrs' */
579 $ldap->cat ($dn);
580 $this->attrs= $ldap->fetch();
582 /* Walk through attributes */
583 foreach ($this->attributes as $val){
585 /* Skip the ones in skip list */
586 if (in_array($val, $skip)){
587 continue;
588 }
590 if (isset($this->attrs["$val"][0])){
592 /* If attribute is set, replace dynamic parts:
593 %sn, %givenName and %uid. Fill these in our local variables. */
594 $value= $this->attrs["$val"][0];
596 foreach (array("sn", "givenName", "uid") as $repl){
597 if (preg_match("/%$repl/i", $value)){
598 $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
599 }
600 }
601 $this->$val= $value;
602 }
603 }
605 /* Is Account? */
606 $found= TRUE;
607 foreach ($this->objectclasses as $obj){
608 if (preg_match('/top/i', $obj)){
609 continue;
610 }
611 if (!in_array_ics ($obj, $this->attrs['objectClass'])){
612 $found= FALSE;
613 break;
614 }
615 }
616 if ($found){
617 $this->is_account= TRUE;
618 }
619 }
621 /* \brief Indicate whether a password change is needed or not */
622 function password_change_needed()
623 {
624 return FALSE;
625 }
628 /*! \brief Show header message for tab dialogs */
629 function show_enable_header($button_text, $text, $disabled= FALSE)
630 {
631 if (($disabled == TRUE) || (!$this->acl_is_createable())){
632 $state= "disabled";
633 } else {
634 $state= "";
635 }
636 $display = "<div class='plugin-enable-header'>\n";
637 $display.= "<p>$text</p>\n";
638 $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
639 $display.= "</div>\n";
641 return($display);
642 }
645 /*! \brief Show header message for tab dialogs */
646 function show_disable_header($button_text, $text, $disabled= FALSE)
647 {
648 if (($disabled == TRUE) || !$this->acl_is_removeable()){
649 $state= "disabled";
650 } else {
651 $state= "";
652 }
653 $display = "<div class='plugin-disable-header'>\n";
654 $display.= "<p>$text</p>\n";
655 $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
656 $display.= "</div>\n";
657 return($display);
658 }
662 /* Create unique DN */
663 function create_unique_dn2($data, $base)
664 {
665 $ldap= $this->config->get_ldap_link();
666 $base= preg_replace("/^,*/", "", $base);
668 /* Try to use plain entry first */
669 $dn= "$data,$base";
670 $attribute= preg_replace('/=.*$/', '', $data);
671 $ldap->cat ($dn, array('dn'));
672 if (!$ldap->fetch()){
673 return ($dn);
674 }
676 /* Look for additional attributes */
677 foreach ($this->attributes as $attr){
678 if ($attr == $attribute || $this->$attr == ""){
679 continue;
680 }
682 $dn= "$data+$attr=".$this->$attr.",$base";
683 $ldap->cat ($dn, array('dn'));
684 if (!$ldap->fetch()){
685 return ($dn);
686 }
687 }
689 /* None found */
690 return ("none");
691 }
694 /*! \brief Create unique DN */
695 function create_unique_dn($attribute, $base)
696 {
697 $ldap= $this->config->get_ldap_link();
698 $base= preg_replace("/^,*/", "", $base);
700 /* Try to use plain entry first */
701 $dn= "$attribute=".$this->$attribute.",$base";
702 $ldap->cat ($dn, array('dn'));
703 if (!$ldap->fetch()){
704 return ($dn);
705 }
707 /* Look for additional attributes */
708 foreach ($this->attributes as $attr){
709 if ($attr == $attribute || $this->$attr == ""){
710 continue;
711 }
713 $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
714 $ldap->cat ($dn, array('dn'));
715 if (!$ldap->fetch()){
716 return ($dn);
717 }
718 }
720 /* None found */
721 return ("none");
722 }
725 function rebind($ldap, $referral)
726 {
727 $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
728 if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
729 $this->error = "Success";
730 $this->hascon=true;
731 $this->reconnect= true;
732 return (0);
733 } else {
734 $this->error = "Could not bind to " . $credentials['ADMIN'];
735 return NULL;
736 }
737 }
740 /* Recursively copy ldap object */
741 function _copy($src_dn,$dst_dn)
742 {
743 $ldap=$this->config->get_ldap_link();
744 $ldap->cat($src_dn);
745 $attrs= $ldap->fetch();
747 /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
748 $ds= ldap_connect($this->config->current['SERVER']);
749 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
750 if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
751 ldap_set_rebind_proc($ds, array(&$this, "rebind"));
752 }
754 $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
755 $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
756 $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
758 /* Fill data from LDAP */
759 $new= array();
760 if ($sr) {
761 $ei=ldap_first_entry($ds, $sr);
762 if ($ei) {
763 foreach($attrs as $attr => $val){
764 if ($info = @ldap_get_values_len($ds, $ei, $attr)){
765 for ($i= 0; $i<$info['count']; $i++){
766 if ($info['count'] == 1){
767 $new[$attr]= $info[$i];
768 } else {
769 $new[$attr][]= $info[$i];
770 }
771 }
772 }
773 }
774 }
775 }
777 /* close conncetion */
778 ldap_unbind($ds);
780 /* Adapt naming attribute */
781 $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
782 $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
783 $new[$dst_name]= LDAP::fix($dst_val);
785 /* Check if this is a department.
786 * If it is a dep. && there is a , override in his ou
787 * change \2C to , again, else this entry can't be saved ...
788 */
789 if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
790 $new['ou'] = str_replace("\\\\,",",",$new['ou']);
791 }
793 /* Save copy */
794 $ldap->connect();
795 $ldap->cd($this->config->current['BASE']);
797 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
799 /* FAIvariable=.../..., cn=..
800 could not be saved, because the attribute FAIvariable was different to
801 the dn FAIvariable=..., cn=... */
803 if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
805 if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
806 $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
807 }
808 $ldap->cd($dst_dn);
809 $ldap->add($new);
811 if (!$ldap->success()){
812 trigger_error("Trying to save $dst_dn failed.",
813 E_USER_WARNING);
814 return(FALSE);
815 }
816 return(TRUE);
817 }
820 /* This is a workaround function. */
821 function copy($src_dn, $dst_dn)
822 {
823 /* Rename dn in possible object groups */
824 $ldap= $this->config->get_ldap_link();
825 $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
826 array('cn'));
827 while ($attrs= $ldap->fetch()){
828 $og= new ogroup($this->config, $ldap->getDN());
829 unset($og->member[$src_dn]);
830 $og->member[$dst_dn]= $dst_dn;
831 $og->save ();
832 }
834 $ldap->cat($dst_dn);
835 $attrs= $ldap->fetch();
836 if (count($attrs)){
837 trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
838 E_USER_WARNING);
839 return (FALSE);
840 }
842 $ldap->cat($src_dn);
843 $attrs= $ldap->fetch();
844 if (!count($attrs)){
845 trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
846 E_USER_WARNING);
847 return (FALSE);
848 }
850 $ldap->cd($src_dn);
851 $ldap->search("objectClass=*",array("dn"));
852 while($attrs = $ldap->fetch()){
853 $src = $attrs['dn'];
854 $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
855 $this->_copy($src,$dst);
856 }
857 return (TRUE);
858 }
862 /*! \brief Rename/Move a given src_dn to the given dest_dn
863 *
864 * Move a given ldap object indentified by $src_dn to the
865 * given destination $dst_dn
866 *
867 * - Ensure that all references are updated (ogroups)
868 * - Update ACLs
869 * - Update accessTo
870 *
871 * \param string 'src_dn' the source DN.
872 * \param string 'dst_dn' the destination DN.
873 * \return boolean TRUE on success else FALSE.
874 */
875 function rename($src_dn, $dst_dn)
876 {
877 $start = microtime(1);
879 /* Try to move the source entry to the destination position */
880 $ldap = $this->config->get_ldap_link();
881 $ldap->cd($this->config->current['BASE']);
882 $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
883 if (!$ldap->rename_dn($src_dn,$dst_dn)){
884 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());
885 @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn -- TO: $dst_dn",
886 "Ldap Protocol v3 implementation error, falling back to maunal method.");
887 return(FALSE);
888 }
890 /* Get list of users,groups and roles within this tree,
891 maybe we have to update ACL references.
892 */
893 $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
894 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
895 foreach($leaf_objs as $obj){
896 $new_dn = $obj['dn'];
897 $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
898 $this->update_acls($old_dn,$new_dn);
899 }
901 // Migrate objectgroups if needed
902 $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
903 "ogroups", array(get_ou("group", "ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
905 // Walk through all objectGroups
906 foreach($ogroups as $ogroup){
907 // Migrate old to new dn
908 $o_ogroup= new ogroup($this->config,$ogroup['dn']);
909 if (isset($o_ogroup->member[$src_dn])) {
910 unset($o_ogroup->member[$src_dn]);
911 }
912 $o_ogroup->member[$dst_dn]= $dst_dn;
914 // Save object group
915 $o_ogroup->save();
916 }
918 // Migrate rfc groups if needed
919 $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);
921 // Walk through all POSIX groups
922 foreach($groups as $group){
924 // Migrate old to new dn
925 $o_group= new group($this->config,$group['dn']);
926 $o_group->save();
927 }
929 /* Update roles to use the new entry dn */
930 $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);
932 // Walk through all roles
933 foreach($roles as $role){
934 $role = new roleGeneric($this->config,$role['dn']);
935 $key= array_search($src_dn, $role->roleOccupant);
936 if($key !== FALSE){
937 $role->roleOccupant[$key] = $dst_dn;
938 $role->save();
939 }
940 }
942 // Update 'manager' attributes from gosaDepartment and inetOrgPerson
943 $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
944 $ocs = $ldap->get_objectclasses();
945 if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
946 $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
947 }
948 $leaf_deps= get_list($filter,array("all"),$this->config->current['BASE'],
949 array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
950 foreach($leaf_deps as $entry){
951 $update = array('manager' => $dst_dn);
952 $ldap->cd($entry['dn']);
953 $ldap->modify($update);
954 if(!$ldap->success()){
955 trigger_error(sprintf("Failed to update manager for %s: %s", bold($entry['dn']), $ldap->get_error()));
956 }
957 }
959 // Migrate 'dyn-groups' here. labeledURIObject
960 if(class_available('DynamicLdapGroup')) {
961 DynamicLdapGroup::moveDynGroup($this->config,$src_dn,$dst_dn);
962 }
964 /* Check if there are gosa departments moved.
965 If there were deps moved, the force reload of config->deps.
966 */
967 $leaf_deps= get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
968 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
970 if(count($leaf_deps)){
971 $this->config->get_departments();
972 $this->config->make_idepartments();
973 session::global_set("config",$this->config);
974 $ui =get_userinfo();
975 $ui->reset_acl_cache();
976 }
978 return(TRUE);
979 }
983 function move($src_dn, $dst_dn)
984 {
985 /* Do not copy if only upper- lowercase has changed */
986 if(strtolower($src_dn) == strtolower($dst_dn)){
987 return(TRUE);
988 }
990 stats::log('plugin', $class = get_class($this), $action = 'move', $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
992 /* Try to move the entry instead of copy & delete
993 */
994 if(TRUE){
996 /* Try to move with ldap routines, if this was not successfull
997 fall back to the old style copy & remove method
998 */
999 if($this->rename($src_dn, $dst_dn)){
1000 return(TRUE);
1001 }else{
1002 // See code below.
1003 }
1004 }
1006 /* Copy source to destination */
1007 if (!$this->copy($src_dn, $dst_dn)){
1008 return (FALSE);
1009 }
1011 /* Delete source */
1012 $ldap= $this->config->get_ldap_link();
1013 $ldap->rmdir_recursive($src_dn);
1014 if (!$ldap->success()){
1015 trigger_error("Trying to delete $src_dn failed.",
1016 E_USER_WARNING);
1017 return (FALSE);
1018 }
1020 return (TRUE);
1021 }
1024 /* \brief Move/Rename complete trees */
1025 function recursive_move($src_dn, $dst_dn)
1026 {
1027 /* Check if the destination entry exists */
1028 $ldap= $this->config->get_ldap_link();
1030 /* Check if destination exists - abort */
1031 $ldap->cat($dst_dn, array('dn'));
1032 if ($ldap->fetch()){
1033 trigger_error("recursive_move $dst_dn already exists.",
1034 E_USER_WARNING);
1035 return (FALSE);
1036 }
1038 $this->copy($src_dn, $dst_dn);
1040 /* Remove src_dn */
1041 $ldap->cd($src_dn);
1042 $ldap->recursive_remove($src_dn);
1043 return (TRUE);
1044 }
1047 function saveCopyDialog(){
1048 }
1051 function getCopyDialog(){
1052 return(array("string"=>"","status"=>""));
1053 }
1056 /*! \brief Prepare for Copy & Paste */
1057 function PrepareForCopyPaste($source)
1058 {
1059 $todo = $this->attributes;
1060 if(isset($this->CopyPasteVars)){
1061 $todo = array_merge($todo,$this->CopyPasteVars);
1062 }
1064 if(count($this->objectclasses)){
1065 $this->is_account = TRUE;
1066 foreach($this->objectclasses as $class){
1067 if(!in_array($class,$source['objectClass'])){
1068 $this->is_account = FALSE;
1069 }
1070 }
1071 }
1073 foreach($todo as $var){
1074 if (isset($source[$var])){
1075 if(isset($source[$var]['count'])){
1076 if($source[$var]['count'] > 1){
1077 $tmp= $source[$var];
1078 unset($tmp['count']);
1079 $this->$var = $tmp;
1080 }else{
1081 $this->$var = $source[$var][0];
1082 }
1083 }else{
1084 $this->$var= $source[$var];
1085 }
1086 }
1087 }
1088 }
1090 /*! \brief Get gosaUnitTag for the given DN
1091 If this is called from departmentGeneric, we have to skip this
1092 tagging procedure.
1093 */
1094 function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1095 {
1096 /* Skip tagging? */
1097 if($this->skipTagging){
1098 return;
1099 }
1101 /* No dn? Self-operation... */
1102 if ($dn == ""){
1103 $dn= $this->dn;
1105 /* No tag? Find it yourself... */
1106 if ($tag == ""){
1107 $len= strlen($dn);
1109 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1110 $relevant= array();
1111 foreach ($this->config->adepartments as $key => $ntag){
1113 /* This one is bigger than our dn, its not relevant... */
1114 if ($len < strlen($key)){
1115 continue;
1116 }
1118 /* This one matches with the latter part. Break and don't fix this entry */
1119 if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1120 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1121 $relevant[strlen($key)]= $ntag;
1122 continue;
1123 }
1125 }
1127 /* If we've some relevant tags to set, just get the longest one */
1128 if (count($relevant)){
1129 ksort($relevant);
1130 $tmp= array_keys($relevant);
1131 $idx= end($tmp);
1132 $tag= $relevant[$idx];
1133 $this->gosaUnitTag= $tag;
1134 }
1135 }
1136 }
1138 /*! \brief Add unit tag */
1139 /* Remove tags that may already be here... */
1140 remove_objectClass("gosaAdministrativeUnitTag", $at);
1141 if (isset($at['gosaUnitTag'])){
1142 unset($at['gosaUnitTag']);
1143 }
1145 /* Set tag? */
1146 if ($tag != ""){
1147 add_objectClass("gosaAdministrativeUnitTag", $at);
1148 $at['gosaUnitTag']= $tag;
1149 }
1151 /* Initially this object was tagged.
1152 - But now, it is no longer inside a tagged department.
1153 So force the remove of the tag.
1154 (objectClass was already removed obove)
1155 */
1156 if($tag == "" && $this->gosaUnitTag){
1157 $at['gosaUnitTag'] = array();
1158 }
1159 }
1162 /*! \brief Test for removability of the object
1163 *
1164 * Allows testing of conditions for removal of object. If removal should be aborted
1165 * the function needs to remove an error message.
1166 * */
1167 function allow_remove()
1168 {
1169 $reason= "";
1170 return $reason;
1171 }
1174 /*! \brief Test if snapshotting is enabled
1175 *
1176 * Test weither snapshotting is enabled or not. There will also be some errors posted,
1177 * if the configuration failed
1178 * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1179 */
1180 function snapshotEnabled()
1181 {
1182 return $this->config->snapshotEnabled();
1183 }
1186 /*! \brief Return plugin informations for acl handling
1187 * See class_core.inc for examples.
1188 */
1189 static function plInfo()
1190 {
1191 return array();
1192 }
1195 function set_acl_base($base)
1196 {
1197 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$base."</b>","<b>ACL-Base:</b> ");
1198 $this->acl_base= $base;
1199 }
1202 function set_acl_category($category)
1203 {
1204 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$category."</b>(/".get_class($this).")","<b>ACL-Category:</b> ");
1205 $this->acl_category= "$category/";
1206 }
1209 function acl_is_writeable($attribute,$skip_write = FALSE)
1210 {
1211 if($this->read_only) return(FALSE);
1212 $ui= get_userinfo();
1213 return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1214 }
1217 function acl_is_readable($attribute)
1218 {
1219 $ui= get_userinfo();
1220 return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1221 }
1224 function acl_is_createable($base ="")
1225 {
1226 if($this->read_only) return(FALSE);
1227 $ui= get_userinfo();
1228 if($base == "") $base = $this->acl_base;
1229 return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1230 }
1233 function acl_is_removeable($base ="")
1234 {
1235 if($this->read_only) return(FALSE);
1236 $ui= get_userinfo();
1237 if($base == "") $base = $this->acl_base;
1238 return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1239 }
1242 function acl_is_moveable($base = "")
1243 {
1244 if($this->read_only) return(FALSE);
1245 $ui= get_userinfo();
1246 if($base == "") $base = $this->acl_base;
1247 return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1248 }
1251 function acl_have_any_permissions()
1252 {
1253 }
1256 function getacl($attribute,$skip_write= FALSE)
1257 {
1258 $ui= get_userinfo();
1259 $skip_write |= $this->read_only;
1260 return $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1261 }
1264 /*! \brief Returns a list of all available departments for this object.
1265 *
1266 * If this object is new, all departments we are allowed to create a new user in
1267 * are returned. If this is an existing object, return all deps.
1268 * We are allowed to move tis object too.
1269 * \return array [dn] => "..name" // All deps. we are allowed to act on.
1270 */
1271 function get_allowed_bases()
1272 {
1273 $ui = get_userinfo();
1274 $deps = array();
1276 /* Is this a new object ? Or just an edited existing object */
1277 if(!$this->initially_was_account && $this->is_account){
1278 $new = true;
1279 }else{
1280 $new = false;
1281 }
1283 foreach($this->config->idepartments as $dn => $name){
1284 if($new && $this->acl_is_createable($dn)){
1285 $deps[$dn] = $name;
1286 }elseif(!$new && $this->acl_is_moveable($dn)){
1287 $deps[$dn] = $name;
1288 }
1289 }
1291 /* Add current base */
1292 if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1293 $deps[$this->base] = $this->config->idepartments[$this->base];
1294 }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1296 }else{
1297 trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1298 }
1299 return($deps);
1300 }
1303 /* This function updates ACL settings if $old_dn was used.
1304 * \param string 'old_dn' specifies the actually used dn
1305 * \param string 'new_dn' specifies the destiantion dn
1306 */
1307 function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1308 {
1309 /* Check if old_dn is empty. This should never happen */
1310 if(empty($old_dn) || empty($new_dn)){
1311 trigger_error("Failed to check acl dependencies, wrong dn given.");
1312 return;
1313 }
1315 /* Update userinfo if necessary */
1316 $ui = session::global_get('ui');
1317 if($ui->dn == $old_dn){
1318 $ui->dn = $new_dn;
1319 session::global_set('ui',$ui);
1320 new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1321 }
1323 /* Object was moved, ensure that all acls will be moved too */
1324 if($new_dn != $old_dn && $old_dn != "new"){
1326 /* get_ldap configuration */
1327 $update = array();
1328 $ldap = $this->config->get_ldap_link();
1329 $ldap->cd ($this->config->current['BASE']);
1330 $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1331 while($attrs = $ldap->fetch()){
1332 $acls = array();
1333 $found = false;
1334 for($i = 0 ; $i < $attrs['gosaAclEntry']['count'] ; $i ++ ){
1335 $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1337 /* Roles uses antoher data storage order, members are stored int the third part,
1338 while the members in direct ACL assignments are stored in the second part.
1339 */
1340 $id = ($acl_parts[1] == "role") ? 3 : 2;
1342 /* Update member entries to use $new_dn instead of old_dn
1343 */
1344 $members = explode(",",$acl_parts[$id]);
1345 foreach($members as $key => $member){
1346 $member = base64_decode($member);
1347 if($member == $old_dn){
1348 $members[$key] = base64_encode($new_dn);
1349 $found = TRUE;
1350 }
1351 }
1353 /* Check if the selected role has to updated
1354 */
1355 if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1356 $acl_parts[2] = base64_encode($new_dn);
1357 $found = TRUE;
1358 }
1360 /* Build new acl string */
1361 $acl_parts[$id] = implode($members,",");
1362 $acls[] = implode($acl_parts,":");
1363 }
1365 /* Acls for this object must be adjusted */
1366 if($found){
1368 $debug_info= sprintf(_("Changing ACL DN from %s to %s"), bold($old_dn), bold($new_dn));
1369 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1371 $update[$attrs['dn']] =array();
1372 foreach($acls as $acl){
1373 $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1374 }
1375 }
1376 }
1378 /* Write updated acls */
1379 foreach($update as $dn => $attrs){
1380 $ldap->cd($dn);
1381 $ldap->modify($attrs);
1382 }
1383 }
1384 }
1388 /*! \brief Enable the Serial ID check
1389 *
1390 * This function enables the entry Serial ID check. If an entry was edited while
1391 * we have edited the entry too, an error message will be shown.
1392 * To configure this check correctly read the FAQ.
1393 */
1394 function enable_CSN_check()
1395 {
1396 $this->CSN_check_active =TRUE;
1397 $this->entryCSN = getEntryCSN($this->dn);
1398 }
1401 /*! \brief Prepares the plugin to be used for multiple edit
1402 * Update plugin attributes with given array of attribtues.
1403 * \param array Array with attributes that must be updated.
1404 */
1405 function init_multiple_support($attrs,$all)
1406 {
1407 $ldap= $this->config->get_ldap_link();
1408 $this->multi_attrs = $attrs;
1409 $this->multi_attrs_all= $all;
1411 /* Copy needed attributes */
1412 foreach ($this->attributes as $val){
1413 $found= array_key_ics($val, $this->multi_attrs);
1415 if ($found != ""){
1416 if(isset($this->multi_attrs["$val"][0])){
1417 $this->$val= $this->multi_attrs["$val"][0];
1418 }
1419 }
1420 }
1421 }
1424 /*! \brief Enables multiple support for this plugin
1425 */
1426 function enable_multiple_support()
1427 {
1428 $this->ignore_account = TRUE;
1429 $this->multiple_support_active = TRUE;
1430 }
1433 /*! \brief Returns all values that have been modfied in multiple edit mode.
1434 \return array Cotaining all modified values.
1435 */
1436 function get_multi_edit_values()
1437 {
1438 $ret = array();
1439 foreach($this->attributes as $attr){
1440 if(in_array($attr,$this->multi_boxes)){
1441 $ret[$attr] = $this->$attr;
1442 }
1443 }
1444 return($ret);
1445 }
1448 /*! \brief Update class variables with values collected by multiple edit.
1449 */
1450 function set_multi_edit_values($attrs)
1451 {
1452 foreach($attrs as $name => $value){
1453 $this->$name = $value;
1454 }
1455 }
1458 /*! \brief Generates the html output for this node for multi edit*/
1459 function multiple_execute()
1460 {
1461 /* This one is empty currently. Fabian - please fill in the docu code */
1462 session::global_set('current_class_for_help',get_class($this));
1464 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1465 session::set('LOCK_VARS_TO_USE',array());
1466 session::set('LOCK_VARS_USED_GET',array());
1467 session::set('LOCK_VARS_USED_POST',array());
1468 session::set('LOCK_VARS_USED_REQUEST',array());
1470 return("Multiple edit is currently not implemented for this plugin.");
1471 }
1474 /*! \brief Save HTML posted data to object for multiple edit
1475 */
1476 function multiple_save_object()
1477 {
1478 if(empty($this->entryCSN) && $this->CSN_check_active){
1479 $this->entryCSN = getEntryCSN($this->dn);
1480 }
1482 /* Save values to object */
1483 $this->multi_boxes = array();
1484 foreach ($this->attributes as $val){
1486 /* Get selected checkboxes from multiple edit */
1487 if(isset($_POST["use_".$val])){
1488 $this->multi_boxes[] = $val;
1489 }
1491 if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
1493 $data= $this->$val = get_post($val);
1494 if ($this->$val != $data){
1495 $this->is_modified= TRUE;
1496 }
1498 /* IE post fix */
1499 if(isset($data[0]) && $data[0] == chr(194)) {
1500 $data = "";
1501 }
1502 $this->$val= $data;
1503 }
1504 }
1505 }
1508 /*! \brief Returns all attributes of this plugin,
1509 to be able to detect multiple used attributes
1510 in multi_plugg::detect_multiple_used_attributes().
1511 @return array Attributes required for intialization of multi_plug
1512 */
1513 public function get_multi_init_values()
1514 {
1515 $attrs = $this->attrs;
1516 return($attrs);
1517 }
1520 /*! \brief Check given values in multiple edit
1521 \return array Error messages
1522 */
1523 function multiple_check()
1524 {
1525 $message = plugin::check();
1526 return($message);
1527 }
1529 function get_used_snapshot_bases()
1530 {
1531 return(array());
1532 }
1534 function is_modal_dialog()
1535 {
1536 return(isset($this->dialog) && $this->dialog);
1537 }
1540 /*! \brief Forward command execution requests
1541 * to the hook execution method.
1542 */
1543 function handle_post_events($mode, $addAttrs= array())
1544 {
1545 if(!in_array($mode, array('add','remove','modify'))){
1546 trigger_error(sprintf("Invalid post event type given %s! Valid types are [add,modify,remove].", bold($mode)));
1547 return;
1548 }
1549 switch ($mode){
1550 case "add":
1551 plugin::callHook($this,"POSTCREATE", $addAttrs);
1552 break;
1554 case "modify":
1555 plugin::callHook($this,"POSTMODIFY", $addAttrs);
1556 break;
1558 case "remove":
1559 plugin::callHook($this,"POSTREMOVE", $addAttrs);
1560 break;
1561 }
1562 }
1565 /*! \brief Calls external hooks which are defined for this plugin (gosa.conf)
1566 * Replaces placeholder by class values of this plugin instance.
1567 * @param Allows to a add special replacements.
1568 */
1569 static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(), &$returnCode = NULL)
1570 {
1571 global $config;
1572 $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
1574 if ($command != ""){
1576 // Walk trough attributes list and add the plugins attributes.
1577 foreach ($plugin->attributes as $attr){
1578 if (!is_array($plugin->$attr)){
1579 $addAttrs[$attr] = $plugin->$attr;
1580 }
1581 }
1582 $ui = get_userinfo();
1583 $addAttrs['callerDN']=$ui->dn;
1584 $addAttrs['dn']=$plugin->dn;
1585 $addAttrs['location']=$config->current['NAME'];
1587 // Sort attributes by length, ensures correct replacement
1588 $tmp = array();
1589 foreach($addAttrs as $name => $value){
1590 $tmp[$name] = strlen($name);
1591 }
1592 arsort($tmp);
1594 // Now replace the placeholder
1595 foreach ($tmp as $name => $len){
1596 $value = $addAttrs[$name];
1597 $command= str_replace("%$name", "$value", $command);
1598 }
1600 // If there are still some %.. in our command, try to fill these with some other class vars
1601 if(preg_match("/%/",$command)){
1602 $attrs = get_object_vars($plugin);
1603 foreach($attrs as $name => $value){
1604 if(is_array($value)){
1605 $s = "";
1606 foreach($value as $val){
1607 if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
1608 $s .= '"'.$val.'",';
1609 }
1610 }
1611 $value = '['.trim($s,',').']';
1612 }
1613 if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
1614 continue;
1615 }
1616 $command= preg_replace("/%$name/", $value, $command);
1617 }
1618 }
1620 if (check_command($command)){
1622 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
1623 exec($command, $arr, $returnCode);
1624 $returnOutput = $arr;
1626 if($returnCode != 0){
1627 $str = implode("\n",$arr);
1628 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1629 $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
1630 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1631 }elseif(is_array($arr)){
1632 $str = implode("\n",$arr);
1633 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
1634 }
1635 } else {
1636 $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
1637 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1638 }
1639 }
1640 }
1641 }
1643 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1644 ?>