Merge changes I7cbc950c,I9b675535

* changes:
  Edit Assignee Permissions
  Read/Write Assignee field in ChangeNotes
This commit is contained in:
David Pursehouse
2016-09-20 14:57:21 +00:00
committed by Gerrit Code Review
11 changed files with 119 additions and 0 deletions

View File

@@ -887,6 +887,14 @@ The change owner, branch owners, project owners, and site administrators
can always edit or remove hashtags (even without having the `Edit Hashtags`
access right assigned).
[[category_edit_assigned_to]]
=== Edit Assignee
This category permits users to set who is assigned to a change that is
uploaded for review.
The change owner, ref owners, and the user currently assigned to a change
can always change the assignee.
[[example_roles]]
== Examples of typical roles in a project

View File

@@ -29,6 +29,7 @@ public class Permission implements Comparable<Permission> {
public static final String CREATE_SIGNED_TAG = "createSignedTag";
public static final String DELETE_DRAFTS = "deleteDrafts";
public static final String EDIT_HASHTAGS = "editHashtags";
public static final String EDIT_ASSIGNEE = "editAssignee";
public static final String EDIT_TOPIC_NAME = "editTopicName";
public static final String FORGE_AUTHOR = "forgeAuthor";
public static final String FORGE_COMMITTER = "forgeCommitter";
@@ -74,6 +75,7 @@ public class Permission implements Comparable<Permission> {
NAMES_LC.add(VIEW_DRAFTS.toLowerCase());
NAMES_LC.add(EDIT_TOPIC_NAME.toLowerCase());
NAMES_LC.add(EDIT_HASHTAGS.toLowerCase());
NAMES_LC.add(EDIT_ASSIGNEE.toLowerCase());
NAMES_LC.add(DELETE_DRAFTS.toLowerCase());
NAMES_LC.add(PUBLISH_DRAFTS.toLowerCase());

View File

@@ -127,6 +127,7 @@ permissionNames = \
createSignedTag, \
delete, \
deleteDrafts, \
editAssignee, \
editHashtags, \
editTopicName, \
forgeAuthor, \
@@ -150,6 +151,7 @@ createTag = Create Annotated Tag
createSignedTag = Create Signed Tag
delete = Delete Reference
deleteDrafts = Delete Drafts
editAssignee = Edit Assignee
editHashtags = Edit Hashtags
editTopicName = Edit Topic Name
forgeAuthor = Forge Author Identity

View File

@@ -65,6 +65,7 @@ import java.util.Locale;
import java.util.Set;
public class ChangeNoteUtil {
public static final FooterKey FOOTER_ASSIGNEE = new FooterKey("Assignee");
public static final FooterKey FOOTER_BRANCH = new FooterKey("Branch");
public static final FooterKey FOOTER_CHANGE_ID = new FooterKey("Change-id");
public static final FooterKey FOOTER_COMMIT = new FooterKey("Commit");

View File

@@ -399,6 +399,13 @@ public class ChangeNotes extends AbstractChangeNotes<ChangeNotes> {
return state.reviewerUpdates();
}
/**
* @return an Account.Id of the user assigned to this change.
*/
public Account.Id getAssignee() {
return state.assignee();
}
/**
*
* @return a ImmutableSet of all hashtags for this change sorted in alphabetical order.

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server.notedb;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_COMMIT;
@@ -136,6 +137,7 @@ class ChangeNotesParser {
private String branch;
private Change.Status status;
private String topic;
private Account.Id assignee;
private Set<String> hashtags;
private Timestamp createdOn;
private Timestamp lastUpdatedOn;
@@ -210,6 +212,7 @@ class ChangeNotesParser {
submissionId,
status,
assignee,
hashtags,
patchSets,
buildApprovals(),
@@ -317,6 +320,8 @@ class ChangeNotesParser {
parseHashtags(commit);
parseAssignee(commit);
if (submissionId == null) {
submissionId = parseSubmissionId(commit);
}
@@ -473,6 +478,18 @@ class ChangeNotesParser {
}
}
private void parseAssignee(ChangeNotesCommit commit)
throws ConfigInvalidException {
if (assignee != null) {
return;
}
String assigneeValue = parseOneFooter(commit, FOOTER_ASSIGNEE);
if (assigneeValue != null) {
PersonIdent ident = RawParseUtils.parsePersonIdent(assigneeValue);
assignee = noteUtil.parseIdent(ident, id);
}
}
private void parseTag(ChangeNotesCommit commit)
throws ConfigInvalidException {
tag = null;

View File

@@ -59,6 +59,7 @@ public abstract class ChangeNotesState {
return new AutoValue_ChangeNotesState(
change.getId(),
null,
null,
ImmutableSet.<String>of(),
ImmutableSortedMap.<PatchSet.Id, PatchSet>of(),
ImmutableListMultimap.<PatchSet.Id, PatchSetApproval>of(),
@@ -84,6 +85,7 @@ public abstract class ChangeNotesState {
@Nullable String originalSubject,
@Nullable String submissionId,
@Nullable Change.Status status,
@Nullable Account.Id assignee,
@Nullable Set<String> hashtags,
Map<PatchSet.Id, PatchSet> patchSets,
Multimap<PatchSet.Id, PatchSetApproval> approvals,
@@ -111,6 +113,7 @@ public abstract class ChangeNotesState {
originalSubject,
submissionId,
status),
assignee,
ImmutableSet.copyOf(hashtags),
ImmutableSortedMap.copyOf(patchSets, ReviewDbUtil.intKeyOrdering()),
ImmutableListMultimap.copyOf(approvals),
@@ -153,6 +156,7 @@ public abstract class ChangeNotesState {
@Nullable abstract ChangeColumns columns();
// Other related to this Change.
@Nullable abstract Account.Id assignee();
abstract ImmutableSet<String> hashtags();
abstract ImmutableSortedMap<PatchSet.Id, PatchSet> patchSets();
abstract ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals();

View File

@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_ASSIGNEE;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_BRANCH;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_CHANGE_ID;
import static com.google.gerrit.server.notedb.ChangeNoteUtil.FOOTER_COMMIT;
@@ -122,6 +123,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
private String submissionId;
private String topic;
private String commit;
private Account.Id assignee;
private Set<String> hashtags;
private String changeMessage;
private String tag;
@@ -379,6 +381,10 @@ public class ChangeUpdate extends AbstractChangeUpdate {
this.hashtags = hashtags;
}
public void setAssignee(Account.Id assignee) {
this.assignee = assignee;
}
public Map<Account.Id, ReviewerStateInternal> getReviewers() {
return reviewers;
}
@@ -547,6 +553,11 @@ public class ChangeUpdate extends AbstractChangeUpdate {
addFooter(msg, FOOTER_COMMIT, commit);
}
if (assignee != null) {
addFooter(msg, FOOTER_ASSIGNEE);
addIdent(msg, assignee).append('\n');
}
Joiner comma = Joiner.on(',');
if (hashtags != null) {
addFooter(msg, FOOTER_HASHTAGS, comma.join(hashtags));
@@ -647,6 +658,7 @@ public class ChangeUpdate extends AbstractChangeUpdate {
&& status == null
&& submissionId == null
&& submitRecords == null
&& assignee == null
&& hashtags == null
&& topic == null
&& commit == null

View File

@@ -352,6 +352,16 @@ public class ChangeControl {
return false;
}
/** Is this user assigned to this change? */
public boolean isAssignee() {
Account.Id currentAssignee = notes.getAssignee();
if (currentAssignee != null && getUser().isIdentifiedUser()) {
Account.Id id = getUser().getAccountId();
return id.equals(currentAssignee);
}
return false;
}
/** Is this user a reviewer for the change? */
public boolean isReviewer(ReviewDb db) throws OrmException {
return isReviewer(db, null);
@@ -414,6 +424,13 @@ public class ChangeControl {
return getRefControl().canForceEditTopicName();
}
public boolean canEditAssignee() {
return isOwner()
|| getProjectControl().isOwner()
|| getRefControl().canEditAssignee()
|| isAssignee();
}
/** Can this user edit the hashtag name? */
public boolean canEditHashtags() {
return isOwner() // owner (aka creator) of the change can edit hashtags

View File

@@ -447,6 +447,10 @@ public class RefControl {
return canPerform(Permission.EDIT_HASHTAGS);
}
public boolean canEditAssignee() {
return canPerform(Permission.EDIT_ASSIGNEE);
}
/** @return true if this user can force edit topic names. */
public boolean canForceEditTopicName() {
return canForcePerform(Permission.EDIT_TOPIC_NAME);

View File

@@ -49,6 +49,7 @@ import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.config.GerritServerId;
import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
import com.google.gerrit.server.util.RequestId;
import com.google.gerrit.testutil.TestChanges;
@@ -79,6 +80,9 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
@Inject
private ChangeNoteUtil noteUtil;
@Inject
private @GerritServerId String serverId;
@Test
public void tagChangeMessage() throws Exception {
String tag = "jenkins";
@@ -561,6 +565,47 @@ public class ChangeNotesTest extends AbstractChangeNotesTest {
assertThat(updated.getObjectId()).isEqualTo(initial.getObjectId());
}
@Test
public void assigneeCommit() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
update.setAssignee(otherUserId);
ObjectId result = update.commit();
assertThat(result).isNotNull();
try (RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(update.getResult());
rw.parseBody(commit);
String strIdent =
otherUser.getName()
+ " <"
+ otherUserId
+ "@"
+ serverId
+ ">";
assertThat(commit.getFullMessage())
.contains("Assignee: " + strIdent);
}
}
@Test
public void assigneeChangeNotes() throws Exception {
Change c = newChange();
ChangeUpdate update = newUpdate(c, changeOwner);
update.setAssignee(otherUserId);
update.commit();
ChangeNotes notes = newNotes(c);
assertThat(notes.getAssignee()).isEqualTo(otherUserId);
update = newUpdate(c, changeOwner);
update.setAssignee(changeOwner.getAccountId());
update.commit();
notes = newNotes(c);
assertThat(notes.getAssignee()).isEqualTo(changeOwner.getAccountId());
}
@Test
public void hashtagCommit() throws Exception {
Change c = newChange();