Change OutgoingEmail to use a soy instead of string concatenation.
For better support of html features (for example `<style>` blocks) in
Email Clients emails need to be properly formated with <!DOCTYPE>
<html> <head> and <body> tags.
Additionally similarly changes are planned for ChangeEmail as they allow
for more structure in the templates.
Google-Bug-Id: b/266904943
Release-Notes: skip
Change-Id: Iabf8f738b6775c21b3aeb34d27dde8c618d2535e
diff --git a/java/com/google/gerrit/pgm/init/SitePathInitializer.java b/java/com/google/gerrit/pgm/init/SitePathInitializer.java
index a057e66..d0d03b5 100644
--- a/java/com/google/gerrit/pgm/init/SitePathInitializer.java
+++ b/java/com/google/gerrit/pgm/init/SitePathInitializer.java
@@ -122,6 +122,8 @@
extractMailExample("DeleteReviewerHtml.soy");
extractMailExample("DeleteVote.soy");
extractMailExample("DeleteVoteHtml.soy");
+ extractMailExample("Email.soy");
+ extractMailExample("EmailHtml.soy");
extractMailExample("Footer.soy");
extractMailExample("FooterHtml.soy");
extractMailExample("ChangeHeader.soy");
diff --git a/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java b/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
index 0eaafb8..7bc319f 100644
--- a/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
+++ b/java/com/google/gerrit/server/mail/send/MailSoySauceLoader.java
@@ -65,6 +65,8 @@
"DeleteReviewerHtml.soy",
"DeleteVote.soy",
"DeleteVoteHtml.soy",
+ "Email.soy",
+ "EmailHtml.soy",
"InboundEmailRejection.soy",
"InboundEmailRejectionHtml.soy",
"Footer.soy",
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index b18b137..ca19150 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -41,6 +41,8 @@
import com.google.gerrit.server.update.RetryableAction.ActionType;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.gerrit.server.validators.ValidationException;
+import com.google.template.soy.data.SanitizedContent.ContentKind;
+import com.google.template.soy.data.UnsafeSanitizedContentOrdainer;
import com.google.template.soy.jbcsrc.api.SoySauce;
import java.net.MalformedURLException;
import java.net.URL;
@@ -154,6 +156,22 @@
this.messageId = messageId;
}
+ private String constructTextEmail() {
+ soyContext.put("body", textBody.toString());
+ soyContext.put("footer", textTemplate("Footer"));
+ return textTemplate("Email");
+ }
+
+ private String constructHtmlEmail() {
+ soyContext.put(
+ "body", UnsafeSanitizedContentOrdainer.ordainAsSafe(htmlBody.toString(), ContentKind.HTML));
+ soyContext.put(
+ "footer",
+ UnsafeSanitizedContentOrdainer.ordainAsSafe(
+ soyHtmlTemplate("FooterHtml"), ContentKind.HTML));
+ return soyHtmlTemplate("EmailHtml");
+ }
+
/** Format and enqueue the message for delivery. */
public void send() throws EmailException {
try {
@@ -192,10 +210,6 @@
if (messageId == null) {
throw new IllegalStateException("All emails must have a messageId");
}
- appendText(textTemplate("Footer"));
- if (useHtml()) {
- appendHtml(soyHtmlTemplate("FooterHtml"));
- }
Set<Address> smtpRcptToPlaintextOnly = new HashSet<>();
if (shouldSendMessage()) {
@@ -295,16 +309,15 @@
setHeader(FieldName.REPLY_TO, j.toString());
}
- String textPart = textBody.toString();
OutgoingEmailValidationListener.Args va = new OutgoingEmailValidationListener.Args();
va.messageClass = messageClass;
va.smtpFromAddress = smtpFromAddress;
va.smtpRcptTo = smtpRcptTo;
va.headers = headers;
- va.body = textPart;
+ va.body = constructTextEmail();
if (useHtml()) {
- va.htmlBody = htmlBody.toString();
+ va.htmlBody = constructHtmlEmail();
} else {
va.htmlBody = null;
}
diff --git a/resources/com/google/gerrit/server/mail/Email.soy b/resources/com/google/gerrit/server/mail/Email.soy
new file mode 100644
index 0000000..9afea72
--- /dev/null
+++ b/resources/com/google/gerrit/server/mail/Email.soy
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2023 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.Email}
+
+/**
+ * The .Email template defines the structure of the content in the email.
+ */
+{template Email kind="text"}
+ {@param body: string}
+ {@param footer: string}
+ {$body}
+ {$footer}
+{/template}
diff --git a/resources/com/google/gerrit/server/mail/EmailHtml.soy b/resources/com/google/gerrit/server/mail/EmailHtml.soy
new file mode 100644
index 0000000..5b5ea63
--- /dev/null
+++ b/resources/com/google/gerrit/server/mail/EmailHtml.soy
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2023 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.EmailHtml}
+
+/**
+ * The .EmailHtml template defines the structure of the content in the email.
+ */
+{template EmailHtml}
+ {@param styles: css}
+ {@param body: html}
+ {@param footer: html}
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <style>
+ {$styles}
+ </style>
+ </head>
+ <body>
+ {$body}
+ {$footer}
+ </body>
+ </html>
+{/template}