Merge branch 'stable-2.13' into stable-2.14

* stable-2.13:
  Add configurable support for in-progress status
  Add config option to control jobs panel position
  Add config option for panels jobs ordering

Change-Id: I90c7d4faa2275f00be715c345075e557ada220b5
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java
index 4538e86..731da60 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/ConfigInfo.java
@@ -20,10 +20,18 @@
   final native boolean showJobsSummaryPanel() /*-{ return this.show_jobs_summary_panel ? true : false; }-*/;
   final native boolean showJobsPanel() /*-{ return this.show_jobs_panel ? true : false; }-*/;
   final native boolean showJobsDropDownPanel() /*-{ return this.show_jobs_drop_down_panel ? true : false; }-*/;
+  final native boolean showJobsBelowRelatedInfoBlock() /*-{ return this.show_jobs_below_related_info_block ? true : false; }-*/;
+  final native boolean enableInProgressStatus() /*-{ return this.enable_in_progress_status ? true : false; }-*/;
+  final native String sortJobsPanel() /*-{ return this.sort_jobs_panel }-*/;
+  final native String sortJobsDropDownPanel() /*-{ return this.sort_jobs_drop_down_panel }-*/;
 
   final native void setShowJobsSummaryPanel(boolean s) /*-{ this.show_jobs_summary_panel = s; }-*/;
   final native void setShowJobsPanel(boolean s) /*-{ this.show_jobs_panel = s; }-*/;
   final native void setShowJobsDropDownPanel(boolean s) /*-{ this.show_jobs_drop_down_panel = s; }-*/;
+  final native void setShowJobsBelowRelatedInfoBlock(boolean s) /*-{ this.show_jobs_below_related_info_block = s; }-*/;
+  final native void setEnableInProgressStatus() /*-{ this.enable_in_progress_status = s; }-*/;
+  final native void setSortJobsPanel(String s) /*-{ this.sort_jobs_panel = s; }-*/;
+  final native void setSortJobsDropDownPanel(String s) /*-{ this.sort_jobs_drop_down_panel = s; }-*/;
 
   static ConfigInfo create() {
     ConfigInfo g = (ConfigInfo) createObject();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsDropDownPanel.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsDropDownPanel.java
index e3a9e31..c4c5dc7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsDropDownPanel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/JobsDropDownPanel.java
@@ -36,6 +36,12 @@
  */
 public class JobsDropDownPanel extends FlowPanel {
   static class Factory implements Panel.EntryPoint {
+    private final ConfigInfo info;
+
+    public Factory(ConfigInfo info) {
+      this.info = info;
+    }
+
     @Override
     public void onLoad(Panel panel) {
       RevisionInfo rev =
@@ -44,11 +50,11 @@
         return;
       }
 
-      panel.setWidget(new JobsDropDownPanel(panel));
+      panel.setWidget(new JobsDropDownPanel(panel, info));
     }
   }
 
-  JobsDropDownPanel(Panel panel) {
+  JobsDropDownPanel(Panel panel, ConfigInfo info) {
     ChangeInfo change =
         panel.getObject(GerritUiExtensionPoint.Key.CHANGE_INFO).cast();
     String decodedChangeId = URL.decodePathSegment(change.id());
@@ -56,7 +62,7 @@
         panel.getObject(GerritUiExtensionPoint.Key.REVISION_INFO).cast();
     new RestApi("changes").id(decodedChangeId).view("revisions").id(rev.id())
         .view(Plugin.get().getPluginName(), "verifications")
-        .addParameter("sort", "REPORTER")
+        .addParameter("sort", info.sortJobsDropDownPanel())
         .addParameter("filter", "CURRENT")
         .get(new AsyncCallback<NativeMap<VerificationInfo>>() {
           @Override
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 81465b1..a2d7724 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
@@ -35,7 +35,14 @@
  * Extension for change screen that displays a status below the label info.
  */
 public class JobsPanel extends FlowPanel {
+  private final ConfigInfo info;
   static class Factory implements Panel.EntryPoint {
+    private final ConfigInfo info;
+
+    public Factory(ConfigInfo info) {
+      this.info = info;
+    }
+
     @Override
     public void onLoad(Panel panel) {
       RevisionInfo rev =
@@ -44,11 +51,12 @@
         return;
       }
 
-      panel.setWidget(new JobsPanel(panel));
+      panel.setWidget(new JobsPanel(panel, info));
     }
   }
 
-  JobsPanel(Panel panel) {
+  JobsPanel(Panel panel, ConfigInfo info) {
+    this.info = info;
     final ChangeInfo change =
         panel.getObject(GerritUiExtensionPoint.Key.CHANGE_INFO).cast();
     String decodedChangeId = URL.decodePathSegment(change.id());
@@ -56,7 +64,8 @@
         panel.getObject(GerritUiExtensionPoint.Key.REVISION_INFO).cast();
     new RestApi("changes").id(decodedChangeId).view("revisions").id(rev.id())
         .view(Plugin.get().getPluginName(), "verifications")
-        .addParameter("sort", "REPORTER").addParameter("filter", "CURRENT")
+        .addParameter("sort", info.sortJobsPanel())
+        .addParameter("filter", "CURRENT")
         .get(new AsyncCallback<NativeMap<VerificationInfo>>() {
           @Override
           public void onSuccess(NativeMap<VerificationInfo> result) {
@@ -81,7 +90,11 @@
       HorizontalPanel p = new HorizontalPanel();
       short vote = jobs.get(key).value();
       if (vote > 0) {
-        p.add(new Image(VerifyStatusPlugin.RESOURCES.greenCheck()));
+        p.add(new Image(
+                ((vote == 2) && info.enableInProgressStatus())
+                    ? VerifyStatusPlugin.RESOURCES.progress()
+                    : VerifyStatusPlugin.RESOURCES.greenCheck()
+                ));
       } else if (vote < 0) {
         p.add(new Image(VerifyStatusPlugin.RESOURCES.redNot()));
       } else if (vote == 0) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/Resources.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/Resources.java
index 2ac8b97..358c033 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/Resources.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/Resources.java
@@ -37,4 +37,7 @@
   @Source("donut.png")
   public ImageResource rerun();
 
+  @Source("loader.gif")
+  public ImageResource progress();
+
 }
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 2e2575c..38bfc31 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
@@ -41,13 +41,15 @@
             }
             if (info.showJobsPanel()) {
               Plugin.get().panel(
-                  GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
-                  new JobsPanel.Factory());
+                  info.showJobsBelowRelatedInfoBlock()
+                      ? GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_RELATED_INFO_BLOCK
+                      : GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
+                  new JobsPanel.Factory(info));
             }
             if (info.showJobsDropDownPanel()) {
               Plugin.get().panel(
                   GerritUiExtensionPoint.CHANGE_SCREEN_HEADER_RIGHT_OF_POP_DOWNS,
-                  new JobsDropDownPanel.Factory());
+                  new JobsDropDownPanel.Factory(info));
             }
           }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/loader.gif b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/loader.gif
new file mode 100644
index 0000000..e40f19a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/client/loader.gif
Binary files differ
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/public/verify-status.css b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/public/verify-status.css
index caf61ac..c8915d3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/public/verify-status.css
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/public/verify-status.css
@@ -2,3 +2,6 @@
   border-spacing: 0px 5px;
 }
 
+div.gwt-TabPanelBottom div {
+  height: 100% !important;
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java
index 8733fb0..d1e5cf2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetConfig.java
@@ -37,6 +37,14 @@
     info.showJobsPanel = cfg.getBoolean("showJobsPanel", true);
     info.showJobsDropDownPanel = cfg.getBoolean("showJobsDropDownPanel", true);
     info.showJobsSummaryPanel = cfg.getBoolean("showJobsSummaryPanel", true);
+    info.showJobsBelowRelatedInfoBlock = cfg.getBoolean(
+        "showJobsBelowRelatedInfoBlock", false);
+    info.enableInProgressStatus = cfg.getBoolean(
+        "enableInProgressStatus", false);
+    info.sortJobsPanel = cfg.getEnum(JobsSorting.values(),
+        "sortJobsPanel", JobsSorting.REPORTER);
+    info.sortJobsDropDownPanel = cfg.getEnum(JobsSorting.values(),
+        "sortJobsDropDownPanel", JobsSorting.REPORTER);
     return info;
   }
 
@@ -44,5 +52,9 @@
     Boolean showJobsPanel;
     Boolean showJobsDropDownPanel;
     Boolean showJobsSummaryPanel;
+    Boolean showJobsBelowRelatedInfoBlock;
+    Boolean enableInProgressStatus;
+    JobsSorting sortJobsPanel;
+    JobsSorting sortJobsDropDownPanel;
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetVerifications.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetVerifications.java
index 6745b66..7e67516 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetVerifications.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/GetVerifications.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gwtorm.server.OrmException;
@@ -42,14 +43,10 @@
     this.schemaFactory = schemaFactory;
   }
 
-  private String sort;
-  private String filter;
-
   @Option(name = "--sort", aliases = {"-s"}, metaVar = "SORT",
       usage = "Sort the list by an entry")
-  public void setSort(String sort) {
-    this.sort = sort.toUpperCase();
-  }
+  private JobsSorting sort;
+  private String filter;
 
   @Option(name = "--filter", aliases = {"-f"}, metaVar = "FILTER",
       usage = "filter the results")
@@ -72,40 +69,50 @@
     return info;
   }
 
-  private void sortJobs(List<PatchSetVerification> jobs, String order) {
-    if (order.equals("REPORTER")) {
-      // sort the jobs list by reporter(A-Z)/Name(A-Z)/Granted(Z-A)
-      Collections.sort(jobs, new Comparator<PatchSetVerification>() {
-        @Override
-        public int compare(PatchSetVerification a, PatchSetVerification b) {
-          return new CompareToBuilder()
-              .append(a.getReporter(),b.getReporter())
-              .append(a.getName(), b.getName())
-              .append(b.getGranted(),a.getGranted())
-              .toComparison();
-        }
-      });
-    } else if (order.equals("NAME")) {
-      // sort the jobs list by Name(A-Z)/Granted(Z-A)
-      Collections.sort(jobs, new Comparator<PatchSetVerification>() {
-        @Override
-        public int compare(PatchSetVerification a, PatchSetVerification b) {
-          return new CompareToBuilder()
-              .append(a.getName(),b.getName())
-              .append(b.getGranted(),a.getGranted())
-              .toComparison();
-        }
-      });
-    } else if (order.equals("DATE")) {
-      // sort the jobs list by Granted(Z-A)
-      Collections.sort(jobs, new Comparator<PatchSetVerification>() {
-        @Override
-        public int compare(PatchSetVerification a, PatchSetVerification b) {
-          return new CompareToBuilder()
-              .append(b.getGranted(),a.getGranted())
-              .toComparison();
-        }
-      });
+  private void sortJobs(List<PatchSetVerification> jobs,
+    @Nullable JobsSorting order) {
+    if (order == null) {
+      return;
+    }
+    switch (order) {
+      case REPORTER:
+        // sort the jobs list by reporter(A-Z)/Name(A-Z)/Granted(Z-A)
+        Collections.sort(jobs, new Comparator<PatchSetVerification>() {
+          @Override
+          public int compare(PatchSetVerification a, PatchSetVerification b) {
+            return new CompareToBuilder()
+                .append(a.getReporter(),b.getReporter())
+                .append(a.getName(), b.getName())
+                .append(b.getGranted(),a.getGranted())
+                .toComparison();
+          }
+        });
+        break;
+      case NAME:
+        // sort the jobs list by Name(A-Z)/Granted(Z-A)
+        Collections.sort(jobs, new Comparator<PatchSetVerification>() {
+          @Override
+          public int compare(PatchSetVerification a, PatchSetVerification b) {
+            return new CompareToBuilder()
+                .append(a.getName(),b.getName())
+                .append(b.getGranted(),a.getGranted())
+                .toComparison();
+          }
+        });
+        break;
+      case DATE:
+        // sort the jobs list by Granted(Z-A)
+        Collections.sort(jobs, new Comparator<PatchSetVerification>() {
+          @Override
+          public int compare(PatchSetVerification a, PatchSetVerification b) {
+            return new CompareToBuilder()
+                .append(b.getGranted(),a.getGranted())
+                .toComparison();
+          }
+        });
+        break;
+      default:
+        break;
     }
   }
 
@@ -124,7 +131,7 @@
       if (filter != null && !filter.isEmpty()) {
         if (filter.equals("CURRENT") ) {
           // logic to get current jobs assumes list is sorted by reporter
-          sortJobs(result, "REPORTER");
+          sortJobs(result, JobsSorting.REPORTER);
           isSorted = true;
           String prevReporter = "";
           String prevName = "";
@@ -157,14 +164,8 @@
       }
 
       // sort jobs
-      if (sort != null && !sort.isEmpty()) {
-        if (sort.equals("REPORTER") && !isSorted) {
-          sortJobs(jobs, "REPORTER");
-        } else if (sort.equals("NAME")) {
-          sortJobs(jobs, "NAME");
-        } else if (sort.equals("DATE")) {
-          sortJobs(jobs, "DATE");
-        }
+      if ((sort != JobsSorting.REPORTER) || !isSorted) {
+        sortJobs(jobs, sort);
       }
 
       for (PatchSetVerification v : jobs) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/JobsSorting.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/JobsSorting.java
new file mode 100644
index 0000000..cb4b7dc
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/JobsSorting.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 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.server;
+
+public enum JobsSorting {
+  DATE,
+  REPORTER,
+  NAME
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java
index 9fee1a1..d7304d3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/verifystatus/server/PutConfig.java
@@ -41,6 +41,10 @@
     public Boolean showJobsPanel;
     public Boolean showJobsDropDownPanel;
     public Boolean showJobsSummaryPanel;
+    public Boolean showJobsBelowRelatedInfoBlock;
+    public Boolean enableInProgressStatus;
+    public String sortJobsPanel;
+    public String sortJobsDropDownPanel;
   }
 
   private final PluginConfigFactory cfgFactory;
@@ -84,6 +88,30 @@
     } else {
       cfg.unset("plugin", pluginName, "showJobsSummaryPanel");
     }
+    if (input.showJobsBelowRelatedInfoBlock != null) {
+      cfg.setBoolean("plugin", pluginName, "showJobsBelowRelatedInfoBlock",
+          input.showJobsBelowRelatedInfoBlock);
+    } else {
+      cfg.unset("plugin", pluginName, "showJobsBelowRelatedInfoBlock");
+    }
+    if (input.enableInProgressStatus != null) {
+      cfg.setBoolean("plugin", pluginName, "enableInProgressStatus",
+          input.enableInProgressStatus);
+    } else {
+      cfg.unset("plugin", pluginName, "enableInProgressStatus");
+    }
+    if (input.sortJobsPanel != null) {
+      cfg.setEnum("plugin", pluginName, "sortJobsPanel",
+          JobsSorting.valueOf(input.sortJobsPanel));
+    } else {
+      cfg.unset("plugin", pluginName, "sortJobsPanel");
+    }
+    if (input.sortJobsDropDownPanel != null) {
+      cfg.setEnum("plugin", pluginName, "sortJobsDropDownPanel",
+          JobsSorting.valueOf(input.sortJobsDropDownPanel));
+    } else {
+      cfg.unset("plugin", pluginName, "sortJobsDropDownPanel");
+    }
 
     cfg.save();
     cfgFactory.getFromGerritConfig(pluginName, true);
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index f80a646..d037730 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -72,13 +72,25 @@
 |:------------- |:-------|
 |less than 0    |Failed  |
 |0              |Unstable|
-|greater than 0 |Passed  |
+|1              |Passed  |
+|2              |In-progress if enableInProgressStatus=true, otherwise Passed|
+|greater than 2 |Passed  |
 
 
 The information icon is an indicator that a job has abstained from voting
 (or is a non-voting job).  Abstaining typically indicates that a job's
 score may not factor into determining the combined vote.
 
+If enableInProgressStatus is true then the loading icon is an indicator that a job
+has been set with value 2 and identified as 'in progress'.
+_NOTE_: The Jenkins [Gerrit verify status reporter plugin] currently doesn't
+support in-progress or any equivalent status so enabling progress status makes
+sense only if one uses own postbuild/prebuild custom pipeline logic for setting
+statuses via API. For instance:
+1. In-progress status with value=2 is sent via API as the first step of the build.
+2. From postbuild section actual finish status is calculated in groovy script and
+is send via API.
+
 
 ### <a id="configure-panels"> @PLUGIN@ configure-panels
 
@@ -91,11 +103,15 @@
 
 #### Parameters
 
-|Field Name             |Description|
-|:----------------------|:----------|
-|showJobsPanel          | Whether jobs panel should be displayed (default to true)|
-|showJobsDropDownPanel  | Whether jobs drop down panel should be displayed (default to true)|
-|showJobsSummaryPanel   | Whether jobs summary panel should be displayed (default to true)|
+|Field Name                    |Description|
+|:-----------------------------|:----------|
+|showJobsPanel                 | Whether jobs panel should be displayed (default to true)|
+|showJobsDropDownPanel         | Whether jobs drop down panel should be displayed (default to true)|
+|showJobsSummaryPanel          | Whether jobs summary panel should be displayed (default to true)|
+|showJobsBelowRelatedInfoBlock | Whether jobs panel should be positioned below related info block (default to false)|
+|enableInProgressStatus        | Whether value=2 should be treated as 'in progress' status (default to false)|
+|sortJobsPanel                 | The order of jobs sorting on jobs panel (REPORTER,NAME,DATE default to REPORTER). Both upper and lower cases are allowed.|
+|sortJobsDropDownPanel         | The order of jobs sorting on jobs drop down panel (REPORTER,NAME,DATE default to REPORTER). Both upper and lower cases are allowed.|
 
 
 #### Example
diff --git a/src/main/resources/Documentation/rest-api-config.md b/src/main/resources/Documentation/rest-api-config.md
index c4936a5..de16a62 100644
--- a/src/main/resources/Documentation/rest-api-config.md
+++ b/src/main/resources/Documentation/rest-api-config.md
@@ -67,11 +67,15 @@
 The `ConfigInfo` entity contains the configuration of the @PLUGIN@
 plugin.
 
-|Field Name               |Description|
-|:------------------------|:----------|
-|show_jobs_panel          | Whether jobs panel should be displayed|
-|show_jobs_drop_down_panel| Whether jobs drop down panel should be displayed|
-|show_jobs_summary_panel  | Whether jobs summary panel should be displayed|
+|Field Name                         |Description|
+|:----------------------------------|:----------|
+|show_jobs_panel                    | Whether jobs panel should be displayed|
+|show_jobs_drop_down_panel          | Whether jobs drop down panel should be displayed|
+|show_jobs_summary_panel            | Whether jobs summary panel should be displayed|
+|show_jobs_below_related_info_block | Whether jobs panel should be positioned below related info block|
+|enable_in_progress_status          | Whether value=2 should be treated as 'in progress' status|
+|sort_jobs_panel                    | The order of jobs sorting on jobs panel (REPORTER,NAME,DATE)|
+|sort_jobs_drop_down_panel          | The order of jobs sorting on jobs drop down panel (REPORTER,NAME,DATE)|
 
 
 SEE ALSO