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