summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java31
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java82
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/TopicUtil.java77
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java2
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeDeferredEvent.java24
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java9
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/DeferredSender.java46
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java18
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/TopicControl.java12
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java107
-rw-r--r--gerrit-server/src/main/resources/com/google/gerrit/server/mail/Deferred.vm45
11 files changed, 444 insertions, 9 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 4f99a17550..3fa53e53ce 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -31,6 +31,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.events.ApprovalAttribute;
import com.google.gerrit.server.events.ChangeAbandonedEvent;
+import com.google.gerrit.server.events.ChangeDeferredEvent;
import com.google.gerrit.server.events.ChangeEvent;
import com.google.gerrit.server.events.ChangeMergedEvent;
import com.google.gerrit.server.events.ChangeRestoreEvent;
@@ -98,6 +99,9 @@ public class ChangeHookRunner {
/** Filename of the change abandoned hook. */
private final File changeAbandonedHook;
+ /** Filename of the change deferred hook. */
+ private final File changeDeferredHook;
+
/** Filename of the change abandoned hook. */
private final File changeRestoredHook;
@@ -148,6 +152,7 @@ public class ChangeHookRunner {
commentAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "commentAddedHook", "comment-added")).getPath());
changeMergedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeMergedHook", "change-merged")).getPath());
changeAbandonedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeAbandonedHook", "change-abandoned")).getPath());
+ changeDeferredHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeDeferredHook", "change-deferred")).getPath());
changeRestoredHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeRestoredHook", "change-restored")).getPath());
refUpdatedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "refUpdatedHook", "ref-updated")).getPath());
}
@@ -328,6 +333,32 @@ public class ChangeHookRunner {
}
/**
+ * Fire the Change Deferred Hook.
+ *
+ * @param change The change itself.
+ * @param account The gerrit user who deferred the change.
+ * @param reason Reason for deferring the change.
+ */
+ public void doChangeDeferredHook(final Change change, final Account account, final String reason) {
+ final ChangeDeferredEvent event = new ChangeDeferredEvent();
+
+ event.change = eventFactory.asChangeAttribute(change);
+ event.deferrer = eventFactory.asAccountAttribute(account);
+ event.reason = reason;
+ fireEvent(change, event);
+
+ final List<String> args = new ArrayList<String>();
+ addArg(args, "--change", event.change.id);
+ addArg(args, "--change-url", event.change.url);
+ addArg(args, "--project", event.change.project);
+ addArg(args, "--branch", event.change.branch);
+ addArg(args, "--deferrer", getDisplayName(account));
+ addArg(args, "--reason", reason == null ? "" : reason);
+
+ runHook(openRepository(change), changeDeferredHook, args);
+ }
+
+ /**
* Fire the Change Restored Hook.
*
* @param change The change itself.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index cd837aa752..53eb9ab5c0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -41,6 +41,7 @@ import com.google.gerrit.server.git.MergeQueue;
import com.google.gerrit.server.git.ReplicationQueue;
import com.google.gerrit.server.git.StagingUtil;
import com.google.gerrit.server.mail.AbandonedSender;
+import com.google.gerrit.server.mail.DeferredSender;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.mail.RestoredSender;
import com.google.gerrit.server.mail.RevertedSender;
@@ -260,7 +261,8 @@ public class ChangeUtil {
new AtomicUpdate<Change>() {
@Override
public Change update(Change change) {
- if (change.getStatus().isOpen()
+ if ((change.getStatus().isOpen()
+ || change.getStatus() == Change.Status.DEFERRED)
&& change.currentPatchSetId().equals(patchSetId)) {
change.setStatus(Change.Status.ABANDONED);
ChangeUtil.updated(change);
@@ -298,6 +300,79 @@ public class ChangeUtil {
hooks.doChangeAbandonedHook(updatedChange, user.getAccount(), message);
}
+ public static void defer(final PatchSet.Id patchSetId,
+ final IdentifiedUser user, final String message, final ReviewDb db,
+ final DeferredSender.Factory senderFactory,
+ final ChangeHookRunner hooks) throws NoSuchChangeException,
+ InvalidChangeOperationException, EmailException, OrmException {
+ defer(patchSetId, user, message, db, senderFactory, hooks, true);
+ }
+
+ public static void defer(final PatchSet.Id patchSetId,
+ final IdentifiedUser user, final String message, final ReviewDb db,
+ final DeferredSender.Factory senderFactory,
+ final ChangeHookRunner hooks, final boolean sendMail) throws NoSuchChangeException,
+ InvalidChangeOperationException, EmailException, OrmException {
+ final Change.Id changeId = patchSetId.getParentKey();
+ final PatchSet patch = db.patchSets().get(patchSetId);
+ if (patch == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ final ChangeMessage cmsg =
+ new ChangeMessage(new ChangeMessage.Key(changeId, ChangeUtil
+ .messageUUID(db)), user.getAccountId());
+ final StringBuilder msgBuf =
+ new StringBuilder("Patch Set " + patchSetId.get() + ": Deferred");
+ if (message != null && message.length() > 0) {
+ msgBuf.append("\n\n");
+ msgBuf.append(message);
+ }
+ cmsg.setMessage(msgBuf.toString());
+
+ final Change updatedChange = db.changes().atomicUpdate(changeId,
+ new AtomicUpdate<Change>() {
+ @Override
+ public Change update(Change change) {
+ if ((change.getStatus().isOpen()
+ || change.getStatus() == Change.Status.ABANDONED)
+ && change.currentPatchSetId().equals(patchSetId)) {
+ change.setStatus(Change.Status.DEFERRED);
+ ChangeUtil.updated(change);
+ return change;
+ } else {
+ return null;
+ }
+ }
+ });
+
+ if (updatedChange == null) {
+ throw new InvalidChangeOperationException(
+ "Change is no longer open or patchset is not latest");
+ }
+
+ db.changeMessages().insert(Collections.singleton(cmsg));
+
+ final List<PatchSetApproval> approvals =
+ db.patchSetApprovals().byChange(changeId).toList();
+ for (PatchSetApproval a : approvals) {
+ a.cache(updatedChange);
+ }
+ db.patchSetApprovals().update(approvals);
+
+ if (senderFactory != null) {
+ // Email the reviewers
+ final DeferredSender cm = senderFactory.create(updatedChange);
+ cm.setFrom(user.getAccountId());
+ cm.setChangeMessage(cmsg);
+ cm.send();
+ } else {
+ log.error("Cannot send email when deferring a change.");
+ }
+
+ hooks.doChangeDeferredHook(updatedChange, user.getAccount(), message);
+ }
+
public static void revert(final PatchSet.Id patchSetId,
final IdentifiedUser user, final String message, final ReviewDb db,
final RevertedSender.Factory revertedSenderFactory,
@@ -460,7 +535,8 @@ public class ChangeUtil {
new AtomicUpdate<Change>() {
@Override
public Change update(Change change) {
- if (change.getStatus() == Change.Status.ABANDONED
+ if ((change.getStatus() == Change.Status.ABANDONED ||
+ change.getStatus() == Change.Status.DEFERRED)
&& change.currentPatchSetId().equals(patchSetId)) {
change.setStatus(Change.Status.NEW);
ChangeUtil.updated(change);
@@ -473,7 +549,7 @@ public class ChangeUtil {
if (updatedChange == null) {
throw new InvalidChangeOperationException(
- "Change is not abandoned or patchset is not latest");
+ "Change is not abandoned/deferred or patchset is not latest");
}
db.changeMessages().insert(Collections.singleton(cmsg));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/TopicUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/TopicUtil.java
index 4e1b6360d1..deb67d826b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/TopicUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/TopicUtil.java
@@ -45,6 +45,7 @@ import com.google.gerrit.server.project.NoSuchRefException;
import com.google.gerrit.server.project.NoSuchTopicException;
import com.google.gerrit.server.project.TopicControl;
import com.google.gerrit.server.mail.AbandonedSender;
+import com.google.gerrit.server.mail.DeferredSender;
import com.google.gerrit.server.mail.AddReviewerSender;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.mail.RestoredSender;
@@ -287,6 +288,79 @@ public static ChangeSetApproval createStagingApproval(
}
}
+ public static void defer(final ChangeSet.Id changeSetId,
+ final IdentifiedUser user, final String message, final ReviewDb db,
+ final DeferredSender.Factory deferredSenderFactory,
+ final ChangeHookRunner hooks) throws NoSuchTopicException,
+ NoSuchChangeException, InvalidChangeOperationException,
+ EmailException, OrmException {
+ final Topic.Id topicId = changeSetId.getParentKey();
+ final ChangeSet changeSet = db.changeSets().get(changeSetId);
+ if (changeSet == null) {
+ throw new NoSuchTopicException(topicId);
+ }
+
+ final TopicMessage tmsg =
+ new TopicMessage(new TopicMessage.Key(topicId, ChangeUtil
+ .messageUUID(db)), user.getAccountId());
+ final StringBuilder msgBuf =
+ new StringBuilder("Change Set " + changeSetId.get() + ": Deferred");
+ if (message != null && message.length() > 0) {
+ msgBuf.append("\n\n");
+ msgBuf.append(message);
+ }
+ tmsg.setMessage(msgBuf.toString());
+
+ final Topic updatedTopic = db.topics().atomicUpdate(topicId,
+ new AtomicUpdate<Topic>() {
+ @Override
+ public Topic update(Topic topic) {
+ if (topic.getStatus().isOpen()
+ && topic.currentChangeSetId().equals(changeSetId)) {
+ topic.setStatus(Change.Status.DEFERRED);
+ TopicUtil.updated(topic);
+ return topic;
+ } else {
+ return null;
+ }
+ }
+ });
+
+ Change lastChange = null;
+ if (updatedTopic == null) {
+ throw new InvalidChangeOperationException(
+ "Topic is no longer open or changeset is not latest");
+ } else {
+ // Defer the changes belonging to the Topic
+ //
+ List<Change> toDefer = db.changes().byTopicOpenAll(topicId).toList();
+ for (Change c : toDefer) {
+ ChangeUtil.defer(c.currentPatchSetId(), user, message, db,
+ deferredSenderFactory, hooks, false);
+ }
+ lastChange = toDefer.get(toDefer.size() - 1);
+ }
+
+ db.topicMessages().insert(Collections.singleton(tmsg));
+
+ final List<ChangeSetApproval> approvals =
+ db.changeSetApprovals().byTopic(topicId).toList();
+ for (ChangeSetApproval a : approvals) {
+ a.cache(updatedTopic);
+ }
+ db.changeSetApprovals().update(approvals);
+
+ // Email the reviewers
+ // TODO Topic support
+ // Meanwhile, sending mails in "behalf" of the last change of the topic
+ if (lastChange != null) {
+ final DeferredSender cm = deferredSenderFactory.create(lastChange);
+ cm.setFrom(user.getAccountId());
+ cm.setTopicMessage(tmsg);
+ cm.send();
+ }
+ }
+
public static void revert(final ChangeSet.Id changeSetId,
final IdentifiedUser user, final String message, final ReviewDb db,
final RevertedSender.Factory revertedSenderFactory,
@@ -560,10 +634,11 @@ public static ChangeSetApproval createStagingApproval(
final AbstractEntity.Status tStatus = t.getStatus();
if (t.getTopic().equals(topicName)) {
if (tStatus.equals(AbstractEntity.Status.ABANDONED) ||
+ tStatus.equals(AbstractEntity.Status.DEFERRED) ||
tStatus.equals(AbstractEntity.Status.MERGED)) continue;
// If we don't have a mess in our DB, we must have only
// one topic with the same String in a different status than
- // MERGED or ABANDONED
+ // MERGED, ABANDONED or DEFERRED
//
else return t;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index b3edf0dd4e..8c79a46a0d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -30,6 +30,7 @@ import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.git.StagingMergeDelegate;
import com.google.gerrit.server.git.SubmitMergeDelegate;
import com.google.gerrit.server.mail.AbandonedSender;
+import com.google.gerrit.server.mail.DeferredSender;
import com.google.gerrit.server.mail.AddReviewerSender;
import com.google.gerrit.server.mail.BuildApprovedSender;
import com.google.gerrit.server.mail.BuildRejectedSender;
@@ -81,6 +82,7 @@ public class GerritRequestModule extends FactoryModule {
factory(PublishTopicComments.Factory.class);
factory(ReplacePatchSetSender.Factory.class);
factory(AbandonedSender.Factory.class);
+ factory(DeferredSender.Factory.class);
factory(RevertedSender.Factory.class);
factory(RestoredSender.Factory.class);
factory(CommentSender.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeDeferredEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeDeferredEvent.java
new file mode 100644
index 0000000000..8edec816d1
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/ChangeDeferredEvent.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2010 The Android Open Source Project,
+// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+//
+// 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.events;
+
+public class ChangeDeferredEvent extends ChangeEvent {
+ public final String type = "change-deferred";
+ public ChangeAttribute change;
+ public PatchSetAttribute patchSet;
+ public AccountAttribute deferrer;
+ public String reason;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 893cca64dc..b5fc953486 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -955,6 +955,11 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
reject(cmd, "change " + change.getId() + " closed");
return false;
}
+ if (change.getStatus().equals(AbstractEntity.Status.DEFERRED)
+ && topic == null) {
+ reject(cmd, "change " + change.getId() + " closed");
+ return false;
+ }
Topic.Id topicId = topic != null ? topic.getId() : null;
ChangeSet.Id csId = topic != null ? topic.currentChangeSetId() : null;
@@ -986,6 +991,10 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
reject(cmd, "change " + change.getId() + " closed");
return false;
}
+ if (change.getStatus().equals(AbstractEntity.Status.DEFERRED) && !topic) {
+ reject(cmd, "change " + change.getId() + " closed");
+ return false;
+ }
if (toReplace.containsKey(change.getId())) {
reject(cmd, "duplicate request");
return false;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeferredSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeferredSender.java
new file mode 100644
index 0000000000..04a4a4520b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeferredSender.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project,
+// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+//
+// 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.mail;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/** Send notice about a change being deferred by its owner. */
+public class DeferredSender extends ReplyToChangeSender {
+ public static interface Factory {
+ DeferredSender create(Change change);
+ }
+
+ @Inject
+ public DeferredSender(EmailArguments ea, @Assisted Change c) {
+ super(ea, c, "defer");
+ }
+
+ @Override
+ protected void init() throws EmailException {
+ super.init();
+
+ ccAllApprovals();
+ bccStarredBy();
+ bccWatchesNotifyAllComments();
+ }
+
+ @Override
+ protected void formatChange() throws EmailException {
+ appendText(velocifyFile("Deferred.vm"));
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index c9652fef87..ce9023df04 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -166,9 +166,25 @@ public class ChangeControl {
&& (change.getStatus() != Change.Status.INTEGRATING);
}
+ /** Can this user defer this change? */
+ public boolean canDefer() {
+ if (change.getTopicId() != null) return false;
+ boolean userCan = isOwner() // owner (aka creator) of the change can defer
+ || getRefControl().isOwner() // branch owner can defer
+ || getProjectControl().isOwner() // project owner can defer
+ || getCurrentUser().isAdministrator() // site administers are god
+ ;
+
+ // Cannot defer changes that are already processed by the continuous
+ // integration system.
+ return userCan
+ && (change.getStatus() != Change.Status.INTEGRATING);
+ }
+
/** Can this user restore this change? */
public boolean canRestore() {
- return canAbandon(); // Anyone who can abandon the change can restore it back
+ // Anyone who can abandon or defer the change can restore it back
+ return canAbandon() || canDefer();
}
/** All value ranges of any allowed label permission. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/TopicControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/TopicControl.java
index d9cf3a964f..ba55b44af9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/TopicControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/TopicControl.java
@@ -152,9 +152,19 @@ public class TopicControl {
;
}
+ /** Can this user defer this topic? */
+ public boolean canDefer() {
+ return isOwner() // owner (aka creator) of the change can defer
+ || getRefControl().isOwner() // branch owner can defer
+ || getProjectControl().isOwner() // project owner can defer
+ || getCurrentUser().isAdministrator() // site administers are god
+ ;
+ }
+
/** Can this user restore this topic? */
public boolean canRestore() {
- return canAbandon(); // Anyone who can abandon the change can restore it back
+ // Anyone who can abandon or defer the change can restore it back
+ return canAbandon() || canDefer();
}
/** All value ranges of any allowed label permission. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
index b31bf655de..3c6df14a0b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
@@ -76,7 +76,8 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
@Rewrite("-status:merged")
public Predicate<ChangeData> r00_notMerged() {
return or(ChangeStatusPredicate.open(dbProvider),
- new ChangeStatusPredicate(dbProvider, Change.Status.ABANDONED));
+ new ChangeStatusPredicate(dbProvider, Change.Status.ABANDONED),
+ new ChangeStatusPredicate(dbProvider, Change.Status.DEFERRED));
}
@SuppressWarnings("unchecked")
@@ -89,6 +90,14 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
@SuppressWarnings("unchecked")
@NoCostComputation
+ @Rewrite("-status:deferred")
+ public Predicate<ChangeData> r00_notDeferred() {
+ return or(ChangeStatusPredicate.open(dbProvider),
+ new ChangeStatusPredicate(dbProvider, Change.Status.MERGED));
+ }
+
+ @SuppressWarnings("unchecked")
+ @NoCostComputation
@Rewrite("sortkey_before:z A=(age:*)")
public Predicate<ChangeData> r00_ageToSortKey(@Named("A") AgePredicate a) {
String cut = ChangeUtil.sortKey(a.getCut(), Integer.MAX_VALUE);
@@ -248,6 +257,50 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
};
}
+ @Rewrite("status:deferred P=(project:*) S=(sortkey_after:*) L=(limit:*)")
+ public Predicate<ChangeData> r10_byProjectDeferredPrev(
+ @Named("P") final ProjectPredicate p,
+ @Named("S") final SortKeyPredicate.After s,
+ @Named("L") final IntPredicate<ChangeData> l) {
+ return new PaginatedSource(40000, s.getValue(), l.intValue()) {
+ @Override
+ ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+ throws OrmException {
+ return a.byProjectClosedPrev(Change.Status.DEFERRED.getCode(), //
+ p.getValueKey(), key, limit);
+ }
+
+ @Override
+ public boolean match(ChangeData cd) throws OrmException {
+ return cd.change(dbProvider).getStatus() == Change.Status.DEFERRED
+ && p.match(cd) //
+ && s.match(cd);
+ }
+ };
+ }
+
+ @Rewrite("status:deferred P=(project:*) S=(sortkey_before:*) L=(limit:*)")
+ public Predicate<ChangeData> r10_byProjectDeferredNext(
+ @Named("P") final ProjectPredicate p,
+ @Named("S") final SortKeyPredicate.Before s,
+ @Named("L") final IntPredicate<ChangeData> l) {
+ return new PaginatedSource(40000, s.getValue(), l.intValue()) {
+ @Override
+ ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+ throws OrmException {
+ return a.byProjectClosedNext(Change.Status.DEFERRED.getCode(), //
+ p.getValueKey(), key, limit);
+ }
+
+ @Override
+ public boolean match(ChangeData cd) throws OrmException {
+ return cd.change(dbProvider).getStatus() == Change.Status.DEFERRED
+ && p.match(cd) //
+ && s.match(cd);
+ }
+ };
+ }
+
@Rewrite("status:open S=(sortkey_after:*) L=(limit:*)")
public Predicate<ChangeData> r20_byOpenPrev(
@Named("S") final SortKeyPredicate.After s,
@@ -381,11 +434,59 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
}
@SuppressWarnings("unchecked")
+ @Rewrite("status:deferred S=(sortkey_after:*) L=(limit:*)")
+ public Predicate<ChangeData> r20_byDeferredPrev(
+ @Named("S") final SortKeyPredicate.After s,
+ @Named("L") final IntPredicate<ChangeData> l) {
+ return new PaginatedSource(50000, s.getValue(), l.intValue()) {
+ {
+ init("r20_byDeferredPrev", s, l);
+ }
+
+ @Override
+ ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+ throws OrmException {
+ return a.allClosedPrev(Change.Status.DEFERRED.getCode(), key, limit);
+ }
+
+ @Override
+ public boolean match(ChangeData cd) throws OrmException {
+ return cd.change(dbProvider).getStatus() == Change.Status.DEFERRED
+ && s.match(cd);
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ @Rewrite("status:deferred S=(sortkey_before:*) L=(limit:*)")
+ public Predicate<ChangeData> r20_byDeferredNext(
+ @Named("S") final SortKeyPredicate.Before s,
+ @Named("L") final IntPredicate<ChangeData> l) {
+ return new PaginatedSource(50000, s.getValue(), l.intValue()) {
+ {
+ init("r20_byDeferredNext", s, l);
+ }
+
+ @Override
+ ResultSet<Change> scan(ChangeAccess a, String key, int limit)
+ throws OrmException {
+ return a.allClosedNext(Change.Status.DEFERRED.getCode(), key, limit);
+ }
+
+ @Override
+ public boolean match(ChangeData cd) throws OrmException {
+ return cd.change(dbProvider).getStatus() == Change.Status.DEFERRED
+ && s.match(cd);
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
@Rewrite("status:closed S=(sortkey_after:*) L=(limit:*)")
public Predicate<ChangeData> r20_byClosedPrev(
@Named("S") final SortKeyPredicate.After s,
@Named("L") final IntPredicate<ChangeData> l) {
- return or(r20_byMergedPrev(s, l), r20_byAbandonedPrev(s, l));
+ return or(r20_byMergedPrev(s, l), r20_byAbandonedPrev(s, l), r20_byDeferredPrev(s, l));
}
@SuppressWarnings("unchecked")
@@ -393,7 +494,7 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
public Predicate<ChangeData> r20_byClosedNext(
@Named("S") final SortKeyPredicate.Before s,
@Named("L") final IntPredicate<ChangeData> l) {
- return or(r20_byMergedNext(s, l), r20_byAbandonedNext(s, l));
+ return or(r20_byMergedNext(s, l), r20_byAbandonedNext(s, l), r20_byDeferredNext(s, l));
}
@SuppressWarnings("unchecked")
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Deferred.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Deferred.vm
new file mode 100644
index 0000000000..25447313f3
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Deferred.vm
@@ -0,0 +1,45 @@
+## Copyright (C) 2010 The Android Open Source Project,
+## Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+##
+## 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.
+##
+##
+## Template Type:
+## -------------
+## This is a velocity mail template, see: http://velocity.apache.org and the
+## gerrit-docs:config-mail.txt for more info on modifying gerrit mail templates.
+##
+## Template File Names and extensions:
+## ----------------------------------
+## Gerrit will use templates ending in ".vm" but will ignore templates ending
+## in ".vm.example". If a .vm template does not exist, the default internal
+## gerrit template which is the same as the .vm.example will be used. If you
+## want to override the default template, copy the .vm.example file to a .vm
+## file and edit it appropriately.
+##
+## This Template:
+## --------------
+## The Deferred.vm template will determine the contents of the email related
+## to a change being deferred. It is a ChangeEmail: see ChangeSubject.vm and
+## ChangeFooter.vm.
+##
+$fromName has deferred this change.
+
+Change subject: $change.subject
+......................................................................
+
+
+#if ($coverLetter)
+$coverLetter
+
+#end