Add reviewer.enableByEmail to ProjectConfig
This change adds a setting to enable reviewers and CCs by email to the ProjectConfig. It also adds tests, docs, API and UI support. Bug: Issue 4134 Change-Id: Ibe6ccb80f9f71f2a430afcd6e12b3f27cf8fdbd6
This commit is contained in:
@@ -291,6 +291,18 @@ Branches not listed in this section will not be included in the mergeability
|
||||
check. If the `branchOrder` section is not defined then the mergeability of a
|
||||
change into other branches will not be done.
|
||||
|
||||
[[reviewer-section]]
|
||||
=== reviewer section
|
||||
|
||||
Defines config options to adjust a project's reviewer workflow such as enabling
|
||||
reviewers and CCs by email.
|
||||
|
||||
[[reviewer.enableByEmail]]reviewer.enableByEmail::
|
||||
+
|
||||
A boolean indicating if reviewers and CCs that do not currently have a Gerrit
|
||||
account can be added to a change by providing their email address.
|
||||
|
||||
Defaults to `false`.
|
||||
|
||||
[[file-groups]]
|
||||
== The file +groups+
|
||||
|
@@ -31,6 +31,7 @@ public class ConfigInfo {
|
||||
public InheritedBooleanInfo enableSignedPush;
|
||||
public InheritedBooleanInfo requireSignedPush;
|
||||
public InheritedBooleanInfo rejectImplicitMerges;
|
||||
public InheritedBooleanInfo enableReviewerByEmail;
|
||||
public MaxObjectSizeLimitInfo maxObjectSizeLimit;
|
||||
public SubmitType submitType;
|
||||
public ProjectState state;
|
||||
|
@@ -86,6 +86,7 @@ public class ProjectInfoScreen extends ProjectScreen {
|
||||
private ListBox enableSignedPush;
|
||||
private ListBox requireSignedPush;
|
||||
private ListBox rejectImplicitMerges;
|
||||
private ListBox enableReviewerByEmail;
|
||||
private NpTextBox maxObjectSizeLimit;
|
||||
private Label effectiveMaxObjectSizeLimit;
|
||||
private Map<String, Map<String, HasEnabled>> pluginConfigWidgets;
|
||||
@@ -191,6 +192,7 @@ public class ProjectInfoScreen extends ProjectScreen {
|
||||
requireChangeID.setEnabled(isOwner);
|
||||
rejectImplicitMerges.setEnabled(isOwner);
|
||||
maxObjectSizeLimit.setEnabled(isOwner);
|
||||
enableReviewerByEmail.setEnabled(isOwner);
|
||||
|
||||
if (pluginConfigWidgets != null) {
|
||||
for (Map<String, HasEnabled> widgetMap : pluginConfigWidgets.values()) {
|
||||
@@ -264,6 +266,10 @@ public class ProjectInfoScreen extends ProjectScreen {
|
||||
saveEnabler.listenTo(rejectImplicitMerges);
|
||||
grid.addHtml(AdminConstants.I.rejectImplicitMerges(), rejectImplicitMerges);
|
||||
|
||||
enableReviewerByEmail = newInheritedBooleanBox();
|
||||
saveEnabler.listenTo(enableReviewerByEmail);
|
||||
grid.addHtml(AdminConstants.I.rejectImplicitMerges(), enableReviewerByEmail);
|
||||
|
||||
maxObjectSizeLimit = new NpTextBox();
|
||||
saveEnabler.listenTo(maxObjectSizeLimit);
|
||||
effectiveMaxObjectSizeLimit = new Label();
|
||||
@@ -395,6 +401,7 @@ public class ProjectInfoScreen extends ProjectScreen {
|
||||
setBool(requireSignedPush, result.requireSignedPush());
|
||||
}
|
||||
setBool(rejectImplicitMerges, result.rejectImplicitMerges());
|
||||
setBool(enableReviewerByEmail, result.enableReviewerByEmail());
|
||||
setSubmitType(result.submitType());
|
||||
setState(result.state());
|
||||
maxObjectSizeLimit.setText(result.maxObjectSizeLimit().configuredValue());
|
||||
|
@@ -57,6 +57,9 @@ public class ConfigInfo extends JavaScriptObject {
|
||||
public final native InheritedBooleanInfo rejectImplicitMerges()
|
||||
/*-{ return this.reject_implicit_merges; }-*/ ;
|
||||
|
||||
public final native InheritedBooleanInfo enableReviewerByEmail()
|
||||
/*-{ return this.enable_reviewer_by_email; }-*/ ;
|
||||
|
||||
public final SubmitType submitType() {
|
||||
return SubmitType.valueOf(submitTypeRaw());
|
||||
}
|
||||
|
@@ -99,6 +99,8 @@ public final class Project {
|
||||
|
||||
protected InheritableBoolean rejectImplicitMerges;
|
||||
|
||||
protected InheritableBoolean enableReviewerByEmail;
|
||||
|
||||
protected Project() {}
|
||||
|
||||
public Project(Project.NameKey nameKey) {
|
||||
@@ -112,6 +114,7 @@ public final class Project {
|
||||
createNewChangeForAllNotInTarget = InheritableBoolean.INHERIT;
|
||||
enableSignedPush = InheritableBoolean.INHERIT;
|
||||
requireSignedPush = InheritableBoolean.INHERIT;
|
||||
enableReviewerByEmail = InheritableBoolean.INHERIT;
|
||||
}
|
||||
|
||||
public Project.NameKey getNameKey() {
|
||||
@@ -154,6 +157,10 @@ public final class Project {
|
||||
return rejectImplicitMerges;
|
||||
}
|
||||
|
||||
public InheritableBoolean getEnableReviewerByEmail() {
|
||||
return enableReviewerByEmail;
|
||||
}
|
||||
|
||||
public void setUseContributorAgreements(final InheritableBoolean u) {
|
||||
useContributorAgreements = u;
|
||||
}
|
||||
|
@@ -155,6 +155,9 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
ImmutableSet.of(
|
||||
"MaxWithBlock", "AnyWithBlock", "MaxNoBlock", "NoBlock", "NoOp", "PatchSetLock");
|
||||
|
||||
private static final String REVIEWER = "reviewer";
|
||||
private static final String KEY_ENABLE_REVIEWER_BY_EMAIL = "enableByEmail";
|
||||
|
||||
private static final String LEGACY_PERMISSION_PUSH_TAG = "pushTag";
|
||||
private static final String LEGACY_PERMISSION_PUSH_SIGNED_TAG = "pushSignedTag";
|
||||
|
||||
@@ -182,6 +185,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
private boolean checkReceivedObjects;
|
||||
private Set<String> sectionsWithUnknownPermissions;
|
||||
private boolean hasLegacyPermissions;
|
||||
private boolean enableReviewerByEmail;
|
||||
|
||||
public static ProjectConfig read(MetaDataUpdate update)
|
||||
throws IOException, ConfigInvalidException {
|
||||
@@ -435,6 +439,16 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
return checkReceivedObjects;
|
||||
}
|
||||
|
||||
/** @return the enableReviewerByEmail for this project, default is false. */
|
||||
public boolean getEnableReviewerByEmail() {
|
||||
return enableReviewerByEmail;
|
||||
}
|
||||
|
||||
/** Set enableReviewerByEmail for this project, default is false. */
|
||||
public void setEnableReviewerByEmail(boolean val) {
|
||||
enableReviewerByEmail = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all GroupReferences use current group name, repairing stale ones.
|
||||
*
|
||||
@@ -526,6 +540,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
mimeTypes = new ConfiguredMimeTypes(projectName.get(), rc);
|
||||
loadPluginSections(rc);
|
||||
loadReceiveSection(rc);
|
||||
loadReviewerSection(rc);
|
||||
}
|
||||
|
||||
private void loadAccountsSection(Config rc, Map<String, GroupReference> groupsByName) {
|
||||
@@ -933,6 +948,10 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
maxObjectSizeLimit = rc.getLong(RECEIVE, null, KEY_MAX_OBJECT_SIZE_LIMIT, 0);
|
||||
}
|
||||
|
||||
private void loadReviewerSection(Config rc) {
|
||||
enableReviewerByEmail = rc.getBoolean(REVIEWER, null, KEY_ENABLE_REVIEWER_BY_EMAIL, false);
|
||||
}
|
||||
|
||||
private void loadPluginSections(Config rc) {
|
||||
pluginConfigs = new HashMap<>();
|
||||
for (String plugin : rc.getSubsections(PLUGIN)) {
|
||||
@@ -1067,6 +1086,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
saveAccessSections(rc, keepGroups);
|
||||
saveNotifySections(rc, keepGroups);
|
||||
savePluginSections(rc, keepGroups);
|
||||
saveReviewerSection(rc);
|
||||
groupList.retainUUIDs(keepGroups);
|
||||
saveLabelSections(rc);
|
||||
saveSubscribeSections(rc);
|
||||
@@ -1288,40 +1308,55 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
|
||||
setBooleanConfigKey(
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_ALLOW_POST_SUBMIT,
|
||||
label.allowPostSubmit(),
|
||||
LabelType.DEF_ALLOW_POST_SUBMIT);
|
||||
setBooleanConfigKey(
|
||||
rc, name, KEY_COPY_MIN_SCORE, label.isCopyMinScore(), LabelType.DEF_COPY_MIN_SCORE);
|
||||
setBooleanConfigKey(
|
||||
rc, name, KEY_COPY_MAX_SCORE, label.isCopyMaxScore(), LabelType.DEF_COPY_MAX_SCORE);
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_COPY_MIN_SCORE,
|
||||
label.isCopyMinScore(),
|
||||
LabelType.DEF_COPY_MIN_SCORE);
|
||||
setBooleanConfigKey(
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_COPY_MAX_SCORE,
|
||||
label.isCopyMaxScore(),
|
||||
LabelType.DEF_COPY_MAX_SCORE);
|
||||
setBooleanConfigKey(
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_COPY_ALL_SCORES_ON_TRIVIAL_REBASE,
|
||||
label.isCopyAllScoresOnTrivialRebase(),
|
||||
LabelType.DEF_COPY_ALL_SCORES_ON_TRIVIAL_REBASE);
|
||||
setBooleanConfigKey(
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_COPY_ALL_SCORES_IF_NO_CODE_CHANGE,
|
||||
label.isCopyAllScoresIfNoCodeChange(),
|
||||
LabelType.DEF_COPY_ALL_SCORES_IF_NO_CODE_CHANGE);
|
||||
setBooleanConfigKey(
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_COPY_ALL_SCORES_IF_NO_CHANGE,
|
||||
label.isCopyAllScoresIfNoChange(),
|
||||
LabelType.DEF_COPY_ALL_SCORES_IF_NO_CHANGE);
|
||||
setBooleanConfigKey(
|
||||
rc,
|
||||
LABEL,
|
||||
name,
|
||||
KEY_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE,
|
||||
label.isCopyAllScoresOnMergeFirstParentUpdate(),
|
||||
LabelType.DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE);
|
||||
setBooleanConfigKey(
|
||||
rc, name, KEY_CAN_OVERRIDE, label.canOverride(), LabelType.DEF_CAN_OVERRIDE);
|
||||
rc, LABEL, name, KEY_CAN_OVERRIDE, label.canOverride(), LabelType.DEF_CAN_OVERRIDE);
|
||||
List<String> values = Lists.newArrayListWithCapacity(label.getValues().size());
|
||||
for (LabelValue value : label.getValues()) {
|
||||
values.add(value.format());
|
||||
@@ -1335,11 +1370,11 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
}
|
||||
|
||||
private static void setBooleanConfigKey(
|
||||
Config rc, String name, String key, boolean value, boolean defaultValue) {
|
||||
Config rc, String section, String name, String key, boolean value, boolean defaultValue) {
|
||||
if (value == defaultValue) {
|
||||
rc.unset(LABEL, name, key);
|
||||
rc.unset(section, name, key);
|
||||
} else {
|
||||
rc.setBoolean(LABEL, name, key, value);
|
||||
rc.setBoolean(section, name, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1367,6 +1402,11 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
}
|
||||
}
|
||||
|
||||
private void saveReviewerSection(Config rc) {
|
||||
setBooleanConfigKey(
|
||||
rc, REVIEWER, null, KEY_ENABLE_REVIEWER_BY_EMAIL, enableReviewerByEmail, false);
|
||||
}
|
||||
|
||||
private void saveGroupList() throws IOException {
|
||||
saveUTF8(GroupList.FILE_NAME, groupList.asText());
|
||||
}
|
||||
|
@@ -58,6 +58,7 @@ public class ConfigInfoImpl extends ConfigInfo {
|
||||
InheritedBooleanInfo enableSignedPush = new InheritedBooleanInfo();
|
||||
InheritedBooleanInfo requireSignedPush = new InheritedBooleanInfo();
|
||||
InheritedBooleanInfo rejectImplicitMerges = new InheritedBooleanInfo();
|
||||
InheritedBooleanInfo enableReviewerByEmail = new InheritedBooleanInfo();
|
||||
|
||||
useContributorAgreements.value = projectState.isUseContributorAgreements();
|
||||
useSignedOffBy.value = projectState.isUseSignedOffBy();
|
||||
@@ -73,6 +74,7 @@ public class ConfigInfoImpl extends ConfigInfo {
|
||||
enableSignedPush.configuredValue = p.getEnableSignedPush();
|
||||
requireSignedPush.configuredValue = p.getRequireSignedPush();
|
||||
rejectImplicitMerges.configuredValue = p.getRejectImplicitMerges();
|
||||
enableReviewerByEmail.configuredValue = p.getEnableReviewerByEmail();
|
||||
|
||||
ProjectState parentState = Iterables.getFirst(projectState.parents(), null);
|
||||
if (parentState != null) {
|
||||
@@ -85,6 +87,7 @@ public class ConfigInfoImpl extends ConfigInfo {
|
||||
enableSignedPush.inheritedValue = projectState.isEnableSignedPush();
|
||||
requireSignedPush.inheritedValue = projectState.isRequireSignedPush();
|
||||
rejectImplicitMerges.inheritedValue = projectState.isRejectImplicitMerges();
|
||||
enableReviewerByEmail.inheritedValue = projectState.isEnableReviewerByEmail();
|
||||
}
|
||||
|
||||
this.useContributorAgreements = useContributorAgreements;
|
||||
|
@@ -394,6 +394,10 @@ public class ProjectState {
|
||||
return getInheritableBoolean(Project::getRejectImplicitMerges);
|
||||
}
|
||||
|
||||
public boolean isEnableReviewerByEmail() {
|
||||
return getInheritableBoolean(Project::getEnableReviewerByEmail);
|
||||
}
|
||||
|
||||
public LabelTypes getLabelTypes() {
|
||||
Map<String, LabelType> types = new LinkedHashMap<>();
|
||||
for (ProjectState s : treeInOrder()) {
|
||||
|
@@ -105,7 +105,9 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
|
||||
+ " accepted = group Developers\n" //
|
||||
+ " accepted = group Staff\n" //
|
||||
+ " autoVerify = group Developers\n" //
|
||||
+ " agreementUrl = http://www.example.com/agree\n")) //
|
||||
+ " agreementUrl = http://www.example.com/agree\n" //
|
||||
+ "[reviewer]\n" //
|
||||
+ " enableByEmail = true\n")) //
|
||||
));
|
||||
|
||||
ProjectConfig cfg = read(rev);
|
||||
@@ -132,6 +134,8 @@ public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
|
||||
assertThat(submit.getExclusiveGroup()).isTrue();
|
||||
assertThat(read.getExclusiveGroup()).isTrue();
|
||||
assertThat(push.getExclusiveGroup()).isFalse();
|
||||
|
||||
assertThat(cfg.getEnableReviewerByEmail()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user