Code

Some fixed for app groups
[gosa.git] / gosa-plugins / goto / admin / groups / apps / class_groupApplication2.inc
index 8e9d263216c9ec7d571352356a6a82a5da0b5a24..d1822939b4093ec8a7f368230168669815335401 100644 (file)
@@ -8,11 +8,16 @@ class appgroup2 extends plugin
 
 
   var $config;
+  var $curbase;
 
   /* Contains the menu structure in an array.
    */
   var $a_Structure= array();
   var $a_Structure_on_load = array();
+  var $Releases;
+  var $FAIrelease = 0;
+  var $apps = array();
+  var $_cache = array();
 
   public function __construct(&$config, $dn= NULL, $parent= NULL)
   {
@@ -20,6 +25,88 @@ class appgroup2 extends plugin
     $this->dn = $dn; 
     $this->_load_menu_structure();
     $this->a_Structure_on_load = $this->a_Structure;
+
+    /* Check if we have relase mangement enabled and prepare group application for release management */
+    $tmp = $config->search("faiManagement", "CLASS",array('menu','tabs'));
+    if(!empty($tmp)){
+      $this->enableReleaseManagement = true;
+    }
+
+    /* Get required release informations */
+    if($this->enableReleaseManagement){
+      $this->Releases   = $this->getReleases();
+#      $this->FAIrelease = 0;
+    }
+#    $this->curbase = $this->config->current['BASE'];
+    $this->reload();
+  }
+
+
+  function reload()
+  {
+    $ret = array();
+    $release_info = $this->Releases[$this->FAIrelease];
+
+    if(1 | !isset($this->_cache['ReleaseApps'][$release_info['suffix']])){
+
+      $ldap = $this->config->get_ldap_link();
+      $ldap->cd($this->config->current['BASE']);
+      $ldap->search("ou=apps",array("dn"));
+      $app_deps = array();
+      while($attrs = $ldap->fetch()){
+        $app_deps[] = $attrs['dn'];
+      }
+
+      foreach($app_deps as $dep){
+        $ldap->cd($dep);
+        $ldap->search("objectClass=FAIbranch",array("dn"));
+        while($attrs = $ldap->fetch()){
+          $app_deps[] = $attrs['dn'];
+        }
+      }
+
+      foreach($app_deps as $dep){
+        if(preg_match("/^".normalizePreg($release_info['suffix'])."/",$dep)){
+          $ret = array_merge($ret,get_list("(objectClass=gosaApplication)","application",$dep,array("*"),GL_NONE));
+        }
+      }
+      $this->_cache['ReleaseApps'][$release_info['suffix']] = $ret;
+    } 
+    $this->apps = $this->_cache['ReleaseApps'][$release_info['suffix']];
+  }
+
+
+  function getReleases()
+  {
+    /* Only display those releases that we are able to read */
+    $dn     = $this->config->current['BASE'];
+    $filter = "(&(objectClass=organizationalUnit)(objectClass=FAIbranch))";
+    $res    = get_list($filter,"application", $dn, array("ou"), GL_SUBSEARCH);
+
+    $ret =array(array("name" => "/" , "parts" => array(),"suffix" => get_ou('applicationou')));
+    foreach($res as $attrs){
+      if(preg_match("/".get_ou('applicationou')."/",$attrs['dn'])){
+        $bb     = preg_replace("/".get_ou('applicationou').".*/","",$attrs['dn']);
+        $parts  = array_reverse(split("ou=",$bb));
+
+        $str ="";
+        foreach($parts as $key => $part){
+          if(empty($part)) {
+            unset($parts[$key]);
+            continue;
+          }
+          $part = str_replace(",","",$part);
+          $str .= $part."/";
+          $parts[$key] = $part;
+        }
+        $name = preg_replace("/\/$/","",$str);
+        if(empty($name)) {
+          $name ="/";
+        }
+        $ret[] = array("name" => $name , "dn" => $attrs['dn'], "parts" => $parts,"suffix" => $bb.get_ou('applicationou'));
+      }
+    }
+    return($ret);
   }
 
 
@@ -45,7 +132,7 @@ class appgroup2 extends plugin
           }
         }else{
 
-          $priority = -1;
+          $priority = 1;
           if(isset($attrs['gosaApplicationPriority'])){
             $priority= $attrs['gosaApplicationPriority'][0];
           }
@@ -53,15 +140,16 @@ class appgroup2 extends plugin
             $priority ++;
           }
 
+          $data = array();
           if(in_array("gotoSubmenuEntry",$attrs['objectClass'])){
             $type = "FOLDER";
           }elseif(in_array("gotoMenuEntry",$attrs['objectClass'])){
             $type = "ENTRY";
           }elseif(in_array("FAIbranch",$attrs['objectClass'])){
             $type = "RELEASE";
+            $data['FAIstate'] = $attrs['FAIstate'][0];
           }
 
-          $data = array();
           $data['DN']       = $attrs['dn'];
           $data['NAME']     = $name;
           $data['TYPE']     = $type;
@@ -69,6 +157,7 @@ class appgroup2 extends plugin
           $data['ENTRIES']  = array();
           $data['UNIQID']   = uniqid();
           $data['PARENT']   = $parent_id;
+          $data['STATUS']   = "LOADED";
           $cur[$priority]   = $data;
           ksort($cur);
         }
@@ -86,33 +175,226 @@ class appgroup2 extends plugin
     $this->__construct($this->config,$this->dn);
 
     $smarty = get_smarty();
-    $smarty->assign("entries",$this->_get_all_entries(TRUE));
+
+    /* Create application list */
+    $div = new divSelectBox("appgroup");
+    $div->SetHeight(300);
+    $departments = array();
+    $res = get_list("(objectClass=gosaDepartment)", "application", $this->curbase,array("description","cn","ou"),GL_SIZELIMIT);
+    foreach($res as $value){
+      $fdn = $value['dn'];
+      $fdn = preg_replace("/".normalizePreg($this->curbase)."/","",$fdn);
+      $fdn= @LDAP::fix($fdn);
+      if($value["description"][0]!=".."){
+        $departments[$value['dn']]= convert_department_dn($fdn)." - [".$value["description"][0]."]";
+      }else{
+        $departments[$value['dn']]=convert_department_dn($fdn)." ["._("Back")."]";
+      }
+    }
+
+    /* Create 'open' and 'add' links */
+#    if($this->acl_is_writeable("gosaMemberApplication",$this->no_release_acls)){
+      $linkadd  = "<a href='?plug=".$_GET['plug']."&amp;act=add&amp;id=%s'>%s</a>";
+ #   }else{
+  #    $linkadd = "%s";
+   # }
+    $linkopen = "<a href='?plug=".$_GET['plug']."&amp;act=depopen&amp;depid=%s'>%s</a>";
+
+    /* Create base back entry */
+    $base_back = preg_replace("/^[^,]+,/","",$this->curbase);
+    if((strlen($base_back)>= strlen($this->config->current['BASE']))&&($this->curbase!=$this->config->current['BASE'])){
+      $div->AddEntry(array(
+            array("string"=>sprintf($linkopen,base64_encode($base_back),".. ["._("back")."]"),
+              "attach"=>"style='border:0px;'")
+            ));
+    }
+
+    /* Append departments for current base */
+    foreach($departments as $key => $app){
+      $div->AddEntry(array(
+            array("string"=>"<img class='center' src='images/folder.png' alt='"._("department")."'>&nbsp;".sprintf($linkopen,
+                base64_encode($key),$app),
+              "attach"=>"style='border:0px;'")
+            ));
+    }
+
+  
+    /* Add applications found on this base */
+    $used_apps = $this->_get_used_entry_name();
+    foreach($this->apps as $key => $app){
+      if(in_array($app['cn'][0],$used_apps)){
+        continue;
+      }
+      if(!preg_match("/".get_ou('applicationou').normalizePreg($this->curbase)."$/",$app['dn'])){
+        continue;
+      }
+
+      $name = $app['cn'][0];
+      if(isset($app['description'])){
+        $name .= "&nbsp;[".$app['description'][0]."]";
+      }
+      $div->AddEntry(array(
+            array("string"=>sprintf("<input class='center' type='checkbox' value='1' name='AddApp_%s'>",$key).
+              "<img class='center' src='images/select_application.png' alt='"._("application")."'>&nbsp;".sprintf($linkadd,$key,$name),
+              "attach"=>"style='border:0px;'")
+            ));
+    }
+
+    
+    $smarty->assign("FAIrelease",$this->FAIrelease);
+    $smarty->assign("app_list",$div->DrawList());
+    $smarty->assign("releases",$this->Releases);
+    $smarty->assign("folders" , $this->_get_folder_names());
+    $entries = $this->_get_entries_for_release($this->FAIrelease);
+    $smarty->assign("entries",$entries);
     $display= $smarty->fetch (get_template_path('app_list.tpl', TRUE, dirname(__FILE__)));
     return($display);
   }
 
-
-  /* !\brief Remove the given id from the menu structure.
-      @param  String  ID to of the entry we want to remove.
-      @return Boolean TRUE on success
-   */
-  function _remove_entry_id($id,$start = NULL)
+    
+  function _get_folder_names()
   {
-    if($start == NULL){
-      $start = &$this->a_Structure;
+    $data = $this->_get_entries_for_release($this->FAIrelease);
+    $ret = array("BASE" => ".");
+    foreach($data as $entry){
+      if($entry['TYPE'] == "FOLDER"){
+        $ret[$entry['UNIQID']] = $entry['NAME'];
+      }
     }
-    foreach($start as $name => $entry){
-      if($entry['UNIQID'] == $id){
-        unset($start[$name]);
-        return(TRUE);
+    return($ret);
+  }
+
+  function _get_used_entry_name()
+  {
+    $data = $this->_get_entries_for_release($this->FAIrelease);
+    $ret = array();
+    foreach($data as $entry){
+      if($entry['TYPE'] == "ENTRY"){
+        $ret[] = $entry['NAME'];
       }
-      if(isset($entry['ENTRIES']) && count($start[$name]['ENTRIES'])){
-        if($this->_remove_entry_id($id,&$start[$name]['ENTRIES'])){
-          return(TRUE);
+    }
+    return($ret);
+  }
+
+  function _get_entries_for_release($release,$cur = NULL)
+  {
+    $all = $this->_get_all_entries();
+    $key = $this->_get_release_key($release);
+    if(isset($all[$key])){
+      return($this->_get_all_entries(TRUE,TRUE,&$all[$key]['ENTRIES']));
+    } 
+    return(array());
+  }
+
+
+  function _edit_entry_edit($id)
+  {
+  }
+
+
+  function remove_from_parent()
+  {
+  }
+
+
+  function save()
+  {
+    $ldap = $this->config->get_ldap_link();
+    $all = $this->_get_all_entries();
+    $prio = 0;
+    $Actions = array("Remove" => array(),"Edit" => array() , "Add" => array());
+    foreach($all as $entry){
+      $prio ++;
+      $cur = $entry;
+      $dn = "";
+
+      do{  
+        if($cur['TYPE'] == "ENTRY"){
+          $dn.= "cn=".$cur['NAME'].",";
+        }elseif($cur['TYPE'] == "FOLDER"){
+          $dn.= "cn=".$cur['NAME'].",";
+        }elseif($cur['TYPE'] == "RELEASE"){
+          $dn.= "ou=".$cur['NAME'].",";
+        }
+        $cur = $all[$cur['PARENT']];
+        if(!isset($cur['PARENT'])){
+          $cur = NULL;
+        }
+      }while(is_array($cur));
+      $cur_dn = $dn.$this->dn;
+
+      $attrs = array();
+      switch($entry['TYPE']){
+        case "ENTRY"    :
+        { 
+          $attrs['objectClass'] = "gotoMenuEntry";
+          $attrs['cn']          = $entry['NAME'];
+          $attrs['gosaApplicationPriority'] = $prio;
         }
+        break;
+        case "FOLDER"   : 
+        { 
+          $attrs['objectClass'] = "gotoSubmenuEntry";
+          $attrs['cn']          = $entry['NAME'];
+          $attrs['gosaApplicationPriority'] = $prio;
+        }
+        break;
+        case "RELEASE"  : 
+        { 
+          $attrs['ou']            = $entry['NAME'];
+          $attrs['objectClass']   = array();
+          $attrs['objectClass'][] = "top";
+          $attrs['objectClass'][] = "organizationalUnit";
+          $attrs['objectClass'][] = "FAIbranch";
+          $attrs['FAIstate']      = $entry['FAIstate'];
+        }
+        break;
+      }
+  
+      if($entry['STATUS'] == "LOADED"){
+        continue;
+      }
+      if($entry['STATUS'] == "REMOVED"){
+        $Actions['Remove'][$cur_dn] = $cur_dn;
+      }
+      if($entry['STATUS'] == "EDITED"){
+        $Actions['Edit'][$cur_dn] = $attrs;
+      }
+      if($entry['STATUS'] == "ADDED"){
+        $Actions['Add'][$cur_dn] = $attrs;
+      }
+    }
+
+    $ldap = $this->config->get_ldap_link();
+    $ldap->cd($this->config->current['BASE']);
+    foreach($Actions['Remove'] as $dn){
+      $ldap->cd($dn);
+      $ldap->cat($dn);
+      if($ldap->count()){
+        $ldap->rmdir_recursive($dn);
+      }
+    }
+    foreach($Actions['Add'] as $dn => $data){
+      $ldap->cd($dn);
+      $ldap->cat($dn);
+      if(!$ldap->count()){
+        $ldap->add($data);
+      }
+    }
+    foreach($Actions['Edit'] as $dn => $data){
+      $ldap->cd($dn);
+      $ldap->cat($dn);
+      if($ldap->count()){
+        $ldap->modify($data);
       }
     }
-    return(FALSE);
+  }
+
+
+  function check()
+  {
   }
 
 
@@ -147,121 +429,154 @@ class appgroup2 extends plugin
         break;
       }
     }
+    if(isset($_POST['FAIrelease'])){
+      $this->FAIrelease = $_POST['FAIrelease'];
+    }
+    if(isset($_GET['act']) && $_GET['act'] == 'depopen'){
+      $this->curbase = base64_decode($_GET['depid']);
+    }
+    if(isset($_POST['add_to_folder']) && isset($_POST['folder'])){
+      $folder = $_POST['folder'];
+      foreach($_POST as $name => $value){
+        if(preg_match("/^AddApp_[0-9]*$/",$name)){
+          $this->_add_app_id($folder,preg_replace("/^AddApp_/","",$name));   
+        }
+      }
+    }
+    if(isset($_POST['add_menu_to_folder']) && isset($_POST['menu_folder'])){
+      $folder = $_POST['menu_folder'];
+      $name = $_POST['menu_folder_name'];
+      if(strlen($name) > 0 && preg_match("/[a-z ]/i",$name)){
+        $this->_add_sub_folder($folder,$name);
+      }
+    }
+    $this->reload();
   }
 
   
-  function _edit_entry_edit($id)
+  function _get_release_key($release)
   {
+    $release_info = $this->Releases[$release];
+    $cur = &$this->a_Structure;
+    $s_key = "";
+    $found = FALSE;
+    foreach($release_info['parts'] as $name){
+      foreach($cur as $key => $obj){
+        if($obj['TYPE'] == "RELEASE" && $obj['NAME'] == $name){
+          $s_key = $cur[$key]['UNIQID'];
+          $cur = &$cur[$key]['ENTRIES'];
+          $found = TRUE;
+          break;
+        }
+        $found = FALSE;
+      }
+    }
+    if($found){
+      return($s_key);  
+    }  
+    return(FALSE);
   }
 
-
-  /* !\brief Switch one entry with another 
-     @param  String  from  The source ID.
-     @param  String  type  "up"/"down" type switched.
-     @param  String  to    The destination ID.
-     return  Boolean TRUE on success 
-   */
-  function __switch_entries($from,$type,$to)
+  
+  function _add_sub_folder($folder,$name)
   {
     $all = $this->_get_all_entries();
-
-    $o_to   = &$all[$to];
-    $o_from = &$all[$from];
-
-    /***********************
-     * Source == Destination 
-     * Move into next parent. 
-     ************************/
-    if($to == $from){
-      $to = $o_to['PARENT'];
-      $o_to   = &$all[$to];
+    if($folder == "BASE"){
+      $folder = $this->_get_release_key($this->FAIrelease);
     }
+    
+    if(isset($all[$folder])){
+      $a_folder = array();
+      $a_folder['STATUS'] = "ADDED";
+      $a_folder['NAME']   = $name;
+      $a_folder['UNIQID'] = uniqid();
+      $a_folder['PARENT'] = $folder;      
+      $a_folder['TYPE']   = "FOLDER";
+      $all[$folder]['ENTRIES'][] = $a_folder;
+    }
+  }
 
-    /***********************
-     * Target is container 
-     ************************/
-    if(in_array($o_to['TYPE'],array("FOLDER","RELEASE"))){
-
-      /***********************
-       * Move into parent folder  
-       *   + Target 
-       *   |-> Source
-       ************************/
-      if($to == $o_from['PARENT']){
-        /* Check if source is a folder object 
-         */
-        $o_to_sub = &$all[$o_to['PARENT']]; 
-        if(in_array($o_to_sub['TYPE'],array("FOLDER","RELEASE"))){
-
-          /* Removing old */
-          $tmp = array();
-          foreach($o_to['ENTRIES'] as $key => $entry){
-            if($entry['UNIQID'] == $from){
-              if(!$this->_remove_entry_id($from)){
-                return(FALSE);
-              }
-            }
-          }
-
-          /* Adding new */
-          $tmp2 = array();
-          foreach($o_to_sub['ENTRIES'] as $key => $entry){
-
-            if($type == "up"){
-              if($entry['UNIQID'] == $to){
-                $o_from['PARENT'] = $o_to_sub['UNIQID'];
-                $tmp2[] = &$o_from;
-              }
-              $tmp2[] = &$o_to_sub['ENTRIES'][$key];
-            }else{
-
-              $tmp2[] = &$o_to_sub['ENTRIES'][$key];
-              if($entry['UNIQID'] == $to){
-                $o_from['PARENT'] = $o_to_sub['UNIQID'];
-                $tmp2[] = &$o_from;
-              }
-            }
-          }
-          $all[$o_to_sub['UNIQID']]['ENTRIES'] = $tmp2;
-        } 
-      }else{
 
-        /***********************
-         * Target is NOT parent container
-         *  + Parent Folder
-         *  |-> Source 
-         *  + Destination
-         ************************/
+  /* !\brief Remove the given id from the menu structure.
+      @param  String  ID to of the entry we want to remove.
+      @return Boolean TRUE on success
+   */
+  function _remove_entry_id($id)
+  {
+    $all = $this->_get_all_entries();
+    if(isset($all[$id])){
+      $all[$id]['STATUS'] = "REMOVED";
+      return(TRUE);
+    }
+    return(FALSE);
+  }
 
-        /* Removing old */
-        $o_to   = &$all[$to];
-        $o_from = &$all[$from];
-        $this->_remove_entry_id($from);
-        $o_from['PARENT']     = $to;
-        $o_to['ENTRIES'][]    = $o_from;
+  
+  /* !\brief Remove the given id from the menu structure.
+      @param  String  ID to of the entry we want to remove.
+      @return Boolean TRUE on success
+   */
+  function _add_entry($folder_id,$entry,$pos = 0)
+  {
+    $all = $this->_get_all_entries();
 
+    if(isset($all[$folder_id])){
+
+      $folder  = &$all[$folder_id];
+      $entries = $entry['ENTRIES'];
+
+      $entry['UNIQID'] = uniqid();     
+      $entry['PARENT'] = $folder_id;
+      $entry['ENTRIES']= array();
+      $entry['STATUS'] = "ADDED";
+      
+      $cnt = 0; 
+      $new = array();
+      $added =FALSE;
+      foreach($folder['ENTRIES'] as $key => $obj){
+        if($pos == $cnt){
+          $new[] = $entry;
+          $added = TRUE;
+          $cnt ++;
+        }
+        $new[] = $obj;
       }
-    }else{
-
-      /***********************
-       * Source and Destination in some Folder.
-       *  + Parent folder
-       *  |-> Source
-       *  |-> Destination
-       ************************/
-      $o_to   = &$all[$to];
-      $o_from = &$all[$from];
-
-      if($o_to['PARENT'] == $o_from['PARENT']){
-        $tmp = $all[$to];
-        $all[$to]   = $o_from;
-        $all[$from] = $tmp;
+      if(!$added){
+        $new[] = $entry;
       }
+      $all[$folder_id]['ENTRIES'] = $new;
+      foreach($entries as $sub){
+        $this->_add_entry($entry['UNIQID'],$sub);
+      }
+      return(TRUE);
     }
+    return(FALSE);
   }
 
+  
+  function _add_app_id($folder_id,$app_id)
+  {
+    $all = $this->_get_all_entries();
+    if($folder_id == "BASE"){
+      $folder_id = $this->_get_release_key($this->FAIrelease);
+    }
+    if(isset($all[$folder_id]) && isset($this->apps[$app_id])){
+
+      $new = array();
+      $new['TYPE']  = "ENTRY";
+      $new['NAME']  = $this->apps[$app_id]['cn'][0];
+      $new['UNIQID']= uniqid(); 
+      $new['PARENT']= $folder_id;
+      $new['STATUS']= "ADDED";
+      $all[$folder_id]['ENTRIES'][] = $new;
+    }
+  }
 
+
+  /*! \brief move the object identified by the given ID one position up.
+      @param  String  The ID of the entry to be moved.
+      @return Boolean TRUE on success, else FALSE;
+   */
   function _move_entry_up($id)
   {
     $all = $this->_get_all_entries(TRUE);
@@ -269,8 +584,7 @@ class appgroup2 extends plugin
     foreach($all as $entry){
       if(isset($entry['UNIQID']) && $entry['UNIQID'] == $id){
         if($parent != FALSE){
-          $this->__switch_entries($id,"up",$parent);
-          return;
+          return($this->__switch_entries($id,"up",$parent));
         }
       }else{
         if(in_array($entry['TYPE'],array("CLOSE","OPEN"))){
@@ -280,9 +594,14 @@ class appgroup2 extends plugin
         }
       }
     }
+    return(FALSE);
   }
 
 
+  /*! \brief move the object identified by the given ID one position down.
+      @param  String  The ID of the entry to be moved.
+      @return Boolean TRUE on success, else FALSE;
+   */
   function _move_entry_down($id)
   {
     $all = $this->_get_all_entries(TRUE);
@@ -298,39 +617,65 @@ class appgroup2 extends plugin
           $parent = $entry['UNIQID'];
         }
         if($found){
-          $this->__switch_entries($id,"down",$parent);
-          return;
+          return($this->__switch_entries($id,"down",$parent));
         }
       }
     }
+    return(FALSE);
   }
 
 
-   
-  function _get_all_entries($add_tags = FALSE,$cur = NULL,$depth = 0)
+  /*! \brief  Return all entries linear. 
+      @param  Boolean   $add_tags  If TRUE, OPEN/CLOSE Tags will be appended.
+      @param  &Array    Start here, Pointer to an array.
+   */ 
+  function _get_all_entries($add_tags = FALSE, $skip_release = FALSE, $cur = NULL)
   {
     $ret = array();
-    $depth ++;
     if($cur == NULL){
       $cur = &$this->a_Structure;
     }
     foreach($cur as $key => $entry){
 
+      if($skip_release && $entry['TYPE'] == "RELEASE"){
+        continue;
+      }    
+      if($entry['TYPE'] == "ENTRY"){
+        $found = FALSE;
+        foreach($this->apps as $app){
+          if($app['cn'][0] == $entry['NAME']){
+            $found = TRUE;
+            if(isset($app['description'][0])){
+              $entry['INFO'] = "[".$app['description'][0]."]";
+            }
+            break;
+          }
+        } 
+        if(!$found){
+          $entry['INFO'] = "<font color='red'>"._("Not available in release.")."</font>";
+        }
+      }
+      
+
       $tmp = $entry;
       if(!$add_tags){
         $ret[$tmp['UNIQID']] = &$cur[$key];
         if(isset($entry['ENTRIES']) && count($entry['ENTRIES'])){
-          $ret = array_merge($ret,$this->_get_all_entries($add_tags,&$cur[$key]['ENTRIES'],$depth));
+          $ret = array_merge($ret,$this->_get_all_entries($add_tags,$skip_release,&$cur[$key]['ENTRIES']));
         }
       }else{
-      if(isset($tmp['ENTRIES'])){
-        unset($tmp['ENTRIES']);
-      }
-        $ret[] = $tmp;
-        if(isset($entry['ENTRIES']) && count($entry['ENTRIES'])){
-          $ret[] = array("TYPE" => "OPEN", "PARENT" => $entry['UNIQID']);
-          $ret = array_merge($ret,$this->_get_all_entries($add_tags,&$cur[$key]['ENTRIES'],$depth));
-          $ret[] = array("TYPE" => "CLOSE" , "PARENT" => $entry['UNIQID']);
+      
+        if(isset($tmp['ENTRIES'])){
+          unset($tmp['ENTRIES']);
+        }
+
+        if($tmp['STATUS'] != "REMOVED"){
+          $ret[] = $tmp;
+          if(isset($entry['ENTRIES']) && count($entry['ENTRIES'])){
+            $ret[] = array("TYPE" => "OPEN", "PARENT" => $entry['UNIQID']);
+            $ret = array_merge($ret,$this->_get_all_entries($add_tags,$skip_release,&$cur[$key]['ENTRIES']));
+            $ret[] = array("TYPE" => "CLOSE" , "PARENT" => $entry['UNIQID']);
+          }
         }
       }
     }
@@ -338,24 +683,107 @@ class appgroup2 extends plugin
   }
 
 
-  function remove_from_parent()
+  /* !\brief Switch one entry with another 
+     @param  String  from  The source ID.
+     @param  String  type  "up"/"down" type switched.
+     @param  String  to    The destination ID.
+     return  Boolean TRUE on success 
+   */
+  function __switch_entries($from,$type,$to)
   {
-  }
+    $all = $this->_get_all_entries();
 
+    $o_to   = &$all[$to];
+    $o_from = &$all[$from];
 
-  function save()
-  {
-  }
+    /***********************
+     * Source == Destination 
+     * Move into next parent. 
+     ************************/
+    if($to == $from){
+      $to = $o_to['PARENT'];
+      $o_to   = &$all[$to];
+    }
 
+    /***********************
+     * Target is container 
+     ************************/
+    if(in_array($o_to['TYPE'],array("FOLDER","RELEASE"))){
 
-  function check()
-  {
-  }
+      /***********************
+       * Move into parent folder  
+       *   + Target 
+       *   |-> Source
+       ************************/
+      if($to == $o_from['PARENT']){
 
+        /* Check if source is a folder object 
+         */
+        $o_to_sub = &$all[$o_to['PARENT']]; 
+        if(in_array($o_to_sub['TYPE'],array("FOLDER","RELEASE"))){
 
+          $o_from['STATUS'] = "ADDED";
 
-  function PrepareForCopyPaste($source)
-  {
+          /* Adding new */
+          $tmp2 = array();  
+          $cnt = 0; 
+          foreach($o_to_sub['ENTRIES'] as $key => $entry){
+            $cnt ++;
+            if($entry['UNIQID'] == $to){
+              break;
+            }
+          }
+          if($type == "up"){
+            $cnt --;
+         }
+         $this->_add_entry($o_to_sub['UNIQID'],$o_from,$cnt);
+
+          /* Removing old */
+          $tmp = array();
+          if(!$this->_remove_entry_id($from)){
+            return(FALSE);
+          }
+        } 
+      }else{
+
+        /***********************
+         * Target is NOT parent container
+         *  + Parent Folder
+         *  |-> Source 
+         *  + Destination
+         ************************/
+
+        /* Removing old */
+        $o_to   = $all[$to];
+        $o_from = $all[$from];
+        $this->_add_entry($to,$o_from);
+        $this->_remove_entry_id($from);
+      }
+    }else{
+
+      /***********************
+       * Source and Destination in some Folder.
+       *  + Parent folder
+       *  |-> Source
+       *  |-> Destination
+       ************************/
+      $o_to   = &$all[$to];
+      $o_from = &$all[$from];
+      $parent = &$all[$o_to['PARENT']];
+
+      if($o_to['PARENT'] == $o_from['PARENT']){
+        $tmp = $all[$to];
+        $all[$to]   = $o_from;
+        $all[$from] = $tmp;
+  
+        /* Ensure that the app priority is updated */
+        foreach($parent['ENTRIES'] as $key => $entry){
+          $parent['ENTRIES'][$key]['STATUS'] = "EDITED";
+        }
+      }
+    }
   }
 
 
@@ -378,6 +806,11 @@ class appgroup2 extends plugin
   }
 
 
+  function PrepareForCopyPaste($source)
+  {
+  }
+
+
   function multiple_save_object()
   {
     if(isset($_POST['group_apps_multi'])){
@@ -393,15 +826,6 @@ class appgroup2 extends plugin
     }
   }
   
-  function multiple_execute()
-  {
-    return($this->execute());
-  }
-
-  function init_multiple_support($attrs,$all)
-  {
-    // Do nothing here
-  }
 
   function get_multi_edit_values()
   {