summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2017-01-19 09:00:13 -0500
committerGitHub <noreply@github.com>2017-01-19 09:00:13 -0500
commitd3a285e64d051aa8d5c4c9854597dfbcce107675 (patch)
treee16ca3d52b6347a754e506aa8cac7457c62d3639 /app
parent61b7226533568f3261fc233538ce998bb71a5345 (diff)
downloadchat-d3a285e64d051aa8d5c4c9854597dfbcce107675.tar.gz
chat-d3a285e64d051aa8d5c4c9854597dfbcce107675.tar.bz2
chat-d3a285e64d051aa8d5c4c9854597dfbcce107675.zip
Migrate functions to app package (#5106)
* Refactor and move session logic into app package * Refactor email functions into the app package * Refactor password update into app package * Migrate user functions to app package * Move team functions into app package * Migrate channel functions into app package * Pass SiteURL through to app functions * Update based on feedback
Diffstat (limited to 'app')
-rw-r--r--app/channel.go340
-rw-r--r--app/email.go236
-rw-r--r--app/notification.go1
-rw-r--r--app/oauth.go2
-rw-r--r--app/session.go54
-rw-r--r--app/session_test.go2
-rw-r--r--app/team.go352
-rw-r--r--app/user.go359
8 files changed, 1331 insertions, 15 deletions
diff --git a/app/channel.go b/app/channel.go
index aa84e12be..b8e02a149 100644
--- a/app/channel.go
+++ b/app/channel.go
@@ -5,6 +5,7 @@ package app
import (
"fmt"
+ "net/http"
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/model"
@@ -190,6 +191,128 @@ func CreateDirectChannel(userId string, otherUserId string) (*model.Channel, *mo
}
}
+func UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) {
+ if result := <-Srv.Store.Channel().Update(channel); result.Err != nil {
+ return nil, result.Err
+ } else {
+ InvalidateCacheForChannel(channel.Id)
+ return channel, nil
+ }
+}
+
+func UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) {
+ var member *model.ChannelMember
+ var err *model.AppError
+ if member, err = GetChannelMember(channelId, userId); err != nil {
+ return nil, err
+ }
+
+ member.Roles = newRoles
+
+ if result := <-Srv.Store.Channel().UpdateMember(member); result.Err != nil {
+ return nil, result.Err
+ }
+
+ InvalidateCacheForUser(userId)
+ return member, nil
+}
+
+func UpdateChannelMemberNotifyProps(data map[string]string, channelId string, userId string) (*model.ChannelMember, *model.AppError) {
+ var member *model.ChannelMember
+ var err *model.AppError
+ if member, err = GetChannelMember(channelId, userId); err != nil {
+ return nil, err
+ }
+
+ // update whichever notify properties have been provided, but don't change the others
+ if markUnread, exists := data["mark_unread"]; exists {
+ member.NotifyProps["mark_unread"] = markUnread
+ }
+
+ if desktop, exists := data["desktop"]; exists {
+ member.NotifyProps["desktop"] = desktop
+ }
+
+ if result := <-Srv.Store.Channel().UpdateMember(member); result.Err != nil {
+ return nil, result.Err
+ } else {
+ InvalidateCacheForUser(userId)
+ return member, nil
+ }
+}
+
+func DeleteChannel(channel *model.Channel, userId string) *model.AppError {
+ uc := Srv.Store.User().Get(userId)
+ scm := Srv.Store.Channel().GetMember(channel.Id, userId)
+ ihc := Srv.Store.Webhook().GetIncomingByChannel(channel.Id)
+ ohc := Srv.Store.Webhook().GetOutgoingByChannel(channel.Id)
+
+ if uresult := <-uc; uresult.Err != nil {
+ return uresult.Err
+ } else if scmresult := <-scm; scmresult.Err != nil {
+ return scmresult.Err
+ } else if ihcresult := <-ihc; ihcresult.Err != nil {
+ return ihcresult.Err
+ } else if ohcresult := <-ohc; ohcresult.Err != nil {
+ return ohcresult.Err
+ } else {
+ user := uresult.Data.(*model.User)
+ incomingHooks := ihcresult.Data.([]*model.IncomingWebhook)
+ outgoingHooks := ohcresult.Data.([]*model.OutgoingWebhook)
+ // Don't need to do anything with channel member, just wanted to confirm it exists
+
+ if channel.DeleteAt > 0 {
+ err := model.NewLocAppError("deleteChannel", "api.channel.delete_channel.deleted.app_error", nil, "")
+ err.StatusCode = http.StatusBadRequest
+ return err
+ }
+
+ if channel.Name == model.DEFAULT_CHANNEL {
+ err := model.NewLocAppError("deleteChannel", "api.channel.delete_channel.cannot.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
+ err.StatusCode = http.StatusBadRequest
+ return err
+ }
+
+ T := utils.GetUserTranslations(user.Locale)
+
+ post := &model.Post{
+ ChannelId: channel.Id,
+ Message: fmt.Sprintf(T("api.channel.delete_channel.archived"), user.Username),
+ Type: model.POST_CHANNEL_DELETED,
+ UserId: userId,
+ }
+
+ if _, err := CreatePost(post, channel.TeamId, false); err != nil {
+ l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err)
+ }
+
+ now := model.GetMillis()
+ for _, hook := range incomingHooks {
+ if result := <-Srv.Store.Webhook().DeleteIncoming(hook.Id, now); result.Err != nil {
+ l4g.Error(utils.T("api.channel.delete_channel.incoming_webhook.error"), hook.Id)
+ }
+ }
+
+ for _, hook := range outgoingHooks {
+ if result := <-Srv.Store.Webhook().DeleteOutgoing(hook.Id, now); result.Err != nil {
+ l4g.Error(utils.T("api.channel.delete_channel.outgoing_webhook.error"), hook.Id)
+ }
+ }
+
+ if dresult := <-Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil {
+ return dresult.Err
+ }
+ InvalidateCacheForChannel(channel.Id)
+
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, channel.TeamId, "", "", nil)
+ message.Add("channel_id", channel.Id)
+
+ Publish(message)
+ }
+
+ return nil
+}
+
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) {
if channel.DeleteAt > 0 {
return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.deleted.app_error", nil, "")
@@ -205,7 +328,7 @@ func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelM
if result := <-tmchan; result.Err != nil {
return nil, result.Err
} else {
- teamMember := result.Data.(model.TeamMember)
+ teamMember := result.Data.(*model.TeamMember)
if teamMember.DeleteAt > 0 {
return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.deleted.app_error", nil, "")
}
@@ -216,8 +339,8 @@ func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelM
return nil, result.Err
}
} else {
- channelMember := result.Data.(model.ChannelMember)
- return &channelMember, nil
+ channelMember := result.Data.(*model.ChannelMember)
+ return channelMember, nil
}
newMember := &model.ChannelMember{
@@ -396,6 +519,62 @@ func GetChannelByName(channelName, teamId string) (*model.Channel, *model.AppErr
}
}
+func GetChannelsForUser(teamId string, userId string) (*model.ChannelList, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetChannels(teamId, userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelList), nil
+ }
+}
+
+func GetChannelsUserNotIn(teamId string, userId string, offset int, limit int) (*model.ChannelList, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetMoreChannels(teamId, userId, offset, limit); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelList), nil
+ }
+}
+
+func GetChannelMember(channelId string, userId string) (*model.ChannelMember, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetMember(channelId, userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelMember), nil
+ }
+}
+
+func GetChannelMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetMembersByIds(channelId, userIds); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelMembers), nil
+ }
+}
+
+func GetChannelMembersForUser(teamId string, userId string) (*model.ChannelMembers, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetMembersForUser(teamId, userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelMembers), nil
+ }
+}
+
+func GetChannelMemberCount(channelId string) (int64, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetMemberCount(channelId, true); result.Err != nil {
+ return 0, result.Err
+ } else {
+ return result.Data.(int64), nil
+ }
+}
+
+func GetChannelCounts(teamId string, userId string) (*model.ChannelCounts, *model.AppError) {
+ if result := <-Srv.Store.Channel().GetChannelCounts(teamId, userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelCounts), nil
+ }
+}
+
func JoinChannel(channel *model.Channel, userId string) *model.AppError {
userChan := Srv.Store.User().Get(userId)
memberChan := Srv.Store.Channel().GetMember(channel.Id, userId)
@@ -421,6 +600,76 @@ func JoinChannel(channel *model.Channel, userId string) *model.AppError {
return nil
}
+func LeaveChannel(channelId string, userId string) *model.AppError {
+ sc := Srv.Store.Channel().Get(channelId, true)
+ uc := Srv.Store.User().Get(userId)
+ ccm := Srv.Store.Channel().GetMemberCount(channelId, false)
+
+ if cresult := <-sc; cresult.Err != nil {
+ return cresult.Err
+ } else if uresult := <-uc; uresult.Err != nil {
+ return cresult.Err
+ } else if ccmresult := <-ccm; ccmresult.Err != nil {
+ return ccmresult.Err
+ } else {
+ channel := cresult.Data.(*model.Channel)
+ user := uresult.Data.(*model.User)
+ membersCount := ccmresult.Data.(int64)
+
+ if channel.Type == model.CHANNEL_DIRECT {
+ err := model.NewLocAppError("LeaveChannel", "api.channel.leave.direct.app_error", nil, "")
+ err.StatusCode = http.StatusBadRequest
+ return err
+ }
+
+ if channel.Type == model.CHANNEL_PRIVATE && membersCount == 1 {
+ err := model.NewLocAppError("LeaveChannel", "api.channel.leave.last_member.app_error", nil, "userId="+user.Id)
+ err.StatusCode = http.StatusBadRequest
+ return err
+ }
+
+ if err := RemoveUserFromChannel(userId, userId, channel); err != nil {
+ return err
+ }
+
+ go PostUserAddRemoveMessage(userId, channel.Id, channel.TeamId, fmt.Sprintf(utils.T("api.channel.leave.left"), user.Username), model.POST_JOIN_LEAVE)
+ }
+
+ return nil
+}
+
+func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError {
+ if channel.DeleteAt > 0 {
+ err := model.NewLocAppError("RemoveUserFromChannel", "api.channel.remove_user_from_channel.deleted.app_error", nil, "")
+ err.StatusCode = http.StatusBadRequest
+ return err
+ }
+
+ if channel.Name == model.DEFAULT_CHANNEL {
+ return model.NewLocAppError("RemoveUserFromChannel", "api.channel.remove.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
+ }
+
+ if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil {
+ return cmresult.Err
+ }
+
+ InvalidateCacheForUser(userIdToRemove)
+ InvalidateCacheForChannel(channel.Id)
+
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
+ message.Add("user_id", userIdToRemove)
+ message.Add("remover_id", removerUserId)
+ go Publish(message)
+
+ // because the removed user no longer belongs to the channel we need to send a separate websocket event
+ userMsg := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", "", userIdToRemove, nil)
+ userMsg.Add("channel_id", channel.Id)
+ userMsg.Add("remover_id", removerUserId)
+ go Publish(userMsg)
+
+ return nil
+}
+
func PostUserAddRemoveMessage(userId, channelId, teamId, message, postType string) *model.AppError {
post := &model.Post{
ChannelId: channelId,
@@ -434,3 +683,88 @@ func PostUserAddRemoveMessage(userId, channelId, teamId, message, postType strin
return nil
}
+
+func GetNumberOfChannelsOnTeam(teamId string) (int, *model.AppError) {
+ // Get total number of channels on current team
+ if result := <-Srv.Store.Channel().GetTeamChannels(teamId); result.Err != nil {
+ return 0, result.Err
+ } else {
+ return len(*result.Data.(*model.ChannelList)), nil
+ }
+}
+
+func SetActiveChannel(userId string, channelId string) *model.AppError {
+ status, err := GetStatus(userId)
+ if err != nil {
+ status = &model.Status{userId, model.STATUS_ONLINE, false, model.GetMillis(), channelId}
+ } else {
+ status.ActiveChannel = channelId
+ if !status.Manual {
+ status.Status = model.STATUS_ONLINE
+ }
+ status.LastActivityAt = model.GetMillis()
+ }
+
+ AddStatusCache(status)
+
+ return nil
+}
+
+func UpdateChannelLastViewedAt(channelIds []string, userId string) *model.AppError {
+ if result := <-Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId); result.Err != nil {
+ return result.Err
+ }
+
+ return nil
+}
+
+func SearchChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
+ if result := <-Srv.Store.Channel().SearchInTeam(teamId, term); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelList), nil
+ }
+}
+
+func SearchChannelsUserNotIn(teamId string, userId string, term string) (*model.ChannelList, *model.AppError) {
+ if result := <-Srv.Store.Channel().SearchMore(userId, teamId, term); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.ChannelList), nil
+ }
+}
+
+func ViewChannel(view *model.ChannelView, teamId string, userId string, clearPushNotifications bool) *model.AppError {
+ channelIds := []string{view.ChannelId}
+
+ var pchan store.StoreChannel
+ if len(view.PrevChannelId) > 0 {
+ channelIds = append(channelIds, view.PrevChannelId)
+
+ if *utils.Cfg.EmailSettings.SendPushNotifications && clearPushNotifications {
+ pchan = Srv.Store.User().GetUnreadCountForChannel(userId, view.ChannelId)
+ }
+ }
+
+ uchan := Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId)
+
+ if pchan != nil {
+ if result := <-pchan; result.Err != nil {
+ return result.Err
+ } else {
+ if result.Data.(int64) > 0 {
+ ClearPushNotification(userId, view.ChannelId)
+ }
+ }
+ }
+
+ if result := <-uchan; result.Err != nil {
+ return result.Err
+ }
+
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, teamId, "", userId, nil)
+ message.Add("channel_id", view.ChannelId)
+ go Publish(message)
+
+ return nil
+}
diff --git a/app/email.go b/app/email.go
new file mode 100644
index 000000000..007a24505
--- /dev/null
+++ b/app/email.go
@@ -0,0 +1,236 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "fmt"
+ "html/template"
+ "net/url"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+)
+
+func SendChangeUsernameEmail(oldUsername, newUsername, email, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ subject := fmt.Sprintf("[%v] %v", utils.Cfg.TeamSettings.SiteName, T("api.templates.username_change_subject",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName}))
+
+ bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.username_change_body.title")
+ bodyPage.Html["Info"] = template.HTML(T("api.templates.username_change_body.info",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewUsername": newUsername}))
+
+ if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendEmailChangeVerifyEmail(userId, newUserEmail, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId+utils.Cfg.EmailSettings.InviteSalt), url.QueryEscape(newUserEmail))
+
+ subject := fmt.Sprintf("[%v] %v", utils.Cfg.TeamSettings.SiteName, T("api.templates.email_change_verify_subject",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName}))
+
+ bodyPage := utils.NewHTMLTemplate("email_change_verify_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title")
+ bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName})
+ bodyPage.Props["VerifyUrl"] = link
+ bodyPage.Props["VerifyButton"] = T("api.templates.email_change_verify_body.button")
+
+ if err := utils.SendMail(newUserEmail, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendEmailChangeVerifyEmail", "api.user.send_email_change_verify_email_and_forget.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ subject := fmt.Sprintf("[%v] %v", utils.Cfg.TeamSettings.SiteName, T("api.templates.email_change_subject",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName}))
+
+ bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.email_change_body.title")
+ bodyPage.Html["Info"] = template.HTML(T("api.templates.email_change_body.info",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "NewEmail": newEmail}))
+
+ if err := utils.SendMail(oldEmail, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendVerifyEmail(userId, userEmail, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId+utils.Cfg.EmailSettings.InviteSalt), url.QueryEscape(userEmail))
+
+ url, _ := url.Parse(siteURL)
+
+ subject := T("api.templates.verify_subject",
+ map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
+
+ bodyPage := utils.NewHTMLTemplate("verify_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": url.Host})
+ bodyPage.Props["Info"] = T("api.templates.verify_body.info")
+ bodyPage.Props["VerifyUrl"] = link
+ bodyPage.Props["Button"] = T("api.templates.verify_body.button")
+
+ if err := utils.SendMail(userEmail, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendVerifyEmail", "api.user.send_verify_email_and_forget.failed.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendSignInChangeEmail(email, method, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ subject := T("api.templates.singin_change_email.subject",
+ map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
+
+ bodyPage := utils.NewHTMLTemplate("signin_change_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title")
+ bodyPage.Html["Info"] = template.HTML(T("api.templates.singin_change_email.body.info",
+ map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "Method": method}))
+
+ if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendWelcomeEmail(userId string, email string, verified bool, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ rawUrl, _ := url.Parse(siteURL)
+
+ subject := T("api.templates.welcome_subject", map[string]interface{}{"ServerURL": rawUrl.Host})
+
+ bodyPage := utils.NewHTMLTemplate("welcome_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": rawUrl.Host})
+ bodyPage.Props["Info"] = T("api.templates.welcome_body.info")
+ bodyPage.Props["Button"] = T("api.templates.welcome_body.button")
+ bodyPage.Props["Info2"] = T("api.templates.welcome_body.info2")
+ bodyPage.Props["Info3"] = T("api.templates.welcome_body.info3")
+ bodyPage.Props["SiteURL"] = siteURL
+
+ if *utils.Cfg.NativeAppSettings.AppDownloadLink != "" {
+ bodyPage.Props["AppDownloadInfo"] = T("api.templates.welcome_body.app_download_info")
+ bodyPage.Props["AppDownloadLink"] = *utils.Cfg.NativeAppSettings.AppDownloadLink
+ }
+
+ if !verified {
+ link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&email=%s", siteURL, userId, model.HashPassword(userId+utils.Cfg.EmailSettings.InviteSalt), url.QueryEscape(email))
+ bodyPage.Props["VerifyUrl"] = link
+ }
+
+ if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendPasswordChangeEmail(email, method, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ subject := T("api.templates.password_change_subject",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "SiteName": utils.Cfg.TeamSettings.SiteName})
+
+ bodyPage := utils.NewHTMLTemplate("password_change_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = T("api.templates.password_change_body.title")
+ bodyPage.Html["Info"] = template.HTML(T("api.templates.password_change_body.info",
+ map[string]interface{}{"TeamDisplayName": utils.Cfg.TeamSettings.SiteName, "TeamURL": siteURL, "Method": method}))
+
+ if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendMfaChangeEmail(email string, activated bool, locale, siteURL string) *model.AppError {
+ T := utils.GetUserTranslations(locale)
+
+ subject := T("api.templates.mfa_change_subject",
+ map[string]interface{}{"SiteName": utils.Cfg.TeamSettings.SiteName})
+
+ bodyPage := utils.NewHTMLTemplate("mfa_change_body", locale)
+ bodyPage.Props["SiteURL"] = siteURL
+
+ bodyText := ""
+ if activated {
+ bodyText = "api.templates.mfa_activated_body.info"
+ bodyPage.Props["Title"] = T("api.templates.mfa_activated_body.title")
+ } else {
+ bodyText = "api.templates.mfa_deactivated_body.info"
+ bodyPage.Props["Title"] = T("api.templates.mfa_deactivated_body.title")
+ }
+
+ bodyPage.Html["Info"] = template.HTML(T(bodyText,
+ map[string]interface{}{"SiteURL": siteURL}))
+
+ if err := utils.SendMail(email, subject, bodyPage.Render()); err != nil {
+ return model.NewLocAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error())
+ }
+
+ return nil
+}
+
+func SendInviteEmails(team *model.Team, senderName string, invites []string, siteURL string) {
+ for _, invite := range invites {
+ if len(invite) > 0 {
+ senderRole := utils.T("api.team.invite_members.member")
+
+ subject := utils.T("api.templates.invite_subject",
+ map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName, "SiteName": utils.ClientCfg["SiteName"]})
+
+ bodyPage := utils.NewHTMLTemplate("invite_body", model.DEFAULT_LOCALE)
+ bodyPage.Props["SiteURL"] = siteURL
+ bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
+ bodyPage.Html["Info"] = template.HTML(utils.T("api.templates.invite_body.info",
+ map[string]interface{}{"SenderStatus": senderRole, "SenderName": senderName, "TeamDisplayName": team.DisplayName}))
+ bodyPage.Props["Button"] = utils.T("api.templates.invite_body.button")
+ bodyPage.Html["ExtraInfo"] = template.HTML(utils.T("api.templates.invite_body.extra_info",
+ map[string]interface{}{"TeamDisplayName": team.DisplayName, "TeamURL": siteURL + "/" + team.Name}))
+
+ props := make(map[string]string)
+ props["email"] = invite
+ props["id"] = team.Id
+ props["display_name"] = team.DisplayName
+ props["name"] = team.Name
+ props["time"] = fmt.Sprintf("%v", model.GetMillis())
+ data := model.MapToJson(props)
+ hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt))
+ bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", siteURL, url.QueryEscape(data), url.QueryEscape(hash))
+
+ if !utils.Cfg.EmailSettings.SendEmailNotifications {
+ l4g.Info(utils.T("api.team.invite_members.sending.info"), invite, bodyPage.Props["Link"])
+ }
+
+ if err := utils.SendMail(invite, subject, bodyPage.Render()); err != nil {
+ l4g.Error(utils.T("api.team.invite_members.send.error"), err)
+ }
+ }
+ }
+}
diff --git a/app/notification.go b/app/notification.go
index fc1d44f06..060810017 100644
--- a/app/notification.go
+++ b/app/notification.go
@@ -542,7 +542,6 @@ func ClearPushNotification(userId string, channelId string) *model.AppError {
}
func sendToPushProxy(msg model.PushNotification) *model.AppError {
-
msg.ServerId = utils.CfgDiagnosticId
tr := &http.Transport{
diff --git a/app/oauth.go b/app/oauth.go
index 862897b24..3e8b0b8d2 100644
--- a/app/oauth.go
+++ b/app/oauth.go
@@ -27,7 +27,7 @@ func RevokeAccessToken(token string) *model.AppError {
}
if session != nil {
- RemoveAllSessionsForUserId(session.UserId)
+ ClearSessionCacheForUser(session.UserId)
}
return nil
diff --git a/app/session.go b/app/session.go
index 3bb167891..289bb6a2d 100644
--- a/app/session.go
+++ b/app/session.go
@@ -14,6 +14,18 @@ import (
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
+func CreateSession(session *model.Session) (*model.Session, *model.AppError) {
+ if result := <-Srv.Store.Session().Save(session); result.Err != nil {
+ return nil, result.Err
+ } else {
+ session := result.Data.(*model.Session)
+
+ AddSessionToCache(session)
+
+ return session, nil
+ }
+}
+
func GetSession(token string) (*model.Session, *model.AppError) {
metrics := einterfaces.GetMetricsInterface()
@@ -51,16 +63,48 @@ func GetSession(token string) (*model.Session, *model.AppError) {
return session, nil
}
-func RemoveAllSessionsForUserId(userId string) {
+func GetSessions(userId string) ([]*model.Session, *model.AppError) {
+ if result := <-Srv.Store.Session().GetSessions(userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.Session), nil
+ }
+}
+
+func RevokeAllSessions(userId string) *model.AppError {
+ if result := <-Srv.Store.Session().GetSessions(userId); result.Err != nil {
+ return result.Err
+ } else {
+ sessions := result.Data.([]*model.Session)
+
+ for _, session := range sessions {
+ if session.IsOAuth {
+ RevokeAccessToken(session.Token)
+ } else {
+ if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
+ return result.Err
+ }
+ }
+
+ RevokeWebrtcToken(session.Id)
+ }
+ }
+
+ ClearSessionCacheForUser(userId)
+
+ return nil
+}
+
+func ClearSessionCacheForUser(userId string) {
- RemoveAllSessionsForUserIdSkipClusterSend(userId)
+ ClearSessionCacheForUserSkipClusterSend(userId)
if einterfaces.GetClusterInterface() != nil {
- einterfaces.GetClusterInterface().RemoveAllSessionsForUserId(userId)
+ einterfaces.GetClusterInterface().ClearSessionCacheForUser(userId)
}
}
-func RemoveAllSessionsForUserIdSkipClusterSend(userId string) {
+func ClearSessionCacheForUserSkipClusterSend(userId string) {
keys := sessionCache.Keys()
for _, key := range keys {
@@ -132,7 +176,7 @@ func RevokeSession(session *model.Session) *model.AppError {
}
RevokeWebrtcToken(session.Id)
- RemoveAllSessionsForUserId(session.UserId)
+ ClearSessionCacheForUser(session.UserId)
return nil
}
diff --git a/app/session_test.go b/app/session_test.go
index 352395c76..aea31cf86 100644
--- a/app/session_test.go
+++ b/app/session_test.go
@@ -22,7 +22,7 @@ func TestCache(t *testing.T) {
t.Fatal("should have items")
}
- RemoveAllSessionsForUserId(session.UserId)
+ ClearSessionCacheForUser(session.UserId)
rkeys := sessionCache.Keys()
if len(rkeys) != len(keys)-1 {
diff --git a/app/team.go b/app/team.go
index 495e0773f..28d667268 100644
--- a/app/team.go
+++ b/app/team.go
@@ -4,6 +4,11 @@
package app
import (
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/model"
@@ -24,6 +29,57 @@ func CreateTeam(team *model.Team) (*model.Team, *model.AppError) {
}
}
+func UpdateTeam(team *model.Team) (*model.Team, *model.AppError) {
+ var oldTeam *model.Team
+ var err *model.AppError
+ if oldTeam, err = GetTeam(team.Id); err != nil {
+ return nil, err
+ }
+
+ oldTeam.DisplayName = team.DisplayName
+ oldTeam.Description = team.Description
+ oldTeam.InviteId = team.InviteId
+ oldTeam.AllowOpenInvite = team.AllowOpenInvite
+ oldTeam.CompanyName = team.CompanyName
+ oldTeam.AllowedDomains = team.AllowedDomains
+
+ if result := <-Srv.Store.Team().Update(oldTeam); result.Err != nil {
+ return nil, result.Err
+ }
+
+ return oldTeam, nil
+}
+
+func UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) {
+ var member *model.TeamMember
+ if result := <-Srv.Store.Team().GetTeamsForUser(userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ members := result.Data.([]*model.TeamMember)
+ for _, m := range members {
+ if m.TeamId == teamId {
+ member = m
+ }
+ }
+ }
+
+ if member == nil {
+ err := model.NewLocAppError("UpdateTeamMemberRoles", "api.team.update_member_roles.not_a_member", nil, "userId="+userId+" teamId="+teamId)
+ err.StatusCode = http.StatusBadRequest
+ return nil, err
+ }
+
+ member.Roles = newRoles
+
+ if result := <-Srv.Store.Team().UpdateMember(member); result.Err != nil {
+ return nil, result.Err
+ }
+
+ ClearSessionCacheForUser(userId)
+
+ return member, nil
+}
+
func JoinUserToTeamById(teamId string, user *model.User) *model.AppError {
if result := <-Srv.Store.Team().Get(teamId); result.Err != nil {
return result.Err
@@ -32,6 +88,55 @@ func JoinUserToTeamById(teamId string, user *model.User) *model.AppError {
}
}
+func JoinUserToTeamByHash(userId string, hash string, data string) (*model.Team, *model.AppError) {
+ props := model.MapFromJson(strings.NewReader(data))
+
+ if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
+ return nil, model.NewLocAppError("JoinUserToTeamByHash", "api.user.create_user.signup_link_invalid.app_error", nil, "")
+ }
+
+ t, timeErr := strconv.ParseInt(props["time"], 10, 64)
+ if timeErr != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
+ return nil, model.NewLocAppError("JoinUserToTeamByHash", "api.user.create_user.signup_link_expired.app_error", nil, "")
+ }
+
+ var team *model.Team
+ var err *model.AppError
+ if team, err = GetTeam(props["id"]); err != nil {
+ return nil, err
+ }
+
+ var user *model.User
+ if user, err = GetUser(userId); err != nil {
+ return nil, err
+ }
+
+ if err := JoinUserToTeam(team, user); err != nil {
+ return nil, err
+ }
+
+ return team, nil
+}
+
+func JoinUserToTeamByInviteId(inviteId string, userId string) (*model.Team, *model.AppError) {
+ var team *model.Team
+ var err *model.AppError
+ if team, err = GetTeamByInviteId(inviteId); err != nil {
+ return nil, err
+ }
+
+ var user *model.User
+ if user, err = GetUser(userId); err != nil {
+ return nil, err
+ }
+
+ if err := JoinUserToTeam(team, user); err != nil {
+ return nil, err
+ }
+
+ return team, nil
+}
+
func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
tm := &model.TeamMember{
@@ -49,7 +154,7 @@ func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
if etmr := <-Srv.Store.Team().GetMember(team.Id, user.Id); etmr.Err == nil {
// Membership alredy exists. Check if deleted and and update, otherwise do nothing
- rtm := etmr.Data.(model.TeamMember)
+ rtm := etmr.Data.(*model.TeamMember)
// Do nothing if already added
if rtm.DeleteAt == 0 {
@@ -75,12 +180,52 @@ func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err)
}
- RemoveAllSessionsForUserId(user.Id)
+ ClearSessionCacheForUser(user.Id)
InvalidateCacheForUser(user.Id)
return nil
}
+func GetTeam(teamId string) (*model.Team, *model.AppError) {
+ if result := <-Srv.Store.Team().Get(teamId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.Team), nil
+ }
+}
+
+func GetTeamByName(name string) (*model.Team, *model.AppError) {
+ if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.Team), nil
+ }
+}
+
+func GetTeamByInviteId(inviteId string) (*model.Team, *model.AppError) {
+ if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.Team), nil
+ }
+}
+
+func GetAllTeams() ([]*model.Team, *model.AppError) {
+ if result := <-Srv.Store.Team().GetAll(); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.Team), nil
+ }
+}
+
+func GetAllOpenTeams() ([]*model.Team, *model.AppError) {
+ if result := <-Srv.Store.Team().GetAllTeamListing(); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.Team), nil
+ }
+}
+
func GetTeamsForUser(userId string) ([]*model.Team, *model.AppError) {
if result := <-Srv.Store.Team().GetTeamsByUserId(userId); result.Err != nil {
return nil, result.Err
@@ -88,3 +233,206 @@ func GetTeamsForUser(userId string) ([]*model.Team, *model.AppError) {
return result.Data.([]*model.Team), nil
}
}
+
+func GetTeamMember(teamId, userId string) (*model.TeamMember, *model.AppError) {
+ if result := <-Srv.Store.Team().GetMember(teamId, userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.TeamMember), nil
+ }
+}
+
+func GetTeamMembersForUser(userId string) ([]*model.TeamMember, *model.AppError) {
+ if result := <-Srv.Store.Team().GetTeamsForUser(userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.TeamMember), nil
+ }
+}
+
+func GetTeamMembers(teamId string, offset int, limit int) ([]*model.TeamMember, *model.AppError) {
+ if result := <-Srv.Store.Team().GetMembers(teamId, offset, limit); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.TeamMember), nil
+ }
+}
+
+func GetTeamMembersByIds(teamId string, userIds []string) ([]*model.TeamMember, *model.AppError) {
+ if result := <-Srv.Store.Team().GetMembersByIds(teamId, userIds); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.TeamMember), nil
+ }
+}
+
+func LeaveTeam(team *model.Team, user *model.User) *model.AppError {
+ var teamMember *model.TeamMember
+ var err *model.AppError
+
+ if teamMember, err = GetTeamMember(team.Id, user.Id); err != nil {
+ return model.NewLocAppError("LeaveTeam", "api.team.remove_user_from_team.missing.app_error", nil, err.Error())
+ }
+
+ var channelList *model.ChannelList
+
+ if result := <-Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil {
+ if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" {
+ channelList = &model.ChannelList{}
+ } else {
+ return result.Err
+ }
+
+ } else {
+ channelList = result.Data.(*model.ChannelList)
+ }
+
+ for _, channel := range *channelList {
+ if channel.Type != model.CHANNEL_DIRECT {
+ InvalidateCacheForChannel(channel.Id)
+ if result := <-Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil {
+ return result.Err
+ }
+ }
+ }
+
+ // Send the websocket message before we actually do the remove so the user being removed gets it.
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LEAVE_TEAM, team.Id, "", "", nil)
+ message.Add("user_id", user.Id)
+ message.Add("team_id", team.Id)
+ Publish(message)
+
+ teamMember.Roles = ""
+ teamMember.DeleteAt = model.GetMillis()
+
+ if result := <-Srv.Store.Team().UpdateMember(teamMember); result.Err != nil {
+ return result.Err
+ }
+
+ if uua := <-Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil {
+ return uua.Err
+ }
+
+ // delete the preferences that set the last channel used in the team and other team specific preferences
+ if result := <-Srv.Store.Preference().DeleteCategory(user.Id, team.Id); result.Err != nil {
+ return result.Err
+ }
+
+ ClearSessionCacheForUser(user.Id)
+ InvalidateCacheForUser(user.Id)
+
+ return nil
+}
+
+func InviteNewUsersToTeam(emailList []string, teamId, senderId, siteURL string) *model.AppError {
+ tchan := Srv.Store.Team().Get(teamId)
+ uchan := Srv.Store.User().Get(senderId)
+
+ var team *model.Team
+ if result := <-tchan; result.Err != nil {
+ return result.Err
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-uchan; result.Err != nil {
+ return result.Err
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ SendInviteEmails(team, user.GetDisplayName(), emailList, siteURL)
+
+ return nil
+}
+
+func FindTeamByName(name string) bool {
+ if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
+ return false
+ } else {
+ return true
+ }
+}
+
+func GetTeamsUnreadForUser(teamId string, userId string) ([]*model.TeamUnread, *model.AppError) {
+ if result := <-Srv.Store.Team().GetTeamsUnreadForUser(teamId, userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ data := result.Data.([]*model.ChannelUnread)
+ var members []*model.TeamUnread
+ membersMap := make(map[string]*model.TeamUnread)
+
+ unreads := func(cu *model.ChannelUnread, tu *model.TeamUnread) *model.TeamUnread {
+ tu.MentionCount += cu.MentionCount
+
+ if cu.NotifyProps["mark_unread"] != model.CHANNEL_MARK_UNREAD_MENTION {
+ tu.MsgCount += (cu.TotalMsgCount - cu.MsgCount)
+ }
+
+ return tu
+ }
+
+ for i := range data {
+ id := data[i].TeamId
+ if mu, ok := membersMap[id]; ok {
+ membersMap[id] = unreads(data[i], mu)
+ } else {
+ membersMap[id] = unreads(data[i], &model.TeamUnread{
+ MsgCount: 0,
+ MentionCount: 0,
+ TeamId: id,
+ })
+ }
+ }
+
+ for _, val := range membersMap {
+ members = append(members, val)
+ }
+
+ return members, nil
+ }
+}
+
+func PermanentDeleteTeam(team *model.Team) *model.AppError {
+ team.DeleteAt = model.GetMillis()
+ if result := <-Srv.Store.Team().Update(team); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Channel().PermanentDeleteByTeam(team.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Team().RemoveAllMembersByTeam(team.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Team().PermanentDelete(team.Id); result.Err != nil {
+ return result.Err
+ }
+
+ return nil
+}
+
+func GetTeamStats(teamId string) (*model.TeamStats, *model.AppError) {
+ tchan := Srv.Store.Team().GetTotalMemberCount(teamId)
+ achan := Srv.Store.Team().GetActiveMemberCount(teamId)
+
+ stats := &model.TeamStats{}
+ stats.TeamId = teamId
+
+ if result := <-tchan; result.Err != nil {
+ return nil, result.Err
+ } else {
+ stats.TotalMemberCount = result.Data.(int64)
+ }
+
+ if result := <-achan; result.Err != nil {
+ return nil, result.Err
+ } else {
+ stats.ActiveMemberCount = result.Data.(int64)
+ }
+
+ return stats, nil
+}
diff --git a/app/user.go b/app/user.go
index 909c8cca9..8324417e8 100644
--- a/app/user.go
+++ b/app/user.go
@@ -15,11 +15,13 @@ import (
"image/png"
"io"
"io/ioutil"
+ "mime/multipart"
"net/http"
"strconv"
"strings"
l4g "github.com/alecthomas/log4go"
+ "github.com/disintegration/imaging"
"github.com/golang/freetype"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
@@ -132,8 +134,8 @@ func CreateUser(user *model.User) (*model.User, *model.AppError) {
ruser := result.Data.(*model.User)
if user.EmailVerified {
- if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
- l4g.Error(utils.T("api.user.create_user.verified.error"), cresult.Err)
+ if err := VerifyUserEmail(ruser.Id); err != nil {
+ l4g.Error(utils.T("api.user.create_user.verified.error"), err)
}
}
@@ -334,6 +336,14 @@ func GetUsersNotInChannel(teamId string, channelId string, offset int, limit int
}
}
+func GetUsersByIds(userIds []string) (map[string]*model.User, *model.AppError) {
+ if result := <-Srv.Store.User().GetProfileByIds(userIds, true); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(map[string]*model.User), nil
+ }
+}
+
func ActivateMfa(userId, token string) *model.AppError {
mfaInterface := einterfaces.GetMfaInterface()
if mfaInterface == nil {
@@ -479,3 +489,348 @@ func GetProfileImage(user *model.User) ([]byte, *model.AppError) {
return img, nil
}
+
+func SetProfileImage(userId string, imageData *multipart.FileHeader) *model.AppError {
+ file, err := imageData.Open()
+ defer file.Close()
+ if err != nil {
+ return model.NewLocAppError("SetProfileImage", "api.user.upload_profile_user.open.app_error", nil, err.Error())
+ }
+
+ // Decode image config first to check dimensions before loading the whole thing into memory later on
+ config, _, err := image.DecodeConfig(file)
+ if err != nil {
+ return model.NewLocAppError("SetProfileImage", "api.user.upload_profile_user.decode_config.app_error", nil, err.Error())
+ } else if config.Width*config.Height > model.MaxImageSize {
+ return model.NewLocAppError("SetProfileImage", "api.user.upload_profile_user.too_large.app_error", nil, err.Error())
+ }
+
+ file.Seek(0, 0)
+
+ // Decode image into Image object
+ img, _, err := image.Decode(file)
+ if err != nil {
+ return model.NewLocAppError("SetProfileImage", "api.user.upload_profile_user.decode.app_error", nil, err.Error())
+ }
+
+ // Scale profile image
+ img = imaging.Resize(img, utils.Cfg.FileSettings.ProfileWidth, utils.Cfg.FileSettings.ProfileHeight, imaging.Lanczos)
+
+ buf := new(bytes.Buffer)
+ err = png.Encode(buf, img)
+ if err != nil {
+ return model.NewLocAppError("SetProfileImage", "api.user.upload_profile_user.encode.app_error", nil, err.Error())
+ }
+
+ path := "users/" + userId + "/profile.png"
+
+ if err := WriteFile(buf.Bytes(), path); err != nil {
+ return model.NewLocAppError("SetProfileImage", "api.user.upload_profile_user.upload_profile.app_error", nil, "")
+ }
+
+ Srv.Store.User().UpdateLastPictureUpdate(userId)
+
+ if user, err := GetUser(userId); err != nil {
+ l4g.Error(utils.T("api.user.get_me.getting.error"), userId)
+ } else {
+ options := utils.Cfg.GetSanitizeOptions()
+ user.SanitizeProfile(options)
+
+ omitUsers := make(map[string]bool, 1)
+ omitUsers[userId] = true
+ message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", omitUsers)
+ message.Add("user", user)
+
+ Publish(message)
+ }
+
+ return nil
+}
+
+func UpdateActive(user *model.User, active bool) (*model.User, *model.AppError) {
+ if active {
+ user.DeleteAt = 0
+ } else {
+ user.DeleteAt = model.GetMillis()
+ }
+
+ if result := <-Srv.Store.User().Update(user, true); result.Err != nil {
+ return nil, result.Err
+ } else {
+ if user.DeleteAt > 0 {
+ if err := RevokeAllSessions(user.Id); err != nil {
+ return nil, err
+ }
+ }
+
+ if extra := <-Srv.Store.Channel().ExtraUpdateByUser(user.Id, model.GetMillis()); extra.Err != nil {
+ return nil, extra.Err
+ }
+
+ ruser := result.Data.([2]*model.User)[0]
+ options := utils.Cfg.GetSanitizeOptions()
+ options["passwordupdate"] = false
+ ruser.Sanitize(options)
+
+ if !active {
+ SetStatusOffline(ruser.Id, false)
+ }
+
+ return ruser, nil
+ }
+}
+
+func UpdateUser(user *model.User, siteURL string) (*model.User, *model.AppError) {
+ if result := <-Srv.Store.User().Update(user, false); result.Err != nil {
+ return nil, result.Err
+ } else {
+ rusers := result.Data.([2]*model.User)
+
+ if rusers[0].Email != rusers[1].Email {
+ go func() {
+ if err := SendEmailChangeEmail(rusers[1].Email, rusers[0].Email, rusers[0].Locale, siteURL); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ if utils.Cfg.EmailSettings.RequireEmailVerification {
+ go func() {
+ if err := SendEmailChangeVerifyEmail(rusers[0].Id, rusers[0].Email, rusers[0].Locale, siteURL); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+ }
+ }
+
+ if rusers[0].Username != rusers[1].Username {
+ go func() {
+ if err := SendChangeUsernameEmail(rusers[1].Username, rusers[0].Username, rusers[0].Email, rusers[0].Locale, siteURL); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+ }
+
+ InvalidateCacheForUser(user.Id)
+
+ return rusers[0], nil
+ }
+}
+
+func UpdatePassword(user *model.User, hashedPassword string) *model.AppError {
+ if result := <-Srv.Store.User().UpdatePassword(user.Id, hashedPassword); result.Err != nil {
+ return model.NewLocAppError("UpdatePassword", "api.user.update_password.failed.app_error", nil, result.Err.Error())
+ }
+
+ return nil
+}
+
+func UpdatePasswordSendEmail(user *model.User, hashedPassword, method, siteURL string) *model.AppError {
+ if err := UpdatePassword(user, hashedPassword); err != nil {
+ return err
+ }
+
+ go func() {
+ if err := SendPasswordChangeEmail(user.Email, method, user.Locale, siteURL); err != nil {
+ l4g.Error(err.Error())
+ }
+ }()
+
+ return nil
+}
+
+func CreatePasswordRecovery(userId string) (*model.PasswordRecovery, *model.AppError) {
+ recovery := &model.PasswordRecovery{}
+ recovery.UserId = userId
+
+ if result := <-Srv.Store.PasswordRecovery().SaveOrUpdate(recovery); result.Err != nil {
+ return nil, result.Err
+ }
+
+ return recovery, nil
+}
+
+func GetPasswordRecovery(code string) (*model.PasswordRecovery, *model.AppError) {
+ if result := <-Srv.Store.PasswordRecovery().GetByCode(code); result.Err != nil {
+ return nil, model.NewLocAppError("GetPasswordRecovery", "api.user.reset_password.invalid_link.app_error", nil, result.Err.Error())
+ } else {
+ return result.Data.(*model.PasswordRecovery), nil
+ }
+}
+
+func DeletePasswordRecoveryForUser(userId string) *model.AppError {
+ if result := <-Srv.Store.PasswordRecovery().Delete(userId); result.Err != nil {
+ return result.Err
+ }
+
+ return nil
+}
+
+func UpdateUserRoles(userId string, newRoles string) (*model.User, *model.AppError) {
+ var user *model.User
+ var err *model.AppError
+ if user, err = GetUser(userId); err != nil {
+ err.StatusCode = http.StatusBadRequest
+ return nil, err
+ }
+
+ user.Roles = newRoles
+ uchan := Srv.Store.User().Update(user, true)
+ schan := Srv.Store.Session().UpdateRoles(user.Id, newRoles)
+
+ var ruser *model.User
+ if result := <-uchan; result.Err != nil {
+ return nil, result.Err
+ } else {
+ ruser = result.Data.([2]*model.User)[0]
+ }
+
+ if result := <-schan; result.Err != nil {
+ // soft error since the user roles were still updated
+ l4g.Error(result.Err)
+ }
+
+ ClearSessionCacheForUser(user.Id)
+
+ return ruser, nil
+}
+
+func PermanentDeleteUser(user *model.User) *model.AppError {
+ l4g.Warn(utils.T("api.user.permanent_delete_user.attempting.warn"), user.Email, user.Id)
+ if user.IsInRole(model.ROLE_SYSTEM_ADMIN.Id) {
+ l4g.Warn(utils.T("api.user.permanent_delete_user.system_admin.warn"), user.Email)
+ }
+
+ if _, err := UpdateActive(user, false); err != nil {
+ return err
+ }
+
+ if result := <-Srv.Store.Session().PermanentDeleteSessionsByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.OAuth().PermanentDeleteAuthDataByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Webhook().PermanentDeleteIncomingByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Webhook().PermanentDeleteOutgoingByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Command().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Preference().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Channel().PermanentDeleteMembersByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Post().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.User().PermanentDelete(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Audit().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Team().RemoveAllMembersByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.PasswordRecovery().Delete(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ l4g.Warn(utils.T("api.user.permanent_delete_user.deleted.warn"), user.Email, user.Id)
+
+ return nil
+}
+
+func PermanentDeleteAllUsers() *model.AppError {
+ if result := <-Srv.Store.User().GetAll(); result.Err != nil {
+ return result.Err
+ } else {
+ users := result.Data.([]*model.User)
+ for _, user := range users {
+ PermanentDeleteUser(user)
+ }
+ }
+
+ return nil
+}
+
+func VerifyUserEmail(userId string) *model.AppError {
+ if err := (<-Srv.Store.User().VerifyEmail(userId)).Err; err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func SearchUsersInChannel(channelId string, term string, searchOptions map[string]bool) ([]*model.User, *model.AppError) {
+ if result := <-Srv.Store.User().SearchInChannel(channelId, term, searchOptions); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.User), nil
+ }
+}
+
+func SearchUsersNotInChannel(teamId string, channelId string, term string, searchOptions map[string]bool) ([]*model.User, *model.AppError) {
+ if result := <-Srv.Store.User().SearchNotInChannel(teamId, channelId, term, searchOptions); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.User), nil
+ }
+}
+
+func SearchUsersInTeam(teamId string, term string, searchOptions map[string]bool) ([]*model.User, *model.AppError) {
+ if result := <-Srv.Store.User().Search(teamId, term, searchOptions); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.([]*model.User), nil
+ }
+}
+
+func AutocompleteUsersInChannel(teamId string, channelId string, term string, searchOptions map[string]bool) (*model.UserAutocompleteInChannel, *model.AppError) {
+ uchan := Srv.Store.User().SearchInChannel(channelId, term, searchOptions)
+ nuchan := Srv.Store.User().SearchNotInChannel(teamId, channelId, term, searchOptions)
+
+ autocomplete := &model.UserAutocompleteInChannel{}
+
+ if result := <-uchan; result.Err != nil {
+ return nil, result.Err
+ } else {
+ autocomplete.InChannel = result.Data.([]*model.User)
+ }
+
+ if result := <-nuchan; result.Err != nil {
+ return nil, result.Err
+ } else {
+ autocomplete.OutOfChannel = result.Data.([]*model.User)
+ }
+
+ return autocomplete, nil
+}
+
+func AutocompleteUsersInTeam(teamId string, term string, searchOptions map[string]bool) (*model.UserAutocompleteInTeam, *model.AppError) {
+ autocomplete := &model.UserAutocompleteInTeam{}
+
+ if result := <-Srv.Store.User().Search(teamId, term, searchOptions); result.Err != nil {
+ return nil, result.Err
+ } else {
+ autocomplete.InTeam = result.Data.([]*model.User)
+ }
+
+ return autocomplete, nil
+}