Merge branch 'master' into PLT-25
Этот коммит содержится в:
@@ -17,14 +17,23 @@ import (
|
||||
|
||||
type commandHandler func(c *Context, command *model.Command) bool
|
||||
|
||||
var commands = []commandHandler{
|
||||
logoutCommand,
|
||||
joinCommand,
|
||||
loadTestCommand,
|
||||
echoCommand,
|
||||
shrugCommand,
|
||||
}
|
||||
|
||||
var (
|
||||
cmds = map[string]string{
|
||||
"logoutCommand": "/logout",
|
||||
"joinCommand": "/join",
|
||||
"loadTestCommand": "/loadtest",
|
||||
"echoCommand": "/echo",
|
||||
"shrugCommand": "/shrug",
|
||||
}
|
||||
commands = []commandHandler{
|
||||
logoutCommand,
|
||||
joinCommand,
|
||||
loadTestCommand,
|
||||
echoCommand,
|
||||
shrugCommand,
|
||||
}
|
||||
commandNotImplementedErr = model.NewAppError("checkCommand", "Command not implemented", "")
|
||||
)
|
||||
var echoSem chan bool
|
||||
|
||||
func InitCommand(r *mux.Router) {
|
||||
@@ -45,7 +54,14 @@ func command(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
checkCommand(c, command)
|
||||
if c.Err != nil {
|
||||
return
|
||||
if c.Err != commandNotImplementedErr {
|
||||
return
|
||||
} else {
|
||||
c.Err = nil
|
||||
command.Response = model.RESP_NOT_IMPLEMENTED
|
||||
w.Write([]byte(command.ToJson()))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
w.Write([]byte(command.ToJson()))
|
||||
}
|
||||
@@ -66,6 +82,23 @@ func checkCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if !command.Suggest {
|
||||
implemented := false
|
||||
for _, cmd := range cmds {
|
||||
bounds := len(cmd)
|
||||
if len(command.Command) < bounds {
|
||||
continue
|
||||
}
|
||||
if command.Command[:bounds] == cmd {
|
||||
implemented = true
|
||||
}
|
||||
}
|
||||
if !implemented {
|
||||
c.Err = commandNotImplementedErr
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range commands {
|
||||
|
||||
if v(c, command) || c.Err != nil {
|
||||
@@ -78,7 +111,7 @@ func checkCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
func logoutCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
cmd := "/logout"
|
||||
cmd := cmds["logoutCommand"]
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Logout"})
|
||||
@@ -97,7 +130,7 @@ func logoutCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
|
||||
func echoCommand(c *Context, command *model.Command) bool {
|
||||
cmd := "/echo"
|
||||
cmd := cmds["echoCommand"]
|
||||
maxThreads := 100
|
||||
|
||||
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
|
||||
@@ -162,10 +195,10 @@ func echoCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
|
||||
func shrugCommand(c *Context, command *model.Command) bool {
|
||||
cmd := "/shrug"
|
||||
cmd := cmds["shrugCommand"]
|
||||
|
||||
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
|
||||
message := "¯\\_(ツ)_/¯"
|
||||
message := `¯\\\_(ツ)_/¯`
|
||||
|
||||
parameters := strings.SplitN(command.Command, " ", 2)
|
||||
if len(parameters) > 1 {
|
||||
@@ -192,7 +225,7 @@ func shrugCommand(c *Context, command *model.Command) bool {
|
||||
func joinCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
// looks for "/join channel-name"
|
||||
cmd := "/join"
|
||||
cmd := cmds["joinCommand"]
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
|
||||
@@ -242,7 +275,7 @@ func joinCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
|
||||
func loadTestCommand(c *Context, command *model.Command) bool {
|
||||
cmd := "/loadtest"
|
||||
cmd := cmds["loadTestCommand"]
|
||||
|
||||
// This command is only available when EnableTesting is true
|
||||
if !utils.Cfg.ServiceSettings.EnableTesting {
|
||||
@@ -304,7 +337,7 @@ func contains(items []string, token string) bool {
|
||||
}
|
||||
|
||||
func loadTestSetupCommand(c *Context, command *model.Command) bool {
|
||||
cmd := "/loadtest setup"
|
||||
cmd := cmds["loadTestCommand"] + " setup"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
|
||||
tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
|
||||
@@ -390,8 +423,8 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
|
||||
func loadTestUsersCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := "/loadtest users"
|
||||
cmd2 := "/loadtest users fuzz"
|
||||
cmd1 := cmds["loadTestCommand"] + " users"
|
||||
cmd2 := cmds["loadTestCommand"] + " users fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
@@ -420,8 +453,8 @@ func loadTestUsersCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
|
||||
func loadTestChannelsCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := "/loadtest channels"
|
||||
cmd2 := "/loadtest channels fuzz"
|
||||
cmd1 := cmds["loadTestCommand"] + " channels"
|
||||
cmd2 := cmds["loadTestCommand"] + " channels fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
@@ -451,8 +484,8 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool {
|
||||
}
|
||||
|
||||
func loadTestPostsCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := "/loadtest posts"
|
||||
cmd2 := "/loadtest posts fuzz"
|
||||
cmd1 := cmds["loadTestCommand"] + " posts"
|
||||
cmd2 := cmds["loadTestCommand"] + " posts fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
|
||||
22
api/file.go
22
api/file.go
@@ -52,6 +52,8 @@ const (
|
||||
RotatedCCW = 6
|
||||
RotatedCCWMirrored = 7
|
||||
RotatedCW = 8
|
||||
|
||||
MaxImageSize = 4096 * 2160 // 4k resolution
|
||||
)
|
||||
|
||||
var fileInfoCache *utils.Cache = utils.NewLru(1000)
|
||||
@@ -125,6 +127,21 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
uid := model.NewId()
|
||||
|
||||
if model.IsFileExtImage(filepath.Ext(files[i].Filename)) {
|
||||
imageNameList = append(imageNameList, uid+"/"+filename)
|
||||
imageDataList = append(imageDataList, buf.Bytes())
|
||||
|
||||
// Decode image config first to check dimensions before loading the whole thing into memory later on
|
||||
config, _, err := image.DecodeConfig(bytes.NewReader(buf.Bytes()))
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("uploadFile", "Unable to upload image file.", err.Error())
|
||||
return
|
||||
} else if config.Width*config.Height > MaxImageSize {
|
||||
c.Err = model.NewAppError("uploadFile", "Unable to upload image file. File is too large.", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + filename
|
||||
|
||||
if err := writeFile(buf.Bytes(), path); err != nil {
|
||||
@@ -132,11 +149,6 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if model.IsFileExtImage(filepath.Ext(files[i].Filename)) {
|
||||
imageNameList = append(imageNameList, uid+"/"+filename)
|
||||
imageDataList = append(imageDataList, buf.Bytes())
|
||||
}
|
||||
|
||||
encName := utils.UrlEncode(filename)
|
||||
|
||||
fileUrl := "/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + encName
|
||||
|
||||
42
api/post.go
42
api/post.go
@@ -820,45 +820,23 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
plainSearchParams, hashtagSearchParams := model.ParseSearchParams(terms)
|
||||
paramsList := model.ParseSearchParams(terms)
|
||||
channels := []store.StoreChannel{}
|
||||
|
||||
var hchan store.StoreChannel
|
||||
if hashtagSearchParams != nil {
|
||||
hchan = Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, hashtagSearchParams)
|
||||
for _, params := range paramsList {
|
||||
channels = append(channels, Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, params))
|
||||
}
|
||||
|
||||
var pchan store.StoreChannel
|
||||
if plainSearchParams != nil {
|
||||
pchan = Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, plainSearchParams)
|
||||
}
|
||||
|
||||
mainList := &model.PostList{}
|
||||
if hchan != nil {
|
||||
if result := <-hchan; result.Err != nil {
|
||||
posts := &model.PostList{}
|
||||
for _, channel := range channels {
|
||||
if result := <-channel; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
mainList = result.Data.(*model.PostList)
|
||||
data := result.Data.(*model.PostList)
|
||||
posts.Extend(data)
|
||||
}
|
||||
}
|
||||
|
||||
plainList := &model.PostList{}
|
||||
if pchan != nil {
|
||||
if result := <-pchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
plainList = result.Data.(*model.PostList)
|
||||
}
|
||||
}
|
||||
|
||||
for _, postId := range plainList.Order {
|
||||
if _, ok := mainList.Posts[postId]; !ok {
|
||||
mainList.AddPost(plainList.Posts[postId])
|
||||
mainList.AddOrder(postId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
w.Write([]byte(mainList.ToJson()))
|
||||
w.Write([]byte(posts.ToJson()))
|
||||
}
|
||||
|
||||
@@ -427,12 +427,18 @@ func TestSearchPostsInChannel(t *testing.T) {
|
||||
channel2 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
|
||||
channel3 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
|
||||
|
||||
post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
post3 := &model.Post{ChannelId: channel2.Id, Message: "other message with no return"}
|
||||
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
|
||||
|
||||
post4 := &model.Post{ChannelId: channel3.Id, Message: "other message with no return"}
|
||||
post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("channel:")).Data.(*model.PostList); len(result.Order) != 0 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
@@ -476,6 +482,10 @@ func TestSearchPostsInChannel(t *testing.T) {
|
||||
if result := Client.Must(Client.SearchPosts("sgtitlereview channel: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 1 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("channel: " + channel2.Name + " channel: " + channel3.Name)).Data.(*model.PostList); len(result.Order) != 3 {
|
||||
t.Fatalf("wrong number of posts returned :) %v :) %v", result.Posts, result.Order)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchPostsFromUser(t *testing.T) {
|
||||
@@ -510,11 +520,12 @@ func TestSearchPostsFromUser(t *testing.T) {
|
||||
post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"}
|
||||
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
|
||||
|
||||
// includes "X has joined the channel" messages for both user2 and user3
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user1.Username)).Data.(*model.PostList); len(result.Order) != 1 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
|
||||
// note that this includes the "User2 has joined the channel" system messages
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user2.Username)).Data.(*model.PostList); len(result.Order) != 3 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
@@ -526,6 +537,33 @@ func TestSearchPostsFromUser(t *testing.T) {
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " in:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 1 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
|
||||
user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user3.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user3.Email, "pwd")
|
||||
Client.Must(Client.JoinChannel(channel1.Id))
|
||||
Client.Must(Client.JoinChannel(channel2.Id))
|
||||
|
||||
// wait for the join/leave messages to be created for user3 since they're done asynchronously
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user2.Username)).Data.(*model.PostList); len(result.Order) != 3 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username)).Data.(*model.PostList); len(result.Order) != 5 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username + " in:" + channel2.Name)).Data.(*model.PostList); len(result.Order) != 3 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
|
||||
if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username + " in:" + channel2.Name + " joined")).Data.(*model.PostList); len(result.Order) != 2 {
|
||||
t.Fatalf("wrong number of posts returned %v", len(result.Order))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPostsCache(t *testing.T) {
|
||||
|
||||
13
api/user.go
13
api/user.go
@@ -814,6 +814,7 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "max-age=86400, public") // 24 hrs
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
w.Write(img)
|
||||
}
|
||||
}
|
||||
@@ -854,6 +855,18 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Decode image config first to check dimensions before loading the whole thing into memory later on
|
||||
config, _, err := image.DecodeConfig(file)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("uploadProfileFile", "Could not decode profile image config.", err.Error())
|
||||
return
|
||||
} else if config.Width*config.Height > MaxImageSize {
|
||||
c.Err = model.NewAppError("uploadProfileFile", "Unable to upload profile image. File is too large.", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
file.Seek(0, 0)
|
||||
|
||||
// Decode image into Image object
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
|
||||
Ссылка в новой задаче
Block a user