summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2009-03-19 14:05:17 -0700
committerShawn O. Pearce <sop@google.com>2009-03-19 14:05:17 -0700
commit20c399d0ea1acda04b3cdfbafef5109a056290a6 (patch)
treec9d8d36922657605109dcd586ab36409812c2f33
parent46ac1fa3b65ff72988e22412f08393ff23d5fa1d (diff)
Allow users to subscribe to submitted change events
Gerrit now emails interested users when a change is submitted in a project that they care about. Signed-off-by: Shawn O. Pearce <sop@google.com>
-rw-r--r--src/main/java/com/google/gerrit/client/account/AccountConstants.java1
-rw-r--r--src/main/java/com/google/gerrit/client/account/AccountConstants.properties1
-rw-r--r--src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java28
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java12
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java4
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java2
-rw-r--r--src/main/java/com/google/gerrit/git/MergeOp.java31
-rw-r--r--src/main/java/com/google/gerrit/server/ChangeMail.java73
-rw-r--r--src/main/webapp/WEB-INF/sql/query_index.sql5
-rw-r--r--src/main/webapp/WEB-INF/sql/upgrade006_007.sql21
10 files changed, 176 insertions, 2 deletions
diff --git a/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index ff8e509cf5..afd1f2e93e 100644
--- a/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/src/main/java/com/google/gerrit/client/account/AccountConstants.java
@@ -70,6 +70,7 @@ public interface AccountConstants extends Constants {
String watchedProjectColumnEmailNotifications();
String watchedProjectColumnNewChanges();
String watchedProjectColumnAllComments();
+ String watchedProjectColumnSubmittedChanges();
String contactFieldFullName();
String contactFieldEmail();
diff --git a/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index c97ebeb9a1..66d2d9f5d5 100644
--- a/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -51,6 +51,7 @@ defaultProjectName = Project Name
watchedProjectColumnEmailNotifications = Email Notifications
watchedProjectColumnNewChanges = New Changes
watchedProjectColumnAllComments = All Comments
+watchedProjectColumnSubmittedChanges = Submitted Changes
contactFieldFullName = Full Name
contactFieldEmail = Preferred Email
diff --git a/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java b/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
index e3e55a740c..7ebd933bd8 100644
--- a/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
+++ b/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
@@ -175,11 +175,13 @@ class ProjectWatchPanel extends Composite {
fmt.setRowSpan(0, 2, 2);
DOM.setElementProperty(fmt.getElement(0, 3), "align", "center");
- fmt.setColSpan(0, 3, 2);
+ fmt.setColSpan(0, 3, 3);
table.setText(1, 0, Util.C.watchedProjectColumnNewChanges());
table.setText(1, 1, Util.C.watchedProjectColumnAllComments());
+ table.setText(1, 2, Util.C.watchedProjectColumnSubmittedChanges());
fmt.addStyleName(1, 0, S_DATA_HEADER);
fmt.addStyleName(1, 1, S_DATA_HEADER);
+ fmt.addStyleName(1, 2, S_DATA_HEADER);
}
@Override
@@ -326,12 +328,36 @@ class ProjectWatchPanel extends Composite {
notifyAllComments.setChecked(k.getWatch().isNotifyAllComments());
table.setWidget(row, 4, notifyAllComments);
}
+ {
+ final CheckBox notifySubmittedChanges = new CheckBox();
+ notifySubmittedChanges.addClickListener(new ClickListener() {
+ public void onClick(final Widget sender) {
+ final boolean oldVal = k.getWatch().isNotifySubmittedChanges();
+ k.getWatch().setNotifySubmittedChanges(notifySubmittedChanges.isChecked());
+ Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ k.getWatch().setNotifySubmittedChanges(oldVal);
+ notifySubmittedChanges.setChecked(oldVal);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ notifySubmittedChanges.setChecked(k.getWatch().isNotifySubmittedChanges());
+ table.setWidget(row, 5, notifySubmittedChanges);
+ }
final FlexCellFormatter fmt = table.getFlexCellFormatter();
fmt.addStyleName(row, 1, S_ICON_CELL);
fmt.addStyleName(row, 2, S_DATA_CELL);
fmt.addStyleName(row, 3, S_DATA_CELL);
fmt.addStyleName(row, 4, S_DATA_CELL);
+ fmt.addStyleName(row, 5, S_DATA_CELL);
setRowItem(row, k);
}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java
index b44edc2380..984a19031f 100644
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java
+++ b/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java
@@ -58,6 +58,10 @@ public final class AccountProjectWatch {
@Column
protected boolean notifyAllComments;
+ /** Automatically receive changes submitted to this project */
+ @Column
+ protected boolean notifySubmittedChanges;
+
protected AccountProjectWatch() {
}
@@ -92,4 +96,12 @@ public final class AccountProjectWatch {
public void setNotifyAllComments(final boolean a) {
notifyAllComments = a;
}
+
+ public boolean isNotifySubmittedChanges() {
+ return notifySubmittedChanges;
+ }
+
+ public void setNotifySubmittedChanges(final boolean a) {
+ notifySubmittedChanges = a;
+ }
}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java
index 0ef0015b32..2d8929bc21 100644
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java
+++ b/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java
@@ -35,4 +35,8 @@ public interface AccountProjectWatchAccess extends
@Query("WHERE notifyAllComments = true AND key.projectId = ?")
ResultSet<AccountProjectWatch> notifyAllComments(Project.Id id)
throws OrmException;
+
+ @Query("WHERE notifySubmittedChanges = true AND key.projectId = ?")
+ ResultSet<AccountProjectWatch> notifySubmittedChanges(Project.Id id)
+ throws OrmException;
}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java b/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
index 411621d68d..9b36b3346d 100644
--- a/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
+++ b/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
@@ -21,7 +21,7 @@ import com.google.gwtorm.client.Sequence;
/** The review service database schema. */
public interface ReviewDb extends Schema {
- public static final int VERSION = 6;
+ public static final int VERSION = 7;
@Relation
SchemaVersionAccess schemaVersion();
diff --git a/src/main/java/com/google/gerrit/git/MergeOp.java b/src/main/java/com/google/gerrit/git/MergeOp.java
index 352730d206..47e1ca54a3 100644
--- a/src/main/java/com/google/gerrit/git/MergeOp.java
+++ b/src/main/java/com/google/gerrit/git/MergeOp.java
@@ -15,6 +15,7 @@
package com.google.gerrit.git;
import com.google.gerrit.client.data.ApprovalType;
+import com.google.gerrit.client.reviewdb.ApprovalCategory;
import com.google.gerrit.client.reviewdb.Branch;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.ChangeApproval;
@@ -23,11 +24,14 @@ import com.google.gerrit.client.reviewdb.PatchSet;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.client.workflow.FunctionState;
+import com.google.gerrit.server.ChangeMail;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.GerritServer;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.Transaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.spearce.jgit.errors.IncorrectObjectTypeException;
import org.spearce.jgit.errors.MissingObjectException;
import org.spearce.jgit.lib.AnyObjectId;
@@ -56,6 +60,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.mail.MessagingException;
+
/**
* Merges changes in submission order into a single branch.
* <p>
@@ -71,6 +77,9 @@ import java.util.Set;
* be merged cleanly.
*/
public class MergeOp {
+ private static final Logger log =
+ LoggerFactory.getLogger(MergeOp.class);
+
private final GerritServer server;
private final PersonIdent myIdent;
private final Branch.NameKey destBranch;
@@ -462,6 +471,7 @@ public class MergeOp {
private void setMerged(Change c, ChangeMessage msg) {
final PatchSet.Id merged = c.currentPatchSetId();
+ ChangeApproval submitter=null;
for (int attempts = 0; attempts < 10; attempts++) {
c.setStatus(Change.Status.MERGED);
ChangeUtil.updated(c);
@@ -481,6 +491,12 @@ public class MergeOp {
at.getCategory().getFunction().run(at, fs);
}
for (ChangeApproval a : approvals) {
+ if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
+ if (submitter == null
+ || a.getGranted().compareTo(submitter.getGranted()) > 0) {
+ submitter = a;
+ }
+ }
a.cache(c);
}
schema.changeApprovals().update(approvals, txn);
@@ -504,6 +520,21 @@ public class MergeOp {
}
}
}
+
+ try {
+ final ChangeMail cm = new ChangeMail(server, c);
+ if (submitter != null) {
+ cm.setFrom(submitter.getAccountId());
+ }
+ cm.setReviewDb(schema);
+ cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()), schema
+ .patchSetInfo().get(c.currentPatchSetId()));
+ cm.sendMerged();
+ } catch (OrmException e){
+ log.error("Cannot send email for submitted patch set " + c.getId(), e);
+ } catch (MessagingException e){
+ log.error("Cannot send email for submitted patch set " + c.getId(), e);
+ }
}
private void setNew(Change c, ChangeMessage msg) {
diff --git a/src/main/java/com/google/gerrit/server/ChangeMail.java b/src/main/java/com/google/gerrit/server/ChangeMail.java
index b0515e96e3..e9370d9ff2 100644
--- a/src/main/java/com/google/gerrit/server/ChangeMail.java
+++ b/src/main/java/com/google/gerrit/server/ChangeMail.java
@@ -295,6 +295,35 @@ public class ChangeMail {
}
}
+ public void sendMerged() throws MessagingException {
+ if (begin("merged")) {
+ final Account a = Common.getAccountCache().get(fromId);
+ if (a == null || a.getFullName() == null || a.getFullName().length() == 0) {
+ body.append("A Gerrit user");
+ } else {
+ body.append(a.getFullName());
+ }
+
+ body.append(" has submitted change ");
+ body.append(change.getChangeId());
+ body.append(" to ");
+ body.append(change.getDest().getShortName());
+ body.append(":\n\n");
+ newChangePatchSetInfo();
+
+ if (changeUrl() != null) {
+ openFooter();
+ body.append("To view visit ");
+ body.append(changeUrl());
+ body.append("\n");
+ }
+
+ initInReplyToChange();
+ submittedTo();
+ send();
+ }
+ }
+
public void sendAbandoned() throws MessagingException {
if (begin("abandon")) {
final Account a = Common.getAccountCache().get(fromId);
@@ -427,6 +456,50 @@ public class ChangeMail {
}
}
+ private void submittedTo() throws MessagingException {
+ // Always to the owner/uploader/author/committer. These people
+ // have a vested interest in the change.
+ //
+ add(RecipientType.TO, change.getOwner());
+ if (patchSet != null) {
+ add(RecipientType.TO, patchSet.getUploader());
+ }
+ if (patchSetInfo != null) {
+ add(RecipientType.TO, patchSetInfo.getAuthor());
+ add(RecipientType.TO, patchSetInfo.getCommitter());
+ }
+ add(RecipientType.CC, reviewers);
+ add(RecipientType.CC, extraCC);
+
+ if (db == null) {
+ // We need a database handle to fetch the interest list.
+ //
+ return;
+ }
+
+ try {
+ // CC anyone else who has posted an approval mark on this change
+ //
+ for (ChangeApproval ap : db.changeApprovals().byChange(change.getId())) {
+ add(RecipientType.CC, ap.getAccountId());
+ }
+
+ // BCC anyone else who has interest in this project's changes
+ //
+ final Project.Id projectId = projectId();
+ if (projectId != null) {
+ for (AccountProjectWatch w : db.accountProjectWatches()
+ .notifySubmittedChanges(projectId)) {
+ add(RecipientType.BCC, w.getAccountId());
+ }
+ }
+ } catch (OrmException err) {
+ // Just don't CC everyone. Better to send a partial message to those
+ // we already have queued up then to fail deliver entirely to people
+ // who have a lower interest in the change.
+ }
+ }
+
private boolean begin(final String messageClass) throws MessagingException {
if (transport != null) {
msg = new MimeMessage(transport);
diff --git a/src/main/webapp/WEB-INF/sql/query_index.sql b/src/main/webapp/WEB-INF/sql/query_index.sql
index e600b52797..012a69fbc3 100644
--- a/src/main/webapp/WEB-INF/sql/query_index.sql
+++ b/src/main/webapp/WEB-INF/sql/query_index.sql
@@ -73,6 +73,11 @@ CREATE INDEX account_project_watches_ntCmt
ON account_project_watches (project_id)
WHERE notify_all_comments = 'Y';
+-- covers: notifySubmittedChanges
+CREATE INDEX account_project_watches_ntSub
+ON account_project_watches (project_id)
+WHERE notify_submitted_changes = 'Y';
+
-- *********************************************************************
-- AccountSshKeyAccess
diff --git a/src/main/webapp/WEB-INF/sql/upgrade006_007.sql b/src/main/webapp/WEB-INF/sql/upgrade006_007.sql
new file mode 100644
index 0000000000..1f2a872f25
--- /dev/null
+++ b/src/main/webapp/WEB-INF/sql/upgrade006_007.sql
@@ -0,0 +1,21 @@
+-- Upgrade: schema_version 6 to 7
+--
+
+ALTER TABLE account_project_watches ADD notify_submitted_changes CHAR(1) DEFAULT 'N' NOT NULL;
+
+UPDATE account_project_watches SET notify_submitted_changes = 'Y'
+WHERE (notify_new_changes = 'Y' OR notify_all_comments = 'Y')
+AND EXISTS (SELECT 1
+ FROM account_group_members m
+ ,projects p
+ WHERE
+ m.account_id = account_project_watches.account_id
+ AND m.group_id = p.owner_group_id
+ AND p.project_id = account_project_watches.project_id);
+
+CREATE INDEX account_project_watches_ntSub
+ON account_project_watches (project_id)
+WHERE notify_submitted_changes = 'Y';
+
+
+UPDATE schema_version SET version_nbr = 7;