Add a screen to display all jobs for a patchset

There can be multiple reports per patchset however the change screen
only shows a single report (the most current one).  We add a link
to allow users to view all reports for a patchet in a seperate screen.
We use a seperate screen because the list of jobs from multiple
reports can be very long and there's not enough space on the change
screen to show them all.

Change-Id: Idb74d1b19095dce8452b898cd0c9708534173e71
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java
index ecc0dc0..7ace329 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsPanel.java
@@ -45,15 +45,15 @@
         panel.getObject(GerritUiExtensionPoint.Key.CHANGE_INFO).cast();
     RevisionInfo rev =
         panel.getObject(GerritUiExtensionPoint.Key.REVISION_INFO).cast();
+    final String patchsetId = change.id() + "," + rev.id();
     new RestApi("changes").id(change.id()).view("revisions").id(rev.id())
         .view(Plugin.get().getPluginName(), "verifications")
-        .addParameter("sort", "REPORTER")
-        .addParameter("filter", "CURRENT")
+        .addParameter("sort", "REPORTER").addParameter("filter", "CURRENT")
         .get(new AsyncCallback<NativeMap<VerificationInfo>>() {
           @Override
           public void onSuccess(NativeMap<VerificationInfo> result) {
             if (!result.isEmpty()) {
-              display(result);
+              display(patchsetId, result);
             }
           }
 
@@ -64,12 +64,11 @@
         });
   }
 
-  private void display(NativeMap<VerificationInfo> jobs) {
+  private void display(String patchsetId, NativeMap<VerificationInfo> jobs) {
     int row = 0;
     int column = 1;
     Grid grid = new Grid(row, column);
     for (String key : jobs.keySet()) {
-      grid.insertRow(row);
       HorizontalPanel p = new HorizontalPanel();
       short vote = jobs.get(key).value();
       if (vote > 0) {
@@ -79,10 +78,12 @@
       } else if (vote == 0) {
         p.add(new Image(VerifyStatusPlugin.RESOURCES.warning()));
       }
-      InlineHyperlink link = new InlineHyperlink(jobs.get(key).name(), jobs.get(key).url());
+      InlineHyperlink link =
+          new InlineHyperlink(jobs.get(key).name(), jobs.get(key).url());
       link.setTitle("view logs");
       p.add(link);
-      InlineLabel label = new InlineLabel(" (" + jobs.get(key).duration() + ")");
+      InlineLabel label =
+          new InlineLabel(" (" + jobs.get(key).duration() + ")");
       label.setTitle("duration");
       p.add(label);
       if (jobs.get(key).category() == "recheck") {
@@ -95,9 +96,16 @@
         img.setTitle("non voting");
         p.add(img);
       }
+      grid.insertRow(row);
       grid.setWidget(row, 0, p);
       row++;
     }
+    HorizontalPanel p = new HorizontalPanel();
+    InlineHyperlink all = new InlineHyperlink("Show All Reports",
+        "/x/" + Plugin.get().getName() + "/jobs/" + patchsetId);
+    p.add(all);
+    grid.insertRow(row);
+    grid.setWidget(row, 0, p);
     add(grid);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java
new file mode 100644
index 0000000..abaeab0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsScreen.java
@@ -0,0 +1,114 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.verifystatus.client;
+
+import com.google.gerrit.client.rpc.NativeMap;
+import com.google.gerrit.plugin.client.FormatUtil;
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gerrit.plugin.client.screen.Screen;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.InlineHyperlink;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+
+public class JobsScreen extends VerticalPanel {
+  static class Factory implements Screen.EntryPoint {
+    @Override
+    public void onLoad(Screen screen) {
+      screen.setPageTitle("Reports for patchset " + screen.getToken(1));
+      screen.show(new JobsScreen(screen.getToken(1)));
+    }
+  }
+
+  JobsScreen(final String patchsetId) {
+    setStyleName("verifystatus-panel");
+    String[] id = patchsetId.split(",");
+    String chagneId = id[0];
+    String revId = id[1];
+
+    new RestApi("changes").id(chagneId).view("revisions").id(revId)
+        .view(Plugin.get().getPluginName(), "verifications")
+        .addParameter("sort", "REPORTER")
+        .get(new AsyncCallback<NativeMap<VerificationInfo>>() {
+          @Override
+          public void onSuccess(NativeMap<VerificationInfo> result) {
+            if (!result.isEmpty()) {
+              display(result);
+            }
+          }
+
+          @Override
+          public void onFailure(Throwable caught) {
+            // never invoked
+          }
+        });
+  }
+
+  private void display(NativeMap<VerificationInfo> jobs) {
+    int columns = 7;
+    FlexTable t = new FlexTable();
+    t.setStyleName("verifystatus-jobsTable");
+    FlexCellFormatter fmt = t.getFlexCellFormatter();
+    for (int c = 0; c < columns; c++) {
+      fmt.addStyleName(0, c, "dataHeader");
+      fmt.addStyleName(0, c, "topMostCell");
+    }
+    fmt.addStyleName(0, 0, "leftMostCell");
+
+    t.setText(0, 0, "Result");
+    t.setText(0, 1, "Name");
+    t.setText(0, 2, "Duration");
+    t.setText(0, 3, "Voting");
+    t.setText(0, 4, "Category");
+    t.setText(0, 5, "Reporter");
+    t.setText(0, 6, "Date");
+
+    int row = 1;
+    for (String key : jobs.keySet()) {
+      VerificationInfo vi = jobs.get(key);
+
+      for (int c = 0; c < columns; c++) {
+        fmt.addStyleName(row, c, "dataCell");
+        fmt.addStyleName(row, 0, "leftMostCell");
+      }
+      short vote = vi.value();
+      if (vote > 0) {
+        t.setWidget(row, 0,
+            new Image(VerifyStatusPlugin.RESOURCES.greenCheck()));
+      } else if (vote < 0) {
+        t.setWidget(row, 0, new Image(VerifyStatusPlugin.RESOURCES.redNot()));
+      } else if (vote == 0) {
+        t.setWidget(row, 0, new Image(VerifyStatusPlugin.RESOURCES.warning()));
+      }
+      InlineHyperlink link = new InlineHyperlink(vi.name(), vi.url());
+      t.setWidget(row, 1, link);
+      t.setText(row, 2, vi.duration());
+      if (vi.abstain()) {
+        t.setText(row, 3, "non-voting");
+      } else {
+        t.setText(row, 3, "voting");
+      }
+
+      t.setText(row, 4, vi.category());
+      t.setText(row, 5, vi.reporter());
+      t.setText(row, 6, FormatUtil.shortFormat(vi.granted()));
+      row++;
+    }
+    add(t);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java
index 4555328..0446757 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/VerifyStatusPlugin.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.verifystatus.client;
 
 import com.google.gerrit.client.GerritUiExtensionPoint;
+
 import  com.googlesource.gerrit.plugins.verifystatus.client.Resources;
 import com.google.gerrit.plugin.client.Plugin;
 import com.google.gerrit.plugin.client.PluginEntryPoint;
@@ -27,6 +28,7 @@
 
   @Override
   public void onPluginLoad() {
+    Plugin.get().screenRegex("jobs/(.*)", new JobsScreen.Factory());
     new RestApi("config").view("server")
         .view(Plugin.get().getPluginName(), "config")
         .get(new AsyncCallback<ConfigInfo>() {