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
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
index 978b87c..1d7195b 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/SitePathInitializer.java
@@ -100,20 +100,30 @@
     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();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
index eafa74d..a828098 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
@@ -51,5 +51,11 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("Abandoned"));
+    appendHtml(soyHtmlTemplate("AbandonedHtml"));
+  }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index b310feb..cbd2ec9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -122,6 +122,9 @@
   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()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteReviewerSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteReviewerSender.java
index 8a26a10..008f343 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteReviewerSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteReviewerSender.java
@@ -66,6 +66,7 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("DeleteReviewer"));
+    appendHtml(soyHtmlTemplate("DeleteReviewerHtml"));
   }
 
   public List<String> getReviewerNames() {
@@ -84,4 +85,9 @@
     super.setupSoyContext();
     soyContextEmailData.put("reviewerNames", getReviewerNames());
   }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteVoteSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteVoteSender.java
index 66f8123..214c655 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteVoteSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/DeleteVoteSender.java
@@ -50,5 +50,11 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("DeleteVote"));
+    appendHtml(soyHtmlTemplate("DeleteVoteHtml"));
+  }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailSoyTofuProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailSoyTofuProvider.java
index 5e0d8b6..9aef496 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailSoyTofuProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailSoyTofuProvider.java
@@ -37,22 +37,32 @@
   // 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;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
index a7eb0af..6adbdb4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
@@ -68,6 +68,7 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("NewChange"));
+    appendHtml(soyHtmlTemplate("NewChangeHtml"));
   }
 
   public List<String> getReviewerNames() {
@@ -86,4 +87,9 @@
     super.setupSoyContext();
     soyContextEmailData.put("reviewerNames", getReviewerNames());
   }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
index 78fe631..1ea84c6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
@@ -109,6 +109,9 @@
     }
 
     init();
+    if (useHtml()) {
+      appendHtml(soyHtmlTemplate("HeaderHtml"));
+    }
     format();
     appendText(textTemplate("Footer"));
     if (useHtml()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
index d86aed3..916e0da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
@@ -73,6 +73,7 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("ReplacePatchSet"));
+    appendHtml(soyHtmlTemplate("ReplacePatchSetHtml"));
   }
 
   public List<String> getReviewerNames() {
@@ -94,4 +95,9 @@
     super.setupSoyContext();
     soyContextEmailData.put("reviewerNames", getReviewerNames());
   }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java
index 5b6d9bc..10564cf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java
@@ -50,5 +50,11 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("Restored"));
+    appendHtml(soyHtmlTemplate("RestoredHtml"));
+  }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java
index 7d1690c..e383d31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RevertedSender.java
@@ -48,5 +48,11 @@
   @Override
   protected void formatChange() throws EmailException {
     appendText(textTemplate("Reverted"));
+    appendHtml(soyHtmlTemplate("RevertedHtml"));
+  }
+
+  @Override
+  protected boolean useHtml() {
+    return true;
   }
 }
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
new file mode 100644
index 0000000..0f16890
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
new file mode 100644
index 0000000..6d04ac3
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
new file mode 100644
index 0000000..2985ef8
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
new file mode 100644
index 0000000..8aa430a
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy
new file mode 100644
index 0000000..fdc3fee
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/HeaderHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
new file mode 100644
index 0000000..fda960d
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
new file mode 100644
index 0000000..9df9a71
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy
new file mode 100644
index 0000000..8a7dcc2
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RestoredHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy
new file mode 100644
index 0000000..03d4dea
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/RevertedHtml.soy
@@ -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}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ViewChangeButton.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ViewChangeButton.soy
new file mode 100644
index 0000000..defde55
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ViewChangeButton.soy
@@ -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}