diff --git a/app/plugin_api.go b/app/plugin_api.go index 68e20f6117..2285b36bbd 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -540,6 +540,24 @@ func (api *PluginAPI) RemoveTeamIcon(teamId string) *model.AppError { return nil } +// Mail Section + +func (api *PluginAPI) SendMail(to, subject, htmlBody string) *model.AppError { + if to == "" { + return model.NewAppError("SendMail", "plugin_api.send_mail.missing_to", nil, "", http.StatusBadRequest) + } + + if subject == "" { + return model.NewAppError("SendMail", "plugin_api.send_mail.missing_subject", nil, "", http.StatusBadRequest) + } + + if htmlBody == "" { + return model.NewAppError("SendMail", "plugin_api.send_mail.missing_htmlbody", nil, "", http.StatusBadRequest) + } + + return api.app.SendMail(to, subject, htmlBody) +} + // Plugin Section func (api *PluginAPI) GetPlugins() ([]*model.Manifest, *model.AppError) { diff --git a/app/plugin_api_test.go b/app/plugin_api_test.go index cbdc812e8d..025306ad41 100644 --- a/app/plugin_api_test.go +++ b/app/plugin_api_test.go @@ -19,6 +19,7 @@ import ( "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/plugin" + "github.com/mattermost/mattermost-server/services/mailservice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -690,3 +691,33 @@ func TestPluginAPIGetDirectChannel(t *testing.T) { require.NotNil(t, err) require.Empty(t, dm3) } + +func TestPluginAPISendMail(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + api := th.SetupPluginAPI() + + to := th.BasicUser.Email + subject := "testing plugin api sending email" + body := "this is a test." + + err := api.SendMail(to, subject, body) + require.Nil(t, err) + + // Check if we received the email + var resultsMailbox mailservice.JSONMessageHeaderInbucket + errMail := mailservice.RetryInbucket(5, func() error { + var err error + resultsMailbox, err = mailservice.GetMailBox(to) + return err + }) + require.Nil(t, errMail) + require.NotZero(t, len(resultsMailbox)) + require.True(t, strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], to)) + + resultsEmail, err1 := mailservice.GetMessageFromMailbox(to, resultsMailbox[len(resultsMailbox)-1].ID) + require.Nil(t, err1) + require.Equal(t, resultsEmail.Subject, subject) + require.Equal(t, resultsEmail.Body.Text, body) + +} diff --git a/i18n/en.json b/i18n/en.json index f49c2bf088..d0411b2f8b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -4934,6 +4934,18 @@ "id": "plugin.api.update_user_status.bad_status", "translation": "Unable to set the user status. Unknown user status." }, + { + "id": "plugin_api.send_mail.missing_htmlbody", + "translation": "Missing HTML Body." + }, + { + "id": "plugin_api.send_mail.missing_to", + "translation": "Missing TO address." + }, + { + "id": "plugin_api.send_mail.missing_subject", + "translation": "Missing email subject." + }, { "id": "store.sql_channel.remove_all_deactivated_members.app_error", "translation": "We could not remove the deactivated users from the channel" diff --git a/plugin/api.go b/plugin/api.go index 132c47a038..33fa6b54a7 100644 --- a/plugin/api.go +++ b/plugin/api.go @@ -465,6 +465,11 @@ type API interface { // do not need to add that info. // keyValuePairs should be primitive go types or other values that can be encoded by encoding/gob LogWarn(msg string, keyValuePairs ...interface{}) + + // SendMail sends an email to a specific address + // + // Minimum server version: 5.7 + SendMail(to, subject, htmlBody string) *model.AppError } var handshake = plugin.HandshakeConfig{ diff --git a/plugin/client_rpc_generated.go b/plugin/client_rpc_generated.go index 676214b695..c248b94daf 100644 --- a/plugin/client_rpc_generated.go +++ b/plugin/client_rpc_generated.go @@ -3556,3 +3556,33 @@ func (s *apiRPCServer) LogWarn(args *Z_LogWarnArgs, returns *Z_LogWarnReturns) e } return nil } + +type Z_SendMailArgs struct { + A string + B string + C string +} + +type Z_SendMailReturns struct { + A *model.AppError +} + +func (g *apiRPCClient) SendMail(to, subject, htmlBody string) *model.AppError { + _args := &Z_SendMailArgs{to, subject, htmlBody} + _returns := &Z_SendMailReturns{} + if err := g.client.Call("Plugin.SendMail", _args, _returns); err != nil { + log.Printf("RPC call to SendMail API failed: %s", err.Error()) + } + return _returns.A +} + +func (s *apiRPCServer) SendMail(args *Z_SendMailArgs, returns *Z_SendMailReturns) error { + if hook, ok := s.impl.(interface { + SendMail(to, subject, htmlBody string) *model.AppError + }); ok { + returns.A = hook.SendMail(args.A, args.B, args.C) + } else { + return encodableError(fmt.Errorf("API SendMail called but not implemented.")) + } + return nil +} diff --git a/plugin/plugintest/api.go b/plugin/plugintest/api.go index 345404d5bf..a7771f4f7e 100644 --- a/plugin/plugintest/api.go +++ b/plugin/plugintest/api.go @@ -1974,6 +1974,22 @@ func (_m *API) SendEphemeralPost(userId string, post *model.Post) *model.Post { return r0 } +// SendMail provides a mock function with given fields: to, subject, htmlBody +func (_m *API) SendMail(to string, subject string, htmlBody string) *model.AppError { + ret := _m.Called(to, subject, htmlBody) + + var r0 *model.AppError + if rf, ok := ret.Get(0).(func(string, string, string) *model.AppError); ok { + r0 = rf(to, subject, htmlBody) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.AppError) + } + } + + return r0 +} + // SetProfileImage provides a mock function with given fields: userId, data func (_m *API) SetProfileImage(userId string, data []byte) *model.AppError { ret := _m.Called(userId, data)