diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go index 3c4e95c6..9750003e 100644 --- a/internal/common/eventmanager.go +++ b/internal/common/eventmanager.go @@ -1484,7 +1484,7 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event } files = append(files, res...) } - err := smtp.SendEmail(recipients, subject, body, smtp.EmailContentTypeTextPlain, files...) + err := smtp.SendEmail(recipients, subject, body, smtp.EmailContentType(c.ContentType), files...) eventManagerLog(logger.LevelDebug, "executed email notification action, elapsed: %s, error: %v", time.Since(startTime), err) if err != nil { diff --git a/internal/common/protocol_test.go b/internal/common/protocol_test.go index de52e8da..45ba8403 100644 --- a/internal/common/protocol_test.go +++ b/internal/common/protocol_test.go @@ -6016,9 +6016,10 @@ func TestEventRuleRenameEvent(t *testing.T) { Type: dataprovider.ActionTypeEmail, Options: dataprovider.BaseEventActionOptions{ EmailConfig: dataprovider.EventActionEmailConfig{ - Recipients: []string{"test@example.com"}, - Subject: `"{{Event}}" from "{{Name}}"`, - Body: `Fs path {{FsPath}}, Target path "{{VirtualTargetDirPath}}/{{TargetName}}", size: {{FileSize}}`, + Recipients: []string{"test@example.com"}, + Subject: `"{{Event}}" from "{{Name}}"`, + ContentType: 1, + Body: `

Fs path {{FsPath}}, Target path "{{VirtualTargetDirPath}}/{{TargetName}}", size: {{FileSize}}

`, }, }, } @@ -6065,6 +6066,7 @@ func TestEventRuleRenameEvent(t *testing.T) { assert.Len(t, email.To, 1) assert.True(t, util.Contains(email.To, "test@example.com")) assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "rename" from "%s"`, user.Username)) + assert.Contains(t, email.Data, "Content-Type: text/html") assert.Contains(t, email.Data, fmt.Sprintf("Target path %q", path.Join("/subdir", testFileName))) } @@ -6378,9 +6380,10 @@ func TestEventRuleCertificate(t *testing.T) { Type: dataprovider.ActionTypeEmail, Options: dataprovider.BaseEventActionOptions{ EmailConfig: dataprovider.EventActionEmailConfig{ - Recipients: []string{"test@example.com"}, - Subject: `"{{Event}} {{StatusString}}"`, - Body: "Domain: {{Name}} Timestamp: {{Timestamp}} {{ErrorString}}", + Recipients: []string{"test@example.com"}, + Subject: `"{{Event}} {{StatusString}}"`, + ContentType: 0, + Body: "Domain: {{Name}} Timestamp: {{Timestamp}} {{ErrorString}}", }, }, } @@ -6446,6 +6449,7 @@ func TestEventRuleCertificate(t *testing.T) { assert.Len(t, email.To, 1) assert.True(t, util.Contains(email.To, "test@example.com")) assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s OK"`, renewalEvent)) + assert.Contains(t, email.Data, "Content-Type: text/plain") assert.Contains(t, email.Data, `Domain: example.com Timestamp`) lastReceivedEmail.reset() diff --git a/internal/dataprovider/eventrule.go b/internal/dataprovider/eventrule.go index b37b62d3..d2a741f5 100644 --- a/internal/dataprovider/eventrule.go +++ b/internal/dataprovider/eventrule.go @@ -477,6 +477,7 @@ type EventActionEmailConfig struct { Subject string `json:"subject,omitempty"` Body string `json:"body,omitempty"` Attachments []string `json:"attachments,omitempty"` + ContentType int `json:"content_type,omitempty"` } // GetRecipientsAsString returns the list of recipients as comma separated string @@ -514,6 +515,9 @@ func (c *EventActionEmailConfig) validate() error { if c.Body == "" { return util.NewValidationError("email body is required") } + if c.ContentType < 0 || c.ContentType > 1 { + return util.NewValidationError("invalid email content type") + } for idx, val := range c.Attachments { val = strings.TrimSpace(val) if val == "" { @@ -938,6 +942,7 @@ func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions { EmailConfig: EventActionEmailConfig{ Recipients: emailRecipients, Subject: o.EmailConfig.Subject, + ContentType: o.EmailConfig.ContentType, Body: o.EmailConfig.Body, Attachments: emailAttachments, }, diff --git a/internal/httpd/httpd_test.go b/internal/httpd/httpd_test.go index 659eab92..3dca00fd 100644 --- a/internal/httpd/httpd_test.go +++ b/internal/httpd/httpd_test.go @@ -1858,9 +1858,10 @@ func TestActionRuleRelations(t *testing.T) { Type: dataprovider.ActionTypeEmail, Options: dataprovider.BaseEventActionOptions{ EmailConfig: dataprovider.EventActionEmailConfig{ - Recipients: []string{"test@example.net"}, - Subject: "test subject", - Body: "test body", + Recipients: []string{"test@example.net"}, + ContentType: 1, + Subject: "test subject", + Body: "test body", }, }, } @@ -21820,12 +21821,14 @@ func TestWebEventAction(t *testing.T) { action.Options.EmailConfig = dataprovider.EventActionEmailConfig{ Recipients: []string{"address1@example.com", "address2@example.com"}, Subject: "subject", + ContentType: 1, Body: "body", Attachments: []string{"/file1.txt", "/file2.txt"}, } form.Set("type", fmt.Sprintf("%d", action.Type)) form.Set("email_recipients", "address1@example.com, address2@example.com") form.Set("email_subject", action.Options.EmailConfig.Subject) + form.Set("email_content_type", fmt.Sprintf("%d", action.Options.EmailConfig.ContentType)) form.Set("email_body", action.Options.EmailConfig.Body) form.Set("email_attachments", "file1.txt, file2.txt") req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventActionPath, action.Name), @@ -21841,6 +21844,7 @@ func TestWebEventAction(t *testing.T) { assert.Equal(t, action.Type, actionGet.Type) assert.Equal(t, action.Options.EmailConfig.Recipients, actionGet.Options.EmailConfig.Recipients) assert.Equal(t, action.Options.EmailConfig.Subject, actionGet.Options.EmailConfig.Subject) + assert.Equal(t, action.Options.EmailConfig.ContentType, actionGet.Options.EmailConfig.ContentType) assert.Equal(t, action.Options.EmailConfig.Body, actionGet.Options.EmailConfig.Body) assert.Equal(t, action.Options.EmailConfig.Attachments, actionGet.Options.EmailConfig.Attachments) assert.Equal(t, dataprovider.EventActionHTTPConfig{}, actionGet.Options.HTTPConfig) diff --git a/internal/httpd/webadmin.go b/internal/httpd/webadmin.go index 5ef82bae..71791e28 100644 --- a/internal/httpd/webadmin.go +++ b/internal/httpd/webadmin.go @@ -2301,6 +2301,10 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven if r.Form.Get("idp_mode") == "1" { idpMode = 1 } + emailContentType := 0 + if r.Form.Get("email_content_type") == "1" { + emailContentType = 1 + } options := dataprovider.BaseEventActionOptions{ HTTPConfig: dataprovider.EventActionHTTPConfig{ Endpoint: r.Form.Get("http_endpoint"), @@ -2323,6 +2327,7 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven EmailConfig: dataprovider.EventActionEmailConfig{ Recipients: getSliceFromDelimitedValues(r.Form.Get("email_recipients"), ","), Subject: r.Form.Get("email_subject"), + ContentType: emailContentType, Body: r.Form.Get("email_body"), Attachments: emailAttachments, }, diff --git a/internal/httpdtest/httpdtest.go b/internal/httpdtest/httpdtest.go index e53a9763..d6bc6d22 100644 --- a/internal/httpdtest/httpdtest.go +++ b/internal/httpdtest/httpdtest.go @@ -2662,6 +2662,9 @@ func compareEventActionEmailConfigFields(expected, actual dataprovider.EventActi if expected.Subject != actual.Subject { return errors.New("email subject mismatch") } + if expected.ContentType != actual.ContentType { + return errors.New("email content type mismatch") + } if expected.Body != actual.Body { return errors.New("email body mismatch") } diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 0cd281bd..5601cd74 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -7092,6 +7092,15 @@ components: type: string body: type: string + content_type: + type: integer + enum: + - 0 + - 1 + description: | + Content type: + * `0` text/plain + * `1` text/html attachments: type: array items: diff --git a/templates/webadmin/eventaction.html b/templates/webadmin/eventaction.html index a5d94c32..5d25667a 100644 --- a/templates/webadmin/eventaction.html +++ b/templates/webadmin/eventaction.html @@ -474,6 +474,16 @@ along with this program. If not, see . +
+ +
+ +
+
+