diff options
author | Shawn O. Pearce <sop@google.com> | 2009-03-19 14:05:17 -0700 |
---|---|---|
committer | Shawn O. Pearce <sop@google.com> | 2009-03-19 14:05:17 -0700 |
commit | 20c399d0ea1acda04b3cdfbafef5109a056290a6 (patch) | |
tree | c9d8d36922657605109dcd586ab36409812c2f33 | |
parent | 46ac1fa3b65ff72988e22412f08393ff23d5fa1d (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>
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; |