Merge "Move method to create a highlighting html string into Util class"
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index f6b65ca..1325b94 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -651,16 +651,22 @@
 Push Annotated Tag
 ~~~~~~~~~~~~~~~~~~
 
-This category permits users to push an annotated tag object over
-SSH into the project's repository.  Typically this would be done
-with a command line such as:
+This category permits users to push an annotated tag object into the
+project's repository.  Typically this would be done with a command line
+such as:
 
 ====
   git push ssh://USER@HOST:PORT/PROJECT tag v1.0
 ====
 
-Tags must be annotated (created with `git tag -a` or `git tag -s`),
-should exist in the `refs/tags/` namespace, and should be new.
+Or:
+
+====
+  git push https://HOST/PROJECT tag v1.0
+====
+
+Tags must be annotated (created with `git tag -a`), should exist in
+the `refs/tags/` namespace, and should be new.
 
 This category is intended to be used to publish tags when a project
 reaches a stable release point worth remembering in history.
@@ -682,6 +688,28 @@
 requires the same permission as deleting a branch.
 
 
+[[category_push_signed]]
+Push Signed Tag
+~~~~~~~~~~~~~~~
+
+This category permits users to push a PGP signed tag object into the
+project's repository.  Typically this would be done with a command
+line such as:
+
+====
+  git push ssh://USER@HOST:PORT/PROJECT tag v1.0
+====
+
+Or:
+
+====
+  git push https://HOST/PROJECT tag v1.0
+====
+
+Tags must be signed (created with `git tag -s`), should exist in the
+`refs/tags/` namespace, and should be new.
+
+
 [[category_read]]
 Read
 ~~~~
@@ -771,6 +799,28 @@
 assigned).
 
 
+[[category_publish_drafts]]
+Publish Drafts
+~~~~~~~~~~~~~~
+
+This category permits users to publish draft changes uploaded by other
+users.
+
+The change owner can always publish draft changes (even without having
+the `Publish Drafts` access right assigned).
+
+
+[[category_delete_drafts]]
+Delete Drafts
+~~~~~~~~~~~~~
+
+This category permits users to delete draft changes uploaded by other
+users.
+
+The change owner can always delete draft changes (even without having
+the `Delete Drafts` access right assigned).
+
+
 [[category_edit_topic_name]]
 Edit Topic Name
 ~~~~~~~~~~~~~~~
diff --git a/Documentation/error-branch-not-found.txt b/Documentation/error-branch-not-found.txt
index 2aad0e1..bd8d090 100644
--- a/Documentation/error-branch-not-found.txt
+++ b/Documentation/error-branch-not-found.txt
@@ -15,14 +15,14 @@
 * that the branch name in the push specification is typed correctly
   (case sensitive) and
 * that the branch really exists for this project (in the Gerrit WebUI
-  go to 'Admin' -> 'Projects' and browse your project, then click on
+  go to 'Projects' > 'List' and browse your project, then click on
   'Branches' to see all existing branches).
 
 If it was your intention to create a new branch you can either
 
 * bypass code review on push as explained link:user-upload.html#bypass_review[here] or
 * create the new branch in the Gerrit WebUI before pushing (go to
-  'Admin' -> 'Projects' and browse your project, in the 'Branches'
+  'Projects' > 'List' and browse your project, in the 'Branches'
   tab you can then create a new branch).
 
 Please note that you need to be granted the
diff --git a/Documentation/error-not-a-gerrit-project.txt b/Documentation/error-not-a-gerrit-project.txt
index dac98ae..58919d5 100644
--- a/Documentation/error-not-a-gerrit-project.txt
+++ b/Documentation/error-not-a-gerrit-project.txt
@@ -14,7 +14,7 @@
 . Verify that the project name specified as git repository in the
   push command is typed correctly (case sensitive).
 . Verify that you are pushing to the correct Gerrit server.
-. Go in the Gerrit WebUI to 'Admin' -> 'Projects' and check that the
+. Go in the Gerrit WebUI to 'Projects' > 'List' and check that the
   project is listed. If the project is not listed the project either
   does not exist or you don't have
   link:access-control.html#category_read['Read'] access for it. This
diff --git a/Documentation/project-setup.txt b/Documentation/project-setup.txt
index feb4081..8b518d3 100644
--- a/Documentation/project-setup.txt
+++ b/Documentation/project-setup.txt
@@ -54,8 +54,8 @@
 --------------------
 
 The method Gerrit uses to submit a change to a project can be
-modified by any project owner through the project console, `Admin` >
-`Projects`.  The following methods are supported:
+modified by any project owner through the project console, `Projects` >
+`List` > my/project.  The following methods are supported:
 
 * Fast Forward Only
 +
@@ -120,8 +120,8 @@
 
 Additional branches can also be created through the web UI, assuming
 at least one commit already exists in the project repository.
-A project owner can create additional branches under `Admin` >
-`Projects` > `Branches`.  Enter the new branch name, and the
+A project owner can create additional branches under `Projects` >
+`List` > my/project > `Branches`.  Enter the new branch name, and the
 starting Git revision.  Branch names that don't start with `refs/`
 will automatically have `refs/heads/` prefixed to ensure they are
 a standard Git branch name.  Almost any valid SHA-1 expression can
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 7ff0273e..0585651 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
@@ -23,15 +23,18 @@
 public class Permission implements Comparable<Permission> {
   public static final String ABANDON = "abandon";
   public static final String CREATE = "create";
+  public static final String DELETE_DRAFTS = "deleteDrafts";
   public static final String EDIT_TOPIC_NAME = "editTopicName";
   public static final String FORGE_AUTHOR = "forgeAuthor";
   public static final String FORGE_COMMITTER = "forgeCommitter";
   public static final String FORGE_SERVER = "forgeServerAsCommitter";
   public static final String LABEL = "label-";
   public static final String OWNER = "owner";
+  public static final String PUBLISH_DRAFTS = "publishDrafts";
   public static final String PUSH = "push";
   public static final String PUSH_MERGE = "pushMerge";
   public static final String PUSH_TAG = "pushTag";
+  public static final String PUSH_SIGNED_TAG = "pushSignedTag";
   public static final String READ = "read";
   public static final String REBASE = "rebase";
   public static final String REMOVE_REVIEWER = "removeReviewer";
@@ -53,12 +56,15 @@
     NAMES_LC.add(PUSH.toLowerCase());
     NAMES_LC.add(PUSH_MERGE.toLowerCase());
     NAMES_LC.add(PUSH_TAG.toLowerCase());
+    NAMES_LC.add(PUSH_SIGNED_TAG.toLowerCase());
     NAMES_LC.add(LABEL.toLowerCase());
     NAMES_LC.add(REBASE.toLowerCase());
     NAMES_LC.add(REMOVE_REVIEWER.toLowerCase());
     NAMES_LC.add(SUBMIT.toLowerCase());
     NAMES_LC.add(VIEW_DRAFTS.toLowerCase());
     NAMES_LC.add(EDIT_TOPIC_NAME.toLowerCase());
+    NAMES_LC.add(DELETE_DRAFTS.toLowerCase());
+    NAMES_LC.add(PUBLISH_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 abd5890..765ed09 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
@@ -103,29 +103,36 @@
 permissionNames = \
 	abandon, \
 	create, \
+	deleteDrafts, \
 	editTopicName, \
 	forgeAuthor, \
 	forgeCommitter, \
 	forgeServerAsCommitter, \
 	owner, \
+	publishDrafts, \
 	push, \
 	pushMerge, \
 	pushTag, \
+	pushSignedTag, \
 	read, \
 	rebase, \
 	removeReviewer, \
 	submit, \
 	viewDrafts
+
 abandon = Abandon
 create = Create Reference
+deleteDrafts = Delete Drafts
 editTopicName = Edit Topic Name
 forgeAuthor = Forge Author Identity
 forgeCommitter = Forge Committer Identity
 forgeServerAsCommitter = Forge Server Identity
 owner = Owner
+publishDrafts = Publish Drafts
 push = Push
 pushMerge = Push Merge Commit
 pushTag = Push Annotated Tag
+pushSignedTag = Push Signed Tag
 read = Read
 rebase = Rebase
 removeReviewer = Remove Reviewer
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 7f66b51..3632e0c 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
@@ -189,12 +189,14 @@
 
   /** Can this user publish this draft change or any draft patch set of this change? */
   public boolean canPublish(final ReviewDb db) throws OrmException {
-    return isOwner() && isVisible(db);
+    return (isOwner() || getRefControl().canPublishDrafts())
+        && isVisible(db);
   }
 
   /** Can this user delete this draft change or any draft patch set of this change? */
   public boolean canDeleteDraft(final ReviewDb db) throws OrmException {
-    return isOwner() && isVisible(db);
+    return (isOwner() || getRefControl().canDeleteDrafts())
+        && isVisible(db);
   }
 
   /** Can this user rebase this change? */
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 f020af4..4e6f626 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
@@ -279,11 +279,10 @@
       // than if it doesn't have a PGP signature.
       //
       if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
-        return owner || canPerform(Permission.PUSH_TAG);
+        return owner || canPerform(Permission.PUSH_SIGNED_TAG);
       } else {
         return owner || canPerform(Permission.PUSH_TAG);
       }
-
     } else {
       return false;
     }
@@ -354,10 +353,22 @@
     return canPerform(Permission.VIEW_DRAFTS);
   }
 
+  /** @return true if this user can publish draft changes. */
+  public boolean canPublishDrafts() {
+    return canPerform(Permission.PUBLISH_DRAFTS);
+  }
+
+  /** @return true if this user can delete draft changes. */
+  public boolean canDeleteDrafts() {
+    return canPerform(Permission.DELETE_DRAFTS);
+  }
+
+  /** @return true if this user can edit topic names. */
   public boolean canEditTopicName() {
     return canPerform(Permission.EDIT_TOPIC_NAME);
   }
 
+  /** @return true if this user can force edit topic names. */
   public boolean canForceEditTopicName() {
     boolean result = false;
     for (PermissionRule rule : access(Permission.EDIT_TOPIC_NAME)) {