Merge changes I7cbc950c,I9b675535
* changes: Edit Assignee Permissions Read/Write Assignee field in ChangeNotes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user