No longer CC a user by default

Its annoying to CC a user on every change they make through the web
interface, if they performed the change then they know they did it
and don't need a carbon-copy cluttering up their inbox.

Add a user level preference setting to control whether or not we
should CC the user if we are sending an email on their behalf.
By default disable it, because this is a common complaint.

Bug: issue 311
Change-Id: I431cc0e5df34ef44114bf3e5c92c4f2d5e3f0354
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2010-07-13 11:25:11 -07:00
parent 8943adf8dc
commit 5a00b3161d
8 changed files with 94 additions and 7 deletions

View File

@@ -29,6 +29,7 @@ public interface AccountConstants extends Constants {
String contextWholeFile(); String contextWholeFile();
String showSiteHeader(); String showSiteHeader();
String useFlashClipboard(); String useFlashClipboard();
String copySelfOnEmails();
String buttonSaveChanges(); String buttonSaveChanges();
String tabPreferences(); String tabPreferences();

View File

@@ -6,6 +6,7 @@ registeredOn = Registered
accountId = Account ID accountId = Account ID
showSiteHeader = Show Site Header showSiteHeader = Show Site Header
useFlashClipboard = Use Flash Clipboard Widget useFlashClipboard = Use Flash Clipboard Widget
copySelfOnEmails = CC Me On Comments I Write
defaultContextFieldLabel = Default Context: defaultContextFieldLabel = Default Context:
maximumPageSizeFieldLabel = Maximum Page Size: maximumPageSizeFieldLabel = Maximum Page Size:
contextWholeFile = Whole File contextWholeFile = Whole File

View File

@@ -40,6 +40,7 @@ import com.google.gwtjsonrpc.client.VoidResult;
class PreferencePanel extends Composite { class PreferencePanel extends Composite {
private CheckBox showSiteHeader; private CheckBox showSiteHeader;
private CheckBox useFlashClipboard; private CheckBox useFlashClipboard;
private CheckBox copySelfOnEmails;
private ListBox defaultContext; private ListBox defaultContext;
private ListBox maximumPageSize; private ListBox maximumPageSize;
private Button save; private Button save;
@@ -66,6 +67,9 @@ class PreferencePanel extends Composite {
useFlashClipboard = new CheckBox(Util.C.useFlashClipboard()); useFlashClipboard = new CheckBox(Util.C.useFlashClipboard());
useFlashClipboard.addClickHandler(onClickSave); useFlashClipboard.addClickHandler(onClickSave);
copySelfOnEmails = new CheckBox(Util.C.copySelfOnEmails());
copySelfOnEmails.addClickHandler(onClickSave);
maximumPageSize = new ListBox(); maximumPageSize = new ListBox();
for (final short v : PAGESIZE_CHOICES) { for (final short v : PAGESIZE_CHOICES) {
maximumPageSize.addItem(Util.M.rowsPerPage(v), String.valueOf(v)); maximumPageSize.addItem(Util.M.rowsPerPage(v), String.valueOf(v));
@@ -92,7 +96,7 @@ class PreferencePanel extends Composite {
labelIdx = 0; labelIdx = 0;
fieldIdx = 1; fieldIdx = 1;
} }
final Grid formGrid = new Grid(4, 2); final Grid formGrid = new Grid(5, 2);
int row = 0; int row = 0;
formGrid.setText(row, labelIdx, ""); formGrid.setText(row, labelIdx, "");
@@ -103,6 +107,10 @@ class PreferencePanel extends Composite {
formGrid.setWidget(row, fieldIdx, useFlashClipboard); formGrid.setWidget(row, fieldIdx, useFlashClipboard);
row++; row++;
formGrid.setText(row, labelIdx, "");
formGrid.setWidget(row, fieldIdx, copySelfOnEmails);
row++;
formGrid.setText(row, labelIdx, Util.C.maximumPageSizeFieldLabel()); formGrid.setText(row, labelIdx, Util.C.maximumPageSizeFieldLabel());
formGrid.setWidget(row, fieldIdx, maximumPageSize); formGrid.setWidget(row, fieldIdx, maximumPageSize);
row++; row++;
@@ -140,6 +148,7 @@ class PreferencePanel extends Composite {
private void enable(final boolean on) { private void enable(final boolean on) {
showSiteHeader.setEnabled(on); showSiteHeader.setEnabled(on);
useFlashClipboard.setEnabled(on); useFlashClipboard.setEnabled(on);
copySelfOnEmails.setEnabled(on);
maximumPageSize.setEnabled(on); maximumPageSize.setEnabled(on);
defaultContext.setEnabled(on); defaultContext.setEnabled(on);
} }
@@ -147,6 +156,7 @@ class PreferencePanel extends Composite {
private void display(final AccountGeneralPreferences p) { private void display(final AccountGeneralPreferences p) {
showSiteHeader.setValue(p.isShowSiteHeader()); showSiteHeader.setValue(p.isShowSiteHeader());
useFlashClipboard.setValue(p.isUseFlashClipboard()); useFlashClipboard.setValue(p.isUseFlashClipboard());
copySelfOnEmails.setValue(p.isCopySelfOnEmails());
setListBox(maximumPageSize, DEFAULT_PAGESIZE, p.getMaximumPageSize()); setListBox(maximumPageSize, DEFAULT_PAGESIZE, p.getMaximumPageSize());
setListBox(defaultContext, DEFAULT_CONTEXT, p.getDefaultContext()); setListBox(defaultContext, DEFAULT_CONTEXT, p.getDefaultContext());
} }
@@ -177,6 +187,7 @@ class PreferencePanel extends Composite {
final AccountGeneralPreferences p = new AccountGeneralPreferences(); final AccountGeneralPreferences p = new AccountGeneralPreferences();
p.setShowSiteHeader(showSiteHeader.getValue()); p.setShowSiteHeader(showSiteHeader.getValue());
p.setUseFlashClipboard(useFlashClipboard.getValue()); p.setUseFlashClipboard(useFlashClipboard.getValue());
p.setCopySelfOnEmails(copySelfOnEmails.getValue());
p.setMaximumPageSize(getListBox(maximumPageSize, DEFAULT_PAGESIZE)); p.setMaximumPageSize(getListBox(maximumPageSize, DEFAULT_PAGESIZE));
p.setDefaultContext(getListBox(defaultContext, DEFAULT_CONTEXT)); p.setDefaultContext(getListBox(defaultContext, DEFAULT_CONTEXT));

View File

@@ -68,6 +68,10 @@ public final class AccountGeneralPreferences {
@Column(id = 6, length = 20, notNull = false) @Column(id = 6, length = 20, notNull = false)
protected String downloadCommand; protected String downloadCommand;
/** If true we CC the user on their own changes. */
@Column(id = 7)
protected boolean copySelfOnEmail;
public AccountGeneralPreferences() { public AccountGeneralPreferences() {
} }
@@ -135,11 +139,20 @@ public final class AccountGeneralPreferences {
} }
} }
public boolean isCopySelfOnEmails() {
return copySelfOnEmail;
}
public void setCopySelfOnEmails(boolean includeSelfOnEmail) {
copySelfOnEmail = includeSelfOnEmail;
}
public void resetToDefaults() { public void resetToDefaults() {
defaultContext = DEFAULT_CONTEXT; defaultContext = DEFAULT_CONTEXT;
maximumPageSize = DEFAULT_PAGESIZE; maximumPageSize = DEFAULT_PAGESIZE;
showSiteHeader = true; showSiteHeader = true;
useFlashClipboard = true; useFlashClipboard = true;
copySelfOnEmail = false;
downloadUrl = null; downloadUrl = null;
downloadCommand = null; downloadCommand = null;
} }

View File

@@ -19,6 +19,7 @@ import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -118,6 +119,14 @@ abstract class EmailHeader {
list.add(addr); list.add(addr);
} }
void remove(java.lang.String email) {
for (Iterator<Address> i = list.iterator(); i.hasNext();) {
if (i.next().email.equals(email)) {
i.remove();
}
}
}
@Override @Override
boolean isEmpty() { boolean isEmpty() {
return list.isEmpty(); return list.isEmpty();

View File

@@ -32,6 +32,7 @@ import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.mail.EmailHeader.AddressList;
import com.google.gerrit.server.patch.PatchList; import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry; import com.google.gerrit.server.patch.PatchListEntry;
@@ -52,6 +53,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -165,12 +167,37 @@ public abstract class OutgoingEmail {
format(); format();
if (shouldSendMessage()) { if (shouldSendMessage()) {
if (fromId != null) { if (fromId != null) {
final Account fromUser = accountCache.get(fromId).getAccount();
if (fromUser.getGeneralPreferences().isCopySelfOnEmails()) {
// If we are impersonating a user, make sure they receive a CC of // If we are impersonating a user, make sure they receive a CC of
// this message so they can always review and audit what we sent // this message so they can always review and audit what we sent
// on their behalf to others. // on their behalf to others.
// //
add(RecipientType.CC, fromId); add(RecipientType.CC, fromId);
} else if (rcptTo.remove(fromId)) {
// If they don't want a copy, but we queued one up anyway,
// drop them from the recipient lists.
//
if (rcptTo.isEmpty()) {
return;
} }
final String fromEmail = fromUser.getPreferredEmail();
for (Iterator<Address> i = smtpRcptTo.iterator(); i.hasNext();) {
if (i.next().email.equals(fromEmail)) {
i.remove();
}
}
for (EmailHeader hdr : headers.values()) {
if (hdr instanceof AddressList) {
((AddressList) hdr).remove(fromEmail);
}
}
}
}
if (change != null) { if (change != null) {
if (getChangeUrl() != null) { if (getChangeUrl() != null) {
openFooter(); openFooter();

View File

@@ -32,7 +32,7 @@ import java.util.List;
/** A version of the database schema. */ /** A version of the database schema. */
public abstract class SchemaVersion { public abstract class SchemaVersion {
/** The current schema version. */ /** The current schema version. */
private static final Class<? extends SchemaVersion> C = Schema_36.class; private static final Class<? extends SchemaVersion> C = Schema_37.class;
public static class Module extends AbstractModule { public static class Module extends AbstractModule {
@Override @Override

View File

@@ -0,0 +1,25 @@
// Copyright (C) 2010 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.schema;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class Schema_37 extends SchemaVersion {
@Inject
Schema_37(Provider<Schema_36> prior) {
super(prior);
}
}