mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 00:50:31 +00:00
email action: allow to configure Bcc
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
b2781e0bfc
commit
8f934f7c82
13 changed files with 99 additions and 32 deletions
|
@ -55,7 +55,7 @@ If the SMTP configuration is correct you should receive this email.`,
|
|||
logger.ErrorToConsole("unable to initialize SMTP configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = smtp.SendEmail([]string{smtpTestRecipient}, "SFTPGo - Testing Email Settings", "It appears your SFTPGo email is setup correctly!",
|
||||
err = smtp.SendEmail([]string{smtpTestRecipient}, nil, "SFTPGo - Testing Email Settings", "It appears your SFTPGo email is setup correctly!",
|
||||
smtp.EmailContentTypeTextPlain)
|
||||
if err != nil {
|
||||
logger.WarnToConsole("Error sending email: %v", err)
|
||||
|
|
|
@ -389,7 +389,7 @@ func (c *RetentionCheck) sendEmailNotification(errCheck error) error {
|
|||
subject = fmt.Sprintf("Retention check failed for user %q", c.conn.User.Username)
|
||||
}
|
||||
body := "Further details attached."
|
||||
err = smtp.SendEmail([]string{c.Email}, subject, body, smtp.EmailContentTypeTextPlain, files...)
|
||||
err = smtp.SendEmail([]string{c.Email}, nil, subject, body, smtp.EmailContentTypeTextPlain, files...)
|
||||
if err != nil {
|
||||
c.conn.Log(logger.LevelError, "unable to notify retention check result via email: %v, elapsed: %s", err,
|
||||
time.Since(startTime))
|
||||
|
|
|
@ -1441,6 +1441,20 @@ func executeCommandRuleAction(c dataprovider.EventActionCommandConfig, params *E
|
|||
return err
|
||||
}
|
||||
|
||||
func getEmailAddressesWithReplacer(addrs []string, replacer *strings.Replacer) []string {
|
||||
if len(addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
recipients := make([]string, 0, len(addrs))
|
||||
for _, recipient := range addrs {
|
||||
rcpt := replaceWithReplacer(recipient, replacer)
|
||||
if rcpt != "" {
|
||||
recipients = append(recipients, rcpt)
|
||||
}
|
||||
}
|
||||
return recipients
|
||||
}
|
||||
|
||||
func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *EventParams) error {
|
||||
addObjectData := false
|
||||
if params.Object != nil {
|
||||
|
@ -1452,13 +1466,8 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
|
|||
replacer := strings.NewReplacer(replacements...)
|
||||
body := replaceWithReplacer(c.Body, replacer)
|
||||
subject := replaceWithReplacer(c.Subject, replacer)
|
||||
recipients := make([]string, 0, len(c.Recipients))
|
||||
for _, recipient := range c.Recipients {
|
||||
rcpt := replaceWithReplacer(recipient, replacer)
|
||||
if rcpt != "" {
|
||||
recipients = append(recipients, rcpt)
|
||||
}
|
||||
}
|
||||
recipients := getEmailAddressesWithReplacer(c.Recipients, replacer)
|
||||
bcc := getEmailAddressesWithReplacer(c.Bcc, replacer)
|
||||
startTime := time.Now()
|
||||
var files []*mail.File
|
||||
fileAttachments := make([]string, 0, len(c.Attachments))
|
||||
|
@ -1495,7 +1504,7 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
|
|||
}
|
||||
files = append(files, res...)
|
||||
}
|
||||
err := smtp.SendEmail(recipients, subject, body, smtp.EmailContentType(c.ContentType), files...)
|
||||
err := smtp.SendEmail(recipients, bcc, 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 {
|
||||
|
@ -2344,7 +2353,7 @@ func executePwdExpirationCheckForUser(user *dataprovider.User, config dataprovid
|
|||
}
|
||||
subject := "SFTPGo password expiration notification"
|
||||
startTime := time.Now()
|
||||
if err := smtp.SendEmail([]string{user.Email}, subject, body.String(), smtp.EmailContentTypeTextHTML); err != nil {
|
||||
if err := smtp.SendEmail([]string{user.Email}, nil, subject, body.String(), smtp.EmailContentTypeTextHTML); err != nil {
|
||||
eventManagerLog(logger.LevelError, "unable to notify password expiration for user %s: %v, elapsed: %s",
|
||||
user.Username, err, time.Since(startTime))
|
||||
return err
|
||||
|
|
|
@ -3576,6 +3576,7 @@ func TestEventRule(t *testing.T) {
|
|||
Options: dataprovider.BaseEventActionOptions{
|
||||
EmailConfig: dataprovider.EventActionEmailConfig{
|
||||
Recipients: []string{"test1@example.com", "test2@example.com"},
|
||||
Bcc: []string{"test3@example.com"},
|
||||
Subject: `New "{{Event}}" from "{{Name}}" status {{StatusString}}`,
|
||||
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}} Data: {{ObjectData}} {{ErrorString}}",
|
||||
},
|
||||
|
@ -3771,9 +3772,10 @@ func TestEventRule(t *testing.T) {
|
|||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email := lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 2)
|
||||
assert.Len(t, email.To, 3)
|
||||
assert.True(t, util.Contains(email.To, "test1@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test2@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test3@example.com"))
|
||||
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "upload" from "%s" status OK`, user.Username))
|
||||
// test the failure action, we download a file that exceeds the transfer quota limit
|
||||
err = writeSFTPFileNoCheck(path.Join("subdir1", testFileName), 1*1024*1024+65535, client)
|
||||
|
@ -3791,9 +3793,10 @@ func TestEventRule(t *testing.T) {
|
|||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email = lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 2)
|
||||
assert.Len(t, email.To, 3)
|
||||
assert.True(t, util.Contains(email.To, "test1@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test2@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test3@example.com"))
|
||||
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "download" from "%s" status KO`, user.Username))
|
||||
assert.Contains(t, email.Data, `"download" failed`)
|
||||
assert.Contains(t, email.Data, common.ErrReadQuotaExceeded.Error())
|
||||
|
@ -3827,9 +3830,10 @@ func TestEventRule(t *testing.T) {
|
|||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email = lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 2)
|
||||
assert.Len(t, email.To, 3)
|
||||
assert.True(t, util.Contains(email.To, "test1@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test2@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test3@example.com"))
|
||||
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "download" from "%s"`, user.Username))
|
||||
}
|
||||
// test upload action command with arguments
|
||||
|
@ -3869,9 +3873,10 @@ func TestEventRule(t *testing.T) {
|
|||
return lastReceivedEmail.get().From != ""
|
||||
}, 3000*time.Millisecond, 100*time.Millisecond)
|
||||
email := lastReceivedEmail.get()
|
||||
assert.Len(t, email.To, 2)
|
||||
assert.Len(t, email.To, 3)
|
||||
assert.True(t, util.Contains(email.To, "test1@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test2@example.com"))
|
||||
assert.True(t, util.Contains(email.To, "test3@example.com"))
|
||||
assert.Contains(t, email.Data, `Subject: New "delete" from "admin"`)
|
||||
_, err = httpdtest.RemoveEventRule(rule3, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -474,6 +474,7 @@ func (c EventActionCommandConfig) GetArgumentsAsString() string {
|
|||
// EventActionEmailConfig defines the configuration options for SMTP event actions
|
||||
type EventActionEmailConfig struct {
|
||||
Recipients []string `json:"recipients,omitempty"`
|
||||
Bcc []string `json:"bcc,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Attachments []string `json:"attachments,omitempty"`
|
||||
|
@ -485,6 +486,11 @@ func (c EventActionEmailConfig) GetRecipientsAsString() string {
|
|||
return strings.Join(c.Recipients, ",")
|
||||
}
|
||||
|
||||
// GetBccAsString returns the list of bcc as comma separated string
|
||||
func (c EventActionEmailConfig) GetBccAsString() string {
|
||||
return strings.Join(c.Bcc, ",")
|
||||
}
|
||||
|
||||
// GetAttachmentsAsString returns the list of attachments as comma separated string
|
||||
func (c EventActionEmailConfig) GetAttachmentsAsString() string {
|
||||
return strings.Join(c.Attachments, ",")
|
||||
|
@ -509,6 +515,12 @@ func (c *EventActionEmailConfig) validate() error {
|
|||
return util.NewValidationError("invalid email recipients")
|
||||
}
|
||||
}
|
||||
c.Bcc = util.RemoveDuplicates(c.Bcc, false)
|
||||
for _, r := range c.Bcc {
|
||||
if r == "" {
|
||||
return util.NewValidationError("invalid email bcc")
|
||||
}
|
||||
}
|
||||
if c.Subject == "" {
|
||||
return util.NewValidationError("email subject is required")
|
||||
}
|
||||
|
@ -897,6 +909,8 @@ func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
|
|||
o.SetEmptySecretsIfNil()
|
||||
emailRecipients := make([]string, len(o.EmailConfig.Recipients))
|
||||
copy(emailRecipients, o.EmailConfig.Recipients)
|
||||
emailBcc := make([]string, len(o.EmailConfig.Bcc))
|
||||
copy(emailBcc, o.EmailConfig.Bcc)
|
||||
emailAttachments := make([]string, len(o.EmailConfig.Attachments))
|
||||
copy(emailAttachments, o.EmailConfig.Attachments)
|
||||
cmdArgs := make([]string, len(o.CmdConfig.Args))
|
||||
|
@ -941,6 +955,7 @@ func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
|
|||
},
|
||||
EmailConfig: EventActionEmailConfig{
|
||||
Recipients: emailRecipients,
|
||||
Bcc: emailBcc,
|
||||
Subject: o.EmailConfig.Subject,
|
||||
ContentType: o.EmailConfig.ContentType,
|
||||
Body: o.EmailConfig.Body,
|
||||
|
|
|
@ -48,7 +48,7 @@ func testSMTPConfig(w http.ResponseWriter, r *http.Request) {
|
|||
req.Password = configs.SMTP.Password.GetPayload()
|
||||
}
|
||||
}
|
||||
if err := req.SendEmail([]string{req.Recipient}, "SFTPGo - Testing Email Settings",
|
||||
if err := req.SendEmail([]string{req.Recipient}, nil, "SFTPGo - Testing Email Settings",
|
||||
"It appears your SFTPGo email is setup correctly!", smtp.EmailContentTypeTextPlain); err != nil {
|
||||
sendAPIResponse(w, r, err, "", http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -693,7 +693,7 @@ func handleForgotPassword(r *http.Request, username string, isAdmin bool) error
|
|||
return util.NewGenericError("Unable to render password reset template")
|
||||
}
|
||||
startTime := time.Now()
|
||||
if err := smtp.SendEmail([]string{email}, subject, body.String(), smtp.EmailContentTypeTextHTML); err != nil {
|
||||
if err := smtp.SendEmail([]string{email}, nil, subject, body.String(), smtp.EmailContentTypeTextHTML); err != nil {
|
||||
logger.Warn(logSender, middleware.GetReqID(r.Context()), "unable to send password reset code via email: %v, elapsed: %v",
|
||||
err, time.Since(startTime))
|
||||
return util.NewGenericError(fmt.Sprintf("Unable to send confirmation code via email: %v", err))
|
||||
|
|
|
@ -1708,6 +1708,7 @@ func TestBasicActionRulesHandling(t *testing.T) {
|
|||
a.Options = dataprovider.BaseEventActionOptions{
|
||||
EmailConfig: dataprovider.EventActionEmailConfig{
|
||||
Recipients: []string{"email@example.com"},
|
||||
Bcc: []string{"bcc@example.com"},
|
||||
Subject: "Event: {{Event}}",
|
||||
Body: "test mail body",
|
||||
Attachments: []string{"/{{VirtualPath}}"},
|
||||
|
@ -2250,6 +2251,11 @@ func TestEventActionValidation(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "invalid email recipients")
|
||||
action.Options.EmailConfig.Recipients = []string{"a@a.com"}
|
||||
action.Options.EmailConfig.Bcc = []string{""}
|
||||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "invalid email bcc")
|
||||
action.Options.EmailConfig.Bcc = nil
|
||||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "email subject is required")
|
||||
|
@ -21820,6 +21826,7 @@ func TestWebEventAction(t *testing.T) {
|
|||
action.Type = dataprovider.ActionTypeEmail
|
||||
action.Options.EmailConfig = dataprovider.EventActionEmailConfig{
|
||||
Recipients: []string{"address1@example.com", "address2@example.com"},
|
||||
Bcc: []string{"address3@example.com"},
|
||||
Subject: "subject",
|
||||
ContentType: 1,
|
||||
Body: "body",
|
||||
|
@ -21827,6 +21834,7 @@ func TestWebEventAction(t *testing.T) {
|
|||
}
|
||||
form.Set("type", fmt.Sprintf("%d", action.Type))
|
||||
form.Set("email_recipients", "address1@example.com, address2@example.com")
|
||||
form.Set("email_bcc", "address3@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)
|
||||
|
@ -21843,6 +21851,7 @@ func TestWebEventAction(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, action.Type, actionGet.Type)
|
||||
assert.Equal(t, action.Options.EmailConfig.Recipients, actionGet.Options.EmailConfig.Recipients)
|
||||
assert.Equal(t, action.Options.EmailConfig.Bcc, actionGet.Options.EmailConfig.Bcc)
|
||||
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)
|
||||
|
|
|
@ -2326,6 +2326,7 @@ func getEventActionOptionsFromPostFields(r *http.Request) (dataprovider.BaseEven
|
|||
},
|
||||
EmailConfig: dataprovider.EventActionEmailConfig{
|
||||
Recipients: getSliceFromDelimitedValues(r.Form.Get("email_recipients"), ","),
|
||||
Bcc: getSliceFromDelimitedValues(r.Form.Get("email_bcc"), ","),
|
||||
Subject: r.Form.Get("email_subject"),
|
||||
ContentType: emailContentType,
|
||||
Body: r.Form.Get("email_body"),
|
||||
|
|
|
@ -2659,6 +2659,14 @@ func compareEventActionEmailConfigFields(expected, actual dataprovider.EventActi
|
|||
return errors.New("email recipients content mismatch")
|
||||
}
|
||||
}
|
||||
if len(expected.Bcc) != len(actual.Bcc) {
|
||||
return errors.New("email bcc mismatch")
|
||||
}
|
||||
for _, v := range expected.Bcc {
|
||||
if !util.Contains(actual.Bcc, v) {
|
||||
return errors.New("email bcc content mismatch")
|
||||
}
|
||||
}
|
||||
if expected.Subject != actual.Subject {
|
||||
return errors.New("email subject mismatch")
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ func (c *activeConfig) Set(cfg *dataprovider.SMTPConfigs) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *activeConfig) getSMTPClientAndMsg(to []string, subject, body string, contentType EmailContentType,
|
||||
func (c *activeConfig) getSMTPClientAndMsg(to, bcc []string, subject, body string, contentType EmailContentType,
|
||||
attachments ...*mail.File,
|
||||
) (*mail.Client, *mail.Msg, error) {
|
||||
c.RLock()
|
||||
|
@ -114,11 +114,11 @@ func (c *activeConfig) getSMTPClientAndMsg(to []string, subject, body string, co
|
|||
return nil, nil, errors.New("smtp: not configured")
|
||||
}
|
||||
|
||||
return c.config.getSMTPClientAndMsg(to, subject, body, contentType, attachments...)
|
||||
return c.config.getSMTPClientAndMsg(to, bcc, subject, body, contentType, attachments...)
|
||||
}
|
||||
|
||||
func (c *activeConfig) sendEmail(to []string, subject, body string, contentType EmailContentType, attachments ...*mail.File) error {
|
||||
client, msg, err := c.getSMTPClientAndMsg(to, subject, body, contentType, attachments...)
|
||||
func (c *activeConfig) sendEmail(to, bcc []string, subject, body string, contentType EmailContentType, attachments ...*mail.File) error {
|
||||
client, msg, err := c.getSMTPClientAndMsg(to, bcc, subject, body, contentType, attachments...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ func (c *Config) getMailClientOptions() []mail.Option {
|
|||
return options
|
||||
}
|
||||
|
||||
func (c *Config) getSMTPClientAndMsg(to []string, subject, body string, contentType EmailContentType,
|
||||
func (c *Config) getSMTPClientAndMsg(to, bcc []string, subject, body string, contentType EmailContentType,
|
||||
attachments ...*mail.File) (*mail.Client, *mail.Msg, error) {
|
||||
version := version.Get()
|
||||
msg := mail.NewMsg()
|
||||
|
@ -304,6 +304,11 @@ func (c *Config) getSMTPClientAndMsg(to []string, subject, body string, contentT
|
|||
if err := msg.To(to...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(bcc) > 0 {
|
||||
if err := msg.Bcc(bcc...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
msg.Subject(subject)
|
||||
msg.SetDate()
|
||||
msg.SetMessageID()
|
||||
|
@ -326,8 +331,8 @@ func (c *Config) getSMTPClientAndMsg(to []string, subject, body string, contentT
|
|||
}
|
||||
|
||||
// SendEmail tries to send an email using the specified parameters
|
||||
func (c *Config) SendEmail(to []string, subject, body string, contentType EmailContentType, attachments ...*mail.File) error {
|
||||
client, msg, err := c.getSMTPClientAndMsg(to, subject, body, contentType, attachments...)
|
||||
func (c *Config) SendEmail(to, bcc []string, subject, body string, contentType EmailContentType, attachments ...*mail.File) error {
|
||||
client, msg, err := c.getSMTPClientAndMsg(to, bcc, subject, body, contentType, attachments...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -366,8 +371,8 @@ func RenderPasswordExpirationTemplate(buf *bytes.Buffer, data any) error {
|
|||
}
|
||||
|
||||
// SendEmail tries to send an email using the specified parameters.
|
||||
func SendEmail(to []string, subject, body string, contentType EmailContentType, attachments ...*mail.File) error {
|
||||
return config.sendEmail(to, subject, body, contentType, attachments...)
|
||||
func SendEmail(to, bcc []string, subject, body string, contentType EmailContentType, attachments ...*mail.File) error {
|
||||
return config.sendEmail(to, bcc, subject, body, contentType, attachments...)
|
||||
}
|
||||
|
||||
// ReloadProviderConf reloads the configuration from the provider
|
||||
|
|
|
@ -7085,6 +7085,10 @@ components:
|
|||
type: array
|
||||
items:
|
||||
type: string
|
||||
bcc:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
subject:
|
||||
type: string
|
||||
body:
|
||||
|
|
|
@ -453,18 +453,29 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
|
||||
<div class="form-group row action-type action-smtp">
|
||||
<label for="idEmailRecipients" class="col-sm-2 col-form-label">Email recipients</label>
|
||||
<label for="idEmailRecipients" class="col-sm-2 col-form-label">To</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="idEmailRecipients" name="email_recipients" rows="2" placeholder=""
|
||||
aria-describedby="smtpRecipientsHelpBlock">{{.Action.Options.EmailConfig.GetRecipientsAsString}}</textarea>
|
||||
<small id="smtpRecipientsHelpBlock" class="form-text text-muted">
|
||||
Comma separated email recipients. Placeholders are supported
|
||||
Comma separated recipients. Placeholders are supported
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row action-type action-smtp">
|
||||
<label for="idEmailSubject" class="col-sm-2 col-form-label">Email subject</label>
|
||||
<label for="idEmailBcc" class="col-sm-2 col-form-label">Bcc</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="idEmailBcc" name="email_bcc" rows="2" placeholder=""
|
||||
aria-describedby="smtpBccHelpBlock">{{.Action.Options.EmailConfig.GetBccAsString}}</textarea>
|
||||
<small id="smtpBccHelpBlock" class="form-text text-muted">
|
||||
Comma separated Bcc addresses. Placeholders are supported
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row action-type action-smtp">
|
||||
<label for="idEmailSubject" class="col-sm-2 col-form-label">Subject</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" id="idEmailSubject" name="email_subject" placeholder=""
|
||||
value="{{.Action.Options.EmailConfig.Subject}}" maxlength="255" aria-describedby="emailSubjectHelpBlock">
|
||||
|
@ -475,7 +486,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
|
||||
<div class="form-group row action-type action-smtp">
|
||||
<label for="idEmailContentType" class="col-sm-2 col-form-label">Email content type</label>
|
||||
<label for="idEmailContentType" class="col-sm-2 col-form-label">Content type</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control selectpicker" id="idEmailContentType" name="email_content_type">
|
||||
<option value="0" {{ if eq .Action.Options.EmailConfig.ContentType 0 }}selected{{end}}>Text/plain</option>
|
||||
|
@ -485,7 +496,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
|
||||
<div class="form-group row action-type action-smtp">
|
||||
<label for="idEmailBody" class="col-sm-2 col-form-label">Email body</label>
|
||||
<label for="idEmailBody" class="col-sm-2 col-form-label">Body</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="idEmailBody" name="email_body" rows="4" placeholder=""
|
||||
aria-describedby="smtpBodyHelpBlock">{{.Action.Options.EmailConfig.Body}}</textarea>
|
||||
|
@ -496,7 +507,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
|
||||
<div class="form-group row action-type action-smtp">
|
||||
<label for="idEmailAttachments" class="col-sm-2 col-form-label">Email attachments</label>
|
||||
<label for="idEmailAttachments" class="col-sm-2 col-form-label">Attachments</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea class="form-control" id="idEmailAttachments" name="email_attachments" rows="2" placeholder=""
|
||||
aria-describedby="smtpAttachmentsHelpBlock">{{.Action.Options.EmailConfig.GetAttachmentsAsString}}</textarea>
|
||||
|
|
Loading…
Reference in a new issue