Improve email footer format
Gerrit change emails include metadata to support mail filters and mail search. In HTML emails, these are included in the email using a hidden block to avoid visual clutter. However, with [1], it is possible for the footer content to change from message to message in such a way that Gmail quoting moves the delta text into a block that is not hidden. Refactor the footer generation to embed each line in a hidden block. In this way, the content remains hidden when it is changed, and mail search/filters function as before. The list of footers is defined in the email classes rather than the templates to simplify this embedding. Whitespace around footers in HTML emails is partially preserved to improve readability in the message source. [1] I42f3c4fa6fd19ceac137c99fad42952fcf7b7d6b Bug: Issue 4936 Change-Id: Ic6713a587b14a6b0ecf658f4a047675afa97a854
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2010 The Android Open Source Project
|
// Copyright (C) 2016 The Android Open Source Project
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -467,12 +467,20 @@ public abstract class ChangeEmail extends NotificationEmail {
|
|||||||
patchSetData.put("refName", patchSet.getRefName());
|
patchSetData.put("refName", patchSet.getRefName());
|
||||||
soyContext.put("patchSet", patchSetData);
|
soyContext.put("patchSet", patchSetData);
|
||||||
|
|
||||||
soyContext.put("reviewerEmails",
|
|
||||||
getEmailsByState(ReviewerStateInternal.REVIEWER));
|
|
||||||
soyContext.put("ccEmails",
|
|
||||||
getEmailsByState(ReviewerStateInternal.CC));
|
|
||||||
|
|
||||||
// TODO(wyatta): patchSetInfo
|
// TODO(wyatta): patchSetInfo
|
||||||
|
|
||||||
|
footers.add("Gerrit-MessageType: " + messageClass);
|
||||||
|
footers.add("Gerrit-Change-Id: " + change.getKey().get());
|
||||||
|
footers.add("Gerrit-Change-Number: " +
|
||||||
|
Integer.toString(change.getChangeId()));
|
||||||
|
footers.add("Gerrit-PatchSet: " + patchSet.getPatchSetId());
|
||||||
|
footers.add("Gerrit-Owner: " + getNameEmailFor(change.getOwner()));
|
||||||
|
for (String reviewer : getEmailsByState(ReviewerStateInternal.REVIEWER)) {
|
||||||
|
footers.add("Gerrit-Reviewer: " + reviewer);
|
||||||
|
}
|
||||||
|
for (String reviewer : getEmailsByState(ReviewerStateInternal.CC)) {
|
||||||
|
footers.add("Gerrit-CC: " + reviewer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getEmailsByState(ReviewerStateInternal state) {
|
private Set<String> getEmailsByState(ReviewerStateInternal state) {
|
||||||
|
|||||||
@@ -588,12 +588,19 @@ public class CommentSender extends ReplyToChangeSender {
|
|||||||
@Override
|
@Override
|
||||||
protected void setupSoyContext() {
|
protected void setupSoyContext() {
|
||||||
super.setupSoyContext();
|
super.setupSoyContext();
|
||||||
|
boolean hasComments = false;
|
||||||
try (Repository repo = getRepository()) {
|
try (Repository repo = getRepository()) {
|
||||||
soyContext.put("commentFiles", getCommentGroupsTemplateData(repo));
|
List<Map<String, Object>> files = getCommentGroupsTemplateData(repo);
|
||||||
|
soyContext.put("commentFiles", files);
|
||||||
|
hasComments = !files.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
soyContext.put("commentTimestamp", getCommentTimestamp());
|
soyContext.put("commentTimestamp", getCommentTimestamp());
|
||||||
soyContext.put("coverLetterBlocks",
|
soyContext.put("coverLetterBlocks",
|
||||||
commentBlocksToSoyData(CommentFormatter.parse(getCoverLetter())));
|
commentBlocksToSoyData(CommentFormatter.parse(getCoverLetter())));
|
||||||
|
|
||||||
|
footers.add("Gerrit-Comment-Date: " + getCommentTimestamp());
|
||||||
|
footers.add("Gerrit-HasComments: " + (hasComments ? "Yes" : "No"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLine(PatchFile fileInfo, short side, int lineNbr) {
|
private String getLine(PatchFile fileInfo, short side, int lineNbr) {
|
||||||
|
|||||||
@@ -123,5 +123,8 @@ public abstract class NotificationEmail extends OutgoingEmail {
|
|||||||
Map<String, String> branchData = new HashMap<>();
|
Map<String, String> branchData = new HashMap<>();
|
||||||
branchData.put("shortName", branch.getShortName());
|
branchData.put("shortName", branch.getShortName());
|
||||||
soyContext.put("branch", branchData);
|
soyContext.put("branch", branchData);
|
||||||
|
|
||||||
|
footers.add("Gerrit-Project: " + branch.getParentKey().get());
|
||||||
|
footers.add("Gerrit-Branch: " + branch.getShortName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,11 +48,13 @@ import java.net.MalformedURLException;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -74,6 +76,7 @@ public abstract class OutgoingEmail {
|
|||||||
protected VelocityContext velocityContext;
|
protected VelocityContext velocityContext;
|
||||||
protected Map<String, Object> soyContext;
|
protected Map<String, Object> soyContext;
|
||||||
protected Map<String, Object> soyContextEmailData;
|
protected Map<String, Object> soyContextEmailData;
|
||||||
|
protected List<String> footers;
|
||||||
protected final EmailArguments args;
|
protected final EmailArguments args;
|
||||||
protected Account.Id fromId;
|
protected Account.Id fromId;
|
||||||
protected NotifyHandling notify = NotifyHandling.ALL;
|
protected NotifyHandling notify = NotifyHandling.ALL;
|
||||||
@@ -462,8 +465,10 @@ public abstract class OutgoingEmail {
|
|||||||
|
|
||||||
protected void setupSoyContext() {
|
protected void setupSoyContext() {
|
||||||
soyContext = new HashMap<>();
|
soyContext = new HashMap<>();
|
||||||
|
footers = new ArrayList<>();
|
||||||
|
|
||||||
soyContext.put("messageClass", messageClass);
|
soyContext.put("messageClass", messageClass);
|
||||||
|
soyContext.put("footers", footers);
|
||||||
|
|
||||||
soyContextEmailData = new HashMap<>();
|
soyContextEmailData = new HashMap<>();
|
||||||
soyContextEmailData.put("settingsUrl", getSettingsUrl());
|
soyContextEmailData.put("settingsUrl", getSettingsUrl());
|
||||||
|
|||||||
@@ -19,15 +19,7 @@
|
|||||||
/**
|
/**
|
||||||
* The .ChangeFooter template will determine the contents of the footer text
|
* The .ChangeFooter template will determine the contents of the footer text
|
||||||
* that will be appended to ALL emails related to changes.
|
* that will be appended to ALL emails related to changes.
|
||||||
* @param branch
|
|
||||||
* @param ccEmails
|
|
||||||
* @param change
|
|
||||||
* @param changeId
|
|
||||||
* @param email
|
* @param email
|
||||||
* @param messageClass
|
|
||||||
* @param patchSet
|
|
||||||
* @param projectName
|
|
||||||
* @param reviewerEmails
|
|
||||||
*/
|
*/
|
||||||
{template .ChangeFooter autoescape="strict" kind="text"}
|
{template .ChangeFooter autoescape="strict" kind="text"}
|
||||||
--{sp}
|
--{sp}
|
||||||
@@ -44,18 +36,4 @@
|
|||||||
{if $email.changeUrl or $email.settingsUrl}
|
{if $email.changeUrl or $email.settingsUrl}
|
||||||
{\n}
|
{\n}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
Gerrit-MessageType: {$messageClass}{\n}
|
|
||||||
Gerrit-Change-Id: {$changeId}{\n}
|
|
||||||
Gerrit-Change-Number: {$change.changeNumber}{\n}
|
|
||||||
Gerrit-PatchSet: {$patchSet.patchSetId}{\n}
|
|
||||||
Gerrit-Project: {$projectName}{\n}
|
|
||||||
Gerrit-Branch: {$branch.shortName}{\n}
|
|
||||||
Gerrit-Owner: {$change.ownerEmail}{\n}
|
|
||||||
{foreach $reviewer in $reviewerEmails}
|
|
||||||
Gerrit-Reviewer: {$reviewer}{\n}
|
|
||||||
{/foreach}
|
|
||||||
{foreach $reviewer in $ccEmails}
|
|
||||||
Gerrit-CC: {$reviewer}{\n}
|
|
||||||
{/foreach}
|
|
||||||
{/template}
|
{/template}
|
||||||
|
|||||||
@@ -17,21 +17,9 @@
|
|||||||
{namespace com.google.gerrit.server.mail.template}
|
{namespace com.google.gerrit.server.mail.template}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param branch
|
|
||||||
* @param ccEmails
|
|
||||||
* @param change
|
|
||||||
* @param changeId
|
|
||||||
* @param email
|
* @param email
|
||||||
* @param messageClass
|
|
||||||
* @param patchSet
|
|
||||||
* @param projectName
|
|
||||||
* @param reviewerEmails
|
|
||||||
*/
|
*/
|
||||||
{template .ChangeFooterHtml autoescape="strict" kind="html"}
|
{template .ChangeFooterHtml autoescape="strict" kind="html"}
|
||||||
{let $footerStyle kind="css"}
|
|
||||||
display: none;
|
|
||||||
{/let}
|
|
||||||
|
|
||||||
{if $email.changeUrl or $email.settingsUrl}
|
{if $email.changeUrl or $email.settingsUrl}
|
||||||
<p>
|
<p>
|
||||||
{if $email.changeUrl}
|
{if $email.changeUrl}
|
||||||
@@ -44,22 +32,6 @@
|
|||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<p style="{$footerStyle}">
|
|
||||||
Gerrit-MessageType: {$messageClass}<br/>
|
|
||||||
Gerrit-Change-Id: {$changeId}<br/>
|
|
||||||
Gerrit-Change-Number: {$change.changeNumber}<br/>
|
|
||||||
Gerrit-PatchSet: {$patchSet.patchSetId}<br/>
|
|
||||||
Gerrit-Project: {$projectName}<br/>
|
|
||||||
Gerrit-Branch: {$branch.shortName}<br/>
|
|
||||||
Gerrit-Owner: {$change.ownerEmail}
|
|
||||||
{foreach $reviewer in $reviewerEmails}
|
|
||||||
Gerrit-Reviewer: {$reviewer}</br>
|
|
||||||
{/foreach}
|
|
||||||
{foreach $reviewer in $ccEmails}
|
|
||||||
Gerrit-CC: {$reviewer}</br>
|
|
||||||
{/foreach}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{if $email.changeUrl}
|
{if $email.changeUrl}
|
||||||
<div itemscope itemtype="http://schema.org/EmailMessage">
|
<div itemscope itemtype="http://schema.org/EmailMessage">
|
||||||
<div itemscope itemprop="action" itemtype="http://schema.org/ViewAction">
|
<div itemscope itemprop="action" itemtype="http://schema.org/ViewAction">
|
||||||
|
|||||||
@@ -20,14 +20,6 @@
|
|||||||
* The .CommentFooter template will determine the contents of the footer text
|
* The .CommentFooter template will determine the contents of the footer text
|
||||||
* that will be appended to emails related to a user submitting comments on
|
* that will be appended to emails related to a user submitting comments on
|
||||||
* changes.
|
* changes.
|
||||||
* @param commentFiles
|
|
||||||
* @param commentTimestamp
|
|
||||||
*/
|
*/
|
||||||
{template .CommentFooter autoescape="strict" kind="text"}
|
{template .CommentFooter autoescape="strict" kind="text"}
|
||||||
Gerrit-Comment-Date: {$commentTimestamp}
|
|
||||||
{if length($commentFiles) > 0}
|
|
||||||
Gerrit-HasComments: Yes
|
|
||||||
{else}
|
|
||||||
Gerrit-HasComments: No
|
|
||||||
{/if}
|
|
||||||
{/template}
|
{/template}
|
||||||
|
|||||||
@@ -16,21 +16,5 @@
|
|||||||
|
|
||||||
{namespace com.google.gerrit.server.mail.template}
|
{namespace com.google.gerrit.server.mail.template}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param commentFiles
|
|
||||||
* @param commentTimestamp
|
|
||||||
*/
|
|
||||||
{template .CommentFooterHtml autoescape="strict" kind="html"}
|
{template .CommentFooterHtml autoescape="strict" kind="html"}
|
||||||
{let $footerStyle kind="css"}
|
|
||||||
display: none;
|
|
||||||
{/let}
|
|
||||||
|
|
||||||
<p style="{$footerStyle}">
|
|
||||||
Gerrit-Comment-Date: {$commentTimestamp}
|
|
||||||
{if length($commentFiles) > 0}
|
|
||||||
Gerrit-HasComments: Yes
|
|
||||||
{else}
|
|
||||||
Gerrit-HasComments: No
|
|
||||||
{/if}
|
|
||||||
</p>
|
|
||||||
{/template}
|
{/template}
|
||||||
|
|||||||
@@ -20,6 +20,10 @@
|
|||||||
* The .Footer template will determine the contents of the footer text
|
* The .Footer template will determine the contents of the footer text
|
||||||
* appended to the end of all outgoing emails after the ChangeFooter and
|
* appended to the end of all outgoing emails after the ChangeFooter and
|
||||||
* CommentFooter.
|
* CommentFooter.
|
||||||
|
* @param footers
|
||||||
*/
|
*/
|
||||||
{template .Footer}
|
{template .Footer autoescape="strict" kind="text"}
|
||||||
|
{foreach $footer in $footers}
|
||||||
|
{$footer}{\n}
|
||||||
|
{/foreach}
|
||||||
{/template}
|
{/template}
|
||||||
|
|||||||
@@ -16,5 +16,14 @@
|
|||||||
|
|
||||||
{namespace com.google.gerrit.server.mail.template}
|
{namespace com.google.gerrit.server.mail.template}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param footers
|
||||||
|
*/
|
||||||
{template .FooterHtml autoescape="strict" kind="html"}
|
{template .FooterHtml autoescape="strict" kind="html"}
|
||||||
|
{\n}
|
||||||
|
{\n}
|
||||||
|
{foreach $footer in $footers}
|
||||||
|
<div style="display:none">{sp}{$footer}{sp}</div>{\n}
|
||||||
|
{/foreach}
|
||||||
|
{\n}
|
||||||
{/template}
|
{/template}
|
||||||
|
|||||||
Reference in New Issue
Block a user