Index: branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs
===================================================================
--- branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs	(revision 15966)
+++ branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectJobsView.cs	(revision 15969)
@@ -30,4 +30,5 @@
 using HeuristicLab.Data.Views;
 using HeuristicLab.Data;
+using HeuristicLab.Clients.Hive.Views;
 
 namespace HeuristicLab.Clients.Hive.Administrator.Views {
@@ -38,7 +39,18 @@
     private const string JOB_NAME = "Name";
     private const string JOB_OWNER = "Owner";
+    private const string JOB_OWNERID = "Owner Id";
     private const string JOB_DATECREATED = "Date Created";
     private const string JOB_STATE = "State";
     private const string JOB_DESCRIPTION = "Description";
+    private const string JOB_TASKCOUNT = "Tasks";
+    private const string JOB_CALCULATINGTASKCOUNT = "Calculating";
+    private const string JOB_FINISHEDTASKCOUNT = "Finished";
+
+
+    private readonly Color onlineStatusColor = Color.FromArgb(255, 189, 249, 143); // #bdf98f
+    private readonly Color onlineStatusColor2 = Color.FromArgb(255, 157, 249, 143); // #9df98f
+    private readonly Color statisticsPendingStatusColor = Color.FromArgb(255, 249, 210, 145); // #f9d291
+    private readonly Color deletionPendingStatusColor = Color.FromArgb(255, 249, 172, 144); // #f9ac90
+    private readonly Color deletionPendingStatusColor2 = Color.FromArgb(255, 249, 149, 143); // #f9958f
 
     public new Project Content {
@@ -47,6 +59,20 @@
     }
 
-    public ProjectJobsView() /*: base()*/ {
+    public ProjectJobsView() {
       InitializeComponent();
+
+      removeButton.Enabled = false;
+      matrixView.DataGridView.SelectionChanged += DataGridView_SelectionChanged;
+    }
+
+    private void DataGridView_SelectionChanged(object sender, EventArgs e) {
+      if (matrixView.DataGridView.SelectedRows == null || matrixView.DataGridView.SelectedRows.Count == 0) return;
+
+      bool anyDeletable = false;
+      foreach (DataGridViewRow r in matrixView.DataGridView.SelectedRows) {
+        if (((string)r.Cells[0].Value) == JobState.Online.ToString()) anyDeletable = true;
+      }
+
+      removeButton.Enabled = anyDeletable;
     }
 
@@ -54,4 +80,5 @@
     protected override void OnContentChanged() {
       base.OnContentChanged();
+      removeButton.Enabled = false;
       UpdateJobs();
     }
@@ -61,5 +88,5 @@
 
       refreshButton.Enabled = enabled;
-      removeButton.Enabled = enabled;
+      removeButton.Enabled = false;
       matrixView.Enabled = enabled;
     }
@@ -72,10 +99,38 @@
 
     private void refreshButton_Click(object sender, EventArgs e) {
-      HiveAdminClient.Instance.Refresh();
-      // Update list/matrix
+      RefreshJobs();
     }
 
     private async void removeButton_Click(object sender, EventArgs e) {
-      // remove selected job(s)
+      if (matrixView.DataGridView.SelectedRows == null || matrixView.DataGridView.SelectedRows.Count == 0) return;
+
+      var jobNamesToDelete = new List<string>();
+      var jobIdsToDelete = new List<Guid>();
+      foreach (DataGridViewRow r in matrixView.DataGridView.SelectedRows) {
+        if(((string)r.Cells[0].Value) == JobState.Online.ToString()) {
+          jobNamesToDelete.Add(" - " + ((string)r.Cells[3].Value));
+          jobIdsToDelete.Add(Guid.Parse((string)r.Cells[8].Value));
+        }
+      }
+
+      if(jobIdsToDelete.Any()) {
+        var result = MessageBox.Show("Do you really want to remove following job(s):\n\n"
+          + String.Join("\n", jobNamesToDelete),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.YesNo,
+          MessageBoxIcon.Question);
+
+        if (result == DialogResult.Yes) {
+          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+            action: () => {
+              DeleteJobs(jobIdsToDelete);
+            },
+            finallyCallback: () => {
+              matrixView.DataGridView.ClearSelection();
+              removeButton.Enabled = false;
+              RefreshJobs();
+            });
+        }
+      } 
     }
 
@@ -83,25 +138,35 @@
 
     #region Helpers
+    private void RefreshJobs() {
+      HiveAdminClient.Instance.RefreshJobs();
+      UpdateJobs();
+    }
+
     private StringMatrix CreateValueMatrix() {
       var jobs = HiveAdminClient.Instance.Jobs[Content.Id];
-      string[,] values = new string[jobs.Count, 7];
+      var resources = HiveAdminClient.Instance.Resources;
+      string[,] values = new string[jobs.Count, 10];
 
       for(int i = 0; i < jobs.Count; i++) {
         var job = jobs.ElementAt(i);
-        values[i, 0] = job.Id.ToString();
-        values[i, 1] = job.Name;
-        values[i, 2] = job.OwnerUserId.ToString();
-        values[i, 3] = job.OwnerUsername;
-        values[i, 4] = job.DateCreated.ToString();
-        values[i, 5] = job.State.ToString();
-        values[i, 6] = job.Description;
-      }
-
+        
+        values[i, 0] = job.State.ToString();
+        values[i, 1] = job.DateCreated.ToString();
+        values[i, 2] = job.OwnerUsername;
+        values[i, 3] = job.Name;
+        values[i, 4] = job.JobCount.ToString();
+        values[i, 5] = job.CalculatingCount.ToString();
+        values[i, 6] = job.FinishedCount.ToString();
+        values[i, 7] = job.Description;        
+        values[i, 8] = job.Id.ToString();
+        values[i, 9] = job.OwnerUserId.ToString();
+      }
+      
       var matrix = new StringMatrix(values);
-      matrix.ColumnNames = new string[] { JOB_ID, JOB_NAME, JOB_OWNER, JOB_OWNER, JOB_DATECREATED, JOB_STATE, JOB_DESCRIPTION };
+      matrix.ColumnNames = new string[] { JOB_STATE, JOB_DATECREATED, JOB_OWNER, JOB_NAME, JOB_TASKCOUNT, JOB_CALCULATINGTASKCOUNT, JOB_FINISHEDTASKCOUNT, JOB_DESCRIPTION, JOB_ID, JOB_OWNERID };
       matrix.SortableView = true;
       return matrix;
     }
-
+    
     private void UpdateJobs() {
       if (InvokeRequired) Invoke((Action)UpdateJobs);
@@ -115,8 +180,36 @@
         }
         matrixView.Content = matrix;
+        
+        foreach(DataGridViewRow row in matrixView.DataGridView.Rows) {
+          string val = ((string)row.Cells[0].Value);
+          if (val == JobState.Online.ToString()) {
+            row.DefaultCellStyle.BackColor = onlineStatusColor;
+          } else if(val == JobState.StatisticsPending.ToString()) {
+            row.DefaultCellStyle.BackColor = statisticsPendingStatusColor;
+          } else if(val == JobState.DeletionPending.ToString()) {
+            row.DefaultCellStyle.BackColor = deletionPendingStatusColor;
+          }
+        }
+
         matrixView.DataGridView.AutoResizeColumns();
-        //foreach(DataGridViewColumn col in matrixView.DataGridView.Columns) {
-        //  col.Width = -2;
-        //}
+        matrixView.DataGridView.Columns[0].MinimumWidth = 90;
+        matrixView.DataGridView.Columns[1].MinimumWidth = 108;
+      }
+    }
+
+    private void DeleteJobs(List<Guid> jobIds) {
+      try {
+        HiveAdminClient.DeleteJobs(jobIds);
+      } catch (AnonymousUserException) {
+        ShowHiveInformationDialog();
+      }
+    }
+
+    private void ShowHiveInformationDialog() {
+      if (InvokeRequired) Invoke((Action)ShowHiveInformationDialog);
+      else {
+        using (HiveInformationDialog dialog = new HiveInformationDialog()) {
+          dialog.ShowDialog(this);
+        }
       }
     }
Index: branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs
===================================================================
--- branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs	(revision 15966)
+++ branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive/3.3/HiveAdminClient.cs	(revision 15969)
@@ -79,25 +79,4 @@
     }
 
-
-    //private Dictionary<Guid, HashSet<Project>> projectAncestors;
-    //public Dictionary<Guid, HashSet<Project>> ProjectAncestors {
-    //  get { return projectAncestors; }
-    //}
-
-    //private Dictionary<Guid, HashSet<Project>> projectDescendants;
-    //public Dictionary<Guid, HashSet<Project>> ProjectDescendants {
-    //  get { return projectDescendants; }
-    //}
-
-    //private Dictionary<Guid, HashSet<Resource>> resourceAncestors;
-    //public Dictionary<Guid, HashSet<Resource>> ResourceAncestors {
-    //  get { return resourceAncestors; }
-    //}
-
-    //private Dictionary<Guid, HashSet<Resource>> resourceDescendants;
-    //public Dictionary<Guid, HashSet<Resource>> ResourceDescendants {
-    //  get { return resourceDescendants; }
-    //}
-
     private Dictionary<Guid, HashSet<Guid>> projectAncestors;
     public Dictionary<Guid, HashSet<Guid>> ProjectAncestors {
@@ -160,6 +139,10 @@
               .ForEach(a => projectResourceAssignments.Add(a));
             projectIds.ForEach(p => jobs.Add(p, new HiveItemCollection<Job>()));
-            service.GetJobsByProjectIds(projectIds)
-              .ForEach(j => jobs[j.ProjectId].Add(j));
+            var unsortedJobs = service.GetJobsByProjectIds(projectIds)
+              .OrderBy(x => x.DateCreated).ToList();
+            
+            unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
+            unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
+            unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
           }
         });
@@ -264,4 +247,35 @@
     }
 
+    public void RefreshJobs() {
+      var projectIds = new List<Guid>();
+      jobs = new Dictionary<Guid, HiveItemCollection<Job>>();
+
+      HiveServiceLocator.Instance.CallHiveService(service => {
+        service.GetProjectsForAdministration().ForEach(p => projectIds.Add(p.Id));
+        if(projectIds.Any()) {
+          projectIds.ForEach(p => jobs.Add(p, new HiveItemCollection<Job>()));
+          var unsortedJobs = service.GetJobsByProjectIds(projectIds)
+            .OrderBy(x => x.DateCreated).ToList();
+          unsortedJobs.Where(j => j.State == JobState.DeletionPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
+          unsortedJobs.Where(j => j.State == JobState.StatisticsPending).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
+          unsortedJobs.Where(j => j.State == JobState.Online).ToList().ForEach(j => jobs[j.ProjectId].Add(j));
+        }
+      });
+    }
+
+    public void SortJobs() {
+      for(int i = 0; i < jobs.Count; i++) {
+        var projectId = jobs.Keys.ElementAt(i);
+        var unsortedJobs = jobs.Values.ElementAt(i);
+
+        var sortedJobs = new HiveItemCollection<Job>();
+        sortedJobs.AddRange(unsortedJobs.Where(j => j.State == JobState.DeletionPending));
+        sortedJobs.AddRange(unsortedJobs.Where(j => j.State == JobState.StatisticsPending));
+        sortedJobs.AddRange(unsortedJobs.Where(j => j.State == JobState.Online));
+
+        jobs[projectId] = sortedJobs;
+      }
+    }
+
     #endregion
 
@@ -331,4 +345,9 @@
         HiveServiceLocator.Instance.CallHiveService((s) => s.DeleteProject(item.Id));
       }
+    }
+
+    public static void DeleteJobs(List<Guid> jobIds) {
+
+      HiveServiceLocator.Instance.CallHiveService((s) => s.UpdateJobStates(jobIds, JobState.StatisticsPending));
     }
     #endregion
Index: branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive/3.3/ServiceClients/HiveServiceClient.cs
===================================================================
--- branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive/3.3/ServiceClients/HiveServiceClient.cs	(revision 15966)
+++ branches/2839_HiveProjectManagement/HeuristicLab.Clients.Hive/3.3/ServiceClients/HiveServiceClient.cs	(revision 15969)
@@ -75,5 +75,5 @@
         
         public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
-        
+
     }
     
@@ -2400,4 +2400,7 @@
         void UpdateJobState(System.Guid JobId, HeuristicLab.Clients.Hive.JobState jobState);
         
+        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/UpdateJobStates", ReplyAction="http://tempuri.org/IHiveService/UpdateJobStatesResponse")]
+        void UpdateJobStates(System.Collections.Generic.List<System.Guid> jobIds, HeuristicLab.Clients.Hive.JobState jobState);
+        
         [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IHiveService/GetAssignedResourcesForJob", ReplyAction="http://tempuri.org/IHiveService/GetAssignedResourcesForJobResponse")]
         System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedJobResource> GetAssignedResourcesForJob(System.Guid jobId);
@@ -2686,4 +2689,9 @@
         }
         
+        public void UpdateJobStates(System.Collections.Generic.List<System.Guid> jobIds, HeuristicLab.Clients.Hive.JobState jobState)
+        {
+            base.Channel.UpdateJobStates(jobIds, jobState);
+        }
+        
         public System.Collections.Generic.List<HeuristicLab.Clients.Hive.AssignedJobResource> GetAssignedResourcesForJob(System.Guid jobId)
         {
Index: branches/2839_HiveProjectManagement/HeuristicLab.Services.Access/3.3/UserManager.cs
===================================================================
--- branches/2839_HiveProjectManagement/HeuristicLab.Services.Access/3.3/UserManager.cs	(revision 15966)
+++ branches/2839_HiveProjectManagement/HeuristicLab.Services.Access/3.3/UserManager.cs	(revision 15969)
@@ -46,5 +46,5 @@
     public string GetUserNameById(Guid userId) {
       var user = GetUserById(userId);
-      if (user != null) {
+      if (user != null && !string.IsNullOrWhiteSpace(user.UserName)) {
         return user.UserName;
       } else {
Index: branches/2839_HiveProjectManagement/HeuristicLab.Services.Hive/3.3/HiveService.cs
===================================================================
--- branches/2839_HiveProjectManagement/HeuristicLab.Services.Hive/3.3/HiveService.cs	(revision 15966)
+++ branches/2839_HiveProjectManagement/HeuristicLab.Services.Hive/3.3/HiveService.cs	(revision 15969)
@@ -336,29 +336,6 @@
             .Select(x => x.ToDto())
             .ToList();
-          var statistics = taskDao.GetAll()
-              .Where(x => jobs.Select(y => y.Id).Contains(x.JobId))
-              .GroupBy(x => x.JobId)
-              .Select(x => new {
-                x.Key,
-                TotalCount = x.Count(),
-                CalculatingCount = x.Count(y => y.State == DA.TaskState.Calculating),
-                FinishedCount = x.Count(y => CompletedStates.Contains(y.State))
-              })
-              .ToList();
-          foreach (var job in jobs) {
-            var statistic = statistics.FirstOrDefault(x => x.Key == job.Id);
-            if (statistic != null) {
-              job.JobCount = statistic.TotalCount;
-              job.CalculatingCount = statistic.CalculatingCount;
-              job.FinishedCount = statistic.FinishedCount;
-            }
-            job.OwnerUsername = UserManager.GetUserNameById(job.OwnerUserId);
-            if (currentUserId == job.OwnerUserId) {
-              job.Permission = Permission.Full;
-            } else {
-              var jobPermission = jobPermissionDao.GetByJobAndUserId(job.Id, currentUserId);
-              job.Permission = jobPermission == null ? Permission.NotAllowed : jobPermission.Permission.ToDto();
-            }
-          }
+
+          EvaluateJobs(pm, jobs);
           return jobs;
         });
@@ -381,7 +358,10 @@
 
           if(administrationGrantedProjects.Select(x => x.ProjectId).Contains(projectId)) {
-            return jobDao.GetByProjectId(projectId)
+            var jobs = jobDao.GetByProjectId(projectId)
             .Select(x => x.ToDto())
             .ToList();
+
+            EvaluateJobs(pm, jobs);
+            return jobs;
           } else {
             return Enumerable.Empty<DT.Job>();
@@ -407,7 +387,10 @@
 
           if(requestedAndGrantedProjectIds.Any()) {
-            return jobDao.GetByProjectIds(requestedAndGrantedProjectIds)
+            var jobs = jobDao.GetByProjectIds(requestedAndGrantedProjectIds)
               .Select(x => x.ToDto())
               .ToList();
+
+            EvaluateJobs(pm, jobs);
+            return jobs;
           } else {
             return Enumerable.Empty<DT.Job>();
@@ -548,4 +531,44 @@
     }
 
+    public void UpdateJobStates(IEnumerable<Guid> jobIds, DT.JobState jobState) {
+      if (jobState != JobState.StatisticsPending) return; // only process requests for "StatisticsPending"
+
+      RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
+      bool isAdministrator = RoleVerifier.IsInRole(HiveRoles.Administrator);
+      var currentUserId = UserManager.CurrentUserId;
+
+      var pm = PersistenceManager;
+      using (new PerformanceLogger("UpdateJobsState")) {
+        var jobDao = pm.JobDao;
+        var projectDao = pm.ProjectDao;
+        pm.UseTransaction(() => {
+          foreach(var jobId in jobIds) {
+            var job = jobDao.GetById(jobId);
+            if (job != null) {
+
+              var administrationGrantedProjects = projectDao
+                .GetAdministrationGrantedProjectsForUser(currentUserId)
+                .ToList();
+
+              // check if user is granted to administer the job-parenting project
+              if (isAdministrator || administrationGrantedProjects.Contains(job.Project)) {
+                // note: allow solely state changes from "Online" to "StatisticsPending" = deletion request by user for HiveStatisticGenerator
+                if (job.State == DA.JobState.Online) { 
+                  job.State = DA.JobState.StatisticsPending;
+                  foreach (var task in job.Tasks
+                  .Where(x => x.State == DA.TaskState.Waiting
+                    || x.State == DA.TaskState.Paused
+                    || x.State == DA.TaskState.Offline)) {
+                      task.State = DA.TaskState.Aborted;
+                  }
+                  pm.SubmitChanges();
+                }
+              }
+            }
+          }
+        });
+      }
+    }
+
     public IEnumerable<DT.AssignedJobResource> GetAssignedResourcesForJob(Guid jobId) {
       RoleVerifier.AuthenticateForAnyRole(HiveRoles.Administrator, HiveRoles.Client);
@@ -1738,4 +1761,40 @@
     }
 
+    private void EvaluateJobs(IPersistenceManager pm, IEnumerable<DT.Job> jobs) {
+      if (jobs == null || !jobs.Any()) return;
+
+      var currentUserId = UserManager.CurrentUserId;      
+      var taskDao = pm.TaskDao;
+      var jobPermissionDao = pm.JobPermissionDao;
+
+      var statistics = taskDao.GetAll()
+        .Where(x => jobs.Select(y => y.Id).Contains(x.JobId))
+        .GroupBy(x => x.JobId)
+        .Select(x => new {
+          x.Key,
+          TotalCount = x.Count(),
+          CalculatingCount = x.Count(y => y.State == DA.TaskState.Calculating),
+          FinishedCount = x.Count(y => CompletedStates.Contains(y.State))
+        })
+        .ToList();
+
+      foreach (var job in jobs) {
+        var statistic = statistics.FirstOrDefault(x => x.Key == job.Id);
+        if (statistic != null) {
+          job.JobCount = statistic.TotalCount;
+          job.CalculatingCount = statistic.CalculatingCount;
+          job.FinishedCount = statistic.FinishedCount;
+        }
+
+        job.OwnerUsername = UserManager.GetUserNameById(job.OwnerUserId);
+
+        if (currentUserId == job.OwnerUserId) {
+          job.Permission = Permission.Full;
+        } else {
+          var jobPermission = jobPermissionDao.GetByJobAndUserId(job.Id, currentUserId);
+          job.Permission = jobPermission == null ? Permission.NotAllowed : jobPermission.Permission.ToDto();
+        }
+      }
+    }
     #endregion
   }
Index: branches/2839_HiveProjectManagement/HeuristicLab.Services.Hive/3.3/ServiceContracts/IHiveService.cs
===================================================================
--- branches/2839_HiveProjectManagement/HeuristicLab.Services.Hive/3.3/ServiceContracts/IHiveService.cs	(revision 15966)
+++ branches/2839_HiveProjectManagement/HeuristicLab.Services.Hive/3.3/ServiceContracts/IHiveService.cs	(revision 15969)
@@ -94,4 +94,7 @@
 
     [OperationContract]
+    void UpdateJobStates(IEnumerable<Guid> jobIds, JobState jobState);
+
+    [OperationContract]
     IEnumerable<AssignedJobResource> GetAssignedResourcesForJob(Guid jobId);
     #endregion
