diff --git a/email/custom_http.go b/email/custom_http.go
index 33f71f59..d2e855f6 100644
--- a/email/custom_http.go
+++ b/email/custom_http.go
@@ -15,6 +15,8 @@
package email
import (
+ "bytes"
+ "encoding/json"
"fmt"
"net/http"
"net/url"
@@ -27,13 +29,21 @@ type HttpEmailProvider struct {
endpoint string
method string
httpHeaders map[string]string
+ bodyMapping map[string]string
+ contentType string
}
-func NewHttpEmailProvider(endpoint string, method string, httpHeaders map[string]string) *HttpEmailProvider {
+func NewHttpEmailProvider(endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string) *HttpEmailProvider {
+ if contentType == "" {
+ contentType = "application/x-www-form-urlencoded"
+ }
+
client := &HttpEmailProvider{
endpoint: endpoint,
method: method,
httpHeaders: httpHeaders,
+ bodyMapping: bodyMapping,
+ contentType: contentType,
}
return client
}
@@ -41,18 +51,52 @@ func NewHttpEmailProvider(endpoint string, method string, httpHeaders map[string
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
var req *http.Request
var err error
+
+ fromNameField := "fromName"
+ toAddressField := "toAddress"
+ subjectField := "subject"
+ contentField := "content"
+
+ for k, v := range c.bodyMapping {
+ switch k {
+ case "fromName":
+ fromNameField = v
+ case "toAddress":
+ toAddressField = v
+ case "subject":
+ subjectField = v
+ case "content":
+ contentField = v
+ }
+ }
+
if c.method == "POST" || c.method == "PUT" || c.method == "DELETE" {
- formValues := url.Values{}
- formValues.Set("fromName", fromName)
- formValues.Set("toAddress", toAddress)
- formValues.Set("subject", subject)
- formValues.Set("content", content)
- req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
+ bodyMap := make(map[string]string)
+ bodyMap[fromNameField] = fromName
+ bodyMap[toAddressField] = toAddress
+ bodyMap[subjectField] = subject
+ bodyMap[contentField] = content
+
+ var fromValueBytes []byte
+ if c.contentType == "application/json" {
+ fromValueBytes, err = json.Marshal(bodyMap)
+ if err != nil {
+ return err
+ }
+ req, err = http.NewRequest(c.method, c.endpoint, bytes.NewBuffer(fromValueBytes))
+ } else {
+ formValues := url.Values{}
+ for k, v := range bodyMap {
+ formValues.Add(k, v)
+ }
+ req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
+ }
+
if err != nil {
return err
}
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Set("Content-Type", c.contentType)
} else if c.method == "GET" {
req, err = http.NewRequest(c.method, c.endpoint, nil)
if err != nil {
@@ -60,10 +104,10 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
}
q := req.URL.Query()
- q.Add("fromName", fromName)
- q.Add("toAddress", toAddress)
- q.Add("subject", subject)
- q.Add("content", content)
+ q.Add(fromNameField, fromName)
+ q.Add(toAddressField, toAddress)
+ q.Add(subjectField, subject)
+ q.Add(contentField, content)
req.URL.RawQuery = q.Encode()
} else {
return fmt.Errorf("HttpEmailProvider's Send() error, unsupported method: %s", c.method)
diff --git a/email/provider.go b/email/provider.go
index 3ed0a5d8..f0d3bddf 100644
--- a/email/provider.go
+++ b/email/provider.go
@@ -18,11 +18,11 @@ type EmailProvider interface {
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
}
-func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string, httpHeaders map[string]string) EmailProvider {
+func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string) EmailProvider {
if typ == "Azure ACS" {
return NewAzureACSEmailProvider(clientSecret, host)
} else if typ == "Custom HTTP Email" {
- return NewHttpEmailProvider(endpoint, method, httpHeaders)
+ return NewHttpEmailProvider(endpoint, method, httpHeaders, bodyMapping, contentType)
} else if typ == "SendGrid" {
return NewSendgridEmailProvider(clientSecret, host, endpoint)
} else {
diff --git a/object/email.go b/object/email.go
index 51dfa6ec..b63a2b02 100644
--- a/object/email.go
+++ b/object/email.go
@@ -31,7 +31,7 @@ func TestSmtpServer(provider *Provider) error {
}
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
- emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method, provider.HttpHeaders)
+ emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl)
fromAddress := provider.ClientId2
if fromAddress == "" {
diff --git a/web/src/ProviderEditPage.js b/web/src/ProviderEditPage.js
index ed150a1a..e94a58a2 100644
--- a/web/src/ProviderEditPage.js
+++ b/web/src/ProviderEditPage.js
@@ -42,6 +42,13 @@ const defaultUserMapping = {
avatarUrl: "avatarUrl",
};
+const defaultEmailMapping = {
+ fromName: "fromName",
+ toAddress: "toAddress",
+ subject: "subject",
+ content: "content",
+};
+
class ProviderEditPage extends React.Component {
constructor(props) {
super(props);
@@ -72,7 +79,16 @@ class ProviderEditPage extends React.Component {
if (res.status === "ok") {
const provider = res.data;
- provider.userMapping = provider.userMapping || defaultUserMapping;
+ if (provider.type === "Custom HTTP Email") {
+ if (!provider.userMapping) {
+ provider.userMapping = provider.userMapping || defaultEmailMapping;
+ }
+ if (!provider.userMapping?.fromName) {
+ provider.userMapping = defaultEmailMapping;
+ }
+ } else {
+ provider.userMapping = provider.userMapping || defaultUserMapping;
+ }
this.setState({
provider: provider,
});
@@ -146,9 +162,16 @@ class ProviderEditPage extends React.Component {
const requiredKeys = ["id", "username", "displayName"];
const provider = this.state.provider;
- if (value === "" && requiredKeys.includes(key)) {
- Setting.showMessage("error", i18next.t("provider:This field is required"));
- return;
+ if (provider.type === "Custom HTTP Email") {
+ if (value === "") {
+ Setting.showMessage("error", i18next.t("provider:This field is required"));
+ return;
+ }
+ } else {
+ if (value === "" && requiredKeys.includes(key)) {
+ Setting.showMessage("error", i18next.t("provider:This field is required"));
+ return;
+ }
}
provider.userMapping[key] = value;
@@ -184,6 +207,30 @@ class ProviderEditPage extends React.Component {
);
}
+
+ renderEmailMappingInput() {
+ return (
+
+ {Setting.getLabel(i18next.t("provider:From name"), i18next.t("provider:From name - Tooltip"))} :
+ {
+ this.updateUserMappingField("fromName", e.target.value);
+ }} />
+ {Setting.getLabel(i18next.t("provider:From address"), i18next.t("provider:From address - Tooltip"))} :
+ {
+ this.updateUserMappingField("toAddress", e.target.value);
+ }} />
+ {Setting.getLabel(i18next.t("provider:Subject"), i18next.t("provider:Subject - Tooltip"))} :
+ {
+ this.updateUserMappingField("subject", e.target.value);
+ }} />
+ {Setting.getLabel(i18next.t("provider:Email content"), i18next.t("provider:Email content - Tooltip"))} :
+ {
+ this.updateUserMappingField("content", e.target.value);
+ }} />
+
+ );
+ }
+
getClientIdLabel(provider) {
switch (provider.category) {
case "OAuth":
@@ -1097,33 +1144,66 @@ class ProviderEditPage extends React.Component {
)}
-
-
- {Setting.getLabel(i18next.t("general:Method"), i18next.t("provider:Method - Tooltip"))} :
-
-
-
-
-
-
-
- {Setting.getLabel(i18next.t("provider:HTTP header"), i18next.t("provider:HTTP header - Tooltip"))} :
-
-
- {this.updateProviderField("httpHeaders", value);}} />
-
-
+
+
+ {Setting.getLabel(i18next.t("provider:HTTP header"), i18next.t("provider:HTTP header - Tooltip"))} :
+
+
+ {this.updateProviderField("httpHeaders", value);}} />
+
+
+ {this.state.provider.method !== "GET" ?
+
+ {Setting.getLabel(i18next.t("provider:HTTP body mapping"), i18next.t("provider:HTTP body mapping - Tooltip"))} :
+
+
+ {this.renderEmailMappingInput()}
+
+
: null}
+
+ )
+ }
{Setting.getLabel(i18next.t("provider:Email title"), i18next.t("provider:Email title - Tooltip"))} :