Make the ability to view draft changes a grantable permission

Currently only the change owner and explicitly added reviewers
are able to see draft changes.

This change makes the ability to view draft changes a grantable
permission.  Users having this permission will be able to view
draft changes even if they are not the owner or already added
as a reviewer.

A use case for this would be a non-interactive user such as an
automated verification bot that needs to react to all changes,
including drafts.

Bug: issue 1585
Change-Id: Ia57f000d1ac075e61edb604682e85d2d135c50f2
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index efd1f01..2c22c45 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -759,6 +759,18 @@
 See above for details on each category.
 
 
+[[category_view_drafts]]
+View Drafts
+~~~~~~~~~~~
+
+This category permits users to view draft changes uploaded by other
+users.
+
+The change owner and any explicitly added reviewers can always see
+draft changes (even without having the `View Drafts` access right
+assigned).
+
+
 [[category_makeoneup]]
 Your Category Here
 ~~~~~~~~~~~~~~~~~~
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
index 29d9431..aa8cec0 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
@@ -35,6 +35,7 @@
   public static final String REBASE = "rebase";
   public static final String REMOVE_REVIEWER = "removeReviewer";
   public static final String SUBMIT = "submit";
+  public static final String VIEW_DRAFTS = "viewDrafts";
 
   private static final List<String> NAMES_LC;
   private static final int labelIndex;
@@ -55,6 +56,7 @@
     NAMES_LC.add(REBASE.toLowerCase());
     NAMES_LC.add(REMOVE_REVIEWER.toLowerCase());
     NAMES_LC.add(SUBMIT.toLowerCase());
+    NAMES_LC.add(VIEW_DRAFTS.toLowerCase());
 
     labelIndex = NAMES_LC.indexOf(Permission.LABEL);
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 0428c40..edec22d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -116,7 +116,8 @@
 	read, \
 	rebase, \
 	removeReviewer, \
-	submit
+	submit, \
+	viewDrafts
 abandon = Abandon
 create = Create Reference
 forgeAuthor = Forge Author Identity
@@ -130,6 +131,7 @@
 rebase = Rebase
 removeReviewer = Remove Reviewer
 submit = Submit
+viewDrafts = View Drafts
 
 refErrorEmpty = Reference must be supplied
 refErrorBeginSlash = Reference must not start with '/'
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 4494853..4c48a71 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -554,7 +554,7 @@
 
   private boolean isDraftVisible(ReviewDb db, ChangeData cd)
       throws OrmException {
-    return isOwner() || isReviewer(db, cd);
+    return isOwner() || isReviewer(db, cd) || getRefControl().canViewDrafts();
   }
 
   private static boolean isUser(Term who) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index 46f0ffa..31a4ab2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -327,6 +327,11 @@
     return canPerform(Permission.REMOVE_REVIEWER);
   }
 
+  /** @return true if this user can view draft changes. */
+  public boolean canViewDrafts() {
+    return canPerform(Permission.VIEW_DRAFTS);
+  }
+
   /** All value ranges of any allowed label permission. */
   public List<PermissionRange> getLabelRanges() {
     List<PermissionRange> r = new ArrayList<PermissionRange>();