Merge "AccountIT: Add asserts to check reindexing of accounts"
diff --git a/Documentation/dev-stars.txt b/Documentation/dev-stars.txt
index 553ac5b..1fb871a 100644
--- a/Documentation/dev-stars.txt
+++ b/Documentation/dev-stars.txt
@@ -61,6 +61,19 @@
The ignore star is represented by the special star label 'ignore'.
+[[mute-star]]
+== Mute Star
+
+If the "mute/<patchset_id>"-star is set by a user, and <patchset_id>
+matches the current patch set, the change is always reported as "reviewed"
+in the ChangeInfo.
+
+This allows users to "de-highlight" changes in a dashboard until a new
+patchset has been uploaded.
+
+The ChangeInfo muted-field will show if the change is currently in a
+mute state.
+
[[query-stars]]
== Query Stars
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 577f2e1..3c3699c 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -2117,7 +2117,7 @@
.Request
----
- Set /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/private HTTP/1.0
+ PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/private HTTP/1.0
----
.Response
@@ -2148,6 +2148,67 @@
If the change was already not private, the response is "`409 Conflict`".
+[[ignore]]
+=== Ignore
+--
+'PUT /changes/link:#change-id[\{change-id\}]/ignore'
+--
+
+Marks a change as ignored. The change will not be shown in the incoming
+reviews dashboard, and email notifications will be suppressed.
+
+.Request
+----
+ PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/ignore HTTP/1.0
+----
+
+[[unignore]]
+=== Unignore
+--
+'PUT /changes/link:#change-id[\{change-id\}]/unignore'
+--
+
+Un-marks a change as ignored.
+
+.Request
+----
+ PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/unignore HTTP/1.0
+----
+
+[[mute]]
+=== Mute
+--
+'PUT /changes/link:#change-id[\{change-id\}]/mute'
+--
+
+Marks a change as muted.
+
+This allows users to "de-highlight" changes in their dashboard until a new
+patch set is uploaded.
+
+This differs from the link:#ignore[ignore] endpoint, which will mute
+emails and hide the change from dashboard completely until it is
+link:#unignore[unignored] again.
+
+
+.Request
+----
+ PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/mute HTTP/1.0
+----
+
+[[unmute]]
+=== Unmute
+--
+'PUT /changes/link:#change-id[\{change-id\}]/unmute'
+--
+
+Unmutes a change.
+
+.Request
+----
+ PUT /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/unmute HTTP/1.0
+----
+
[[edit-endpoints]]
== Change Edit Endpoints
@@ -5395,6 +5456,8 @@
|`problems` |optional|
A list of link:#problem-info[ProblemInfo] entities describing potential
problems with this change. Only set if link:#check[CHECK] is set.
+|`is_private` |optional, not set if `false`|
+When present, change is marked as private.
|==================================
[[change-input]]
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index a0e9954..16b76b7 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -183,6 +183,48 @@
}
----
+[[check-access]]
+=== Check Access
+--
+'POST /config/server/check.access'
+--
+
+Runs access checks for other users.
+
+Input for the access checks that should be run must be provided in
+the request body inside a
+link:#access-check-input[AccessCheckInput] entity.
+
+.Request
+----
+ POST /config/server/check HTTP/1.0
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "project": "medium",
+ "account": "Kristen.Burns@gerritcodereview.com",
+ "ref": "refs/heads/secret/bla"
+ }
+----
+
+The result is a link:#access-check-info[AccessCheckInfo] entity
+detailing the read access of the given user for the given project (or
+project-ref combination).
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "result": {
+ "message": "user Kristen Burns \u003cKristen.Burns@gerritcodereview.com\u003e (1000098) cannot see ref refs/heads/secret/master in project medium",
+ "status": 403
+ }
+ }
+----
+
[[confirm-email]]
=== Confirm Email
--
@@ -1266,6 +1308,34 @@
[[json-entities]]
== JSON Entities
+[[access-check-info]]
+=== AccessCheckInfo
+The `AccessCheckInfo` entity is the result of a
+an access check.
+
+[options="header",cols="1,^1,5"]
+|=========================================
+|Field Name ||Description
+|`status`||The HTTP status code for the access.
+200 means success, 403 means denied and 404 means the project does not
+exist.
+|`message`|optional|A clarifying message if `status` is not 200.
+=========================================
+
+[[access-check-input]]
+=== AccessCheckInput
+The `AccessCheckInput` entity is a tuple of (account, project) or
+(account, project, ref) for which we want to check access.
+
+[options="header",cols="1,^1,5"]
+|=========================================
+|Field Name ||Description
+|`account`||The account for which to check access
+|`project`||The project for which to check access
+|`ref`|optional|The refname for which to check access
+=========================================
+
+
[[auth-info]]
=== AuthInfo
The `AuthInfo` entity contains information about the authentication
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index 7928512..8e2bff6 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -78,6 +78,12 @@
`Accept-Encoding` request header is set to `gzip`. This may
save on network transfer time for larger responses.
+[[input]]
+=== Input Format
+Unknown JSON parameters will simply be ignored by Gerrit without causing
+an exception. This also applies to case-sensitive parameters, such as
+map keys.
+
[[timestamp]]
=== Timestamp
Timestamps are given in UTC and have the format
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 90e1979..d49ed3f 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -367,6 +367,10 @@
Mergeability of abandoned changes is not computed. This operator will
not find any abandoned but mergeable changes.
+[[ignored]]
+is:ignored::
++
+True if the change is ignored. Same as `star:ignore`.
[[private]]
is:private::
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 2be0000..498286c 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -65,9 +65,11 @@
import com.google.gerrit.extensions.api.changes.ReviewInput.DraftHandling;
import com.google.gerrit.extensions.api.changes.RevisionApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
+import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.Comment.Range;
+import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.client.Side;
@@ -1095,6 +1097,31 @@
}
@Test
+ public void addReviewerThatIsInactiveEmailFallback() throws Exception {
+ assume().that(notesMigration.enabled()).isTrue();
+
+ ConfigInput conf = new ConfigInput();
+ conf.enableReviewerByEmail = InheritableBoolean.TRUE;
+ gApi.projects().name(project.get()).config(conf);
+
+ PushOneCommit.Result result = createChange();
+
+ String username = "user@domain.com";
+ gApi.accounts().create(username).setActive(false);
+
+ AddReviewerInput in = new AddReviewerInput();
+ in.reviewer = username;
+ in.state = ReviewerState.CC;
+ AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
+
+ assertThat(r.input).isEqualTo(username);
+ assertThat(r.error).isNull();
+ // When adding by email, the reviewers field is also empty because we can't
+ // render a ReviewerInfo object for a non-account.
+ assertThat(r.reviewers).isNull();
+ }
+
+ @Test
public void addReviewer() throws Exception {
TestTimeUtil.resetWithClockStep(1, SECONDS);
PushOneCommit.Result r = createChange();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CheckAccessIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CheckAccessIT.java
new file mode 100644
index 0000000..cda5347
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/CheckAccessIT.java
@@ -0,0 +1,140 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.account;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.extensions.api.config.AccessCheckInfo;
+import com.google.gerrit.extensions.api.config.AccessCheckInput;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CheckAccessIT extends AbstractDaemonTest {
+
+ private Project.NameKey normalProject;
+ private Project.NameKey secretProject;
+ private Project.NameKey secretRefProject;
+ private TestAccount privilegedUser;
+ private AccountGroup privilegedGroup;
+
+ @Before
+ public void setUp() throws Exception {
+ normalProject = createProject("normal");
+ secretProject = createProject("secret");
+ secretRefProject = createProject("secretRef");
+ privilegedGroup = groupCache.get(new AccountGroup.NameKey(createGroup("privilegedGroup")));
+
+ privilegedUser = accounts.create("privilegedUser", "snowden@nsa.gov", "Ed Snowden");
+ gApi.groups().id(privilegedGroup.getGroupUUID().get()).addMembers(privilegedUser.username);
+
+ assertThat(gApi.groups().id(privilegedGroup.getGroupUUID().get()).members().get(0).email)
+ .contains("snowden");
+
+ // deny(secretProject, Permission.READ, SystemGroupBackend.REGISTERED_USERS, "refs/*");
+ grant(Permission.READ, secretProject, "refs/*", false, privilegedGroup.getGroupUUID());
+ block(Permission.READ, SystemGroupBackend.REGISTERED_USERS, "refs/*", secretProject);
+
+ // deny/grant/block arg ordering is screwy.
+ deny(secretRefProject, Permission.READ, SystemGroupBackend.ANONYMOUS_USERS, "refs/*");
+ grant(
+ Permission.READ,
+ secretRefProject,
+ "refs/heads/secret/*",
+ false,
+ privilegedGroup.getGroupUUID());
+ block(
+ Permission.READ,
+ SystemGroupBackend.REGISTERED_USERS,
+ "refs/heads/secret/*",
+ secretRefProject);
+ grant(
+ Permission.READ,
+ secretRefProject,
+ "refs/heads/*",
+ false,
+ SystemGroupBackend.REGISTERED_USERS);
+ }
+
+ @Test
+ public void invalidInputs() {
+ List<AccessCheckInput> inputs =
+ ImmutableList.of(
+ new AccessCheckInput(),
+ new AccessCheckInput(user.email, null, null),
+ new AccessCheckInput(null, normalProject.toString(), null),
+ new AccessCheckInput("doesnotexist@invalid.com", normalProject.toString(), null));
+ for (AccessCheckInput input : inputs) {
+ try {
+ gApi.config().server().checkAccess(input);
+ fail(String.format("want RestApiException for %s", newGson().toJson(input)));
+ } catch (RestApiException e) {
+
+ }
+ }
+ }
+
+ @Test
+ public void accessible() {
+ Map<AccessCheckInput, Integer> inputs =
+ ImmutableMap.of(
+ new AccessCheckInput(user.email, normalProject.get(), null), 200,
+ new AccessCheckInput(user.email, secretProject.get(), null), 403,
+ new AccessCheckInput(user.email, "nonexistent", null), 404,
+ new AccessCheckInput(privilegedUser.email, normalProject.get(), null), 200,
+ new AccessCheckInput(privilegedUser.email, secretProject.get(), null), 200);
+
+ for (Map.Entry<AccessCheckInput, Integer> entry : inputs.entrySet()) {
+ String in = newGson().toJson(entry.getKey());
+ AccessCheckInfo info = null;
+
+ try {
+ info = gApi.config().server().checkAccess(entry.getKey());
+ } catch (RestApiException e) {
+ fail(String.format("check.check(%s): exception %s", in, e));
+ }
+
+ int want = entry.getValue();
+ if (want != info.result.status) {
+ fail(String.format("check.access(%s) = %d, want %d", in, info.result.status, want));
+ }
+
+ switch (want) {
+ case 403:
+ assertThat(info.result.message).contains("cannot see");
+ break;
+ case 404:
+ assertThat(info.result.message).contains("does not exist");
+ break;
+ case 200:
+ assertThat(info.result.message).isNull();
+ break;
+ default:
+ fail(String.format("unknown code %d", want));
+ }
+ }
+ }
+}
diff --git a/gerrit-elasticsearch/BUILD b/gerrit-elasticsearch/BUILD
index 8c94830..0affa12 100644
--- a/gerrit-elasticsearch/BUILD
+++ b/gerrit-elasticsearch/BUILD
@@ -52,10 +52,8 @@
name = "elasticsearch_tests",
size = "large",
srcs = glob(["src/test/java/**/*Test.java"]),
- flaky = 1,
tags = [
"elastic",
- "flaky",
],
deps = [
":elasticsearch",
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
index 3a0e20b..ece9edd 100644
--- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
+++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -68,10 +68,9 @@
static void configure(Config config, String port) {
config.setEnum("index", null, "type", IndexType.ELASTICSEARCH);
- config.setString("index", null, "protocol", "http");
- config.setString("index", null, "hostname", "localhost");
- config.setString("index", null, "port", port);
- config.setBoolean("index", "elasticsearch", "test", true);
+ config.setString("elasticsearch", "test", "protocol", "http");
+ config.setString("elasticsearch", "test", "hostname", "localhost");
+ config.setString("elasticsearch", "test", "port", port);
}
static ElasticNodeInfo startElasticsearchNode() throws InterruptedException, ExecutionException {
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index 6064faa..83d36b1 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -88,6 +88,20 @@
void setPrivate(boolean value) throws RestApiException;
/**
+ * Ignore or un-ignore this change.
+ *
+ * @param ignore ignore the change if true
+ */
+ void ignore(boolean ignore) throws RestApiException;
+
+ /**
+ * Mute or un-mute this change.
+ *
+ * @param mute mute the change if true
+ */
+ void mute(boolean mute) throws RestApiException;
+
+ /**
* Create a new change that reverts this change.
*
* @see Changes#id(int)
@@ -483,5 +497,15 @@
public ChangeInfo createMergePatchSet(MergePatchSetInput in) {
throw new NotImplementedException();
}
+
+ @Override
+ public void ignore(boolean ignore) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public void mute(boolean mute) {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/AccessCheckInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/AccessCheckInfo.java
new file mode 100644
index 0000000..4ff5172
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/AccessCheckInfo.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.api.config;
+
+public class AccessCheckInfo {
+ public static class Result {
+ public String message;
+
+ // HTTP status code.
+ public int status;
+ }
+
+ public Result result;
+ // for future extension, we may add inputs / results for bulk checks.
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/AccessCheckInput.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/AccessCheckInput.java
new file mode 100644
index 0000000..80a537c
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/AccessCheckInput.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.api.config;
+
+import com.google.gerrit.common.Nullable;
+
+public class AccessCheckInput {
+ public String account;
+ public String project;
+
+ @Nullable public String ref;
+
+ public AccessCheckInput(String account, String project, @Nullable String ref) {
+ this.account = account;
+ this.project = project;
+ this.ref = ref;
+ }
+
+ public AccessCheckInput() {}
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/Server.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/Server.java
index ee0960c..2280396 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/Server.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/config/Server.java
@@ -36,6 +36,8 @@
ConsistencyCheckInfo checkConsistency(ConsistencyCheckInput in) throws RestApiException;
+ AccessCheckInfo checkAccess(AccessCheckInput in) throws RestApiException;
+
/**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
@@ -75,5 +77,10 @@
public ConsistencyCheckInfo checkConsistency(ConsistencyCheckInput in) {
throw new NotImplementedException();
}
+
+ @Override
+ public AccessCheckInfo checkAccess(AccessCheckInput in) {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 2cb8384..49e4a05 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -36,6 +36,7 @@
public Timestamp updated;
public Timestamp submitted;
public Boolean starred;
+ public Boolean muted;
public Collection<String> stars;
public Boolean reviewed;
public SubmitType submitType;
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
index 3cac62c..abcfe31 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
@@ -136,6 +136,8 @@
public final native boolean starred() /*-{ return this.starred ? true : false; }-*/;
+ public final native boolean muted() /*-{ return this.muted ? true : false; }-*/;
+
public final native boolean reviewed() /*-{ return this.reviewed ? true : false; }-*/;
public final native boolean isPrivate() /*-{ return this.is_private ? true : false; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
index db8301a..52da8e6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchSuggestOracle.java
@@ -136,6 +136,7 @@
suggestions.add("is:merged");
suggestions.add("is:abandoned");
suggestions.add("is:mergeable");
+ suggestions.add("is:ignored");
suggestions.add("status:");
suggestions.add("status:open");
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
index f37cbc2..f8bda64 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -101,7 +101,7 @@
+ who
+ " -owner:"
+ who
- + " -star:ignore) OR assignee:"
+ + " -is:ignored) OR assignee:"
+ who
+ ")";
}
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
index 57bc7f4..64d16c5 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/ldap/LoginForm.html
@@ -65,13 +65,16 @@
var login_form = document.getElementById('login_form');
var f_user = document.getElementById('f_user');
var f_pass = document.getElementById('f_pass');
- f_user.onkeydown = function(e) {
+
+ // Keyup event must be used to avoid issue with Firefox autocomplete
+ // Issue #6083
+ f_user.onkeyup = function(e) {
if (e.keyCode == 13) {
f_pass.focus();
return false;
}
}
- f_pass.onkeydown = function(e) {
+ f_pass.onkeyup = function(e) {
if (e.keyCode == 13) {
login_form.submit();
return false;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 268bb12..9c1541c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -75,6 +75,7 @@
import com.google.gerrit.server.patch.DiffExecutorModule;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
import com.google.gerrit.server.plugins.PluginRestApiModule;
+import com.google.gerrit.server.project.DefaultPermissionBackendModule;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.H2AccountPatchReviewStore;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
@@ -367,6 +368,7 @@
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new SearchingChangeCacheImpl.Module(slave));
modules.add(new InternalAccountDirectory.Module());
+ modules.add(new DefaultPermissionBackendModule());
modules.add(new DefaultCacheFactory.Module());
modules.add(cfgInjector.getInstance(MailReceiver.Module.class));
if (emailModule != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
index 8d2289a..6c342c1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
@@ -17,6 +17,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.FluentIterable;
@@ -56,6 +57,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
@@ -125,6 +127,8 @@
private final GitRepositoryManager repoManager;
private final AllUsersName allUsers;
private final NotesMigration migration;
+ private final PatchListCache patchListCache;
+ private final PatchSetUtil psUtil;
private final String serverId;
@Inject
@@ -132,10 +136,14 @@
GitRepositoryManager repoManager,
AllUsersName allUsers,
NotesMigration migration,
+ PatchListCache patchListCache,
+ PatchSetUtil psUtil,
@GerritServerId String serverId) {
this.repoManager = repoManager;
this.allUsers = allUsers;
this.migration = migration;
+ this.patchListCache = patchListCache;
+ this.psUtil = psUtil;
this.serverId = serverId;
}
@@ -500,4 +508,35 @@
return COMMENT_ORDER.sortedCopy(
FluentIterable.from(comments).transform(plc -> plc.asComment(serverId)));
}
+
+ public void publish(
+ ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag)
+ throws OrmException {
+ ChangeNotes notes = ctx.getNotes();
+ checkArgument(notes != null);
+ if (drafts.isEmpty()) {
+ return;
+ }
+
+ Map<PatchSet.Id, PatchSet> patchSets =
+ psUtil.getAsMap(
+ ctx.getDb(), notes, drafts.stream().map(d -> psId(notes, d)).collect(toSet()));
+ for (Comment d : drafts) {
+ PatchSet ps = patchSets.get(psId(notes, d));
+ if (ps == null) {
+ throw new OrmException("patch set " + ps + " not found");
+ }
+ d.writtenOn = ctx.getWhen();
+ d.tag = tag;
+ // Draft may have been created by a different real user; copy the current real user. (Only
+ // applies to X-Gerrit-RunAs, since modifying drafts via on_behalf_of is not allowed.)
+ ctx.getUser().updateRealAccountId(d::setRealAuthor);
+ setCommentRevId(d, patchListCache, notes.getChange(), ps);
+ }
+ putComments(ctx.getDb(), ctx.getUpdate(psId), PatchLineComment.Status.PUBLISHED, drafts);
+ }
+
+ private static PatchSet.Id psId(ChangeNotes notes, Comment c) {
+ return new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java
index ab942ca..c23d990 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/PatchSetUtil.java
@@ -16,11 +16,17 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+import static com.google.gerrit.server.ChangeUtil.PS_ID_ORDER;
import static com.google.gerrit.server.notedb.PatchSetState.DRAFT;
import static com.google.gerrit.server.notedb.PatchSetState.PUBLISHED;
+import static java.util.function.Function.identity;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.RevId;
@@ -36,6 +42,7 @@
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -63,8 +70,7 @@
public ImmutableCollection<PatchSet> byChange(ReviewDb db, ChangeNotes notes)
throws OrmException {
if (!migration.readChanges()) {
- return ChangeUtil.PS_ID_ORDER.immutableSortedCopy(
- db.patchSets().byChange(notes.getChangeId()));
+ return PS_ID_ORDER.immutableSortedCopy(db.patchSets().byChange(notes.getChangeId()));
}
return notes.load().getPatchSets().values();
}
@@ -73,8 +79,7 @@
throws OrmException {
if (!migration.readChanges()) {
ImmutableMap.Builder<PatchSet.Id, PatchSet> result = ImmutableMap.builder();
- for (PatchSet ps :
- ChangeUtil.PS_ID_ORDER.sortedCopy(db.patchSets().byChange(notes.getChangeId()))) {
+ for (PatchSet ps : PS_ID_ORDER.sortedCopy(db.patchSets().byChange(notes.getChangeId()))) {
result.put(ps.getId(), ps);
}
return result.build();
@@ -82,6 +87,17 @@
return notes.load().getPatchSets();
}
+ public ImmutableMap<PatchSet.Id, PatchSet> getAsMap(
+ ReviewDb db, ChangeNotes notes, Set<PatchSet.Id> patchSetIds) throws OrmException {
+ if (!migration.readChanges()) {
+ patchSetIds = Sets.filter(patchSetIds, p -> p.getParentKey().equals(notes.getChangeId()));
+ return Streams.stream(db.patchSets().get(patchSetIds))
+ .sorted(PS_ID_ORDER)
+ .collect(toImmutableMap(PatchSet::getId, identity()));
+ }
+ return ImmutableMap.copyOf(Maps.filterKeys(notes.load().getPatchSets(), patchSetIds::contains));
+ }
+
public PatchSet insert(
ReviewDb db,
RevWalk rw,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
index 5ca9e19..cbaae1e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -147,6 +147,7 @@
public static final String DEFAULT_LABEL = "star";
public static final String IGNORE_LABEL = "ignore";
+ public static final String MUTE_LABEL = "mute";
public static final ImmutableSortedSet<String> DEFAULT_LABELS =
ImmutableSortedSet.of(DEFAULT_LABEL);
@@ -341,6 +342,48 @@
}
}
+ public void ignore(Account.Id accountId, Project.NameKey project, Change.Id changeId)
+ throws OrmException {
+ star(accountId, project, changeId, ImmutableSet.of(IGNORE_LABEL), ImmutableSet.of());
+ }
+
+ public void unignore(Account.Id accountId, Project.NameKey project, Change.Id changeId)
+ throws OrmException {
+ star(accountId, project, changeId, ImmutableSet.of(), ImmutableSet.of(IGNORE_LABEL));
+ }
+
+ public boolean isIgnoredBy(Change.Id changeId, Account.Id accountId) throws OrmException {
+ return byChange(changeId, IGNORE_LABEL).contains(accountId);
+ }
+
+ private static String getMuteLabel(Change change) {
+ return MUTE_LABEL + "/" + change.currentPatchSetId().get();
+ }
+
+ public void mute(Account.Id accountId, Project.NameKey project, Change change)
+ throws OrmException {
+ star(
+ accountId,
+ project,
+ change.getId(),
+ ImmutableSet.of(getMuteLabel(change)),
+ ImmutableSet.of());
+ }
+
+ public void unmute(Account.Id accountId, Project.NameKey project, Change change)
+ throws OrmException {
+ star(
+ accountId,
+ project,
+ change.getId(),
+ ImmutableSet.of(),
+ ImmutableSet.of(getMuteLabel(change)));
+ }
+
+ public boolean isMutedBy(Change change, Account.Id accountId) throws OrmException {
+ return byChange(change.getId(), getMuteLabel(change)).contains(accountId);
+ }
+
private static StarRef readLabels(Repository repo, String refName) throws IOException {
Ref ref = repo.exactRef(refName);
if (ref == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 937d1eb..c6fc67e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -57,11 +57,13 @@
import com.google.gerrit.server.change.GetHashtags;
import com.google.gerrit.server.change.GetPastAssignees;
import com.google.gerrit.server.change.GetTopic;
+import com.google.gerrit.server.change.Ignore;
import com.google.gerrit.server.change.Index;
import com.google.gerrit.server.change.ListChangeComments;
import com.google.gerrit.server.change.ListChangeDrafts;
import com.google.gerrit.server.change.ListChangeRobotComments;
import com.google.gerrit.server.change.Move;
+import com.google.gerrit.server.change.Mute;
import com.google.gerrit.server.change.PostHashtags;
import com.google.gerrit.server.change.PostReviewers;
import com.google.gerrit.server.change.PublishDraftPatchSet;
@@ -75,6 +77,8 @@
import com.google.gerrit.server.change.Revisions;
import com.google.gerrit.server.change.SubmittedTogether;
import com.google.gerrit.server.change.SuggestChangeReviewers;
+import com.google.gerrit.server.change.Unignore;
+import com.google.gerrit.server.change.Unmute;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.update.UpdateException;
@@ -128,6 +132,10 @@
private final Move move;
private final PutPrivate putPrivate;
private final DeletePrivate deletePrivate;
+ private final Ignore ignore;
+ private final Unignore unignore;
+ private final Mute mute;
+ private final Unmute unmute;
@Inject
ChangeApiImpl(
@@ -165,6 +173,10 @@
Move move,
PutPrivate putPrivate,
DeletePrivate deletePrivate,
+ Ignore ignore,
+ Unignore unignore,
+ Mute mute,
+ Unmute unmute,
@Assisted ChangeResource change) {
this.changeApi = changeApi;
this.revert = revert;
@@ -200,6 +212,10 @@
this.move = move;
this.putPrivate = putPrivate;
this.deletePrivate = deletePrivate;
+ this.ignore = ignore;
+ this.unignore = unignore;
+ this.mute = mute;
+ this.unmute = unmute;
this.change = change;
}
@@ -586,4 +602,22 @@
throw new RestApiException("Cannot index change", e);
}
}
+
+ @Override
+ public void ignore(boolean ignore) throws RestApiException {
+ if (ignore) {
+ this.ignore.apply(change, new Ignore.Input());
+ } else {
+ unignore.apply(change, new Unignore.Input());
+ }
+ }
+
+ @Override
+ public void mute(boolean mute) throws RestApiException {
+ if (mute) {
+ this.mute.apply(change, new Mute.Input());
+ } else {
+ unmute.apply(change, new Unmute.Input());
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/config/ServerImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/config/ServerImpl.java
index d3c5135..21b42dd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/config/ServerImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/config/ServerImpl.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.api.config;
import com.google.gerrit.common.Version;
+import com.google.gerrit.extensions.api.config.AccessCheckInfo;
+import com.google.gerrit.extensions.api.config.AccessCheckInput;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
import com.google.gerrit.extensions.api.config.Server;
@@ -22,6 +24,7 @@
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.ServerInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.config.CheckAccess;
import com.google.gerrit.server.config.CheckConsistency;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.GetDiffPreferences;
@@ -29,6 +32,8 @@
import com.google.gerrit.server.config.GetServerInfo;
import com.google.gerrit.server.config.SetDiffPreferences;
import com.google.gerrit.server.config.SetPreferences;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -43,6 +48,7 @@
private final SetDiffPreferences setDiffPreferences;
private final GetServerInfo getServerInfo;
private final Provider<CheckConsistency> checkConsistency;
+ private final Provider<CheckAccess> checkAccess;
@Inject
ServerImpl(
@@ -51,13 +57,15 @@
GetDiffPreferences getDiffPreferences,
SetDiffPreferences setDiffPreferences,
GetServerInfo getServerInfo,
- Provider<CheckConsistency> checkConsistency) {
+ Provider<CheckConsistency> checkConsistency,
+ Provider<CheckAccess> checkAccess) {
this.getPreferences = getPreferences;
this.setPreferences = setPreferences;
this.getDiffPreferences = getDiffPreferences;
this.setDiffPreferences = setDiffPreferences;
this.getServerInfo = getServerInfo;
this.checkConsistency = checkConsistency;
+ this.checkAccess = checkAccess;
}
@Override
@@ -120,4 +128,13 @@
throw new RestApiException("Cannot check consistency", e);
}
}
+
+ @Override
+ public AccessCheckInfo checkAccess(AccessCheckInput in) throws RestApiException {
+ try {
+ return checkAccess.get().apply(new ConfigResource(), in);
+ } catch (OrmException | IOException | PermissionBackendException e) {
+ throw new RestApiException("Cannot check access", e);
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index 4724ea1..4dc7d36 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -514,6 +514,11 @@
if (user.isIdentifiedUser()) {
Collection<String> stars = cd.stars(user.getAccountId());
out.starred = stars.contains(StarredChangesUtil.DEFAULT_LABEL) ? true : null;
+ out.muted =
+ stars.contains(
+ StarredChangesUtil.MUTE_LABEL + "/" + cd.currentPatchSet().getPatchSetId())
+ ? true
+ : null;
if (!stars.isEmpty()) {
out.stars = stars;
}
@@ -521,7 +526,11 @@
if (in.getStatus().isOpen() && has(REVIEWED) && user.isIdentifiedUser()) {
Account.Id accountId = user.getAccountId();
- out.reviewed = cd.reviewedBy().contains(accountId) ? true : null;
+ if (out.muted != null) {
+ out.reviewed = true;
+ } else {
+ out.reviewed = cd.reviewedBy().contains(accountId) ? true : null;
+ }
}
out.labels = labelsFor(perm, ctl, cd, has(LABELS), has(DETAILED_LABELS));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java
new file mode 100644
index 0000000..83ab811
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Ignore.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.StarredChangesUtil;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class Ignore
+ implements RestModifyView<ChangeResource, Ignore.Input>, UiAction<ChangeResource> {
+ private static final Logger log = LoggerFactory.getLogger(Ignore.class);
+
+ public static class Input {}
+
+ private final Provider<IdentifiedUser> self;
+ private final StarredChangesUtil stars;
+
+ @Inject
+ Ignore(Provider<IdentifiedUser> self, StarredChangesUtil stars) {
+ this.self = self;
+ this.stars = stars;
+ }
+
+ @Override
+ public Description getDescription(ChangeResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Ignore")
+ .setTitle("Ignore the change")
+ .setVisible(!rsrc.isUserOwner() && !isIgnored(rsrc));
+ }
+
+ @Override
+ public Response<String> apply(ChangeResource rsrc, Input input) throws RestApiException {
+ try {
+ if (rsrc.isUserOwner() || isIgnored(rsrc)) {
+ // early exit for own changes and already ignored changes
+ return Response.ok("");
+ }
+ stars.ignore(self.get().getAccountId(), rsrc.getProject(), rsrc.getChange().getId());
+ } catch (OrmException e) {
+ throw new RestApiException("failed to ignore change", e);
+ }
+ return Response.ok("");
+ }
+
+ private boolean isIgnored(ChangeResource rsrc) {
+ try {
+ return stars.isIgnoredBy(rsrc.getChange().getId(), self.get().getAccountId());
+ } catch (OrmException e) {
+ log.error("failed to check ignored star", e);
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 6dd4570..5aee90c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -87,6 +87,10 @@
post(CHANGE_KIND, "move").to(Move.class);
put(CHANGE_KIND, "private").to(PutPrivate.class);
delete(CHANGE_KIND, "private").to(DeletePrivate.class);
+ put(CHANGE_KIND, "ignore").to(Ignore.class);
+ put(CHANGE_KIND, "unignore").to(Unignore.class);
+ put(CHANGE_KIND, "mute").to(Mute.class);
+ put(CHANGE_KIND, "unmute").to(Unmute.class);
post(CHANGE_KIND, "reviewers").to(PostReviewers.class);
get(CHANGE_KIND, "suggest_reviewers").to(SuggestChangeReviewers.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mute.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mute.java
new file mode 100644
index 0000000..d14fec8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mute.java
@@ -0,0 +1,85 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.StarredChangesUtil;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class Mute implements RestModifyView<ChangeResource, Mute.Input>, UiAction<ChangeResource> {
+ private static final Logger log = LoggerFactory.getLogger(Mute.class);
+
+ public static class Input {}
+
+ private final Provider<IdentifiedUser> self;
+ private final StarredChangesUtil stars;
+
+ @Inject
+ Mute(Provider<IdentifiedUser> self, StarredChangesUtil stars) {
+ this.self = self;
+ this.stars = stars;
+ }
+
+ @Override
+ public Description getDescription(ChangeResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Mute")
+ .setTitle("Mute the change to unhighlight it in the dashboard")
+ .setVisible(!rsrc.isUserOwner() && isMuteable(rsrc.getChange()));
+ }
+
+ @Override
+ public Response<String> apply(ChangeResource rsrc, Input input) throws RestApiException {
+ try {
+ if (rsrc.isUserOwner() || isMuted(rsrc.getChange())) {
+ // early exit for own changes and already muted changes
+ return Response.ok("");
+ }
+ stars.mute(self.get().getAccountId(), rsrc.getProject(), rsrc.getChange());
+ } catch (OrmException e) {
+ throw new RestApiException("failed to mute change", e);
+ }
+ return Response.ok("");
+ }
+
+ private boolean isMuted(Change change) {
+ try {
+ return stars.isMutedBy(change, self.get().getAccountId());
+ } catch (OrmException e) {
+ log.error("failed to check muted star", e);
+ }
+ return false;
+ }
+
+ private boolean isMuteable(Change change) {
+ try {
+ return !isMuted(change) && !stars.isIgnoredBy(change.getId(), self.get().getAccountId());
+ } catch (OrmException e) {
+ log.error("failed to check ignored star", e);
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 1322d9c..69aa19d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -29,7 +29,6 @@
import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -871,12 +870,9 @@
toDel.addAll(drafts.values());
break;
case PUBLISH:
- for (Comment e : drafts.values()) {
- toPublish.add(publishComment(ctx, e, ps));
- }
- break;
case PUBLISH_ALL_REVISIONS:
- publishAllRevisions(ctx, drafts, toPublish);
+ commentsUtil.publish(ctx, psId, drafts.values(), in.tag);
+ comments.addAll(drafts.values());
break;
}
ChangeUpdate u = ctx.getUpdate(psId);
@@ -1009,37 +1005,6 @@
return labels;
}
- private Comment publishComment(ChangeContext ctx, Comment c, PatchSet ps) throws OrmException {
- c.writtenOn = ctx.getWhen();
- c.tag = in.tag;
- // Draft may have been created by a different real user; copy the current
- // real user. (Only applies to X-Gerrit-RunAs, since modifying drafts via
- // on_behalf_of is not allowed.)
- ctx.getUser().updateRealAccountId(c::setRealAuthor);
- setCommentRevId(c, patchListCache, ctx.getChange(), checkNotNull(ps));
- return c;
- }
-
- private void publishAllRevisions(
- ChangeContext ctx, Map<String, Comment> drafts, List<Comment> ups) throws OrmException {
- boolean needOtherPatchSets = false;
- for (Comment c : drafts.values()) {
- if (c.key.patchSetId != psId.get()) {
- needOtherPatchSets = true;
- break;
- }
- }
- Map<PatchSet.Id, PatchSet> patchSets =
- needOtherPatchSets
- ? psUtil.byChangeAsMap(ctx.getDb(), ctx.getNotes())
- : ImmutableMap.of(psId, ps);
- for (Comment e : drafts.values()) {
- ups.add(
- publishComment(
- ctx, e, patchSets.get(new PatchSet.Id(ctx.getChange().getId(), e.key.patchSetId))));
- }
- }
-
private Map<String, Short> getAllApprovals(
LabelTypes labelTypes, Map<String, Short> current, Map<String, Short> input) {
Map<String, Short> allApprovals = new HashMap<>();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index eec0641..13d5271 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -240,6 +240,9 @@
reviewer, rsrc, ImmutableSet.of(member.getId()), null, state, notify, accountsToNotify);
}
if (!member.isActive()) {
+ if (allowByEmail && state == CC) {
+ return null;
+ }
return fail(reviewer, MessageFormat.format(ChangeMessages.get().reviewerInactive, reviewer));
}
return fail(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java
new file mode 100644
index 0000000..081fc22
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Unignore.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.StarredChangesUtil;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class Unignore
+ implements RestModifyView<ChangeResource, Unignore.Input>, UiAction<ChangeResource> {
+ private static final Logger log = LoggerFactory.getLogger(Unignore.class);
+
+ public static class Input {}
+
+ private final Provider<IdentifiedUser> self;
+ private final StarredChangesUtil stars;
+
+ @Inject
+ Unignore(Provider<IdentifiedUser> self, StarredChangesUtil stars) {
+ this.self = self;
+ this.stars = stars;
+ }
+
+ @Override
+ public Description getDescription(ChangeResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Unignore")
+ .setTitle("Unignore the change")
+ .setVisible(!rsrc.isUserOwner() && isIgnored(rsrc));
+ }
+
+ @Override
+ public Response<String> apply(ChangeResource rsrc, Input input) throws RestApiException {
+ try {
+ if (rsrc.isUserOwner() || !isIgnored(rsrc)) {
+ // early exit for own changes and not ignored changes
+ return Response.ok("");
+ }
+ stars.unignore(self.get().getAccountId(), rsrc.getProject(), rsrc.getChange().getId());
+ } catch (OrmException e) {
+ throw new RestApiException("failed to unignore change", e);
+ }
+ return Response.ok("");
+ }
+
+ private boolean isIgnored(ChangeResource rsrc) {
+ try {
+ return stars.isIgnoredBy(rsrc.getChange().getId(), self.get().getAccountId());
+ } catch (OrmException e) {
+ log.error("failed to check ignored star", e);
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Unmute.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Unmute.java
new file mode 100644
index 0000000..49b41cb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Unmute.java
@@ -0,0 +1,86 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.webui.UiAction;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.StarredChangesUtil;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class Unmute
+ implements RestModifyView<ChangeResource, Unmute.Input>, UiAction<ChangeResource> {
+ private static final Logger log = LoggerFactory.getLogger(Unmute.class);
+
+ public static class Input {}
+
+ private final Provider<IdentifiedUser> self;
+ private final StarredChangesUtil stars;
+
+ @Inject
+ Unmute(Provider<IdentifiedUser> self, StarredChangesUtil stars) {
+ this.self = self;
+ this.stars = stars;
+ }
+
+ @Override
+ public Description getDescription(ChangeResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Unmute")
+ .setTitle("Unmute the change")
+ .setVisible(!rsrc.isUserOwner() && isUnMuteable(rsrc.getChange()));
+ }
+
+ @Override
+ public Response<String> apply(ChangeResource rsrc, Input input) throws RestApiException {
+ try {
+ if (rsrc.isUserOwner() || !isMuted(rsrc.getChange())) {
+ // early exit for own changes and not muted changes
+ return Response.ok("");
+ }
+ stars.unmute(self.get().getAccountId(), rsrc.getProject(), rsrc.getChange());
+ } catch (OrmException e) {
+ throw new RestApiException("failed to unmute change", e);
+ }
+ return Response.ok("");
+ }
+
+ private boolean isMuted(Change change) {
+ try {
+ return stars.isMutedBy(change, self.get().getAccountId());
+ } catch (OrmException e) {
+ log.error("failed to check muted star", e);
+ }
+ return false;
+ }
+
+ private boolean isUnMuteable(Change change) {
+ try {
+ return isMuted(change) && !stars.isIgnoredBy(change.getId(), self.get().getAccountId());
+ } catch (OrmException e) {
+ log.error("failed to check ignored star", e);
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/CheckAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CheckAccess.java
new file mode 100644
index 0000000..a80d298
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CheckAccess.java
@@ -0,0 +1,130 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.config;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.api.config.AccessCheckInfo;
+import com.google.gerrit.extensions.api.config.AccessCheckInfo.Result;
+import com.google.gerrit.extensions.api.config.AccessCheckInput;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.permissions.RefPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+public class CheckAccess implements RestModifyView<ConfigResource, AccessCheckInput> {
+ private final Provider<IdentifiedUser> currentUser;
+ private final AccountResolver accountResolver;
+ private final Provider<ReviewDb> db;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final ProjectCache projectCache;
+ private final PermissionBackend permissionBackend;
+
+ @Inject
+ CheckAccess(
+ Provider<IdentifiedUser> currentUser,
+ AccountResolver resolver,
+ Provider<ReviewDb> db,
+ IdentifiedUser.GenericFactory userFactory,
+ ProjectCache projectCache,
+ PermissionBackend permissionBackend) {
+ this.currentUser = currentUser;
+ this.accountResolver = resolver;
+ this.db = db;
+ this.userFactory = userFactory;
+ this.projectCache = projectCache;
+ this.permissionBackend = permissionBackend;
+ }
+
+ @Override
+ public AccessCheckInfo apply(ConfigResource unused, AccessCheckInput input)
+ throws OrmException, PermissionBackendException, RestApiException, IOException {
+ permissionBackend.user(currentUser.get()).check(GlobalPermission.ADMINISTRATE_SERVER);
+
+ if (input == null) {
+ throw new BadRequestException("input is required");
+ }
+ if (Strings.isNullOrEmpty(input.account)) {
+ throw new BadRequestException("input requires 'account'");
+ }
+ if (Strings.isNullOrEmpty(input.project)) {
+ throw new BadRequestException("input requires 'project'");
+ }
+
+ Account match = accountResolver.find(db.get(), input.account);
+ if (match == null) {
+ throw new BadRequestException(String.format("cannot find account %s", input.account));
+ }
+
+ AccessCheckInfo info = new AccessCheckInfo();
+ info.result = new Result();
+
+ Project.NameKey key = new Project.NameKey(input.project);
+ if (projectCache.get(key) == null) {
+ info.result.message = String.format("project %s does not exist", key);
+ info.result.status = HttpServletResponse.SC_NOT_FOUND;
+ return info;
+ }
+
+ IdentifiedUser user = userFactory.create(match.getId());
+ try {
+ permissionBackend.user(user).project(key).check(ProjectPermission.ACCESS);
+ } catch (AuthException | PermissionBackendException e) {
+ info.result.message =
+ String.format(
+ "user %s (%s) cannot see project %s",
+ user.getNameEmail(), user.getAccount().getId(), key);
+ info.result.status = HttpServletResponse.SC_FORBIDDEN;
+ return info;
+ }
+
+ if (!Strings.isNullOrEmpty(input.ref)) {
+ try {
+ permissionBackend
+ .user(user)
+ .ref(new Branch.NameKey(key, input.ref))
+ .check(RefPermission.READ);
+ } catch (AuthException | PermissionBackendException e) {
+ info.result.status = HttpServletResponse.SC_FORBIDDEN;
+ info.result.message =
+ String.format(
+ "user %s (%s) cannot see ref %s in project %s",
+ user.getNameEmail(), user.getAccount().getId(), input.ref, key);
+ return info;
+ }
+ }
+
+ info.result.status = HttpServletResponse.SC_OK;
+ return info;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index c41d696..8eaa6ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -160,7 +160,6 @@
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.project.AccessControlModule;
import com.google.gerrit.server.project.CommentLinkProvider;
-import com.google.gerrit.server.project.DefaultPermissionBackendModule;
import com.google.gerrit.server.project.PermissionCollection;
import com.google.gerrit.server.project.ProjectCacheImpl;
import com.google.gerrit.server.project.ProjectNode;
@@ -230,7 +229,6 @@
install(new AccessControlModule());
install(new CmdLineParserModule());
- install(new DefaultPermissionBackendModule());
install(new EmailModule());
install(new ExternalIdModule());
install(new GitModule());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
index 7bf5ad5..4f93a1a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/Module.java
@@ -37,6 +37,7 @@
get(CONFIG_KIND, "version").to(GetVersion.class);
get(CONFIG_KIND, "info").to(GetServerInfo.class);
post(CONFIG_KIND, "check.consistency").to(CheckConsistency.class);
+ post(CONFIG_KIND, "check.access").to(CheckAccess.class);
get(CONFIG_KIND, "preferences").to(GetPreferences.class);
put(CONFIG_KIND, "preferences").to(SetPreferences.class);
get(CONFIG_KIND, "preferences.diff").to(GetDiffPreferences.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
index 0e62c3a..07be8fe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
@@ -32,7 +32,7 @@
import java.util.Set;
@Singleton
-class DefaultPermissionBackend extends PermissionBackend {
+public class DefaultPermissionBackend extends PermissionBackend {
private final ProjectCache projectCache;
@Inject
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackendModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackendModule.java
index 7a30863..4916353 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackendModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackendModule.java
@@ -16,18 +16,26 @@
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
/** Binds the default {@link PermissionBackend}. */
-public class DefaultPermissionBackendModule extends FactoryModule {
+public class DefaultPermissionBackendModule extends AbstractModule {
@Override
protected void configure() {
bind(PermissionBackend.class).to(DefaultPermissionBackend.class).in(Scopes.SINGLETON);
+ install(new LegacyControlsModule());
+ }
- // TODO(sop) Hide ProjectControl, RefControl, ChangeControl related bindings.
- bind(ProjectControl.GenericFactory.class);
- factory(ProjectControl.AssistedFactory.class);
- bind(ChangeControl.GenericFactory.class);
- bind(ChangeControl.Factory.class);
+ /** Binds legacy ProjectControl, RefControl, ChangeControl. */
+ public static class LegacyControlsModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ // TODO(sop) Hide ProjectControl, RefControl, ChangeControl related bindings.
+ bind(ProjectControl.GenericFactory.class);
+ factory(ProjectControl.AssistedFactory.class);
+ bind(ChangeControl.GenericFactory.class);
+ bind(ChangeControl.Factory.class);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 1ae54ee..f4bc629 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -598,6 +598,10 @@
return new SubmittablePredicate(SubmitRecord.Status.OK);
}
+ if ("ignored".equalsIgnoreCase(value)) {
+ return star("ignore");
+ }
+
try {
return status(value);
} catch (IllegalArgumentException e) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index a596ab3..f023eb5 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -1411,7 +1411,8 @@
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChangeWithStatus(repo, Change.Status.MERGED));
- insert(repo, newChangeWithStatus(repo, Change.Status.MERGED));
+ Change change3 = insert(repo, newChangeWithStatus(repo, Change.Status.MERGED));
+ Change change4 = insert(repo, newChange(repo));
gApi.accounts()
.self()
@@ -1425,16 +1426,42 @@
new StarsInput(
new HashSet<>(Arrays.asList(StarredChangesUtil.DEFAULT_LABEL, "green", "blue"))));
+ gApi.accounts()
+ .self()
+ .setStars(
+ change4.getId().toString(), new StarsInput(new HashSet<>(Arrays.asList("ignore"))));
+
// check labeled stars
assertQuery("star:red", change1);
assertQuery("star:blue", change2, change1);
- assertQuery("has:stars", change2, change1);
+ assertQuery("has:stars", change4, change2, change1);
// check default star
assertQuery("has:star", change2);
assertQuery("is:starred", change2);
assertQuery("starredby:self", change2);
assertQuery("star:" + StarredChangesUtil.DEFAULT_LABEL, change2);
+
+ // check ignored
+ assertQuery("is:ignored", change4);
+ assertQuery("-is:ignored", change3, change2, change1);
+ }
+
+ @Test
+ public void byIgnore() throws Exception {
+ TestRepository<Repo> repo = createProject("repo");
+ Account.Id user2 =
+ accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId();
+ Change change1 = insert(repo, newChange(repo), user2);
+ Change change2 = insert(repo, newChange(repo), user2);
+
+ gApi.changes().id(change1.getId().toString()).ignore(true);
+ assertQuery("is:ignored", change1);
+ assertQuery("-is:ignored", change2);
+
+ gApi.changes().id(change1.getId().toString()).ignore(false);
+ assertQuery("is:ignored");
+ assertQuery("-is:ignored", change2, change1);
}
@Test
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
index 4ab7018..bce0a0f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -57,6 +57,7 @@
import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.DiffExecutor;
+import com.google.gerrit.server.project.DefaultPermissionBackendModule;
import com.google.gerrit.server.schema.DataSourceType;
import com.google.gerrit.server.schema.H2AccountPatchReviewStore;
import com.google.gerrit.server.schema.NotesMigrationSchemaFactory;
@@ -149,6 +150,7 @@
});
bind(MetricMaker.class).to(DisabledMetricMaker.class);
install(cfgInjector.getInstance(GerritGlobalModule.class));
+ install(new DefaultPermissionBackendModule());
install(new SearchingChangeCacheImpl.Module());
factory(GarbageCollection.Factory.class);
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index a4e8e3c..f5cd9fc 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -62,6 +62,7 @@
import com.google.gerrit.server.patch.DiffExecutorModule;
import com.google.gerrit.server.plugins.PluginGuiceEnvironment;
import com.google.gerrit.server.plugins.PluginRestApiModule;
+import com.google.gerrit.server.project.DefaultPermissionBackendModule;
import com.google.gerrit.server.schema.DataSourceModule;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.DataSourceType;
@@ -317,6 +318,7 @@
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new SearchingChangeCacheImpl.Module());
modules.add(new InternalAccountDirectory.Module());
+ modules.add(new DefaultPermissionBackendModule());
modules.add(new DefaultCacheFactory.Module());
modules.add(cfgInjector.getInstance(MailReceiver.Module.class));
modules.add(new SmtpEmailSender.Module());
diff --git a/plugins/replication b/plugins/replication
index a6cba7b..8fcaee0 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit a6cba7b3ab4aa1e1ad0f0deb95078aacaa29b37d
+Subproject commit 8fcaee07b6d457b3fc6ed44d9e9d441e3cd174ac
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index 30d72cb..7c12fa2 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -53,7 +53,7 @@
"--polymer_pass",
"--jscomp_off=duplicate",
],
- language = "ECMASCRIPT5_STRICT",
+ language = "ECMASCRIPT5",
deps = [":closure_lib"],
)
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index d4c8380..d4e7ae2 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -44,6 +44,7 @@
'is:abandoned',
'is:closed',
'is:draft',
+ 'is:ignored',
'is:mergeable',
'is:merged',
'is:open',
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index f340184..dab22d6 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -443,7 +443,7 @@
O: options,
q: [
'is:open owner:self',
- 'is:open ((reviewer:self -owner:self -star:ignore) OR assignee:self)',
+ 'is:open ((reviewer:self -owner:self -is:ignored) OR assignee:self)',
'is:closed (owner:self OR reviewer:self OR assignee:self) -age:4w ' +
'limit:10',
],