Code

Added comments to group/apps
[gosa.git] / gosa-plugins / goto / admin / groups / apps / class_groupApplication.inc
1 <?php
2 class appgroup extends plugin
3 {
4   var $config;
5   var $curbase;
7   /* Contains the menu structure in an array.
8    */
9   var $a_Structure= array();
10   var $a_Structure_on_load = array();
11   var $Releases;
12   var $FAIrelease = 0;
13   var $apps = array();
14   var $_cache = array();
16   var $app_parameter = array();
17   var $edit_entry    = array();
18   var $enableReleaseManagement = FALSE;
19   
21   public function __construct(&$config, $dn= NULL, $parent= NULL)
22   {
23     plugin::plugin($config,$dn,$parent);
24     $this->dn = $dn; 
25     $this->_load_menu_structure();
26     $this->a_Structure_on_load = $this->a_Structure;
28     /* Check if we have relase mangement enabled and prepare group application for release management */
29     $tmp = $config->search("faiManagement", "CLASS",array('menu','tabs'));
30     if(!empty($tmp)){
31       $this->enableReleaseManagement = true;
32     }
34     $this->Releases   = $this->getReleases();
35     $this->FAIrelease = "/";
36     $this->curbase    = $this->config->current['BASE'];
37     $this->reload();
38  
39     /* If we have at least one assigned application-
40        Enable this account.
41      */ 
42     $this->is_account = FALSE;
43     if(count($this->_get_all_entries()) > 1){
44       $this->is_account= TRUE;
45     }   
46     $this->initially_was_account = $this->is_account;
47   }
50   /*! \brief Reload the list of applications for the currently selected release.
51              If necessary, maybe the list is already cached.
52    */
53   function reload()
54   {
55     $ret = array();
56     $release_info = $this->Releases[$this->FAIrelease];
58     /* Check if the available application were already been fetched.
59        If not, build up a list of available applications.
60      */  
61     if(!isset($this->_cache['ReleaseApps'][$release_info['suffix']])){
63       /* First of all, get all application departments to search in.
64        */  
65       $ldap = $this->config->get_ldap_link();
66       $ldap->cd($this->config->current['BASE']);
67       $ldap->search("ou=apps",array("dn"));
68       $app_deps = array();
69       while($attrs = $ldap->fetch()){
70         $app_deps[] = $attrs['dn'];
71       }
73       /* Search all release departments for the above fetched application departments
74        */
75       foreach($app_deps as $dep){
76         $ldap->cd($dep);
77         $ldap->search("objectClass=FAIbranch",array("dn"));
78         while($attrs = $ldap->fetch()){
79           $app_deps[] = $attrs['dn'];
80         }
81       }
83       /* Filter out those releases that match the currently selected release, 
84           and fetch all applications from those departments.
85        */
86       foreach($app_deps as $dep){
87         if(preg_match("/^".normalizePreg($release_info['suffix'])."/",$dep)){
88           $ret = array_merge($ret,get_list("(objectClass=gosaApplication)","application",$dep,array("*"),GL_NONE));
89         }
90       }
92       /* Create a new array containing all fetch apps for the currently selected release,
93           sort it and store results in cache. 
94        */ 
95       $tmp = array();
96       foreach($ret as $key => $app){
97         $tmp[$key] = $app['cn'][0];
98       }
99       natcasesort($tmp);
100       $res = array();
101       foreach($tmp as $key => $app){
102         $res[] = $ret[$key];
103       }
105       $this->_cache['ReleaseApps'][$release_info['suffix']] = $res;
106     } 
107     $this->apps = $this->_cache['ReleaseApps'][$release_info['suffix']];
108   }
110   
111   
112   /*! \brief generate a list of available releases
113       @return return an array with all available releases.
115       e.g.
117       /     "name"    /
118             "found"   1
119             "parts"   Array (empty)
120             "suffix"  ou=apps,
122       halut "name"    halut
123             "found"   1
124             "FAIstate"  
125             "dn"      ou=halut,ou=apps,ou=Direktorium,o=Landeshauptstadt München,c=de
126             "parts"   0 halut
127             "suffix"  ou=halut,ou=apps,
129       This will be used as base for the application menu structure.
130       If there is a menu assigned for a release, this menu will be 
131        appended with the index 'ENTRIES';
132     */
133   function getReleases()
134   {
135     $ret =array("/" => array("name" => "/", "found" => TRUE , "parts" => array(),"suffix" => get_ou('applicationou')));
136     if($this->enableReleaseManagement){
138       /* Only display those releases that we are able to read 
139        */
140       $dn     = get_ou("applicationou").$this->config->current['BASE'];
141       $filter = "(&(objectClass=organizationalUnit)(objectClass=FAIbranch))";
142       $res    = get_sub_list($filter,array("application","fai"), 
143           array(get_ou("applicationou"),get_ou("faiou")),$dn, array("ou","FAIstate"), GL_SUBSEARCH);
145       /* Go through all departments and check which department is a valid 
146           department release.
147        */
148       foreach($res as $attrs){
149         if(preg_match("/".get_ou('applicationou')."/",$attrs['dn'])){
151           /* Parse all returned departments dns into a useable type.
152              (ou=1.0.0,ou=halut,ou=apps  ==> halue/1.0.0)
153            */
154           $bb     = preg_replace("/".get_ou('applicationou').".*/","",$attrs['dn']);
155           $parts  = array_reverse(split("ou=",$bb));
157           $str ="";
158           foreach($parts as $key => $part){
159             if(empty($part)) {
160               unset($parts[$key]);
161               continue;
162             }
163             $part = str_replace(",","",$part);
164             $str .= $part."/";
165             $parts[$key] = $part;
166           }
167           $name = preg_replace("/\/$/","",$str);
168           if(empty($name)) {
169             $name ="/";
170           }
172           $FAIstate = "";
173           if(isset($attrs['FAIstate'])){
174             $FAIstate = $attrs['FAIstate'][0];
175           }
177           /* Check if this department has a menu structure assigned 
178            */
179           $all = $this->_get_all_entries();
180           $found = FALSE;
181           foreach($all as $entry){
182             if(isset($entry['DN']) && preg_match("/^".normalizePreg($bb)."/",$entry['DN'])){
183               $found =TRUE;
184               break;
185             }
186           }
188           $ret[$name] = array("name"     => $name, 
189               "found"    => $found,
190               "FAIstate" => $FAIstate,
191               "dn"       => $attrs['dn'], 
192               "parts"    => $parts,"suffix" => $bb.get_ou('applicationou'));
193         }
194       }
195     }
196     ksort($ret);
197     return($ret);
198   }
201   /*! \brief Load the menu structure from ldap and create a multi dimensional array.
203       This will create a multidimensional array.
204       e.g.:
206       $this->a_Structure =
207   
208       [0]['TYPE']   = "BASE"
209       [0]['ENTRIES']  [0]['TYPE']   = "RELEASE"
210                       [0]['NAME']   = "halut"
211                       [0]['ENTRIES']= array()
212                       ...
213       [0]['ENTRIES']  [1]['TYPE']   = "RELEASE"
214                       [1]['NAME']   = "halut/1.0.0"
215                       [1]['ENTRIES']  [0]['TYPE']   = "TYPE"
216                                       [0]['NAME']   = "Programme"
217                                       [0]['ENTRIES'][0]['TYPE'] = "ENTRY"
218                                                     [0]['NAME'] = "konqueror"
219                                                     [1]['TYPE'] = "ENTRY"
220                                                     [1]['NAME'] = "firefox"
221    */
222   function _load_menu_structure()
223   {
224     /* Create the base object
225      */
226     $base =  array();
227     $base['UNIQID'] = uniqid();
228     $base['PARENT'] = 0; 
229     $base['NAME']   = "";
230     $base['TYPE']   = "BASE";
231     $base['ENTRIES']= array();
232     $base['STATUS'] = "LOADED";
233     
234     $this->a_Structure  = array();
235     $this->a_Structure[0] = $base;
237     /* Search for all Releases/Menu Folders and Menu Entries,
238         to append them to our structure array.
239      */
240     $ldap = $this->config->get_ldap_link();
241     $ldap->cd($this->dn);
242     $ldap->search("(|(objectClass=gotoSubmenuEntry)(objectClass=FAIbranch)(objectClass=gotoMenuEntry))",array("*"));
243     while($attrs = $ldap->fetch()){
245       /* Find the correct position where to add this entry.
246           e.g. If we have cn=firefox,cn=Programme,ou=halut...
248           1. get a pointer to the halut array ($this->a_Structure['0'][ENTRIES''][]['halut'])
249           2. then get a pointer to the halut['ENTRIES'][]['Programme'] array. 
250           3. append 'firefox' to the 'ENTRIES' of our "Programme" pointer.
251        */
252       $cur          = &$this->a_Structure[0]['ENTRIES'];
253       $parent_id    = $base['UNIQID'];
254       $sub_dn       = preg_replace("/,".normalizePreg($this->dn)."$/","",$attrs['dn']);
255       $sub_dn_array = split("\,",$sub_dn);
257       /* Walk through our menu structure array while we have found 
258           the correct position where to add this object. 
259        */
260       $found = true;
261       for($i = (count($sub_dn_array)-1) ; $i >= 0 ; $i--){
262         $name = preg_replace("/^[^=]*+=/","",$sub_dn_array[$i]);
263         
264         /* We haven't found the end node where this object has to be added
265          */
266         if($i > 0){
267           $found =FALSE;
268           foreach($cur as $key => $entry){
269             if($entry['NAME'] == $name){
270               $cur = &$cur[$key]['ENTRIES'];
271               $parent_id = $entry['UNIQID'];
272               $found =true;
273               break;
274             }
275           }
276         }else{
277           
278           if(!$found){
279             break;
280           }
282           /* Get application priority.
283              And ensure that each priority exists once.
284            */
285           $priority = 1;
286           if(isset($attrs['gosaApplicationPriority'])){
287             $priority= $attrs['gosaApplicationPriority'][0];
288           }
289           while(isset($cur[$priority])){
290             $priority ++;
291           }
293           /* Create the data object that should be added 
294              * Folder
295              * Entry
296              * Release
297            */
298           $data = array();
299     
300           /* Add a menu folder 
301            */
302           if(in_array("gotoSubmenuEntry",$attrs['objectClass'])){
303             $type = "FOLDER";
305             $data['ICON'] = "";
306             if(isset($attrs['gosaApplicationIcon'])){
307               $data['ICON'] = $ldap->get_attribute($attrs['dn'],"gosaApplicationIcon");
308             }
310           /* Add a menu entry 
311            */
312           }elseif(in_array("gotoMenuEntry",$attrs['objectClass'])){
314             $type = "ENTRY";
315             $data['INFO'] = "";
316             $data['PARAMETER'] = array();
317             if(isset($attrs['gosaApplicationParameter'])){
318               for($p = 0 ; $p < $attrs['gosaApplicationParameter']['count'] ; $p ++){
319                 if(preg_match("/:/",$attrs['gosaApplicationParameter'][$p])){
320                   $tmp = split(":",$attrs['gosaApplicationParameter'][$p]);
321                   $data['PARAMETER'][$tmp[0]] = $tmp[1];
322                 }elseif($attrs['gosaApplicationParameter'][$p] == "*separator*"){
323                   $type = "SEPERATOR";
324                   $data['PARAMETER'] = array();
325                   break;
326                 }
327               }
328             }
330           /* Add a release
331            */
332           }elseif(in_array("FAIbranch",$attrs['objectClass'])){
333             $type = "RELEASE";
334             if(isset($attrs['FAIstate'][0])){
335               $data['FAIstate'] = $attrs['FAIstate'][0];
336             }else{
337               $data['FAIstate'] = "";
338             }
339           }
341           /* Create object and append it to the current structure pointer 
342            */
343           $data['LDAP_ATTRS'] = $attrs;
344           $data['DN']       = $attrs['dn'];
345           $data['NAME']     = $name;
346           $data['TYPE']     = $type;
347           $data['PRIORITY'] = $priority;
348           $data['ENTRIES']  = array();
349           $data['UNIQID']   = uniqid();
350           $data['PARENT']   = $parent_id;
351           $data['STATUS']   = "LOADED";
352           $cur[$priority]   = $data;
353           ksort($cur);
354         }
355       }
356     }
357   } 
360   function execute()
361   {
362     /* Call parent execute */
363     plugin::execute();
365     if(isset($_GET['r'])) $this->__construct($this->config,$this->dn);
367     if (isset($_POST['modify_state'])){
368       $this->is_account = !$this->is_account;
369     }
371     /* Do we represent a valid account? */
372     if (!$this->is_account){
373       $display= $this->show_disable_header(msgPool::addFeaturesButton(_("Menu")), msgPool::featuresDisabled(_("Menu")));
374       return ($display);
375     }
377     $display= $this->show_disable_header(msgPool::removeFeaturesButton(_("Menu")), msgPool::featuresEnabled(_("Menu")));
379     if(isset($_GET['send'])){
380       $id = $_GET['send'];
381       $all = $this->_get_all_entries();
382       if(isset($all[$id])){
383         send_binary_content($all[$id]['ICON'],$id.".jpg","image/jpeg");
384         exit;
385       }
386     }
388     if(isset($_GET['r']))
389     $this->__construct($this->config,$this->dn);
391     if(count($this->edit_entry)){
392       if($this->edit_entry['TYPE'] == "ENTRY"){
393         $smarty = get_smarty();
394         $smarty->assign("type", "ENTRY");
395         $smarty->assign("entry",$this->edit_entry);
396         $smarty->assign("paras",$this->app_parameter);
397         $display= $smarty->fetch (get_template_path('edit_entry.tpl', TRUE, dirname(__FILE__)));
398         return($display);
399       }
400       if($this->edit_entry['TYPE'] == "FOLDER"){
401         $smarty = get_smarty();
403         session::set("binarytype" , "image/jpeg");
404         session::set("binary" , $this->edit_entry['ICON']);
405   
406         $smarty->assign("rand", microtime(TRUE));
407         $smarty->assign("image_set" , strlen($this->edit_entry['ICON']) > 0); 
408         $smarty->assign("type", "FOLDER");
409         $smarty->assign("entry",$this->edit_entry);
410         $display= $smarty->fetch (get_template_path('edit_entry.tpl', TRUE, dirname(__FILE__)));
411         return($display);
412       }
413     }
415     $smarty = get_smarty();
416     $smarty->assign("plug_id" , $_GET['plug']);
418     /* Create application list */
419     $div = new divSelectBox("appgroup");
420     $div->SetHeight(300);
421     $departments = array();
422     $res = get_list("(objectClass=gosaDepartment)", "application", $this->curbase,array("description","cn","ou"),GL_SIZELIMIT);
423     foreach($res as $value){
424       $fdn = $value['dn'];
425       $fdn = preg_replace("/".normalizePreg($this->curbase)."/","",$fdn);
426       $fdn= @LDAP::fix($fdn);
427       if($value["description"][0]!=".."){
428         $departments[$value['dn']]= convert_department_dn($fdn)." - [".$value["description"][0]."]";
429       }else{
430         $departments[$value['dn']]=convert_department_dn($fdn)." ["._("Back")."]";
431       }
432     }
434     $linkopen = "<a href='?plug=".$_GET['plug']."&amp;act=depopen&amp;depid=%s'>%s</a>";
436     /* Create base back entry */
437     $base_back = preg_replace("/^[^,]+,/","",$this->curbase);
438     if((strlen($base_back)>= strlen($this->config->current['BASE']))&&($this->curbase!=$this->config->current['BASE'])){
439       $div->AddEntry(array(
440             array("string"=>sprintf($linkopen,base64_encode($base_back),".. ["._("back")."]"),
441               "attach"=>"style='border:0px;'")
442             ));
443     }
445     /* Append departments for current base */
446     foreach($departments as $key => $app){
447       $div->AddEntry(array(
448             array("string"=>"<img class='center' src='images/lists/folder.png' alt='"._("department")."'>&nbsp;".sprintf($linkopen,
449                 base64_encode($key),$app),
450               "attach"=>"style='border:0px;'")
451             ));
452     }
454   
455     /* Add applications found on this base */
456     $used_apps = $this->_get_used_entry_name();
457     foreach($this->apps as $key => $app){
458       if(in_array($app['cn'][0],$used_apps)){
459         continue;
460       }
461       if(!preg_match("/".get_ou('applicationou').normalizePreg($this->curbase)."$/",$app['dn'])){
462         continue;
463       }
465       $name = $app['cn'][0];
466       if(isset($app['description'])){
467         $name .= "&nbsp;[".$app['description'][0]."]";
468       }
469       $div->AddEntry(array(
470             array("string"=>sprintf("<input class='center' type='checkbox' value='1' name='AddApp_%s'>",$key).
471               "<img class='center' src='images/select_application.png' alt='"._("application")."'>&nbsp;".$name,
472               "attach"=>"style='border:0px;'")
473             ));
474     }
476     
477     $smarty->assign("enableReleaseManagement",$this->enableReleaseManagement);
478     $smarty->assign("FAIrelease",$this->FAIrelease);
479     $smarty->assign("app_list",$div->DrawList());
480     $smarty->assign("i",0);
481     $smarty->assign("releases",$this->Releases);
482     $smarty->assign("folders" , $this->_get_folder_names());
483     $entries = $this->_get_entries_for_release($this->FAIrelease);
484     $smarty->assign("entries",$entries);
485     $display.= $smarty->fetch (get_template_path('app_list.tpl', TRUE, dirname(__FILE__)));
486     return($display);
487   }
489    
490   /*! \brief Returns all used folder names 
491       @return Array  All used folder names.
492    */ 
493   function _get_folder_names()
494   {
495     $data = $this->_get_entries_for_release($this->FAIrelease);
496     $all    = $this->_get_all_entries();
497     $ret = array("BASE" => ".");
498     foreach($data as $entry){
500       if($entry['TYPE'] == "FOLDER"){
501         $str = $entry['NAME'];
502         $parent = $entry['PARENT'];
503         $i = 10;
504         while(isset($all[$parent]) && $i){  
505           $i --;
506           $parent_o = $all[$parent];
507           $str      = $parent_o['NAME']."/".$str;
508           $parent   = $all[$parent_o['UNIQID']]['PARENT'];
509         }        
510         $ret[$entry['UNIQID']] = $str;
511       }
512     }
513     return($ret);
514   }
517   /*! \brief return all used applications 
518       @return Array  All used applications.
519    */ 
520   function _get_used_entry_name()
521   {
522     $data = $this->_get_entries_for_release($this->FAIrelease);
523     $ret = array();
524     foreach($data as $entry){
525       if($entry['TYPE'] == "ENTRY"){
526         $ret[] = $entry['NAME'];
527       }
528     }
529     return($ret);
530   }
533   /*! \brief Returns all folder an entries for the selected release 
534       @return Array  Returns the complete menu structure for the given array.
535    */ 
536   function _get_entries_for_release($release,$cur = NULL)
537   {
538     $all = $this->_get_all_entries();
539     $key = $this->_get_release_key($release);
540     if(isset($all[$key]) && count($all[$key]['ENTRIES'])){
541       $res = $this->_get_all_entries(TRUE,TRUE,$all[$key]['ENTRIES']);
542       return($res);
543     } 
544     return(array());
545   }
548   /*! \brief Save the currently edited entry 
549     */
550   function _save_entry_edit()
551   {
552     $all    = $this->_get_all_entries();
553     $entry  = $this->edit_entry;
554     $r_entry= &$all[$entry['UNIQID']];
556     if($entry['TYPE'] == "ENTRY"){
557       $r_entry['PARAMETER'] = $this->app_parameter;
558       $r_entry['STATUS'] = "EDITED";
559     }
560     if($entry['TYPE'] == "FOLDER"){
561       $r_entry['ICON']   = $this->edit_entry['ICON'];
562       $r_entry['STATUS'] = "EDITED";
563     }
564     $this->dialog = FALSE;
565     $this->edit_entry = array();
566   }
569   /*! \brief prepare the entry with the given ID, to be edited.
570               Read application Parameter from ldap.
571    */
572   function _edit_entry_edit($id)
573   {
574     $all   = $this->_get_all_entries();
575     $entry = $all[$id];
577     $this->app_parameter = array();
578     if($entry['TYPE'] == "ENTRY"){
579       $found = FALSE;
580       foreach($this->apps as $id => $app){
582         if($app['cn'][0] == $entry['NAME']){
583           $found = TRUE;
584           break;
585         }
586       }
587       if($found){
588       
589         /* Create a list of editable parameter */
590         if(isset($app['gosaApplicationParameter'])){
591           for($i = 0 ; $i < $app['gosaApplicationParameter']['count'] ; $i++) {
592             $para = $app['gosaApplicationParameter'][$i];
593             $tmp  = split(":",$para);
594             $this->app_parameter[$tmp[0]] = $tmp[1];
595           }
596         }
598         /* Overwrite parameters with entry parameters */
599         foreach($entry['PARAMETER'] as $name => $value){
600           $this->app_parameter[$name] = $value;
601         }
602         
603         $this->dialog = TRUE;
604         $this->edit_entry = $entry;
605       }
606     }
608     if($entry['TYPE'] == "FOLDER"){
609       $this->dialog = TRUE;
610       $this->edit_entry = $entry;
611     }
612   }
615   /*! \brief Removes the menu structure from ldap 
616    */
617   function remove_from_parent()
618   {
619     $ldap = $this->config->get_ldap_link();
620     $ldap->cd($this->dn);
621     $ldap->ls("(|(objectClass=gotoSubmenuEntry)(objectClass=FAIbranch)(objectClass=gotoMenuEntry))",$this->dn,array("*"));
622     $a_remove = array();
623     while($attrs = $ldap->fetch()){
624       $a_remove[] = $attrs['dn'];
625     }
626     foreach($a_remove as $remove){
627       $ldap->rmdir_recursive($remove);
628       if (!$ldap->success()){
629         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_DEL, get_class()));
630       }
631     }
632     $this->_load_menu_structure();
633   }
636   function check()
637   {
638     $message = plugin::check();
639     return($message);
640   }
643   /*! \brief Create missing releases, if there is a release selected \
644               that is currently not part of the menu structure \
645               then create this entry
646    */
647   function _check_missing_release($release)
648   {
649     $release_info = $this->Releases[$release];
651     $parent_id = $this->a_Structure[0]['UNIQID'];
652     $cur = &$this->a_Structure[0]['ENTRIES'];
653     for($i = 0 ; $i < count($release_info['parts']) ; $i ++){
654       $part = $release_info['parts'][$i];
655       $found = FALSE;
656       foreach($cur as $key => $name){
657         if($name['NAME'] == $part){
658           $parent_id = $cur[$key]['UNIQID'];
659           $cur = &$cur[$key]['ENTRIES'];
660           
661           $found =TRUE;
662           break;
663         }
664       }
665       if(!$found){
666         $release           =  array();
667         $release['UNIQID'] = uniqid();
668         $release['PARENT'] = $parent_id;
669         $release['NAME']   = $part;
670         $release['TYPE']   = "RELEASE";
671         $release['ENTRIES']= array();
672         $release['STATUS']   = "ADDED";
673         $release['FAIstate'] =  $release_info['FAIstate'];
674         $cur[] = $release;
675         $i --;
676       }
677     }
678   }
682   /*! \brief Moves a given object ($id) in a specified direction ($dir).
683       @param  String The object ID of the object we want to move
684       @dir    String Move "up" or "down"
685    */
686   function _move_entry($id,$dir)
687   {
688     $all   = $this->_get_all_entries();
689     if($dir == "down"){
690       $to = $this->_get_next($id);
691     } 
692     if($dir == "up"){
693       $to = $this->_get_last($id);
694     }
696     if(!$to){
697       return;
698     }
700     $o_to   = $all[$to];
701     $o_from = $all[$id];
703     if($o_to['PARENT'] == $o_from['UNIQID'] && $dir == "down"){
704       $to    = $this->_get_next($to,$o_from['PARENT']); 
705       $o_to  = $all[$to]; 
706     }
707  
708     /* Target is ENTRY && same BASE, just switch */
709     if($o_to['PARENT'] == $o_from['PARENT'] ){
710       $parent = $all[$o_to['PARENT']];
711       $pos = 0;
712       foreach($parent['ENTRIES'] as $entry){
713         $pos ++;
714         if($entry['UNIQID'] == $to){
715           break;
716         }
717       }
718       if($dir == "up" && $pos > 0){
719         $pos --;
720       }
721       $this->_add_entry($parent['UNIQID'],$o_from,$pos);
722       $this->_remove_entry_id($id);
723       return(TRUE);
724     }
725     return(FALSE);
726   }
729    
730   /*! \brief  Returns the railing object ID of the given object.
731       @return String  The id of the trailing object.
732    */ 
733   function _get_last($id)
734   {
735     $all_l = array_reverse($this->_get_entries_for_release($this->FAIrelease));
736     for($i = 0 ; $i < count($all_l) ; $i ++){
737       if(isset($all_l[$i]['UNIQID']) && $all_l[$i]['UNIQID'] == $id){
738         $i++;
739         break;
740       }
741     }
742     while(isset($all_l[$i]) && !in_array($all_l[$i]['TYPE'],array("ENTRY","FOLDER","CLOSE","OPEN")) && $i < count($all_l)){
743       $i++;
744     }
746     if(!isset($all_l[$i])){
747       return(FALSE);
748     }
750     if(in_array($all_l[$i]['TYPE'],array("CLOSE","OPEN"))){
751       return($all_l[$i]['PARENT']);
752     }     
754     return($all_l[$i]['UNIQID']);
755   }
758   /*! \brief  Returns the following object ID of the given object.
759       @return String  The id of the following object.
760    */ 
761   function _get_next($id,$parent = 0)
762   {
763     $all_l = $this->_get_entries_for_release($this->FAIrelease);
764     for($i = 0 ; $i < count($all_l) ; $i ++){
765       if(isset($all_l[$i]['UNIQID']) && $all_l[$i]['UNIQID'] == $id){
766         $i++;
767         break;
768       }
769     }
770     if($parent != 0){
771       while(isset($all_l[$i]) && $all_l[$i]['PARENT'] != $parent){
772         $i++;
773       }
774     }else{
775       while(isset($all_l[$i]) && !in_array($all_l[$i]['TYPE'],array("ENTRY","FOLDER")) && $i < count($all_l)){
776         $i++;
777       }
778     }
779     if(!isset($all_l[$i])){
780       return(FALSE);
781     }
782     if(in_array($all_l[$i]['TYPE'],array("CLOSE","OPEN"))){
783       return($all_l[$i]['PARENT']);
784     }
785     return($all_l[$i]['UNIQID']);
786   }
791   /* !\brief Handle ui POSTS, like sort up/down/delete
792    */ 
793   function save_object()
794   {
795     foreach($_POST as $name => $value){
796       if(preg_match("/del_/",$name)){
797         $id = preg_replace("/^del_/","",$name);
798         $id = preg_replace("/_(x|y)$/","",$id);
799         $this->_remove_entry_id($id);
800         break;
801       }
802       if(preg_match("/app_entry_edit/",$name)){
803         $id = preg_replace("/^app_entry_edit/","",$name);
804         $id = preg_replace("/_(x|y)$/","",$id);
805         $this->_edit_entry_edit($id);
806         break;
807       }
808       if(preg_match("/up_/",$name)){
809         $id = preg_replace("/^up_/","",$name);
810         $id = preg_replace("/_(x|y)$/","",$id);
811         $this->_move_entry($id,"up");
812         break;
813       }
814       if(preg_match("/down_/",$name)){
815         $id = preg_replace("/^down_/","",$name);
816         $id = preg_replace("/_(x|y)$/","",$id);
817         $this->_move_entry($id,"down");
818         break;
819       }
820       if(preg_match("/^parameter_/",$name) && 
821         count($this->edit_entry) && $this->edit_entry['TYPE'] == "ENTRY"){
822         $name = preg_replace("/^parameter_/","",$name);
823         $this->app_parameter[$name] = $value;
824       }
825     }
826     if(isset($_POST['FAIrelease'])){
827       $this->FAIrelease = $_POST['FAIrelease'];
828       $this->_check_missing_release($this->FAIrelease);
829     }
830     if(isset($_GET['act']) && $_GET['act'] == 'depopen'){
831       $this->curbase = base64_decode($_GET['depid']);
832     }
833     if(isset($_POST['add_to_folder']) && isset($_POST['folder'])){
834       $folder = $_POST['folder'];
835       foreach($_POST as $name => $value){
836         if(preg_match("/^AddApp_[0-9]*$/",$name)){
837           $this->_add_app_id($folder,preg_replace("/^AddApp_/","",$name));   
838         }
839       }
840     }
842     /* Add seperator */
843     if(isset($_POST['add_seperator']) && isset($_POST['menu_folder'])){
844       $folder = $_POST['menu_folder'];
845       $this->_add_seperator($folder);
846     }
848     if(isset($_POST['add_menu_to_folder']) && isset($_POST['menu_folder'])){
849       $folder = $_POST['menu_folder'];
850       $name = $_POST['menu_folder_name'];
851       if(strlen($name) > 0 && preg_match("/[a-z ]/i",$name)){
852         $this->_add_sub_folder($folder,$name);
853       }
854     }
855     if(isset($_POST['app_entry_save'])){ 
856       $this->_save_entry_edit();
857     }
859     if(isset($_FILES['folder_image']) && isset($_POST['folder_image_upload'])){
860       if($_FILES['folder_image']['error'] == 0 && $_FILES['folder_image']['size'] > 0){
861         $this->edit_entry['ICON'] = file_get_contents($_FILES['folder_image']['tmp_name']);
862       }
863     }
865     if(isset($_POST['edit_reset_image'])){
866       $this->edit_entry['ICON'] = "";
867     }
869     if(isset($_POST['app_entry_cancel'])){
870       $this->edit_entry = array();
871       $this->dialog = FALSE;
872     }
873     $this->reload();
874   }
876  
877   /*! \brief Returns the UNIQID of the currently selected release 
878    */ 
879   function _get_release_key($release,$add_if_missing = FALSE)
880   {
881     $release_info = $this->Releases[$release];
883     if($release_info['name'] == "/"){
884       return($this->a_Structure['0']['UNIQID']);
885     }
887     $cur = &$this->a_Structure[0]['ENTRIES'];
888     $s_key = "";
889     $found = FALSE;
890     foreach($release_info['parts'] as $name){
891       foreach($cur as $key => $obj){
892         if($obj['TYPE'] == "RELEASE" && $obj['NAME'] == $name){
893           $s_key = $cur[$key]['UNIQID'];
894           $cur = &$cur[$key]['ENTRIES'];
895           $found = TRUE;
896           break;
897         }
898         $found = FALSE;
899       }
900     }
901     if($found){
902       return($s_key);  
903     }  
904     return(FALSE);
905   }
907  
908   /*! \brief Add a new folder folder to the specified folder id
909       @param  String $folder The folder id in where we want to add the new folder.
910       @param  String $name   The name of the new folder.
911    */ 
912   function _add_sub_folder($folder,$name)
913   {
914     $all = $this->_get_all_entries();
915     if($folder == "BASE"){
916       $folder = $this->_get_release_key($this->FAIrelease,TRUE);
917     }
918     
919     if(isset($all[$folder])){
920       $a_folder = array();
921       $a_folder['STATUS'] = "ADDED";
922       $a_folder['NAME']   = $name;
923       $a_folder['UNIQID'] = uniqid();
924       $a_folder['ENTRIES']= array();
925       $a_folder['PARENT'] = $folder;      
926       $a_folder['TYPE']   = "FOLDER";
927       $a_folder['ICON']   = "";
928       $all[$folder]['ENTRIES'][] = $a_folder;
929     }
930   }
933   /* !\brief  Remove the given id from the menu structure.
934       @param  String  ID to of the entry we want to remove.
935       @return Boolean TRUE on success
936    */
937   function _remove_entry_id($id)
938   {
939     $all = $this->_get_all_entries();
940     if(isset($all[$id])){
941       $all[$id]['STATUS'] = "REMOVED";
942       $all[$id]['ENTRIES'] = array();
943       return(TRUE);
944     }
945     return(FALSE);
946   }
948   
949   /* !\brief  Adds an object to a given folder.
950       @param  String  The folder where we should add the entry
951       @param  Array   The entry we want to add.
952       @param  Array   The position in the destination entry array.
953    */
954   function _add_entry($folder_id,$entry,$pos = 0)
955   {
956     $all = $this->_get_all_entries();
958     /* Do not add removed */
959     if($entry['STATUS'] == "REMOVED"){
960       return;
961     }
963     /* Check if the folder exists 
964      */
965     if(isset($all[$folder_id])){
967       /* Check if the entry we want to add, 
968           contains su objects.
969        */
970       if(!isset($entry['ENTRIES'])){
971         $entries = array();
972       }else{
973         $entries = $entry['ENTRIES'];
974       }
975       $folder  = &$all[$folder_id];
977       /* Prepare the entry to be added.
978        */
979       $entry['UNIQID'] = uniqid();     
980       $entry['PARENT'] = $folder_id;
981       $entry['ENTRIES']= array();
982       $entry['STATUS'] = "ADDED";
983      
984       /* Append the ebtry to the given folder 
985           and to the given position \$pos
986        */ 
987       $cnt = 0; 
988       $new = array();
989       $added =FALSE;
990       foreach($folder['ENTRIES'] as $key => $obj){
991         if($obj['STATUS'] == "LOADED"){
992           $obj['STATUS'] = "EDITED";
993         }
994         if($pos == $cnt){
995           $new[] = $entry;
996           $added = TRUE;
997         }
998         $cnt ++;
999         $new[] = $obj;
1000       }
1001       if(!$added){
1002         $new[] = $entry;
1003       }
1004       $all[$folder_id]['ENTRIES'] = &$new;
1005  
1006       /* Add sub entries too.
1007        */ 
1008       foreach($entries as $sub){
1009         $this->_add_entry($entry['UNIQID'],$sub,-1);
1010       }
1011       return(TRUE);
1012     }
1013     return(FALSE);
1014   }
1016  
1017   /*! \brief Add the application identified by $app_id to folder $folder_id 
1018       @param  String  folder_id The UNIQID of the folder where we want to add the new folder.
1019       @param  Integer app_id    The ID of the application which should be added.
1020    */ 
1021   function _add_app_id($folder_id,$app_id)
1022   {
1023     $all = $this->_get_all_entries();
1024     if($folder_id == "BASE"){
1025       $folder_id = $this->_get_release_key($this->FAIrelease);
1026     }
1027     if(isset($all[$folder_id]) && isset($this->apps[$app_id])){
1029       $new = array();
1030       $new['TYPE']  = "ENTRY";
1031       $new['NAME']  = $this->apps[$app_id]['cn'][0];
1032       $new['UNIQID']= uniqid(); 
1033       $new['PARENT']= $folder_id;
1034       $new['PARAMETER']= array();
1035       if(isset($this->apps[$app_id]['description'][0])){
1036         $new['INFO']  = $this->apps[$app_id]['description'][0];
1037       }else{
1038         $new['INFO']  = "";
1039       }
1040       $new['STATUS']= "ADDED";
1041       $all[$folder_id]['ENTRIES'][] = $new;
1042     }
1043   }
1046   /*! \brief Add the application identified by $app_id to folder $folder_id 
1047       @param  String  folder_id The UNIQID of the folder where we want to add the new folder.
1048       @param  Integer app_id    The ID of the application which should be added.
1049    */ 
1050   function _add_seperator($folder_id)
1051   {
1052     $all = $this->_get_all_entries();
1053     if($folder_id == "BASE"){
1054       $folder_id = $this->_get_release_key($this->FAIrelease);
1055     }
1057     if(isset($all[$folder_id])){
1058       $new = array();
1059       $new['TYPE']  = "SEPERATOR";
1060       $new['NAME']  = "SEPERATOR";
1061       $new['UNIQID']= uniqid(); 
1062       $new['PARENT']= $folder_id;
1063       $new['PARAMETER']= array();
1064       $new['STATUS']= "ADDED";
1065       $all[$folder_id]['ENTRIES'][] = $new;
1066     }
1067   }
1070   /*! \brief  Return all entries linear. ($this->a_Structure is a multidimensional array) 
1071       @param  Boolean   $add_tags  If TRUE, OPEN/CLOSE Tags will be appended.
1072                         Used in the smarty template to display logical sperations.
1073       @param  &Array    Start here, Pointer to an array.
1074    */ 
1075   function _get_all_entries($add_tags = FALSE, $skip_release = FALSE, &$cur = NULL)
1076   {
1077     $ret = array();
1078     if($cur == NULL){
1079       $cur = &$this->a_Structure;
1080     }
1082     /* Walk through all entries and append them to our return array 
1083      */
1084     foreach($cur as $key => $entry){
1085       if($skip_release && $entry['TYPE'] == "RELEASE"){
1086         continue;
1087       }    
1088       if($entry['TYPE'] == "ENTRY"){
1089         $found = FALSE;
1090         foreach($this->apps as $app){
1091           if($app['cn'][0] == $entry['NAME']){
1092             $found = TRUE;
1093             if(isset($app['description'][0])){
1094               $entry['INFO'] = "[".$app['description'][0]."]";
1095             }
1096             break;
1097           }
1098         } 
1099         if(!$found){
1100           $entry['INFO'] = "<font color='red'>"._("Not available in release.")."</font>";
1101         }
1102       }
1104       $tmp = $entry;
1105   
1106       /* Recursive resolution of the subentries  
1107          There are two methods.
1108             - Just add sub entries  
1109             - Add sub entries and additionaly add OPEN / CLOSE tags to be able 
1110                 to display logical seperators in the smarty template.
1111        */ 
1112       if(!$add_tags){
1113         $ret[$tmp['UNIQID']] = &$cur[$key];
1114         if(isset($entry['ENTRIES']) && count($entry['ENTRIES'])){
1115           $ret = array_merge($ret,$this->_get_all_entries($add_tags,$skip_release,$cur[$key]['ENTRIES']));
1116         }
1117       }else{
1118       
1119         if(isset($tmp['ENTRIES'])){
1120           unset($tmp['ENTRIES']);
1121         }
1122         if($tmp['STATUS'] != "REMOVED"){
1123           $ret[] = $tmp;
1124           if(isset($entry['ENTRIES']) && count($entry['ENTRIES'])){
1125             $add = false;
1126             foreach($entry['ENTRIES'] as $entry){
1127               if($entry['STATUS'] != "REMOVED"){
1128                 $add = TRUE;
1129                 break;
1130               }
1131             }
1133             if($add){
1134               $ret[] = array("TYPE" => "OPEN", "PARENT" => $entry['PARENT']);
1135               $ret = array_merge($ret,$this->_get_all_entries($add_tags,$skip_release,$cur[$key]['ENTRIES']));
1136               $ret[] = array("TYPE" => "CLOSE" , "PARENT" => $entry['PARENT']);
1137             }
1138           }
1139         }
1140       }
1141     }
1142     return($ret);
1143   }
1146   /*! \brief Save this plugin data to ldap.
1147              Save the current menu structure to ldap.
1148    */
1149   function save()
1150   {
1151     $ldap = $this->config->get_ldap_link();
1152     $all = $this->_get_all_entries();
1153     $prio = 0;
1154     $Actions = array("Remove" => array(),"Edit" => array() , "Add" => array());
1157     /* Walk through the menu structure and build up the ldap data object, 
1158         the entry dn and the entry priority.
1159      */
1160     $sep_id = 0;
1161     foreach($all as $entry){
1162       $prio ++;
1163       $cur = $entry;
1164       $dn = "";
1166       /* Build entry dn
1167        */
1168       do{  
1169         if($cur['TYPE'] == "SEPERATOR"){
1170           $sep_id ++;
1171           $dn.= "cn=seperator_".$sep_id.",";
1172         }elseif($cur['TYPE'] == "ENTRY"){
1173           $dn.= "cn=".$cur['NAME'].",";
1174         }elseif($cur['TYPE'] == "FOLDER"){
1175           $dn.= "cn=".$cur['NAME'].",";
1176         }elseif($cur['TYPE'] == "RELEASE"){
1177           $dn.= "ou=".$cur['NAME'].",";
1178         }elseif($cur['TYPE'] == "BASE"){
1179         }
1180         if(!isset($all[$cur['PARENT']])){
1181           $cur = NULL;
1182         }else{
1183           $cur = $all[$cur['PARENT']];
1184         }
1185       }while(is_array($cur));
1187       $cur_dn = $dn.$this->dn;
1188       $attrs = array();
1190       /* Build entry data object.
1191        */
1192       switch($entry['TYPE']){
1193         case "SEPERATOR"    :
1194         { 
1195           $attrs['objectClass'] = array("gotoMenuEntry");
1196           $attrs['cn']          = "seperator_".$sep_id;
1197           $attrs['gosaApplicationPriority'] = $prio;
1198           $attrs['gosaApplicationParameter'] = "*separator*";
1199         }
1200         break;
1201         case "ENTRY"    :
1202         { 
1203           $attrs['objectClass'] = array("gotoMenuEntry");
1204           $attrs['cn']          = $entry['NAME'];
1205           $attrs['gosaApplicationPriority'] = $prio;
1206           $attrs['gosaApplicationParameter'] = array(); 
1207           
1208           foreach($entry['PARAMETER'] as $name => $value){
1209             $attrs['gosaApplicationParameter'][] = $name.":".$value; 
1210           }
1211           if($entry['STATUS'] == "ADDED" && !count($attrs['gosaApplicationParameter'])){
1212             unset($attrs['gosaApplicationParameter']);
1213           } 
1214         }
1215         break;
1216         case "FOLDER"   : 
1217         { 
1218           $attrs['objectClass'] = array("gotoSubmenuEntry");
1219           $attrs['cn']          = $entry['NAME'];
1220           $attrs['gosaApplicationPriority'] = $prio;
1221           if($entry['STATUS'] != "ADDED"){
1222             $attrs['gosaApplicationIcon'] = array();
1223           }
1224           
1225           if(!empty($entry['ICON'])){
1226             $attrs['gosaApplicationIcon']     = $entry['ICON'];
1227           }
1228         }
1229         break;
1230         case "RELEASE"  : 
1231         {
1232           $attrs['ou']            = $entry['NAME'];
1233           $attrs['objectClass']   = array();
1234           $attrs['objectClass'][] = "top";
1235           $attrs['objectClass'][] = "organizationalUnit";
1236           $attrs['objectClass'][] = "FAIbranch";
1237           if(!empty($entry['FAIstate'])){
1238             $attrs['FAIstate']      = $entry['FAIstate'];
1239           }
1240         }
1241         break;
1242       }
1243  
1244       /* Append missing ObjectClasses,  ...  Tagging 
1245        */
1246       if(isset($entry['LDAP_ATTRS'])){
1247         for($i = 0 ; $i < $entry['LDAP_ATTRS']['objectClass']['count']; $i ++){
1248           $oc = $entry['LDAP_ATTRS']['objectClass'][$i];
1249           if(!in_array($oc,$attrs['objectClass'])){
1250             $attrs['objectClass'][] = $oc;
1251           }
1252         }
1253       }
1254   
1255       /* Create an array containing all operations sorted by type. (add,remove...)
1256        */
1257       if($entry['STATUS'] == "LOADED"){
1258         continue;
1259       }
1260       if($entry['STATUS'] == "REMOVED"){
1261         if(isset($entry['DN'])){
1262           $Actions['Remove'][$entry['DN']] = $entry['DN'];
1263         }else{
1264           $Actions['Remove'][$cur_dn] = $cur_dn;
1265         }
1266       }
1267       if($entry['STATUS'] == "EDITED"){
1268         $Actions['Edit'][$cur_dn] = $attrs;
1269       }
1270       if($entry['STATUS'] == "ADDED"){
1271         $Actions['Add'][$cur_dn] = $attrs;
1272       }
1273     }
1275     /* First remove entries
1276      */
1277     $ldap = $this->config->get_ldap_link();
1278     $ldap->cd($this->config->current['BASE']);
1279     foreach($Actions['Remove'] as $dn){
1280       $ldap->cd($dn);
1281       $ldap->cat($dn);
1282       if($ldap->count()){
1283         $ldap->rmdir_recursive($dn);
1284         if (!$ldap->success()){
1285           msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_DEL, get_class()));
1286         }
1287       }
1288     }
1289     
1290     /* Add new entries
1291      */
1292     foreach($Actions['Add'] as $dn => $data){
1293       $ldap->cd($dn);
1294       $ldap->cat($dn);
1295       if(!$ldap->count()){
1296         $ldap->add($data);
1297         if (!$ldap->success()){
1298           msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_DEL, get_class()));
1299         }
1300       }
1301     }
1303     /* Modify entries
1304      */
1305     foreach($Actions['Edit'] as $dn => $data){
1306       $ldap->cd($dn);
1307       $ldap->cat($dn);
1308       if($ldap->count()){
1309         $ldap->modify($data);
1310         if (!$ldap->success()){
1311           msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, LDAP_DEL, get_class()));
1312         }
1313       }
1314     }
1316     $this->_load_menu_structure();
1317   }
1320   /*! \brief  Return plugin informations for acl handling  
1321       @return Array containing all plugin ACL informations
1322    */ 
1323   static function plInfo()
1324   {
1325     return (array(
1326           "plShortName"   => _("Applications"),
1327           "plDescription" => _("Group applications"),
1328           "plSelfModify"  => FALSE,
1329           "plDepends"     => array(),
1330           "plPriority"    => 0,
1331           "plSection"     => array("admin"),
1332           "plCategory"    => array("groups"),
1333           "plProvidedAcls"=> array(
1334             "gosaMemberApplication"     => _("Application"),
1335             "FAIrelease"                => _("Release"),
1336             "gosaApplicationParameter"  => _("Application parameter"))
1337           ));
1338   }
1341   /* \brief   Prepare this plugin to be copied.
1342               Adapt all required attributes from the source object.
1343               In this case, update the menu structure too, mark all elements
1344                as newly added, so they will be saved in save();
1345    */
1346   function PrepareForCopyPaste($source)
1347   {
1348     plugin::PrepareForCopyPaste($source);
1349    
1350     $tmp = new appgroup($this->config,$source['dn']);
1351     $this->is_account = TRUE;
1352     $this->a_Structure = $tmp->a_Structure;
1353     $all = $this->_get_all_entries();
1354     foreach($all as &$entry){
1355       if(isset($entry['STATUS'])){
1356         $entry['STATUS'] = "ADDED";
1357       }
1358     }
1359   }
1362   /*! \brief  Save HTML posts in multiple edit mode
1363    */
1364   function multiple_save_object()
1365   {
1366     if(isset($_POST['group_apps_multi'])){
1367       $this->save_object(); 
1368       plugin::multiple_save_object();    
1369   
1370       /* Get posts */
1371       foreach(array("apps") as $attr){
1372         if(isset($_POST['use_'.$attr])) {
1373           $this->multi_boxes[] = $attr;
1374         }
1375       }
1376     }
1377   }
1378   
1380   /*! \brief  Return values used in multiple edit mode.
1381               Some values can be modified for multiple 
1382               groups at the same time.
1383       @return Array  All values that support multiple edit.
1384    */
1385   function get_multi_edit_values()
1386   {
1387     $ret = plugin::get_multi_edit_values();
1389     if(in_array("apps",$this->multi_boxes)){
1390       $ret['gosaApplicationParameter'] = $this->gosaApplicationParameter;
1391       $ret['Categories']               = $this->Categories;
1392       $ret['gosaMemberApplication']    = $this->gosaMemberApplication;
1393       $ret['FAIrelease']               = $this->FAIrelease;
1394       $ret['appoption']                = $this->appoption;
1395     }
1396     return($ret);
1397   }
1399 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1400 ?>