Changed the `body` settings of the email
moved from `text_body` and `html_body` to a more structured `body` object as follows: ``` { "body" : { "text" : "the text body", "html" : "the html body" } } ``` `body` can also accpet a string, in which case it will default to the text body of the email: ``` { "body" : "the text body of the email" } ``` the above is a syntactic sugar for the following: ``` { "body" : { "text" : "the text body of the email" } } ``` Original commit: elastic/x-pack-elasticsearch@92406ac2a1
This commit is contained in:
parent
45f39ca2a2
commit
87d4f67b10
|
@ -137,9 +137,15 @@ public class Email implements ToXContent {
|
|||
builder.field(Field.BCC.getPreferredName(), bcc, params);
|
||||
}
|
||||
builder.field(Field.SUBJECT.getPreferredName(), subject);
|
||||
builder.field(Field.TEXT_BODY.getPreferredName(), textBody);
|
||||
if (htmlBody != null) {
|
||||
builder.field(Field.HTML_BODY.getPreferredName(), htmlBody);
|
||||
if (textBody != null || htmlBody != null) {
|
||||
builder.startObject(Field.BODY.getPreferredName());
|
||||
if (textBody != null) {
|
||||
builder.field(Field.BODY_TEXT.getPreferredName(), textBody);
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
builder.field(Field.BODY_HTML.getPreferredName(), htmlBody);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
@ -191,12 +197,27 @@ public class Email implements ToXContent {
|
|||
email.sentDate(new DateTime(parser.text()));
|
||||
} else if (Field.SUBJECT.match(currentFieldName)) {
|
||||
email.subject(parser.text());
|
||||
} else if (Field.TEXT_BODY.match(currentFieldName)) {
|
||||
email.textBody(parser.text());
|
||||
} else if (Field.HTML_BODY.match(currentFieldName)) {
|
||||
email.htmlBody(parser.text());
|
||||
} else if (Field.BODY.match(currentFieldName)) {
|
||||
String bodyField = currentFieldName;
|
||||
if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
email.textBody(parser.text());
|
||||
} else if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (currentFieldName == null) {
|
||||
throw new ParseException("could not parse email. empty [{}] field", bodyField);
|
||||
} else if (Email.Field.BODY_TEXT.match(currentFieldName)) {
|
||||
email.textBody(parser.text());
|
||||
} else if (Email.Field.BODY_HTML.match(currentFieldName)) {
|
||||
email.htmlBody(parser.text());
|
||||
} else {
|
||||
throw new ParseException("could not parse email. unexpected field [{}.{}] field", bodyField, currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ParseException("could not parse email. unrecognized field [" + currentFieldName + "]");
|
||||
throw new ParseException("could not parse email. unexpected field [{}]", currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -563,12 +584,12 @@ public class Email implements ToXContent {
|
|||
|
||||
public static class ParseException extends EmailException {
|
||||
|
||||
public ParseException(String msg) {
|
||||
super(msg);
|
||||
public ParseException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
public ParseException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
public ParseException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,8 +603,9 @@ public class Email implements ToXContent {
|
|||
ParseField CC = new ParseField("cc");
|
||||
ParseField BCC = new ParseField("bcc");
|
||||
ParseField SUBJECT = new ParseField("subject");
|
||||
ParseField TEXT_BODY = new ParseField("text_body");
|
||||
ParseField HTML_BODY = new ParseField("html_body");
|
||||
ParseField BODY = new ParseField("body");
|
||||
ParseField BODY_TEXT = new ParseField("text");
|
||||
ParseField BODY_HTML = new ParseField("html");
|
||||
}
|
||||
|
||||
}
|
|
@ -5,18 +5,19 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.email.service;
|
||||
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.actions.ActionException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EmailException extends ActionException {
|
||||
public class EmailException extends WatcherException {
|
||||
|
||||
public EmailException(String msg) {
|
||||
super(msg);
|
||||
public EmailException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
public EmailException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
public EmailException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.watcher.actions.email.service;
|
|||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
import org.owasp.html.*;
|
||||
|
@ -207,11 +208,15 @@ public class EmailTemplate implements ToXContent {
|
|||
if (subject != null) {
|
||||
builder.field(Email.Field.SUBJECT.getPreferredName(), subject, params);
|
||||
}
|
||||
if (textBody != null) {
|
||||
builder.field(Email.Field.TEXT_BODY.getPreferredName(), textBody, params);
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
builder.field(Email.Field.HTML_BODY.getPreferredName(), htmlBody, params);
|
||||
if (textBody != null || htmlBody != null) {
|
||||
builder.startObject(Email.Field.BODY.getPreferredName());
|
||||
if (textBody != null) {
|
||||
builder.field(Email.Field.BODY_TEXT.getPreferredName(), textBody, params);
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
builder.field(Email.Field.BODY_HTML.getPreferredName(), htmlBody, params);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
@ -252,7 +257,7 @@ public class EmailTemplate implements ToXContent {
|
|||
public Builder replyTo(String... replyTo) {
|
||||
Template[] templates = new Template[replyTo.length];
|
||||
for (int i = 0; i < templates.length; i++) {
|
||||
templates[i] = Template.inline(replyTo[i]).build();
|
||||
templates[i] = Template.defaultType(replyTo[i]).build();
|
||||
}
|
||||
return replyTo(templates);
|
||||
}
|
||||
|
@ -286,7 +291,7 @@ public class EmailTemplate implements ToXContent {
|
|||
public Builder to(String... to) {
|
||||
Template[] templates = new Template[to.length];
|
||||
for (int i = 0; i < templates.length; i++) {
|
||||
templates[i] = Template.inline(to[i]).build();
|
||||
templates[i] = Template.defaultType(to[i]).build();
|
||||
}
|
||||
return to(templates);
|
||||
}
|
||||
|
@ -307,7 +312,7 @@ public class EmailTemplate implements ToXContent {
|
|||
public Builder cc(String... cc) {
|
||||
Template[] templates = new Template[cc.length];
|
||||
for (int i = 0; i < templates.length; i++) {
|
||||
templates[i] = Template.inline(cc[i]).build();
|
||||
templates[i] = Template.defaultType(cc[i]).build();
|
||||
}
|
||||
return cc(templates);
|
||||
}
|
||||
|
@ -328,7 +333,7 @@ public class EmailTemplate implements ToXContent {
|
|||
public Builder bcc(String... bcc) {
|
||||
Template[] templates = new Template[bcc.length];
|
||||
for (int i = 0; i < templates.length; i++) {
|
||||
templates[i] = Template.inline(bcc[i]).build();
|
||||
templates[i] = Template.defaultType(bcc[i]).build();
|
||||
}
|
||||
return bcc(templates);
|
||||
}
|
||||
|
@ -347,7 +352,7 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
|
||||
public Builder subject(String subject) {
|
||||
return subject(Template.inline(subject));
|
||||
return subject(Template.defaultType(subject));
|
||||
}
|
||||
|
||||
public Builder subject(Template.Builder subject) {
|
||||
|
@ -360,7 +365,7 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
|
||||
public Builder textBody(String text) {
|
||||
return textBody(Template.inline(text));
|
||||
return textBody(Template.defaultType(text));
|
||||
}
|
||||
|
||||
public Builder textBody(Template.Builder text) {
|
||||
|
@ -373,7 +378,7 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
|
||||
public Builder htmlBody(String html, boolean sanitizeHtmlBody) {
|
||||
return htmlBody(Template.inline(html), sanitizeHtmlBody);
|
||||
return htmlBody(Template.defaultType(html), sanitizeHtmlBody);
|
||||
}
|
||||
|
||||
public Builder htmlBody(Template.Builder html, boolean sanitizeHtmlBody) {
|
||||
|
@ -391,6 +396,54 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
}
|
||||
|
||||
static String sanitizeHtml(String html, final Map<String, Attachment> attachments){
|
||||
ElementPolicy onlyCIDImgPolicy = new AttachementVerifyElementPolicy(attachments);
|
||||
PolicyFactory policy = Sanitizers.FORMATTING
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowElements("img", "table", "tr", "td", "style", "body", "head", "hr")
|
||||
.allowAttributes("src").onElements("img")
|
||||
.allowAttributes("class").onElements("style")
|
||||
.allowUrlProtocols("cid")
|
||||
.allowCommonInlineFormattingElements()
|
||||
.allowElements(onlyCIDImgPolicy, "img")
|
||||
.allowStyling(CssSchema.DEFAULT)
|
||||
.toFactory())
|
||||
.and(Sanitizers.LINKS)
|
||||
.and(Sanitizers.BLOCKS);
|
||||
return policy.sanitize(html);
|
||||
}
|
||||
|
||||
private static class AttachementVerifyElementPolicy implements ElementPolicy {
|
||||
|
||||
private final Map<String, Attachment> attachments;
|
||||
|
||||
AttachementVerifyElementPolicy(Map<String, Attachment> attachments) {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(@ParametersAreNonnullByDefault String elementName, @ParametersAreNonnullByDefault List<String> attrs) {
|
||||
if (attrs.size() == 0) {
|
||||
return elementName;
|
||||
}
|
||||
for (int i = 0; i < attrs.size(); ++i) {
|
||||
if(attrs.get(i).equals("src") && i < attrs.size() - 1) {
|
||||
String srcValue = attrs.get(i+1);
|
||||
if (!srcValue.startsWith("cid:")) {
|
||||
return null; //Disallow anything other than content ids
|
||||
}
|
||||
String contentId = srcValue.substring(4);
|
||||
if (attachments.containsKey(contentId)) {
|
||||
return elementName;
|
||||
} else {
|
||||
return null; //This cid wasn't found
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementName;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
|
||||
private final EmailTemplate.Builder builder = builder();
|
||||
|
@ -447,10 +500,26 @@ public class EmailTemplate implements ToXContent {
|
|||
builder.priority(Template.parse(parser));
|
||||
} else if (Email.Field.SUBJECT.match(fieldName)) {
|
||||
builder.subject(Template.parse(parser));
|
||||
} else if (Email.Field.TEXT_BODY.match(fieldName)) {
|
||||
builder.textBody(Template.parse(parser));
|
||||
} else if (Email.Field.HTML_BODY.match(fieldName)) {
|
||||
builder.htmlBody(Template.parse(parser), sanitizeHtmlBody);
|
||||
} else if (Email.Field.BODY.match(fieldName)) {
|
||||
if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
|
||||
builder.textBody(Template.parse(parser));
|
||||
} else if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||
XContentParser.Token token;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (currentFieldName == null) {
|
||||
throw new ParseException("could not parse email template. empty [{}] field", fieldName);
|
||||
} else if (Email.Field.BODY_TEXT.match(currentFieldName)) {
|
||||
builder.textBody(Template.parse(parser));
|
||||
} else if (Email.Field.BODY_HTML.match(currentFieldName)) {
|
||||
builder.htmlBody(Template.parse(parser), sanitizeHtmlBody);
|
||||
} else {
|
||||
throw new ParseException("could not parse email template. unknown field [{}.{}] field", fieldName, currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -462,51 +531,14 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
}
|
||||
|
||||
static String sanitizeHtml(String html, final Map<String, Attachment> attachments){
|
||||
ElementPolicy onlyCIDImgPolicy = new AttachementVerifyElementPolicy(attachments);
|
||||
PolicyFactory policy = Sanitizers.FORMATTING
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowElements("img", "table", "tr", "td", "style", "body", "head", "hr")
|
||||
.allowAttributes("src").onElements("img")
|
||||
.allowAttributes("class").onElements("style")
|
||||
.allowUrlProtocols("cid")
|
||||
.allowCommonInlineFormattingElements()
|
||||
.allowElements(onlyCIDImgPolicy, "img")
|
||||
.allowStyling(CssSchema.DEFAULT)
|
||||
.toFactory())
|
||||
.and(Sanitizers.LINKS)
|
||||
.and(Sanitizers.BLOCKS);
|
||||
return policy.sanitize(html);
|
||||
}
|
||||
public static class ParseException extends WatcherException {
|
||||
|
||||
private static class AttachementVerifyElementPolicy implements ElementPolicy {
|
||||
|
||||
private final Map<String, Attachment> attachments;
|
||||
|
||||
AttachementVerifyElementPolicy(Map<String, Attachment> attachments) {
|
||||
this.attachments = attachments;
|
||||
public ParseException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(@ParametersAreNonnullByDefault String elementName, @ParametersAreNonnullByDefault List<String> attrs) {
|
||||
if (attrs.size() == 0) {
|
||||
return elementName;
|
||||
}
|
||||
for (int i = 0; i < attrs.size(); ++i) {
|
||||
if(attrs.get(i).equals("src") && i < attrs.size() - 1) {
|
||||
String srcValue = attrs.get(i+1);
|
||||
if (!srcValue.startsWith("cid:")) {
|
||||
return null; //Disallow anything other than content ids
|
||||
}
|
||||
String contentId = srcValue.substring(4);
|
||||
if (attachments.containsKey(contentId)) {
|
||||
return elementName;
|
||||
} else {
|
||||
return null; //This cid wasn't found
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementName;
|
||||
public ParseException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -203,19 +203,37 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
builder.field("subject", subject);
|
||||
}
|
||||
}
|
||||
if (textBody != null) {
|
||||
if (textBody != null && htmlBody == null) {
|
||||
if (randomBoolean()) {
|
||||
builder.field("text_body", textBody.getTemplate());
|
||||
builder.field("body", textBody.getTemplate());
|
||||
} else {
|
||||
builder.field("text_body", textBody);
|
||||
builder.startObject("body");
|
||||
if (randomBoolean()) {
|
||||
builder.field("text", textBody.getTemplate());
|
||||
} else {
|
||||
builder.field("text", textBody);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
if (randomBoolean()) {
|
||||
builder.field("html_body", htmlBody.getTemplate());
|
||||
} else {
|
||||
builder.field("html_body", htmlBody);
|
||||
|
||||
if (textBody != null || htmlBody != null) {
|
||||
builder.startObject("body");
|
||||
if (textBody != null) {
|
||||
if (randomBoolean()) {
|
||||
builder.field("text", textBody.getTemplate());
|
||||
} else {
|
||||
builder.field("text", textBody);
|
||||
}
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
if (randomBoolean()) {
|
||||
builder.field("html", htmlBody.getTemplate());
|
||||
} else {
|
||||
builder.field("html", htmlBody);
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
BytesReference bytes = builder.bytes();
|
||||
logger.info("email action json [{}]", bytes.toUtf8());
|
||||
|
@ -286,6 +304,15 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
if (randomBoolean()) {
|
||||
emailTemplate.replyTo(randomBoolean() ? "reply@domain" : "reply1@domain,reply2@domain");
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.subject("_subject");
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.textBody("_text_body");
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.htmlBody("_html_body", randomBoolean());
|
||||
}
|
||||
EmailTemplate email = emailTemplate.build();
|
||||
Authentication auth = randomBoolean() ? null : new Authentication("_user", new Secret("_passwd".toCharArray()));
|
||||
Profile profile = randomFrom(Profile.values());
|
||||
|
@ -301,6 +328,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
XContentBuilder builder = jsonBuilder();
|
||||
executable.toXContent(builder, params);
|
||||
BytesReference bytes = builder.bytes();
|
||||
logger.info(bytes.toUtf8());
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
ExecutableEmailAction parsed = new EmailActionFactory(ImmutableSettings.EMPTY, service, engine)
|
||||
|
|
Loading…
Reference in New Issue