Upgrade most change emails with HTML

Add HTML templates for change emails which do not require additional
template data to be provided in order for it to be formatted in HTML.
Introduces a HeaderHtml template counterpart to FooterHtml.

In this change, there are minimal visual deviations from the plain-text
emails. URLs are added as anchor tags. Commit messages, diffs and "cover
letter"s are set in PRE tags to preserve whitespace and to use monospace
fonts. HR tags are used instead of the text-based horizontal rules in
the text emails.

Change email types upgraded with this change:

* Abandon Change
* Delete Reviewer
* Delete Vote
* New Change
* New Patch Set
* Restore Change
* Revert Change

Change-Id: Ic1171d6288335aa69e12f076558e861934bfa150
This commit is contained in:
Wyatt Allen
2016-09-02 10:56:05 -07:00
parent 2962ec29c7
commit f94255f94e
21 changed files with 538 additions and 1 deletions

View File

@@ -100,20 +100,30 @@ public class SitePathInitializer {
chmod(0700, site.tmp_dir);
extractMailExample("Abandoned.soy");
extractMailExample("AbandonedHtml.soy");
extractMailExample("AddKey.soy");
extractMailExample("ChangeFooter.soy");
extractMailExample("ChangeFooterHtml.soy");
extractMailExample("ChangeSubject.soy");
extractMailExample("Comment.soy");
extractMailExample("CommentFooter.soy");
extractMailExample("DeleteReviewer.soy");
extractMailExample("DeleteReviewerHtml.soy");
extractMailExample("DeleteVote.soy");
extractMailExample("DeleteVoteHtml.soy");
extractMailExample("Footer.soy");
extractMailExample("FooterHtml.soy");
extractMailExample("HeaderHtml.soy");
extractMailExample("Merged.soy");
extractMailExample("NewChange.soy");
extractMailExample("NewChangeHtml.soy");
extractMailExample("RegisterNewEmail.soy");
extractMailExample("ReplacePatchSet.soy");
extractMailExample("ReplacePatchSetHtml.soy");
extractMailExample("Restored.soy");
extractMailExample("RestoredHtml.soy");
extractMailExample("Reverted.soy");
extractMailExample("RevertedHtml.soy");
if (!ui.isBatch()) {
System.err.println();

View File

@@ -51,5 +51,11 @@ public class AbandonedSender extends ReplyToChangeSender {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("Abandoned"));
appendHtml(soyHtmlTemplate("AbandonedHtml"));
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -122,6 +122,9 @@ public abstract class ChangeEmail extends NotificationEmail {
protected void format() throws EmailException {
formatChange();
appendText(textTemplate("ChangeFooter"));
if (useHtml()) {
appendHtml(soyHtmlTemplate("ChangeFooterHtml"));
}
try {
TreeSet<String> names = new TreeSet<>();
for (Account.Id who : changeData.reviewers().all()) {

View File

@@ -66,6 +66,7 @@ public class DeleteReviewerSender extends ReplyToChangeSender {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("DeleteReviewer"));
appendHtml(soyHtmlTemplate("DeleteReviewerHtml"));
}
public List<String> getReviewerNames() {
@@ -84,4 +85,9 @@ public class DeleteReviewerSender extends ReplyToChangeSender {
super.setupSoyContext();
soyContextEmailData.put("reviewerNames", getReviewerNames());
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -50,5 +50,11 @@ public class DeleteVoteSender extends ReplyToChangeSender {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("DeleteVote"));
appendHtml(soyHtmlTemplate("DeleteVoteHtml"));
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -37,22 +37,32 @@ public class MailSoyTofuProvider implements Provider<SoyTofu> {
// Note: will fail to construct the tofu object if this array is empty.
private static final String[] TEMPLATES = {
"Abandoned.soy",
"AbandonedHtml.soy",
"AddKey.soy",
"AddKeyHtml.soy",
"ChangeSubject.soy",
"ChangeFooter.soy",
"ChangeFooterHtml.soy",
"ChangeSubject.soy",
"Comment.soy",
"CommentFooter.soy",
"DeleteReviewer.soy",
"DeleteReviewerHtml.soy",
"DeleteVote.soy",
"DeleteVoteHtml.soy",
"Footer.soy",
"FooterHtml.soy",
"HeaderHtml.soy",
"Merged.soy",
"NewChange.soy",
"NewChangeHtml.soy",
"RegisterNewEmail.soy",
"ReplacePatchSet.soy",
"ReplacePatchSetHtml.soy",
"Restored.soy",
"RestoredHtml.soy",
"Reverted.soy",
"RevertedHtml.soy",
"ViewChangeButton.soy",
};
private final SitePaths site;

View File

@@ -68,6 +68,7 @@ public abstract class NewChangeSender extends ChangeEmail {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("NewChange"));
appendHtml(soyHtmlTemplate("NewChangeHtml"));
}
public List<String> getReviewerNames() {
@@ -86,4 +87,9 @@ public abstract class NewChangeSender extends ChangeEmail {
super.setupSoyContext();
soyContextEmailData.put("reviewerNames", getReviewerNames());
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -109,6 +109,9 @@ public abstract class OutgoingEmail {
}
init();
if (useHtml()) {
appendHtml(soyHtmlTemplate("HeaderHtml"));
}
format();
appendText(textTemplate("Footer"));
if (useHtml()) {

View File

@@ -73,6 +73,7 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("ReplacePatchSet"));
appendHtml(soyHtmlTemplate("ReplacePatchSetHtml"));
}
public List<String> getReviewerNames() {
@@ -94,4 +95,9 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
super.setupSoyContext();
soyContextEmailData.put("reviewerNames", getReviewerNames());
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -50,5 +50,11 @@ public class RestoredSender extends ReplyToChangeSender {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("Restored"));
appendHtml(soyHtmlTemplate("RestoredHtml"));
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -48,5 +48,11 @@ public class RevertedSender extends ReplyToChangeSender {
@Override
protected void formatChange() throws EmailException {
appendText(textTemplate("Reverted"));
appendHtml(soyHtmlTemplate("RevertedHtml"));
}
@Override
protected boolean useHtml() {
return true;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param coverLetter
* @param email
* @param fromName
*/
{template .AbandonedHtml autoescape="strict" kind="html"}
<p>
{$fromName} has abandoned this change.
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
{if $coverLetter}
<pre>{$coverLetter}</pre>
{/if}
{/template}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param branch
* @param change
* @param changeId
* @param email
* @param messageClass
* @param patchSet
* @param projectName
*/
{template .ChangeFooterHtml autoescape="strict" kind="html"}
{if $email.changeUrl or $email.settingsUrl}
<p>
{if $email.changeUrl}
To view, visit <a href="{$email.changeUrl}">this change</a>.
{/if}
{if $email.changeUrl and $email.settingsUrl}{sp}{/if}
{if $email.settingsUrl}
To unsubscribe, visit <a href="{$email.settingsUrl}">settings</a>.
{/if}
</p>
{/if}
<p style="color: #555;">
Gerrit-MessageType: {$messageClass}<br/>
Gerrit-Change-Id: {$changeId}<br/>
Gerrit-PatchSet: {$patchSet.patchSetId}<br/>
Gerrit-Project: {$projectName}<br/>
Gerrit-Branch: {$branch.shortName}<br/>
Gerrit-Owner: {$change.ownerEmail}
</p>
{/template}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param coverLetter
* @param email
* @param fromName
*/
{template .DeleteReviewerHtml autoescape="strict" kind="html"}
<p>
{$fromName} has removed{sp}
{foreach $reviewerName in $email.reviewerNames}
{if not isFirst($reviewerName)},{sp}{/if}
{$reviewerName}
{/foreach}{sp}
from this change.
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
{if $coverLetter}
<pre>{$coverLetter}</pre>
{/if}
{/template}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param coverLetter
* @param email
* @param fromName
*/
{template .DeleteVoteHtml autoescape="strict" kind="html"}
<p>
{$fromName} removed a vote on this change.
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
{if $coverLetter}
<pre>{$coverLetter}</pre>
{/if}
{/template}

View File

@@ -0,0 +1,20 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
{template .HeaderHtml autoescape="strict" kind="html"}
{/template}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param email
* @param fromName
* @param patchSet
* @param projectName
*/
{template .NewChangeHtml autoescape="strict" kind="html"}
{if $email.reviewerNames}
<p>
Hello{sp}
{foreach $reviewerName in $email.reviewerNames}
{if not isFirst($reviewerName)},{sp}{/if}
{$reviewerName}
{/foreach},
</p>
<p>
I'd like you to do a code review.
</p>
{if $email.changeUrl}
<p>
Please visit <a href="{$email.changeUrl}">this change</a> to review.
</p>
{/if}
{else}
<p>
{$fromName} has uploaded a new change for review.
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
<pre>
{$email.changeDetail}
</pre>
{if $email.sshHost}
<pre>
git pull ssh:{print '//'}{$email.sshHost}/{$projectName}
{sp}{$patchSet.refName}
</pre>
{/if}
{if $email.includeDiff}
<pre>
{$email.unifiedDiff}
</pre>
{/if}
{/template}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param email
* @param fromName
* @param patchSet
* @param projectName
*/
{template .ReplacePatchSetHtml autoescape="strict" kind="html"}
{if $email.reviewerNames}
<p>
Hello{sp}
{foreach $reviewerName in $email.reviewerNames}
{$reviewerName},{sp}
{/foreach}
</p>
<p>
I'd like you to reexamine a change.
</p>
{if $email.changeUrl}
<p>
Please visit <a href="{$email.changeUrl}">this change</a> to look at{sp}
the new patch set (#{$patchSet.patchSetId}).
</p>
{/if}
{else}
<p>
{$fromName} has uploaded a new patch set (#{$patchSet.patchSetId}).
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
<pre>
{$email.changeDetail}
</pre>
{if $email.sshHost}
<pre>
git pull ssh:{print '//'}{$email.sshHost}/{$projectName}{sp}
{$patchSet.refName}
</pre>
{/if}
{/template}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param coverLetter
* @param email
* @param fromName
*/
{template .RestoredHtml autoescape="strict" kind="html"}
<p>
{$fromName} has restored this change.
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
{if $coverLetter}
<pre>{$coverLetter}</pre>
{/if}
{/template}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* @param change
* @param coverLetter
* @param email
* @param fromName
*/
{template .RevertedHtml autoescape="strict" kind="html"}
<p>
{$fromName} has reverted this change.
</p>
{if $email.changeUrl}
<p>
{call .ViewChangeButton data="all" /}
</p>
{/if}
<p>
Change subject: {$change.subject}
</p>
<hr/>
{if $coverLetter}
<pre>{$coverLetter}</pre>
{/if}
{/template}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (C) 2016 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.
*/
{namespace com.google.gerrit.server.mail.template}
/**
* Private template to generate "View Change" buttons.
* @param email
*/
{template .ViewChangeButton private="true" autoescape="strict" kind="html"}
<a href="{$email.changeUrl}">View Change</a>
{/template}