MM-15452 - Add ability to override LHS icon for bot accounts (#11423)
* MM-15452 - Add ability to override LHS icon for bot accounts * MM-15452 - Added translations * MM-15452 - Updated GetIconImage test to check returned image * MM-15452 - Added Delete handler for /icon endpoint, invalidating user cache on set/delete * MM-15452 - Moved /icon routes under bot/, addressed other pr feedback * MM-15452 - More conflict resolutoin * MM-15452 Restoring api4/user.go * MM-15452 - Using require as opposed to t for test assertions * MM-15452 - Updated as per PR feedback
Этот коммит содержится в:
коммит произвёл
Jesús Espino
родитель
0d05fe32af
Коммит
2ecca12bed
132
api4/bot.go
132
api4/bot.go
@@ -4,7 +4,11 @@
|
||||
package api4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
@@ -17,6 +21,10 @@ func (api *API) InitBot() {
|
||||
api.BaseRoutes.Bot.Handle("/disable", api.ApiSessionRequired(disableBot)).Methods("POST")
|
||||
api.BaseRoutes.Bot.Handle("/enable", api.ApiSessionRequired(enableBot)).Methods("POST")
|
||||
api.BaseRoutes.Bot.Handle("/assign/{user_id:[A-Za-z0-9]+}", api.ApiSessionRequired(assignBot)).Methods("POST")
|
||||
|
||||
api.BaseRoutes.Bot.Handle("/icon", api.ApiSessionRequiredTrustRequester(getBotIconImage)).Methods("GET")
|
||||
api.BaseRoutes.Bot.Handle("/icon", api.ApiSessionRequired(setBotIconImage)).Methods("POST")
|
||||
api.BaseRoutes.Bot.Handle("/icon", api.ApiSessionRequired(deleteBotIconImage)).Methods("DELETE")
|
||||
}
|
||||
|
||||
func createBot(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -217,3 +225,127 @@ func assignBot(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Write(bot.ToJson())
|
||||
}
|
||||
|
||||
func getBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireBotUserId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
botUserId := c.Params.BotUserId
|
||||
|
||||
canSee, err := c.App.UserCanSeeOtherUser(c.App.Session.UserId, botUserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !canSee {
|
||||
c.SetPermissionError(model.PERMISSION_VIEW_MEMBERS)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.App.GetUser(botUserId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
if !user.IsBot {
|
||||
c.Err = model.MakeBotNotFoundError(botUserId)
|
||||
return
|
||||
}
|
||||
|
||||
etag := strconv.FormatInt(user.LastPictureUpdate, 10)
|
||||
if c.HandleEtag(etag, "Get Icon Image", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
img, readFailed, err := c.App.GetBotIconImage(user.Id)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if readFailed {
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%v, public", 5*60)) // 5 mins
|
||||
} else {
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%v, public", 24*60*60)) // 24 hrs
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "image/svg+xml")
|
||||
w.Write(img)
|
||||
}
|
||||
|
||||
func setBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
|
||||
c.RequireBotUserId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
botUserId := c.Params.BotUserId
|
||||
|
||||
if err := c.App.SessionHasPermissionToManageBot(c.App.Session, botUserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.App.GetBot(botUserId, true); err != nil {
|
||||
c.Err = model.MakeBotNotFoundError(botUserId)
|
||||
return
|
||||
}
|
||||
|
||||
if r.ContentLength > *c.App.Config().FileSettings.MaxFileSize {
|
||||
c.Err = model.NewAppError("setBotIconImage", "api.bot.set_bot_icon_image.too_large.app_error", nil, "", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(*c.App.Config().FileSettings.MaxFileSize); err != nil {
|
||||
c.Err = model.NewAppError("setBotIconImage", "api.bot.set_bot_icon_image.parse.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
imageArray, ok := m.File["image"]
|
||||
if !ok {
|
||||
c.Err = model.NewAppError("setBotIconImage", "api.bot.set_bot_icon_image.no_file.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(imageArray) <= 0 {
|
||||
c.Err = model.NewAppError("setBotIconImage", "api.bot.set_bot_icon_image.array.app_error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
imageData := imageArray[0]
|
||||
if err := c.App.SetBotIconImage(botUserId, imageData); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func deleteBotIconImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer io.Copy(ioutil.Discard, r.Body)
|
||||
|
||||
c.RequireBotUserId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
botUserId := c.Params.BotUserId
|
||||
|
||||
if err := c.App.SessionHasPermissionToManageBot(c.App.Session, botUserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.DeleteBotIconImage(botUserId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
196
api4/bot_test.go
196
api4/bot_test.go
@@ -4,11 +4,17 @@
|
||||
package api4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils/fileutils"
|
||||
"github.com/mattermost/mattermost-server/utils/testutils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -1090,6 +1096,196 @@ func TestAssignBot(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetBotIconImage(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
user := th.BasicUser
|
||||
|
||||
defer th.RestoreDefaultRolePermissions(th.SaveDefaultRolePermissions())
|
||||
|
||||
th.AddPermissionToRole(model.PERMISSION_CREATE_BOT.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_BOTS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_READ_BOTS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.EnableBotAccountCreation = true
|
||||
})
|
||||
|
||||
bot := &model.Bot{
|
||||
Username: GenerateTestUsername(),
|
||||
Description: "bot",
|
||||
}
|
||||
bot, resp := th.Client.CreateBot(bot)
|
||||
CheckCreatedStatus(t, resp)
|
||||
defer th.App.PermanentDeleteBot(bot.UserId)
|
||||
|
||||
badData, err := testutils.ReadTestFile("test.png")
|
||||
require.Nil(t, err)
|
||||
|
||||
goodData, err := testutils.ReadTestFile("test.svg")
|
||||
require.Nil(t, err)
|
||||
|
||||
// SetBotIconImage only allowed for bots
|
||||
_, resp = th.SystemAdminClient.SetBotIconImage(user.Id, goodData)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
// png/jpg is not allowed
|
||||
ok, resp := th.Client.SetBotIconImage(bot.UserId, badData)
|
||||
require.False(t, ok, "Should return false, set icon image only allows svg")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
ok, resp = th.Client.SetBotIconImage(model.NewId(), badData)
|
||||
require.False(t, ok, "Should return false, set icon image not allowed")
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
_, resp = th.Client.SetBotIconImage(bot.UserId, goodData)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
// status code returns either forbidden or unauthorized
|
||||
// note: forbidden is set as default at Client4.SetBotIconImage when request is terminated early by server
|
||||
th.Client.Logout()
|
||||
_, resp = th.Client.SetBotIconImage(bot.UserId, badData)
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
CheckForbiddenStatus(t, resp)
|
||||
} else if resp.StatusCode == http.StatusUnauthorized {
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
} else {
|
||||
require.Fail(t, "Should have failed either forbidden or unauthorized")
|
||||
}
|
||||
|
||||
_, resp = th.SystemAdminClient.SetBotIconImage(bot.UserId, goodData)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
info := &model.FileInfo{Path: "/bots/" + bot.UserId + "/icon.svg"}
|
||||
err = th.cleanupTestFile(info)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestGetBotIconImage(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
defer th.RestoreDefaultRolePermissions(th.SaveDefaultRolePermissions())
|
||||
|
||||
th.AddPermissionToRole(model.PERMISSION_CREATE_BOT.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_BOTS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_READ_BOTS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.EnableBotAccountCreation = true
|
||||
})
|
||||
|
||||
bot := &model.Bot{
|
||||
Username: GenerateTestUsername(),
|
||||
Description: "bot",
|
||||
}
|
||||
bot, resp := th.Client.CreateBot(bot)
|
||||
CheckCreatedStatus(t, resp)
|
||||
defer th.App.PermanentDeleteBot(bot.UserId)
|
||||
|
||||
// Get icon image for user with no icon
|
||||
data, resp := th.Client.GetBotIconImage(bot.UserId)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
require.Equal(t, 0, len(data))
|
||||
|
||||
// Set an icon image
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
svgFile, fileErr := os.Open(filepath.Join(path, "test.svg"))
|
||||
require.NoError(t, fileErr)
|
||||
defer svgFile.Close()
|
||||
|
||||
expectedData, err := ioutil.ReadAll(svgFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
svgFile.Seek(0, 0)
|
||||
fpath := fmt.Sprintf("/bots/%v/icon.svg", bot.UserId)
|
||||
_, err = th.App.WriteFile(svgFile, fpath)
|
||||
require.Nil(t, err)
|
||||
|
||||
data, resp = th.Client.GetBotIconImage(bot.UserId)
|
||||
CheckNoError(t, resp)
|
||||
require.Equal(t, expectedData, data)
|
||||
|
||||
_, resp = th.Client.GetBotIconImage("junk")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
_, resp = th.Client.GetBotIconImage(model.NewId())
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
th.Client.Logout()
|
||||
_, resp = th.Client.GetBotIconImage(bot.UserId)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
|
||||
_, resp = th.SystemAdminClient.GetBotIconImage(bot.UserId)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
info := &model.FileInfo{Path: "/bots/" + bot.UserId + "/icon.svg"}
|
||||
err = th.cleanupTestFile(info)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestDeleteBotIconImage(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
defer th.RestoreDefaultRolePermissions(th.SaveDefaultRolePermissions())
|
||||
|
||||
th.AddPermissionToRole(model.PERMISSION_CREATE_BOT.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_BOTS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_READ_BOTS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.EnableBotAccountCreation = true
|
||||
})
|
||||
|
||||
bot := &model.Bot{
|
||||
Username: GenerateTestUsername(),
|
||||
Description: "bot",
|
||||
}
|
||||
bot, resp := th.Client.CreateBot(bot)
|
||||
CheckCreatedStatus(t, resp)
|
||||
defer th.App.PermanentDeleteBot(bot.UserId)
|
||||
|
||||
// Get icon image for user with no icon
|
||||
data, resp := th.Client.GetBotIconImage(bot.UserId)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
require.Equal(t, 0, len(data))
|
||||
|
||||
// Set an icon image
|
||||
svgData, err := testutils.ReadTestFile("test.svg")
|
||||
require.Nil(t, err)
|
||||
|
||||
_, resp = th.Client.SetBotIconImage(bot.UserId, svgData)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
fpath := fmt.Sprintf("/bots/%v/icon.svg", bot.UserId)
|
||||
exists, err := th.App.FileExists(fpath)
|
||||
require.Nil(t, err)
|
||||
require.True(t, exists, "icon.svg needs to exist for the user")
|
||||
|
||||
data, resp = th.Client.GetBotIconImage(bot.UserId)
|
||||
CheckNoError(t, resp)
|
||||
require.Equal(t, svgData, data)
|
||||
|
||||
success, resp := th.Client.DeleteBotIconImage("junk")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
require.False(t, success)
|
||||
|
||||
success, resp = th.Client.DeleteBotIconImage(model.NewId())
|
||||
CheckNotFoundStatus(t, resp)
|
||||
require.False(t, success)
|
||||
|
||||
success, resp = th.Client.DeleteBotIconImage(bot.UserId)
|
||||
CheckNoError(t, resp)
|
||||
require.True(t, success)
|
||||
|
||||
th.Client.Logout()
|
||||
success, resp = th.Client.DeleteBotIconImage(bot.UserId)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
require.False(t, success)
|
||||
|
||||
exists, err = th.App.FileExists(fpath)
|
||||
require.Nil(t, err)
|
||||
require.False(t, exists, "icon.svg should not for the user")
|
||||
}
|
||||
|
||||
func sToP(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
77
app/bot.go
77
app/bot.go
@@ -4,6 +4,10 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
@@ -137,7 +141,7 @@ func (a *App) PermanentDeleteBot(botUserId string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateBotOwner changes a bot's owner to the given value
|
||||
// UpdateBotOwner changes a bot's owner to the given value.
|
||||
func (a *App) UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.AppError) {
|
||||
bot, err := a.Srv.Store.Bot().Get(botUserId, true)
|
||||
if err != nil {
|
||||
@@ -154,7 +158,7 @@ func (a *App) UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.A
|
||||
return bot, nil
|
||||
}
|
||||
|
||||
// disableUserBots disables all bots owned by the given user
|
||||
// disableUserBots disables all bots owned by the given user.
|
||||
func (a *App) disableUserBots(userId string) *model.AppError {
|
||||
perPage := 20
|
||||
for {
|
||||
@@ -188,7 +192,74 @@ func (a *App) disableUserBots(userId string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertUserToBot converts a user to bot
|
||||
// ConvertUserToBot converts a user to bot.
|
||||
func (a *App) ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError) {
|
||||
return a.Srv.Store.Bot().Save(model.BotFromUser(user))
|
||||
}
|
||||
|
||||
// SetBotIconImage sets LHS icon for a bot.
|
||||
func (a *App) SetBotIconImage(botUserId string, imageData *multipart.FileHeader) *model.AppError {
|
||||
if len(*a.Config().FileSettings.DriverName) == 0 {
|
||||
return model.NewAppError("SetBotIconImage", "api.bot.icon_image.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
file, err := imageData.Open()
|
||||
if err != nil {
|
||||
return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.open.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err = parseSVG(file); err != nil {
|
||||
return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.parse.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Set icon
|
||||
file.Seek(0, 0)
|
||||
if _, err := a.WriteFile(file, getBotIconPath(botUserId)); err != nil {
|
||||
return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := a.Srv.Store.User().UpdateLastPictureUpdate(botUserId); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
a.invalidateUserCacheAndPublish(botUserId)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteBotIconImage deletes LHS icon for a bot.
|
||||
func (a *App) DeleteBotIconImage(botUserId string) *model.AppError {
|
||||
if len(*a.Config().FileSettings.DriverName) == 0 {
|
||||
return model.NewAppError("DeleteBotIconImage", "api.bot.icon_image.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// Delete icon
|
||||
if err := a.RemoveFile(getBotIconPath(botUserId)); err != nil {
|
||||
return model.NewAppError("DeleteBotIconImage", "api.bot.delete_bot_icon_image.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := a.Srv.Store.User().UpdateLastPictureUpdate(botUserId); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
a.invalidateUserCacheAndPublish(botUserId)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBotIconImage retrieves LHS icon for a bot.
|
||||
func (a *App) GetBotIconImage(botUserId string) ([]byte, bool, *model.AppError) {
|
||||
if len(*a.Config().FileSettings.DriverName) == 0 {
|
||||
return nil, false, model.NewAppError("GetBotIconImage", "api.bot.icon_image.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
data, err := a.ReadFile(getBotIconPath(botUserId))
|
||||
if err != nil {
|
||||
return nil, false, model.NewAppError("GetBotIconImage", "api.bot.get_bot_icon_image.read.app_error", nil, err.Error(), http.StatusNotFound)
|
||||
}
|
||||
|
||||
return data, false, nil
|
||||
}
|
||||
|
||||
func getBotIconPath(botUserId string) string {
|
||||
return fmt.Sprintf("bots/%v/icon.svg", botUserId)
|
||||
}
|
||||
|
||||
34
app/user.go
34
app/user.go
@@ -899,21 +899,7 @@ func (a *App) SetProfileImageFromFile(userId string, file io.Reader) *model.AppE
|
||||
if err := a.Srv.Store.User().UpdateLastPictureUpdate(userId); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(userId)
|
||||
|
||||
user, userErr := a.GetUser(userId)
|
||||
if userErr != nil {
|
||||
mlog.Error(fmt.Sprintf("Error in getting users profile for id=%v forcing logout", userId), mlog.String("user_id", userId))
|
||||
return nil
|
||||
}
|
||||
|
||||
options := a.Config().GetSanitizeOptions()
|
||||
user.SanitizeProfile(options)
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
a.Publish(message)
|
||||
a.invalidateUserCacheAndPublish(userId)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -2227,3 +2213,21 @@ func (a *App) getListOfAllowedChannelsForTeam(teamId string, viewRestrictions *m
|
||||
|
||||
return listOfAllowedChannels, nil
|
||||
}
|
||||
|
||||
// invalidateUserCacheAndPublish Invalidates cache for a user and publishes user updated event
|
||||
func (a *App) invalidateUserCacheAndPublish(userId string) {
|
||||
a.InvalidateCacheForUser(userId)
|
||||
|
||||
user, userErr := a.GetUser(userId)
|
||||
if userErr != nil {
|
||||
mlog.Error(fmt.Sprintf("Error in getting users profile for id=%v, err=%v", userId, userErr.Error()), mlog.String("user_id", userId))
|
||||
return
|
||||
}
|
||||
|
||||
options := a.Config().GetSanitizeOptions()
|
||||
user.SanitizeProfile(options)
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
36
i18n/en.json
36
i18n/en.json
@@ -135,6 +135,42 @@
|
||||
"id": "api.bot.create_disabled",
|
||||
"translation": "Bot creation has been disabled."
|
||||
},
|
||||
{
|
||||
"id": "api.bot.delete_bot_icon_image.app_error",
|
||||
"translation": "Couldn't delete icon image"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.get_bot_icon_image.read.app_error",
|
||||
"translation": "Unable to read icon image file"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.icon_image.storage.app_error",
|
||||
"translation": "Image storage is not configured."
|
||||
},
|
||||
{
|
||||
"id": "api.bot.set_bot_icon_image.app_error",
|
||||
"translation": "Couldn't upload icon image"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.set_bot_icon_image.array.app_error",
|
||||
"translation": "Empty array under 'image' in request"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.set_bot_icon_image.no_file.app_error",
|
||||
"translation": "No file under 'image' in request"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.set_bot_icon_image.open.app_error",
|
||||
"translation": "Could not open image file"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.set_bot_icon_image.parse.app_error",
|
||||
"translation": "Could not parse multipart form"
|
||||
},
|
||||
{
|
||||
"id": "api.bot.set_bot_icon_image.too_large.app_error",
|
||||
"translation": "Unable to upload icon image. File is too large."
|
||||
},
|
||||
{
|
||||
"id": "api.bot.teams_channels.add_message_mobile",
|
||||
"translation": "Please add me to teams and channels you want me to interact in. To do this, use the browser or Mattermost Desktop App."
|
||||
|
||||
@@ -1497,6 +1497,72 @@ func (c *Client4) AssignBot(botUserId, newOwnerId string) (*Bot, *Response) {
|
||||
return BotFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
|
||||
// SetBotIconImage sets icon image of the user.
|
||||
func (c *Client4) SetBotIconImage(botUserId string, data []byte) (bool, *Response) {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
part, err := writer.CreateFormFile("image", "icon.svg")
|
||||
if err != nil {
|
||||
return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.set_bot_icon_image.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
|
||||
}
|
||||
|
||||
if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
|
||||
return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.set_bot_icon_image.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
|
||||
}
|
||||
|
||||
if err = writer.Close(); err != nil {
|
||||
return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.set_bot_icon_image.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
|
||||
}
|
||||
|
||||
rq, err := http.NewRequest("POST", c.ApiUrl+c.GetBotRoute(botUserId)+"/icon", bytes.NewReader(body.Bytes()))
|
||||
if err != nil {
|
||||
return false, &Response{Error: NewAppError("SetBotIconImage", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)}
|
||||
}
|
||||
rq.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
|
||||
}
|
||||
|
||||
rp, err := c.HttpClient.Do(rq)
|
||||
if err != nil || rp == nil {
|
||||
return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetBotRoute(botUserId)+"/icon", "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)}
|
||||
}
|
||||
defer closeBody(rp)
|
||||
|
||||
if rp.StatusCode >= 300 {
|
||||
return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
|
||||
}
|
||||
|
||||
return CheckStatusOK(rp), BuildResponse(rp)
|
||||
}
|
||||
|
||||
// GetBotIconImage gets user's LHS icon image. Must be logged in.
|
||||
func (c *Client4) GetBotIconImage(botUserId string) ([]byte, *Response) {
|
||||
r, appErr := c.DoApiGet(c.GetBotRoute(botUserId)+"/icon", "")
|
||||
if appErr != nil {
|
||||
return nil, BuildErrorResponse(r, appErr)
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
data, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, NewAppError("GetBotIconImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode))
|
||||
}
|
||||
return data, BuildResponse(r)
|
||||
}
|
||||
|
||||
// DeleteBotIconImage deletes user's LHS icon image. Must be logged in.
|
||||
func (c *Client4) DeleteBotIconImage(botUserId string) (bool, *Response) {
|
||||
r, appErr := c.DoApiDelete(c.GetBotRoute(botUserId) + "/icon")
|
||||
if appErr != nil {
|
||||
return false, BuildErrorResponse(r, appErr)
|
||||
}
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
|
||||
// Team Section
|
||||
|
||||
// CreateTeam creates a team in the system based on the provided team struct.
|
||||
|
||||
14
tests/test.svg
Обычный файл
14
tests/test.svg
Обычный файл
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 3.8.1 (29687) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>github [#142]</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g id="Dribbble-Light-Preview" transform="translate(-140.000000, -7559.000000)" >
|
||||
<g id="icons" transform="translate(56.000000, 160.000000)">
|
||||
<path d="M94,7399 C99.523,7399 104,7403.59 104,7409.253 C104,7413.782 101.138,7417.624 97.167,7418.981 C96.66,7419.082 96.48,7418.762 96.48,7418.489 C96.48,7418.151 96.492,7417.047 96.492,7415.675 C96.492,7414.719 96.172,7414.095 95.813,7413.777 C98.04,7413.523 100.38,7412.656 100.38,7408.718 C100.38,7407.598 99.992,7406.684 99.35,7405.966 C99.454,7405.707 99.797,7404.664 99.252,7403.252 C99.252,7403.252 98.414,7402.977 96.505,7404.303 C95.706,7404.076 94.85,7403.962 94,7403.958 C93.15,7403.962 92.295,7404.076 91.497,7404.303 C89.586,7402.977 88.746,7403.252 88.746,7403.252 C88.203,7404.664 88.546,7405.707 88.649,7405.966 C88.01,7406.684 87.619,7407.598 87.619,7408.718 C87.619,7412.646 89.954,7413.526 92.175,7413.785 C91.889,7414.041 91.63,7414.493 91.54,7415.156 C90.97,7415.418 89.522,7415.871 88.63,7414.304 C88.63,7414.304 88.101,7413.319 87.097,7413.247 C87.097,7413.247 86.122,7413.234 87.029,7413.87 C87.029,7413.87 87.684,7414.185 88.139,7415.37 C88.139,7415.37 88.726,7417.2 91.508,7416.58 C91.513,7417.437 91.522,7418.245 91.522,7418.489 C91.522,7418.76 91.338,7419.077 90.839,7418.982 C86.865,7417.627 84,7413.783 84,7409.253 C84,7403.59 88.478,7399 94,7399" id="github-[#142]"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 1.8 KiB |
Ссылка в новой задаче
Block a user