diff options
Diffstat (limited to 'api')
-rw-r--r-- | api/admin.go | 22 | ||||
-rw-r--r-- | api/admin_test.go | 4 | ||||
-rw-r--r-- | api/apitestlib.go | 21 | ||||
-rw-r--r-- | api/auto_channels.go | 74 | ||||
-rw-r--r-- | api/auto_constants.go | 36 | ||||
-rw-r--r-- | api/auto_environment.go | 99 | ||||
-rw-r--r-- | api/auto_posts.go | 103 | ||||
-rw-r--r-- | api/auto_teams.go | 81 | ||||
-rw-r--r-- | api/auto_users.go | 110 | ||||
-rw-r--r-- | api/channel.go | 16 | ||||
-rw-r--r-- | api/channel_test.go | 33 | ||||
-rw-r--r-- | api/command.go | 434 | ||||
-rw-r--r-- | api/command_away.go | 43 | ||||
-rw-r--r-- | api/command_echo.go | 97 | ||||
-rw-r--r-- | api/command_expand_collapse.go | 87 | ||||
-rw-r--r-- | api/command_invite_people.go | 64 | ||||
-rw-r--r-- | api/command_join.go | 57 | ||||
-rw-r--r-- | api/command_loadtest.go | 439 | ||||
-rw-r--r-- | api/command_logout.go | 48 | ||||
-rw-r--r-- | api/command_me.go | 37 | ||||
-rw-r--r-- | api/command_msg.go | 95 | ||||
-rw-r--r-- | api/command_offline.go | 43 | ||||
-rw-r--r-- | api/command_online.go | 43 | ||||
-rw-r--r-- | api/command_shortcuts.go | 94 | ||||
-rw-r--r-- | api/command_shrug.go | 42 | ||||
-rw-r--r-- | api/context.go | 6 | ||||
-rw-r--r-- | api/file.go | 2 | ||||
-rw-r--r-- | api/post.go | 55 | ||||
-rw-r--r-- | api/post_test.go | 147 | ||||
-rw-r--r-- | api/user.go | 32 | ||||
-rw-r--r-- | api/user_test.go | 29 |
31 files changed, 366 insertions, 2127 deletions
diff --git a/api/admin.go b/api/admin.go index 3aa1dc67d..59890c739 100644 --- a/api/admin.go +++ b/api/admin.go @@ -46,7 +46,7 @@ func InitAdmin() { } func getLogs(c *Context, w http.ResponseWriter, r *http.Request) { - lines, err := app.GetLogs() + lines, err := app.GetLogs(0, 100000) if err != nil { c.Err = err return @@ -142,7 +142,7 @@ func testEmail(c *Context, w http.ResponseWriter, r *http.Request) { } func getComplianceReports(c *Context, w http.ResponseWriter, r *http.Request) { - crs, err := app.GetComplianceReports() + crs, err := app.GetComplianceReports(0, 10000) if err != nil { c.Err = err return @@ -230,12 +230,6 @@ func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) { } func uploadBrandImage(c *Context, w http.ResponseWriter, r *http.Request) { - if len(utils.Cfg.FileSettings.DriverName) == 0 { - c.Err = model.NewLocAppError("uploadBrandImage", "api.admin.upload_brand_image.storage.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - if r.ContentLength > *utils.Cfg.FileSettings.MaxFileSize { c.Err = model.NewLocAppError("uploadBrandImage", "api.admin.upload_brand_image.too_large.app_error", nil, "") c.Err.StatusCode = http.StatusRequestEntityTooLarge @@ -383,7 +377,7 @@ func addCertificate(c *Context, w http.ResponseWriter, r *http.Request) { fileData := fileArray[0] - if err := app.AddSamlCertificate(fileData); err != nil { + if err := app.WriteSamlFile(fileData); err != nil { c.Err = err return } @@ -393,7 +387,7 @@ func addCertificate(c *Context, w http.ResponseWriter, r *http.Request) { func removeCertificate(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) - if err := app.RemoveSamlCertificate(props["filename"]); err != nil { + if err := app.RemoveSamlFile(props["filename"]); err != nil { c.Err = err return } @@ -403,7 +397,13 @@ func removeCertificate(c *Context, w http.ResponseWriter, r *http.Request) { func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) { status := app.GetSamlCertificateStatus() - w.Write([]byte(model.StringInterfaceToJson(status))) + + statusMap := map[string]interface{}{} + statusMap["IdpCertificateFile"] = status.IdpCertificateFile + statusMap["PrivateKeyFile"] = status.PrivateKeyFile + statusMap["PublicCertificateFile"] = status.PublicCertificateFile + + w.Write([]byte(model.StringInterfaceToJson(statusMap))) } func getRecentlyActiveUsers(c *Context, w http.ResponseWriter, r *http.Request) { diff --git a/api/admin_test.go b/api/admin_test.go index 801ad8f21..dc569620e 100644 --- a/api/admin_test.go +++ b/api/admin_test.go @@ -225,7 +225,7 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { t.Fatal() } - if rows[0].Value != 3 { + if rows[0].Value != 4 { t.Log(rows.ToJson()) t.Fatal() } @@ -245,7 +245,7 @@ func TestGetTeamAnalyticsStandard(t *testing.T) { t.Fatal() } - if rows[2].Value != 5 { + if rows[2].Value != 6 { t.Log(rows.ToJson()) t.Fatal() } diff --git a/api/apitestlib.go b/api/apitestlib.go index ea0d12d4a..89a65518a 100644 --- a/api/apitestlib.go +++ b/api/apitestlib.go @@ -6,6 +6,7 @@ package api import ( "time" + "github.com/mattermost/platform/api4" "github.com/mattermost/platform/app" "github.com/mattermost/platform/model" "github.com/mattermost/platform/store" @@ -21,6 +22,7 @@ type TestHelper struct { BasicUser2 *model.User BasicChannel *model.Channel BasicPost *model.Post + PinnedPost *model.Post SystemAdminClient *model.Client SystemAdminTeam *model.Team @@ -42,6 +44,7 @@ func SetupEnterprise() *TestHelper { InitRouter() app.StartServer() utils.InitHTML() + api4.InitApi(false) InitApi() utils.EnableDebugLogForTest() app.Srv.Store.MarkSystemRanUnitTests() @@ -91,6 +94,9 @@ func (me *TestHelper) InitBasic() *TestHelper { me.BasicChannel = me.CreateChannel(me.BasicClient, me.BasicTeam) me.BasicPost = me.CreatePost(me.BasicClient, me.BasicChannel) + pinnedPostChannel := me.CreateChannel(me.BasicClient, me.BasicTeam) + me.PinnedPost = me.CreatePinnedPost(me.BasicClient, pinnedPostChannel) + return me } @@ -265,6 +271,21 @@ func (me *TestHelper) CreatePost(client *model.Client, channel *model.Channel) * return r } +func (me *TestHelper) CreatePinnedPost(client *model.Client, channel *model.Channel) *model.Post { + id := model.NewId() + + post := &model.Post{ + ChannelId: channel.Id, + Message: "message_" + id, + IsPinned: true, + } + + utils.DisableDebugLogForTest() + r := client.Must(client.CreatePost(post)).Data.(*model.Post) + utils.EnableDebugLogForTest() + return r +} + func (me *TestHelper) LoginBasic() { utils.DisableDebugLogForTest() me.BasicClient.Must(me.BasicClient.Login(me.BasicUser.Email, me.BasicUser.Password)) diff --git a/api/auto_channels.go b/api/auto_channels.go deleted file mode 100644 index 1d0f0e7d9..000000000 --- a/api/auto_channels.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" -) - -type AutoChannelCreator struct { - client *model.Client - team *model.Team - Fuzzy bool - DisplayNameLen utils.Range - DisplayNameCharset string - NameLen utils.Range - NameCharset string - ChannelType string -} - -func NewAutoChannelCreator(client *model.Client, team *model.Team) *AutoChannelCreator { - return &AutoChannelCreator{ - client: client, - team: team, - Fuzzy: false, - DisplayNameLen: CHANNEL_DISPLAY_NAME_LEN, - DisplayNameCharset: utils.ALPHANUMERIC, - NameLen: CHANNEL_NAME_LEN, - NameCharset: utils.LOWERCASE, - ChannelType: CHANNEL_TYPE, - } -} - -func (cfg *AutoChannelCreator) createRandomChannel() (*model.Channel, bool) { - var displayName string - if cfg.Fuzzy { - displayName = utils.FuzzName() - } else { - displayName = utils.RandomName(cfg.NameLen, cfg.NameCharset) - } - name := utils.RandomName(cfg.NameLen, cfg.NameCharset) - - channel := &model.Channel{ - TeamId: cfg.team.Id, - DisplayName: displayName, - Name: name, - Type: cfg.ChannelType} - - println(cfg.client.GetTeamRoute()) - result, err := cfg.client.CreateChannel(channel) - if err != nil { - err.Translate(utils.T) - println(err.Error()) - println(err.DetailedError) - return nil, false - } - return result.Data.(*model.Channel), true -} - -func (cfg *AutoChannelCreator) CreateTestChannels(num utils.Range) ([]*model.Channel, bool) { - numChannels := utils.RandIntFromRange(num) - channels := make([]*model.Channel, numChannels) - - for i := 0; i < numChannels; i++ { - var err bool - channels[i], err = cfg.createRandomChannel() - if err != true { - return channels, false - } - } - - return channels, true -} diff --git a/api/auto_constants.go b/api/auto_constants.go deleted file mode 100644 index a10ae99f2..000000000 --- a/api/auto_constants.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" -) - -const ( - USER_PASSWORD = "passwd" - CHANNEL_TYPE = model.CHANNEL_OPEN - FUZZ_USER_EMAIL_PREFIX_LEN = 10 - BTEST_TEAM_DISPLAY_NAME = "TestTeam" - BTEST_TEAM_NAME = "z-z-testdomaina" - BTEST_TEAM_EMAIL = "test@nowhere.com" - BTEST_TEAM_TYPE = model.TEAM_OPEN - BTEST_USER_NAME = "Mr. Testing Tester" - BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com" - BTEST_USER_PASSWORD = "passwd" -) - -var ( - TEAM_NAME_LEN = utils.Range{Begin: 10, End: 20} - TEAM_DOMAIN_NAME_LEN = utils.Range{Begin: 10, End: 20} - TEAM_EMAIL_LEN = utils.Range{Begin: 15, End: 30} - USER_NAME_LEN = utils.Range{Begin: 5, End: 20} - USER_EMAIL_LEN = utils.Range{Begin: 15, End: 30} - CHANNEL_DISPLAY_NAME_LEN = utils.Range{Begin: 10, End: 20} - CHANNEL_NAME_LEN = utils.Range{Begin: 5, End: 20} - POST_MESSAGE_LEN = utils.Range{Begin: 100, End: 400} - POST_HASHTAGS_NUM = utils.Range{Begin: 5, End: 10} - POST_MENTIONS_NUM = utils.Range{Begin: 0, End: 3} - TEST_IMAGE_FILENAMES = []string{"test.png", "testjpg.jpg", "testgif.gif"} -) diff --git a/api/auto_environment.go b/api/auto_environment.go deleted file mode 100644 index 6c7bc2d0a..000000000 --- a/api/auto_environment.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" - "math/rand" - "time" -) - -type TestEnvironment struct { - Teams []*model.Team - Environments []TeamEnvironment -} - -func CreateTestEnvironmentWithTeams(client *model.Client, rangeTeams utils.Range, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TestEnvironment, bool) { - rand.Seed(time.Now().UTC().UnixNano()) - - teamCreator := NewAutoTeamCreator(client) - teamCreator.Fuzzy = fuzzy - teams, err := teamCreator.CreateTestTeams(rangeTeams) - if err != true { - return TestEnvironment{}, false - } - - environment := TestEnvironment{teams, make([]TeamEnvironment, len(teams))} - - for i, team := range teams { - userCreator := NewAutoUserCreator(client, team) - userCreator.Fuzzy = fuzzy - randomUser, err := userCreator.createRandomUser() - if err != true { - return TestEnvironment{}, false - } - client.LoginById(randomUser.Id, USER_PASSWORD) - client.SetTeamId(team.Id) - teamEnvironment, err := CreateTestEnvironmentInTeam(client, team, rangeChannels, rangeUsers, rangePosts, fuzzy) - if err != true { - return TestEnvironment{}, false - } - environment.Environments[i] = teamEnvironment - } - - return environment, true -} - -func CreateTestEnvironmentInTeam(client *model.Client, team *model.Team, rangeChannels utils.Range, rangeUsers utils.Range, rangePosts utils.Range, fuzzy bool) (TeamEnvironment, bool) { - rand.Seed(time.Now().UTC().UnixNano()) - - // We need to create at least one user - if rangeUsers.Begin <= 0 { - rangeUsers.Begin = 1 - } - - userCreator := NewAutoUserCreator(client, team) - userCreator.Fuzzy = fuzzy - users, err := userCreator.CreateTestUsers(rangeUsers) - if err != true { - return TeamEnvironment{}, false - } - usernames := make([]string, len(users)) - for i, user := range users { - usernames[i] = user.Username - } - - channelCreator := NewAutoChannelCreator(client, team) - channelCreator.Fuzzy = fuzzy - channels, err := channelCreator.CreateTestChannels(rangeChannels) - - // Have every user join every channel - for _, user := range users { - for _, channel := range channels { - client.LoginById(user.Id, USER_PASSWORD) - client.JoinChannel(channel.Id) - } - } - - if err != true { - return TeamEnvironment{}, false - } - - numPosts := utils.RandIntFromRange(rangePosts) - numImages := utils.RandIntFromRange(rangePosts) / 4 - for j := 0; j < numPosts; j++ { - user := users[utils.RandIntFromRange(utils.Range{Begin: 0, End: len(users) - 1})] - client.LoginById(user.Id, USER_PASSWORD) - for i, channel := range channels { - postCreator := NewAutoPostCreator(client, channel.Id) - postCreator.HasImage = i < numImages - postCreator.Users = usernames - postCreator.Fuzzy = fuzzy - postCreator.CreateRandomPost() - } - } - - return TeamEnvironment{users, channels}, true -} diff --git a/api/auto_posts.go b/api/auto_posts.go deleted file mode 100644 index bb20aadae..000000000 --- a/api/auto_posts.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "bytes" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" - "io" - "os" -) - -type AutoPostCreator struct { - client *model.Client - channelid string - Fuzzy bool - TextLength utils.Range - HasImage bool - ImageFilenames []string - Users []string - Mentions utils.Range - Tags utils.Range -} - -// Automatic poster used for testing -func NewAutoPostCreator(client *model.Client, channelid string) *AutoPostCreator { - return &AutoPostCreator{ - client: client, - channelid: channelid, - Fuzzy: false, - TextLength: utils.Range{Begin: 100, End: 200}, - HasImage: false, - ImageFilenames: TEST_IMAGE_FILENAMES, - Users: []string{}, - Mentions: utils.Range{Begin: 0, End: 5}, - Tags: utils.Range{Begin: 0, End: 7}, - } -} - -func (cfg *AutoPostCreator) UploadTestFile() ([]string, bool) { - filename := cfg.ImageFilenames[utils.RandIntFromRange(utils.Range{Begin: 0, End: len(cfg.ImageFilenames) - 1})] - - path := utils.FindDir("web/static/images") - file, err := os.Open(path + "/" + filename) - defer file.Close() - - data := &bytes.Buffer{} - _, err = io.Copy(data, file) - if err != nil { - return nil, false - } - - resp, appErr := cfg.client.UploadPostAttachment(data.Bytes(), cfg.channelid, filename) - if appErr != nil { - return nil, false - } - - return []string{resp.FileInfos[0].Id}, true -} - -func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) { - var fileIds []string - if cfg.HasImage { - var err1 bool - fileIds, err1 = cfg.UploadTestFile() - if err1 == false { - return nil, false - } - } - - var postText string - if cfg.Fuzzy { - postText = utils.FuzzPost() - } else { - postText = utils.RandomText(cfg.TextLength, cfg.Tags, cfg.Mentions, cfg.Users) - } - - post := &model.Post{ - ChannelId: cfg.channelid, - Message: postText, - FileIds: fileIds} - result, err2 := cfg.client.CreatePost(post) - if err2 != nil { - return nil, false - } - return result.Data.(*model.Post), true -} - -func (cfg *AutoPostCreator) CreateTestPosts(rangePosts utils.Range) ([]*model.Post, bool) { - numPosts := utils.RandIntFromRange(rangePosts) - posts := make([]*model.Post, numPosts) - - for i := 0; i < numPosts; i++ { - var err bool - posts[i], err = cfg.CreateRandomPost() - if err != true { - return posts, false - } - } - - return posts, true -} diff --git a/api/auto_teams.go b/api/auto_teams.go deleted file mode 100644 index b2e1ace85..000000000 --- a/api/auto_teams.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" -) - -type TeamEnvironment struct { - Users []*model.User - Channels []*model.Channel -} - -type AutoTeamCreator struct { - client *model.Client - Fuzzy bool - NameLength utils.Range - NameCharset string - DomainLength utils.Range - DomainCharset string - EmailLength utils.Range - EmailCharset string -} - -func NewAutoTeamCreator(client *model.Client) *AutoTeamCreator { - return &AutoTeamCreator{ - client: client, - Fuzzy: false, - NameLength: TEAM_NAME_LEN, - NameCharset: utils.LOWERCASE, - DomainLength: TEAM_DOMAIN_NAME_LEN, - DomainCharset: utils.LOWERCASE, - EmailLength: TEAM_EMAIL_LEN, - EmailCharset: utils.LOWERCASE, - } -} - -func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) { - var teamEmail string - var teamDisplayName string - var teamName string - if cfg.Fuzzy { - teamEmail = "success+" + model.NewId() + "simulator.amazonses.com" - teamDisplayName = utils.FuzzName() - teamName = utils.FuzzName() - } else { - teamEmail = "success+" + model.NewId() + "simulator.amazonses.com" - teamDisplayName = utils.RandomName(cfg.NameLength, cfg.NameCharset) - teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId() - } - team := &model.Team{ - DisplayName: teamDisplayName, - Name: teamName, - Email: teamEmail, - Type: model.TEAM_OPEN, - } - - result, err := cfg.client.CreateTeam(team) - if err != nil { - return nil, false - } - createdTeam := result.Data.(*model.Team) - return createdTeam, true -} - -func (cfg *AutoTeamCreator) CreateTestTeams(num utils.Range) ([]*model.Team, bool) { - numTeams := utils.RandIntFromRange(num) - teams := make([]*model.Team, numTeams) - - for i := 0; i < numTeams; i++ { - var err bool - teams[i], err = cfg.createRandomTeam() - if err != true { - return teams, false - } - } - - return teams, true -} diff --git a/api/auto_users.go b/api/auto_users.go deleted file mode 100644 index d8cd8d3a3..000000000 --- a/api/auto_users.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" - "github.com/mattermost/platform/utils" - - l4g "github.com/alecthomas/log4go" -) - -type AutoUserCreator struct { - client *model.Client - team *model.Team - EmailLength utils.Range - EmailCharset string - NameLength utils.Range - NameCharset string - Fuzzy bool -} - -func NewAutoUserCreator(client *model.Client, team *model.Team) *AutoUserCreator { - return &AutoUserCreator{ - client: client, - team: team, - EmailLength: USER_EMAIL_LEN, - EmailCharset: utils.LOWERCASE, - NameLength: USER_NAME_LEN, - NameCharset: utils.LOWERCASE, - Fuzzy: false, - } -} - -// Basic test team and user so you always know one -func CreateBasicUser(client *model.Client) *model.AppError { - result, _ := client.FindTeamByName(BTEST_TEAM_NAME) - if result.Data.(bool) == false { - newteam := &model.Team{DisplayName: BTEST_TEAM_DISPLAY_NAME, Name: BTEST_TEAM_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE} - result, err := client.CreateTeam(newteam) - if err != nil { - return err - } - basicteam := result.Data.(*model.Team) - newuser := &model.User{Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD} - result, err = client.CreateUser(newuser, "") - if err != nil { - return err - } - ruser := result.Data.(*model.User) - store.Must(app.Srv.Store.User().VerifyEmail(ruser.Id)) - store.Must(app.Srv.Store.Team().SaveMember(&model.TeamMember{TeamId: basicteam.Id, UserId: ruser.Id})) - } - return nil -} - -func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) { - var userEmail string - var userName string - if cfg.Fuzzy { - userEmail = "success+" + model.NewId() + "simulator.amazonses.com" - userName = utils.FuzzName() - } else { - userEmail = "success+" + model.NewId() + "simulator.amazonses.com" - userName = utils.RandomName(cfg.NameLength, cfg.NameCharset) - } - - user := &model.User{ - Email: userEmail, - Nickname: userName, - Password: USER_PASSWORD} - - result, err := cfg.client.CreateUserWithInvite(user, "", "", cfg.team.InviteId) - if err != nil { - err.Translate(utils.T) - l4g.Error(err.Error()) - return nil, false - } - - ruser := result.Data.(*model.User) - - status := &model.Status{UserId: ruser.Id, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} - if result := <-app.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil { - result.Err.Translate(utils.T) - l4g.Error(result.Err.Error()) - return nil, false - } - - // We need to cheat to verify the user's email - store.Must(app.Srv.Store.User().VerifyEmail(ruser.Id)) - - return result.Data.(*model.User), true -} - -func (cfg *AutoUserCreator) CreateTestUsers(num utils.Range) ([]*model.User, bool) { - numUsers := utils.RandIntFromRange(num) - users := make([]*model.User, numUsers) - - for i := 0; i < numUsers; i++ { - var err bool - users[i], err = cfg.createRandomUser() - if err != true { - return users, false - } - } - - return users, true -} diff --git a/api/channel.go b/api/channel.go index 062f582a7..d408e9478 100644 --- a/api/channel.go +++ b/api/channel.go @@ -39,6 +39,7 @@ func InitChannel() { BaseRoutes.NeedChannel.Handle("/stats", ApiUserRequired(getChannelStats)).Methods("GET") BaseRoutes.NeedChannel.Handle("/members/{user_id:[A-Za-z0-9]+}", ApiUserRequired(getChannelMember)).Methods("GET") BaseRoutes.NeedChannel.Handle("/members/ids", ApiUserRequired(getChannelMembersByIds)).Methods("POST") + BaseRoutes.NeedChannel.Handle("/pinned", ApiUserRequired(getPinnedPosts)).Methods("GET") BaseRoutes.NeedChannel.Handle("/join", ApiUserRequired(join)).Methods("POST") BaseRoutes.NeedChannel.Handle("/leave", ApiUserRequired(leave)).Methods("POST") BaseRoutes.NeedChannel.Handle("/delete", ApiUserRequired(deleteChannel)).Methods("POST") @@ -598,6 +599,21 @@ func getMyChannelMembers(c *Context, w http.ResponseWriter, r *http.Request) { } } +func getPinnedPosts(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + channelId := params["channel_id"] + posts := &model.PostList{} + + if result := <-app.Srv.Store.Channel().GetPinnedPosts(channelId); result.Err != nil { + c.Err = result.Err + return + } else { + posts = result.Data.(*model.PostList) + } + + w.Write([]byte(posts.ToJson())) +} + func addMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] diff --git a/api/channel_test.go b/api/channel_test.go index 93c79d416..08136bc35 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -926,8 +926,8 @@ func TestGetMoreChannelsPage(t *testing.T) { } else { channels := r.Data.(*model.ChannelList) - // 1 for BasicChannel, 2 for open channels created above - if len(*channels) != 3 { + // 1 for BasicChannel, 1 for PinnedPostChannel, 2 for open channels created above + if len(*channels) != 4 { t.Fatal("wrong length") } @@ -990,11 +990,11 @@ func TestGetChannelCounts(t *testing.T) { } else { counts := result.Data.(*model.ChannelCounts) - if len(counts.Counts) != 5 { + if len(counts.Counts) != 6 { t.Fatal("wrong number of channel counts") } - if len(counts.UpdateTimes) != 5 { + if len(counts.UpdateTimes) != 6 { t.Fatal("wrong number of channel update times") } @@ -1024,8 +1024,8 @@ func TestGetMyChannelMembers(t *testing.T) { } else { members := result.Data.(*model.ChannelMembers) - // town-square, off-topic, basic test channel, channel1, channel2 - if len(*members) != 5 { + // town-square, off-topic, basic test channel, pinned post channel, channel1, channel2 + if len(*members) != 6 { t.Fatal("wrong number of members", len(*members)) } } @@ -2117,3 +2117,24 @@ func TestUpdateChannelRoles(t *testing.T) { t.Fatal("Channel member should not be able to promote itself to channel admin:", meta) } } + +func TestGetPinnedPosts(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + + post1 := th.BasicPost + r1 := Client.Must(Client.GetPinnedPosts(post1.ChannelId)).Data.(*model.PostList) + if len(r1.Order) != 0 { + t.Fatal("should not have gotten a pinned post") + } + + post2 := th.PinnedPost + r2 := Client.Must(Client.GetPinnedPosts(post2.ChannelId)).Data.(*model.PostList) + if len(r2.Order) == 0 { + t.Fatal("should have gotten a pinned post") + } + + if _, ok := r2.Posts[post2.Id]; !ok { + t.Fatal("missing pinned post") + } +} diff --git a/api/command.go b/api/command.go index 9acc3485c..2248caf76 100644 --- a/api/command.go +++ b/api/command.go @@ -4,11 +4,8 @@ package api import ( - "crypto/tls" - "fmt" "io/ioutil" "net/http" - "net/url" "strings" l4g "github.com/alecthomas/log4go" @@ -18,27 +15,6 @@ import ( "github.com/mattermost/platform/utils" ) -type CommandProvider interface { - GetTrigger() string - GetCommand(c *Context) *model.Command - DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse -} - -var commandProviders = make(map[string]CommandProvider) - -func RegisterCommandProvider(newProvider CommandProvider) { - commandProviders[newProvider.GetTrigger()] = newProvider -} - -func GetCommandProvider(name string) CommandProvider { - provider, ok := commandProviders[name] - if ok { - return provider - } - - return nil -} - func InitCommand() { l4g.Debug(utils.T("api.command.init.debug")) @@ -58,31 +34,10 @@ func InitCommand() { } func listCommands(c *Context, w http.ResponseWriter, r *http.Request) { - commands := make([]*model.Command, 0, 32) - seen := make(map[string]bool) - for _, value := range commandProviders { - cpy := *value.GetCommand(c) - if cpy.AutoComplete && !seen[cpy.Id] { - cpy.Sanitize() - seen[cpy.Trigger] = true - commands = append(commands, &cpy) - } - } - - if *utils.Cfg.ServiceSettings.EnableCommands { - if result := <-app.Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { - c.Err = result.Err - return - } else { - teamCmds := result.Data.([]*model.Command) - for _, cmd := range teamCmds { - if cmd.AutoComplete && !seen[cmd.Id] { - cmd.Sanitize() - seen[cmd.Trigger] = true - commands = append(commands, cmd) - } - } - } + commands, err := app.ListCommands(c.TeamId, c.T) + if err != nil { + c.Err = err + return } w.Write([]byte(model.CommandListToJson(commands))) @@ -90,9 +45,13 @@ func listCommands(c *Context, w http.ResponseWriter, r *http.Request) { func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { commandArgs := model.CommandArgsFromJson(r.Body) + if commandArgs == nil { + c.SetInvalidParam("executeCommand", "command_args") + return + } if len(commandArgs.Command) <= 1 || strings.Index(commandArgs.Command, "/") != 0 { - c.Err = model.NewLocAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "") + c.Err = model.NewAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "", http.StatusBadRequest) return } @@ -103,232 +62,50 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { } } - parts := strings.Split(commandArgs.Command, " ") - trigger := parts[0][1:] - trigger = strings.ToLower(trigger) - message := strings.Join(parts[1:], " ") - provider := GetCommandProvider(trigger) - - if provider != nil { - response := provider.DoCommand(c, commandArgs, message) - handleResponse(c, w, response, commandArgs, provider.GetCommand(c), true) - return - } else { - - if !*utils.Cfg.ServiceSettings.EnableCommands { - c.Err = model.NewLocAppError("executeCommand", "api.command.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - - chanChan := app.Srv.Store.Channel().Get(commandArgs.ChannelId, true) - teamChan := app.Srv.Store.Team().Get(c.TeamId) - userChan := app.Srv.Store.User().Get(c.Session.UserId) - - if result := <-app.Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { - c.Err = result.Err - return - } else { - - var team *model.Team - if tr := <-teamChan; tr.Err != nil { - c.Err = tr.Err - return - } else { - team = tr.Data.(*model.Team) - } - - var user *model.User - if ur := <-userChan; ur.Err != nil { - c.Err = ur.Err - return - } else { - user = ur.Data.(*model.User) - } - - var channel *model.Channel - if cr := <-chanChan; cr.Err != nil { - c.Err = cr.Err - return - } else { - channel = cr.Data.(*model.Channel) - } - - teamCmds := result.Data.([]*model.Command) - for _, cmd := range teamCmds { - if trigger == cmd.Trigger { - l4g.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, c.Session.UserId)) - - p := url.Values{} - p.Set("token", cmd.Token) - - p.Set("team_id", cmd.TeamId) - p.Set("team_domain", team.Name) - - p.Set("channel_id", commandArgs.ChannelId) - p.Set("channel_name", channel.Name) - - p.Set("user_id", c.Session.UserId) - p.Set("user_name", user.Username) - - p.Set("command", "/"+trigger) - p.Set("text", message) - p.Set("response_url", "not supported yet") - - method := "POST" - if cmd.Method == model.COMMAND_METHOD_GET { - method = "GET" - } - - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, - } - client := &http.Client{Transport: tr} - - req, _ := http.NewRequest(method, cmd.URL, strings.NewReader(p.Encode())) - req.Header.Set("Accept", "application/json") - if cmd.Method == model.COMMAND_METHOD_POST { - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - } - - if resp, err := client.Do(req); err != nil { - c.Err = model.NewLocAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": trigger}, err.Error()) - } else { - if resp.StatusCode == http.StatusOK { - response := model.CommandResponseFromJson(resp.Body) - if response == nil { - c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_empty.app_error", map[string]interface{}{"Trigger": trigger}, "") - } else { - handleResponse(c, w, response, commandArgs, cmd, false) - } - } else { - defer resp.Body.Close() - body, _ := ioutil.ReadAll(resp.Body) - c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]interface{}{"Trigger": trigger, "Status": resp.Status}, string(body)) - } - } - - return - } - } - - } - } - - c.Err = model.NewLocAppError("command", "api.command.execute_command.not_found.app_error", map[string]interface{}{"Trigger": trigger}, "") -} + commandArgs.TeamId = c.TeamId + commandArgs.UserId = c.Session.UserId + commandArgs.T = c.T + commandArgs.Session = c.Session + commandArgs.SiteURL = c.GetSiteURL() -func handleResponse(c *Context, w http.ResponseWriter, response *model.CommandResponse, commandArgs *model.CommandArgs, cmd *model.Command, builtIn bool) { - if c.Err != nil { + response, err := app.ExecuteCommand(commandArgs) + if err != nil { + c.Err = err return } - post := &model.Post{} - post.ChannelId = commandArgs.ChannelId - post.RootId = commandArgs.RootId - post.ParentId = commandArgs.ParentId - post.UserId = c.Session.UserId - - if !builtIn { - post.AddProp("from_webhook", "true") - } - - if utils.Cfg.ServiceSettings.EnablePostUsernameOverride { - if len(cmd.Username) != 0 { - post.AddProp("override_username", cmd.Username) - } else if len(response.Username) != 0 { - post.AddProp("override_username", response.Username) - } - } - - if utils.Cfg.ServiceSettings.EnablePostIconOverride { - if len(cmd.IconURL) != 0 { - post.AddProp("override_icon_url", cmd.IconURL) - } else if len(response.IconURL) != 0 { - post.AddProp("override_icon_url", response.IconURL) - } else { - post.AddProp("override_icon_url", "") - } - } - - if _, err := app.CreateCommandPost(post, c.TeamId, response, c.GetSiteURL()); err != nil { - l4g.Error(err.Error()) - } - w.Write([]byte(response.ToJson())) } func createCommand(c *Context, w http.ResponseWriter, r *http.Request) { - if !*utils.Cfg.ServiceSettings.EnableCommands { - c.Err = model.NewLocAppError("createCommand", "api.command.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } + cmd := model.CommandFromJson(r.Body) - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { - c.Err = model.NewLocAppError("createCommand", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + if cmd == nil { + c.SetInvalidParam("createCommand", "command") return } c.LogAudit("attempt") - cmd := model.CommandFromJson(r.Body) - - if cmd == nil { - c.SetInvalidParam("createCommand", "command") + if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS) return } - cmd.Trigger = strings.ToLower(cmd.Trigger) cmd.CreatorId = c.Session.UserId cmd.TeamId = c.TeamId - if result := <-app.Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { - c.Err = result.Err + rcmd, err := app.CreateCommand(cmd) + if err != nil { + c.Err = err return - } else { - teamCmds := result.Data.([]*model.Command) - for _, existingCommand := range teamCmds { - if cmd.Trigger == existingCommand.Trigger { - c.Err = model.NewLocAppError("createCommand", "api.command.duplicate_trigger.app_error", nil, "") - return - } - } - for _, builtInProvider := range commandProviders { - builtInCommand := *builtInProvider.GetCommand(c) - if cmd.Trigger == builtInCommand.Trigger { - c.Err = model.NewLocAppError("createCommand", "api.command.duplicate_trigger.app_error", nil, "") - return - } - } } - if result := <-app.Srv.Store.Command().Save(cmd); result.Err != nil { - c.Err = result.Err - return - } else { - c.LogAudit("success") - rcmd := result.Data.(*model.Command) - w.Write([]byte(rcmd.ToJson())) - } + c.LogAudit("success") + w.Write([]byte(rcmd.ToJson())) } func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) { - if !*utils.Cfg.ServiceSettings.EnableCommands { - c.Err = model.NewLocAppError("updateCommand", "api.command.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented - return - } - - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { - c.Err = model.NewLocAppError("updateCommand", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden - return - } - - c.LogAudit("attempt") - cmd := model.CommandFromJson(r.Body) if cmd == nil { @@ -336,80 +113,58 @@ func updateCommand(c *Context, w http.ResponseWriter, r *http.Request) { return } - cmd.Trigger = strings.ToLower(cmd.Trigger) + c.LogAudit("attempt") - var oldCmd *model.Command - if result := <-app.Srv.Store.Command().Get(cmd.Id); result.Err != nil { - c.Err = result.Err + oldCmd, err := app.GetCommand(cmd.Id) + if err != nil { + c.Err = err return - } else { - oldCmd = result.Data.(*model.Command) - - if c.Session.UserId != oldCmd.CreatorId && !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) { - c.LogAudit("fail - inappropriate permissions") - c.Err = model.NewLocAppError("updateCommand", "api.command.update.app_error", nil, "user_id="+c.Session.UserId) - return - } - - if c.TeamId != oldCmd.TeamId { - c.Err = model.NewLocAppError("updateCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId) - return - } - - cmd.Id = oldCmd.Id - cmd.Token = oldCmd.Token - cmd.CreateAt = oldCmd.CreateAt - cmd.UpdateAt = model.GetMillis() - cmd.DeleteAt = oldCmd.DeleteAt - cmd.CreatorId = oldCmd.CreatorId - cmd.TeamId = oldCmd.TeamId } - if result := <-app.Srv.Store.Command().Update(cmd); result.Err != nil { - c.Err = result.Err + if c.TeamId != oldCmd.TeamId { + c.Err = model.NewAppError("updateCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest) return - } else { - w.Write([]byte(result.Data.(*model.Command).ToJson())) } -} -func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) { - if !*utils.Cfg.ServiceSettings.EnableCommands { - c.Err = model.NewLocAppError("listTeamCommands", "api.command.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented + if !app.SessionHasPermissionToTeam(c.Session, oldCmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.LogAudit("fail - inappropriate permissions") + c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS) return } - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { - c.Err = model.NewLocAppError("listTeamCommands", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + if c.Session.UserId != oldCmd.CreatorId && !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) { + c.LogAudit("fail - inappropriate permissions") + c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) return } - if result := <-app.Srv.Store.Command().GetByTeam(c.TeamId); result.Err != nil { - c.Err = result.Err + rcmd, err := app.UpdateCommand(oldCmd, cmd) + if err != nil { + c.Err = err return - } else { - cmds := result.Data.([]*model.Command) - w.Write([]byte(model.CommandListToJson(cmds))) } + + c.LogAudit("success") + + w.Write([]byte(rcmd.ToJson())) } -func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) { - if !*utils.Cfg.ServiceSettings.EnableCommands { - c.Err = model.NewLocAppError("regenCommandToken", "api.command.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented +func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) { + if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS) return } - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { - c.Err = model.NewLocAppError("regenCommandToken", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + cmds, err := app.ListTeamCommands(c.TeamId) + if err != nil { + c.Err = err return } - c.LogAudit("attempt") + w.Write([]byte(model.CommandListToJson(cmds))) +} +func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) id := props["id"] @@ -418,45 +173,41 @@ func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) { return } - var cmd *model.Command - if result := <-app.Srv.Store.Command().Get(id); result.Err != nil { - c.Err = result.Err - return - } else { - cmd = result.Data.(*model.Command) + c.LogAudit("attempt") - if c.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)) { - c.LogAudit("fail - inappropriate permissions") - c.Err = model.NewLocAppError("regenToken", "api.command.regen.app_error", nil, "user_id="+c.Session.UserId) - return - } + cmd, err := app.GetCommand(id) + if err != nil { + c.Err = err + return } - cmd.Token = model.NewId() + if c.TeamId != cmd.TeamId { + c.Err = model.NewAppError("regenCommandToken", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest) + return + } - if result := <-app.Srv.Store.Command().Update(cmd); result.Err != nil { - c.Err = result.Err + if !app.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.LogAudit("fail - inappropriate permissions") + c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS) return - } else { - w.Write([]byte(result.Data.(*model.Command).ToJson())) } -} -func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) { - if !*utils.Cfg.ServiceSettings.EnableCommands { - c.Err = model.NewLocAppError("deleteCommand", "api.command.disabled.app_error", nil, "") - c.Err.StatusCode = http.StatusNotImplemented + if c.Session.UserId != cmd.CreatorId && !app.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) { + c.LogAudit("fail - inappropriate permissions") + c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) return } - if !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { - c.Err = model.NewLocAppError("deleteCommand", "api.command.admin_only.app_error", nil, "") - c.Err.StatusCode = http.StatusForbidden + rcmd, err := app.RegenCommandToken(cmd) + if err != nil { + c.Err = err return } - c.LogAudit("attempt") + w.Write([]byte(rcmd.ToJson())) +} +func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) id := props["id"] @@ -465,18 +216,33 @@ func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) { return } - if result := <-app.Srv.Store.Command().Get(id); result.Err != nil { - c.Err = result.Err + c.LogAudit("attempt") + + cmd, err := app.GetCommand(id) + if err != nil { + c.Err = err + return + } + + if c.TeamId != cmd.TeamId { + c.Err = model.NewAppError("deleteCommand", "api.command.team_mismatch.app_error", nil, "user_id="+c.Session.UserId, http.StatusBadRequest) + return + } + + if !app.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_SLASH_COMMANDS) { + c.SetPermissionError(model.PERMISSION_MANAGE_SLASH_COMMANDS) + c.LogAudit("fail - inappropriate permissions") + return + } + + if c.Session.UserId != cmd.CreatorId && !app.SessionHasPermissionToTeam(c.Session, cmd.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) { + c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS) + c.LogAudit("fail - inappropriate permissions") return - } else { - if c.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && !app.SessionHasPermissionToTeam(c.Session, c.TeamId, model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS)) { - c.LogAudit("fail - inappropriate permissions") - c.Err = model.NewLocAppError("deleteCommand", "api.command.delete.app_error", nil, "user_id="+c.Session.UserId) - return - } } - if err := (<-app.Srv.Store.Command().Delete(id, model.GetMillis())).Err; err != nil { + err = app.DeleteCommand(cmd.Id) + if err != nil { c.Err = err return } diff --git a/api/command_away.go b/api/command_away.go deleted file mode 100644 index 6a488c081..000000000 --- a/api/command_away.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type AwayProvider struct { -} - -const ( - CMD_AWAY = "away" -) - -func init() { - RegisterCommandProvider(&AwayProvider{}) -} - -func (me *AwayProvider) GetTrigger() string { - return CMD_AWAY -} - -func (me *AwayProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_AWAY, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_away.desc"), - DisplayName: c.T("api.command_away.name"), - } -} - -func (me *AwayProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - rmsg := c.T("api.command_away.success") - if len(message) > 0 { - rmsg = message + " " + rmsg - } - app.SetStatusAwayIfNeeded(c.Session.UserId, true) - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg} -} diff --git a/api/command_echo.go b/api/command_echo.go deleted file mode 100644 index 38e8e07b5..000000000 --- a/api/command_echo.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "strconv" - "strings" - "time" - - l4g "github.com/alecthomas/log4go" - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -var echoSem chan bool - -type EchoProvider struct { -} - -const ( - CMD_ECHO = "echo" -) - -func init() { - RegisterCommandProvider(&EchoProvider{}) -} - -func (me *EchoProvider) GetTrigger() string { - return CMD_ECHO -} - -func (me *EchoProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_ECHO, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_echo.desc"), - AutoCompleteHint: c.T("api.command_echo.hint"), - DisplayName: c.T("api.command_echo.name"), - } -} - -func (me *EchoProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - if len(message) == 0 { - return &model.CommandResponse{Text: c.T("api.command_echo.message.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - maxThreads := 100 - - delay := 0 - if endMsg := strings.LastIndex(message, "\""); string(message[0]) == "\"" && endMsg > 1 { - if checkDelay, err := strconv.Atoi(strings.Trim(message[endMsg:], " \"")); err == nil { - delay = checkDelay - } - message = message[1:endMsg] - } else if strings.Index(message, " ") > -1 { - delayIdx := strings.LastIndex(message, " ") - delayStr := strings.Trim(message[delayIdx:], " ") - - if checkDelay, err := strconv.Atoi(delayStr); err == nil { - delay = checkDelay - message = message[:delayIdx] - } - } - - if delay > 10000 { - return &model.CommandResponse{Text: c.T("api.command_echo.delay.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - if echoSem == nil { - // We want one additional thread allowed so we never reach channel lockup - echoSem = make(chan bool, maxThreads+1) - } - - if len(echoSem) >= maxThreads { - return &model.CommandResponse{Text: c.T("api.command_echo.high_volume.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - echoSem <- true - go func() { - defer func() { <-echoSem }() - post := &model.Post{} - post.ChannelId = args.ChannelId - post.RootId = args.RootId - post.ParentId = args.ParentId - post.Message = message - post.UserId = c.Session.UserId - - time.Sleep(time.Duration(delay) * time.Second) - - if _, err := app.CreatePost(post, c.TeamId, true, c.GetSiteURL()); err != nil { - l4g.Error(c.T("api.command_echo.create.app_error"), err) - } - }() - - return &model.CommandResponse{} -} diff --git a/api/command_expand_collapse.go b/api/command_expand_collapse.go deleted file mode 100644 index 5adbf4bab..000000000 --- a/api/command_expand_collapse.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "strconv" - - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type ExpandProvider struct { -} - -type CollapseProvider struct { -} - -const ( - CMD_EXPAND = "expand" - CMD_COLLAPSE = "collapse" -) - -func init() { - RegisterCommandProvider(&ExpandProvider{}) - RegisterCommandProvider(&CollapseProvider{}) -} - -func (me *ExpandProvider) GetTrigger() string { - return CMD_EXPAND -} - -func (me *CollapseProvider) GetTrigger() string { - return CMD_COLLAPSE -} - -func (me *ExpandProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_EXPAND, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_expand.desc"), - DisplayName: c.T("api.command_expand.name"), - } -} - -func (me *CollapseProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_COLLAPSE, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_collapse.desc"), - DisplayName: c.T("api.command_collapse.name"), - } -} - -func (me *ExpandProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - return setCollapsePreference(c, false) -} - -func (me *CollapseProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - return setCollapsePreference(c, true) -} - -func setCollapsePreference(c *Context, isCollapse bool) *model.CommandResponse { - pref := model.Preference{ - UserId: c.Session.UserId, - Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, - Name: model.PREFERENCE_NAME_COLLAPSE_SETTING, - Value: strconv.FormatBool(isCollapse), - } - - if result := <-app.Srv.Store.Preference().Save(&model.Preferences{pref}); result.Err != nil { - return &model.CommandResponse{Text: c.T("api.command_expand_collapse.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - socketMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCE_CHANGED, "", "", c.Session.UserId, nil) - socketMessage.Add("preference", pref.ToJson()) - go app.Publish(socketMessage) - - var rmsg string - - if isCollapse { - rmsg = c.T("api.command_collapse.success") - } else { - rmsg = c.T("api.command_expand.success") - } - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg} -} diff --git a/api/command_invite_people.go b/api/command_invite_people.go deleted file mode 100644 index b8f1827b0..000000000 --- a/api/command_invite_people.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "strings" - - l4g "github.com/alecthomas/log4go" - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" -) - -type InvitePeopleProvider struct { -} - -const ( - CMD_INVITE_PEOPLE = "invite_people" -) - -func init() { - RegisterCommandProvider(&InvitePeopleProvider{}) -} - -func (me *InvitePeopleProvider) GetTrigger() string { - return CMD_INVITE_PEOPLE -} - -func (me *InvitePeopleProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_INVITE_PEOPLE, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command.invite_people.desc"), - AutoCompleteHint: c.T("api.command.invite_people.hint"), - DisplayName: c.T("api.command.invite_people.name"), - } -} - -func (me *InvitePeopleProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - if !utils.Cfg.EmailSettings.SendEmailNotifications { - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command.invite_people.email_off")} - } - - emailList := strings.Fields(message) - - for i := len(emailList) - 1; i >= 0; i-- { - emailList[i] = strings.Trim(emailList[i], ",") - if !strings.Contains(emailList[i], "@") { - emailList = append(emailList[:i], emailList[i+1:]...) - } - } - - if len(emailList) == 0 { - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command.invite_people.no_email")} - } - - if err := app.InviteNewUsersToTeam(emailList, c.TeamId, c.Session.UserId, c.GetSiteURL()); err != nil { - l4g.Error(err.Error()) - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command.invite_people.fail")} - } - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command.invite_people.sent")} -} diff --git a/api/command_join.go b/api/command_join.go deleted file mode 100644 index 25c62d8c9..000000000 --- a/api/command_join.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type JoinProvider struct { -} - -const ( - CMD_JOIN = "join" -) - -func init() { - RegisterCommandProvider(&JoinProvider{}) -} - -func (me *JoinProvider) GetTrigger() string { - return CMD_JOIN -} - -func (me *JoinProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_JOIN, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_join.desc"), - AutoCompleteHint: c.T("api.command_join.hint"), - DisplayName: c.T("api.command_join.name"), - } -} - -func (me *JoinProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - if result := <-app.Srv.Store.Channel().GetByName(c.TeamId, message, true); result.Err != nil { - return &model.CommandResponse{Text: c.T("api.command_join.list.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - channel := result.Data.(*model.Channel) - - if channel.Name == message { - - if channel.Type != model.CHANNEL_OPEN { - return &model.CommandResponse{Text: c.T("api.command_join.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - if err := app.JoinChannel(channel, c.Session.UserId, c.GetSiteURL()); err != nil { - return &model.CommandResponse{Text: c.T("api.command_join.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - return &model.CommandResponse{GotoLocation: c.GetTeamURL() + "/channels/" + channel.Name, Text: c.T("api.command_join.success"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - } - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command_join.missing.app_error")} -} diff --git a/api/command_loadtest.go b/api/command_loadtest.go deleted file mode 100644 index cc5c19ab7..000000000 --- a/api/command_loadtest.go +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "io" - "net/http" - "path" - "strconv" - "strings" - - l4g "github.com/alecthomas/log4go" - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" -) - -var usage = `Mattermost load testing commands to help configure the system - - COMMANDS: - - Setup - Creates a testing environment in current team. - /loadtest setup [teams] [fuzz] <Num Channels> <Num Users> <NumPosts> - - Example: - /loadtest setup teams fuzz 10 20 50 - - Users - Add a specified number of random users with fuzz text to current team. - /loadtest users [fuzz] <Min Users> <Max Users> - - Example: - /loadtest users fuzz 5 10 - - Channels - Add a specified number of random channels with fuzz text to current team. - /loadtest channels [fuzz] <Min Channels> <Max Channels> - - Example: - /loadtest channels fuzz 5 10 - - Posts - Add some random posts with fuzz text to current channel. - /loadtest posts [fuzz] <Min Posts> <Max Posts> <Max Images> - - Example: - /loadtest posts fuzz 5 10 3 - - Url - Add a post containing the text from a given url to current channel. - /loadtest url - - Example: - /loadtest http://www.example.com/sample_file.md - - Json - Add a post using the JSON file as payload to the current channel. - /loadtest json url - - Example - /loadtest json http://www.example.com/sample_body.json - -` - -const ( - CMD_LOADTEST = "loadtest" -) - -type LoadTestProvider struct { -} - -func init() { - if !utils.Cfg.ServiceSettings.EnableTesting { - RegisterCommandProvider(&LoadTestProvider{}) - } -} - -func (me *LoadTestProvider) GetTrigger() string { - return CMD_LOADTEST -} - -func (me *LoadTestProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_LOADTEST, - AutoComplete: false, - AutoCompleteDesc: "Debug Load Testing", - AutoCompleteHint: "help", - DisplayName: "loadtest", - } -} - -func (me *LoadTestProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - channelId := args.ChannelId - - //This command is only available when EnableTesting is true - if !utils.Cfg.ServiceSettings.EnableTesting { - return &model.CommandResponse{} - } - - if strings.HasPrefix(message, "setup") { - return me.SetupCommand(c, channelId, message) - } - - if strings.HasPrefix(message, "users") { - return me.UsersCommand(c, channelId, message) - } - - if strings.HasPrefix(message, "channels") { - return me.ChannelsCommand(c, channelId, message) - } - - if strings.HasPrefix(message, "posts") { - return me.PostsCommand(c, channelId, message) - } - - if strings.HasPrefix(message, "url") { - return me.UrlCommand(c, channelId, message) - } - if strings.HasPrefix(message, "json") { - return me.JsonCommand(c, channelId, message) - } - return me.HelpCommand(c, channelId, message) -} - -func (me *LoadTestProvider) HelpCommand(c *Context, channelId string, message string) *model.CommandResponse { - return &model.CommandResponse{Text: usage, ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func (me *LoadTestProvider) SetupCommand(c *Context, channelId string, message string) *model.CommandResponse { - tokens := strings.Fields(strings.TrimPrefix(message, "setup")) - doTeams := contains(tokens, "teams") - doFuzz := contains(tokens, "fuzz") - - numArgs := 0 - if doTeams { - numArgs++ - } - if doFuzz { - numArgs++ - } - - var numTeams int - var numChannels int - var numUsers int - var numPosts int - - // Defaults - numTeams = 10 - numChannels = 10 - numUsers = 10 - numPosts = 10 - - if doTeams { - if (len(tokens) - numArgs) >= 4 { - numTeams, _ = strconv.Atoi(tokens[numArgs+0]) - numChannels, _ = strconv.Atoi(tokens[numArgs+1]) - numUsers, _ = strconv.Atoi(tokens[numArgs+2]) - numPosts, _ = strconv.Atoi(tokens[numArgs+3]) - } - } else { - if (len(tokens) - numArgs) >= 3 { - numChannels, _ = strconv.Atoi(tokens[numArgs+0]) - numUsers, _ = strconv.Atoi(tokens[numArgs+1]) - numPosts, _ = strconv.Atoi(tokens[numArgs+2]) - } - } - client := model.NewClient(c.GetSiteURL()) - - if doTeams { - if err := CreateBasicUser(client); err != nil { - return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - client.Login(BTEST_USER_EMAIL, BTEST_USER_PASSWORD) - environment, err := CreateTestEnvironmentWithTeams( - client, - utils.Range{Begin: numTeams, End: numTeams}, - utils.Range{Begin: numChannels, End: numChannels}, - utils.Range{Begin: numUsers, End: numUsers}, - utils.Range{Begin: numPosts, End: numPosts}, - doFuzz) - if err != true { - return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - l4g.Info("Testing environment created") - for i := 0; i < len(environment.Teams); i++ { - l4g.Info("Team Created: " + environment.Teams[i].Name) - l4g.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + USER_PASSWORD) - } - } - } else { - - var team *model.Team - if tr := <-app.Srv.Store.Team().Get(c.TeamId); tr.Err != nil { - return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - team = tr.Data.(*model.Team) - } - - client.MockSession(c.Session.Token) - client.SetTeamId(c.TeamId) - CreateTestEnvironmentInTeam( - client, - team, - utils.Range{Begin: numChannels, End: numChannels}, - utils.Range{Begin: numUsers, End: numUsers}, - utils.Range{Begin: numPosts, End: numPosts}, - doFuzz) - } - - return &model.CommandResponse{Text: "Created enviroment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func (me *LoadTestProvider) UsersCommand(c *Context, channelId string, message string) *model.CommandResponse { - cmd := strings.TrimSpace(strings.TrimPrefix(message, "users")) - - doFuzz := false - if strings.Index(cmd, "fuzz") == 0 { - doFuzz = true - cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz")) - } - - usersr, err := parseRange(cmd, "") - if err == false { - usersr = utils.Range{Begin: 2, End: 5} - } - - var team *model.Team - if tr := <-app.Srv.Store.Team().Get(c.TeamId); tr.Err != nil { - return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - team = tr.Data.(*model.Team) - } - - client := model.NewClient(c.GetSiteURL()) - client.SetTeamId(team.Id) - userCreator := NewAutoUserCreator(client, team) - userCreator.Fuzzy = doFuzz - userCreator.CreateTestUsers(usersr) - - return &model.CommandResponse{Text: "Added users", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func (me *LoadTestProvider) ChannelsCommand(c *Context, channelId string, message string) *model.CommandResponse { - cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels")) - - doFuzz := false - if strings.Index(cmd, "fuzz") == 0 { - doFuzz = true - cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz")) - } - - channelsr, err := parseRange(cmd, "") - if err == false { - channelsr = utils.Range{Begin: 2, End: 5} - } - - var team *model.Team - if tr := <-app.Srv.Store.Team().Get(c.TeamId); tr.Err != nil { - return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - team = tr.Data.(*model.Team) - } - - client := model.NewClient(c.GetSiteURL()) - client.SetTeamId(team.Id) - client.MockSession(c.Session.Token) - channelCreator := NewAutoChannelCreator(client, team) - channelCreator.Fuzzy = doFuzz - channelCreator.CreateTestChannels(channelsr) - - return &model.CommandResponse{Text: "Added channels", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func (me *LoadTestProvider) PostsCommand(c *Context, channelId string, message string) *model.CommandResponse { - cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts")) - - doFuzz := false - if strings.Index(cmd, "fuzz") == 0 { - doFuzz = true - cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz")) - } - - postsr, err := parseRange(cmd, "") - if err == false { - postsr = utils.Range{Begin: 20, End: 30} - } - - tokens := strings.Fields(cmd) - rimages := utils.Range{Begin: 0, End: 0} - if len(tokens) >= 3 { - if numImages, err := strconv.Atoi(tokens[2]); err == nil { - rimages = utils.Range{Begin: numImages, End: numImages} - } - } - - var usernames []string - if result := <-app.Srv.Store.User().GetProfiles(c.TeamId, 0, 1000); result.Err == nil { - profileUsers := result.Data.([]*model.User) - usernames = make([]string, len(profileUsers)) - i := 0 - for _, userprof := range profileUsers { - usernames[i] = userprof.Username - i++ - } - } - - client := model.NewClient(c.GetSiteURL()) - client.SetTeamId(c.TeamId) - client.MockSession(c.Session.Token) - testPoster := NewAutoPostCreator(client, channelId) - testPoster.Fuzzy = doFuzz - testPoster.Users = usernames - - numImages := utils.RandIntFromRange(rimages) - numPosts := utils.RandIntFromRange(postsr) - for i := 0; i < numPosts; i++ { - testPoster.HasImage = (i < numImages) - testPoster.CreateRandomPost() - } - - return &model.CommandResponse{Text: "Added posts", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func (me *LoadTestProvider) UrlCommand(c *Context, channelId string, message string) *model.CommandResponse { - url := strings.TrimSpace(strings.TrimPrefix(message, "url")) - if len(url) == 0 { - return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - // provide a shortcut to easily access tests stored in doc/developer/tests - if !strings.HasPrefix(url, "http") { - url = "https://raw.githubusercontent.com/mattermost/platform/master/tests/" + url - - if path.Ext(url) == "" { - url += ".md" - } - } - - var contents io.ReadCloser - if r, err := http.Get(url); err != nil { - return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else if r.StatusCode > 400 { - return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - contents = r.Body - } - - bytes := make([]byte, 4000) - - // break contents into 4000 byte posts - for { - length, err := contents.Read(bytes) - if err != nil && err != io.EOF { - return &model.CommandResponse{Text: "Encountered error reading file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - if length == 0 { - break - } - - post := &model.Post{} - post.Message = string(bytes[:length]) - post.ChannelId = channelId - post.UserId = c.Session.UserId - - if _, err := app.CreatePost(post, c.TeamId, false, c.GetSiteURL()); err != nil { - return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - } - - return &model.CommandResponse{Text: "Loaded data", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func (me *LoadTestProvider) JsonCommand(c *Context, channelId string, message string) *model.CommandResponse { - url := strings.TrimSpace(strings.TrimPrefix(message, "json")) - if len(url) == 0 { - return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - // provide a shortcut to easily access tests stored in doc/developer/tests - if !strings.HasPrefix(url, "http") { - url = "https://raw.githubusercontent.com/mattermost/platform/master/tests/" + url - - if path.Ext(url) == "" { - url += ".json" - } - } - - var contents io.ReadCloser - if r, err := http.Get(url); err != nil { - return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else if r.StatusCode > 400 { - return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - contents = r.Body - } - - post := model.PostFromJson(contents) - post.ChannelId = channelId - post.UserId = c.Session.UserId - if post.Message == "" { - post.Message = message - } - - if _, err := app.CreatePost(post, c.TeamId, false, c.GetSiteURL()); err != nil { - return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - return &model.CommandResponse{Text: "Loaded data", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} - -func parseRange(command string, cmd string) (utils.Range, bool) { - tokens := strings.Fields(strings.TrimPrefix(command, cmd)) - var begin int - var end int - var err1 error - var err2 error - switch { - case len(tokens) == 1: - begin, err1 = strconv.Atoi(tokens[0]) - end = begin - if err1 != nil { - return utils.Range{Begin: 0, End: 0}, false - } - case len(tokens) >= 2: - begin, err1 = strconv.Atoi(tokens[0]) - end, err2 = strconv.Atoi(tokens[1]) - if err1 != nil || err2 != nil { - return utils.Range{Begin: 0, End: 0}, false - } - default: - return utils.Range{Begin: 0, End: 0}, false - } - return utils.Range{Begin: begin, End: end}, true -} - -func contains(items []string, token string) bool { - for _, elem := range items { - if elem == token { - return true - } - } - return false -} diff --git a/api/command_logout.go b/api/command_logout.go deleted file mode 100644 index 0eaa9a0ba..000000000 --- a/api/command_logout.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type LogoutProvider struct { -} - -const ( - CMD_LOGOUT = "logout" -) - -func init() { - RegisterCommandProvider(&LogoutProvider{}) -} - -func (me *LogoutProvider) GetTrigger() string { - return CMD_LOGOUT -} - -func (me *LogoutProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_LOGOUT, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_logout.desc"), - AutoCompleteHint: "", - DisplayName: c.T("api.command_logout.name"), - } -} - -func (me *LogoutProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - FAIL := &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command_logout.fail_message")} - SUCCESS := &model.CommandResponse{GotoLocation: "/login"} - - // We can't actually remove the user's cookie from here so we just dump their session and let the browser figure it out - if c.Session.Id != "" { - if err := app.RevokeSessionById(c.Session.Id); err != nil { - return FAIL - } - return SUCCESS - } - return FAIL -} diff --git a/api/command_me.go b/api/command_me.go deleted file mode 100644 index a3cda472a..000000000 --- a/api/command_me.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" -) - -type MeProvider struct { -} - -const ( - CMD_ME = "me" -) - -func init() { - RegisterCommandProvider(&MeProvider{}) -} - -func (me *MeProvider) GetTrigger() string { - return CMD_ME -} - -func (me *MeProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_ME, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_me.desc"), - AutoCompleteHint: c.T("api.command_me.hint"), - DisplayName: c.T("api.command_me.name"), - } -} - -func (me *MeProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, Text: "*" + message + "*"} -} diff --git a/api/command_msg.go b/api/command_msg.go deleted file mode 100644 index 6ed101004..000000000 --- a/api/command_msg.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "strings" - - l4g "github.com/alecthomas/log4go" - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type msgProvider struct { -} - -const ( - CMD_MSG = "msg" -) - -func init() { - RegisterCommandProvider(&msgProvider{}) -} - -func (me *msgProvider) GetTrigger() string { - return CMD_MSG -} - -func (me *msgProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_MSG, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_msg.desc"), - AutoCompleteHint: c.T("api.command_msg.hint"), - DisplayName: c.T("api.command_msg.name"), - } -} - -func (me *msgProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - - splitMessage := strings.SplitN(message, " ", 2) - - parsedMessage := "" - targetUsername := "" - - if len(splitMessage) > 1 { - parsedMessage = strings.SplitN(message, " ", 2)[1] - } - targetUsername = strings.SplitN(message, " ", 2)[0] - targetUsername = strings.TrimPrefix(targetUsername, "@") - - var userProfile *model.User - if result := <-app.Srv.Store.User().GetByUsername(targetUsername); result.Err != nil { - l4g.Error(result.Err.Error()) - return &model.CommandResponse{Text: c.T("api.command_msg.missing.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - userProfile = result.Data.(*model.User) - } - - if userProfile.Id == c.Session.UserId { - return &model.CommandResponse{Text: c.T("api.command_msg.missing.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - - // Find the channel based on this user - channelName := model.GetDMNameFromIds(c.Session.UserId, userProfile.Id) - - targetChannelId := "" - if channel := <-app.Srv.Store.Channel().GetByName(c.TeamId, channelName, true); channel.Err != nil { - if channel.Err.Id == "store.sql_channel.get_by_name.missing.app_error" { - if directChannel, err := app.CreateDirectChannel(c.Session.UserId, userProfile.Id); err != nil { - l4g.Error(err.Error()) - return &model.CommandResponse{Text: c.T("api.command_msg.dm_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } else { - targetChannelId = directChannel.Id - } - } else { - l4g.Error(channel.Err.Error()) - return &model.CommandResponse{Text: c.T("api.command_msg.dm_fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - } else { - targetChannelId = channel.Data.(*model.Channel).Id - } - - if len(parsedMessage) > 0 { - post := &model.Post{} - post.Message = parsedMessage - post.ChannelId = targetChannelId - post.UserId = c.Session.UserId - if _, err := app.CreatePost(post, c.TeamId, true, c.GetSiteURL()); err != nil { - return &model.CommandResponse{Text: c.T("api.command_msg.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} - } - } - - return &model.CommandResponse{GotoLocation: c.GetTeamURL() + "/channels/" + channelName, Text: "", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} -} diff --git a/api/command_offline.go b/api/command_offline.go deleted file mode 100644 index a4bcdf8a5..000000000 --- a/api/command_offline.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type OfflineProvider struct { -} - -const ( - CMD_OFFLINE = "offline" -) - -func init() { - RegisterCommandProvider(&OfflineProvider{}) -} - -func (me *OfflineProvider) GetTrigger() string { - return CMD_OFFLINE -} - -func (me *OfflineProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_OFFLINE, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_offline.desc"), - DisplayName: c.T("api.command_offline.name"), - } -} - -func (me *OfflineProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - rmsg := c.T("api.command_offline.success") - if len(message) > 0 { - rmsg = message + " " + rmsg - } - app.SetStatusOffline(c.Session.UserId, true) - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg} -} diff --git a/api/command_online.go b/api/command_online.go deleted file mode 100644 index 81d3e1fd6..000000000 --- a/api/command_online.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/app" - "github.com/mattermost/platform/model" -) - -type OnlineProvider struct { -} - -const ( - CMD_ONLINE = "online" -) - -func init() { - RegisterCommandProvider(&OnlineProvider{}) -} - -func (me *OnlineProvider) GetTrigger() string { - return CMD_ONLINE -} - -func (me *OnlineProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_ONLINE, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_online.desc"), - DisplayName: c.T("api.command_online.name"), - } -} - -func (me *OnlineProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - rmsg := c.T("api.command_online.success") - if len(message) > 0 { - rmsg = message + " " + rmsg - } - app.SetStatusOnline(c.Session.UserId, c.Session.Id, true) - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg} -} diff --git a/api/command_shortcuts.go b/api/command_shortcuts.go deleted file mode 100644 index 1664221c1..000000000 --- a/api/command_shortcuts.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "bytes" - "strings" - - "github.com/mattermost/platform/model" -) - -type ShortcutsProvider struct { -} - -const ( - CMD_SHORTCUTS = "shortcuts" -) - -func init() { - RegisterCommandProvider(&ShortcutsProvider{}) -} - -func (me *ShortcutsProvider) GetTrigger() string { - return CMD_SHORTCUTS -} - -func (me *ShortcutsProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_SHORTCUTS, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_shortcuts.desc"), - AutoCompleteHint: "", - DisplayName: c.T("api.command_shortcuts.name"), - } -} - -func (me *ShortcutsProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - shortcutIds := [28]string{ - "api.command_shortcuts.header", - // Nav shortcuts - "api.command_shortcuts.nav.header", - "api.command_shortcuts.nav.prev", - "api.command_shortcuts.nav.next", - "api.command_shortcuts.nav.unread_prev", - "api.command_shortcuts.nav.unread_next", - "api.command_shortcuts.nav.switcher", - "api.command_shortcuts.nav.settings", - "api.command_shortcuts.nav.recent_mentions", - // Files shortcuts - "api.command_shortcuts.files.header", - "api.command_shortcuts.files.upload", - // Msg shortcuts - "api.command_shortcuts.msgs.header", - "api.command_shortcuts.msgs.mark_as_read", - "api.command_shortcuts.msgs.reprint_prev", - "api.command_shortcuts.msgs.reprint_next", - "api.command_shortcuts.msgs.edit", - "api.command_shortcuts.msgs.comp_username", - "api.command_shortcuts.msgs.comp_channel", - "api.command_shortcuts.msgs.comp_emoji", - // Browser shortcuts - "api.command_shortcuts.browser.header", - "api.command_shortcuts.browser.channel_prev", - "api.command_shortcuts.browser.channel_next", - "api.command_shortcuts.browser.font_increase", - "api.command_shortcuts.browser.font_decrease", - "api.command_shortcuts.browser.highlight_prev", - "api.command_shortcuts.browser.highlight_next", - "api.command_shortcuts.browser.newline", - } - - var osDependentWords map[string]interface{} - if strings.Contains(message, "mac") { - osDependentWords = map[string]interface{}{ - "CmdOrCtrl": c.T("api.command_shortcuts.cmd"), - "ChannelPrevCmd": c.T("api.command_shortcuts.browser.channel_prev.cmd_mac"), - "ChannelNextCmd": c.T("api.command_shortcuts.browser.channel_next.cmd_mac"), - } - } else { - osDependentWords = map[string]interface{}{ - "CmdOrCtrl": c.T("api.command_shortcuts.ctrl"), - "ChannelPrevCmd": c.T("api.command_shortcuts.browser.channel_prev.cmd"), - "ChannelNextCmd": c.T("api.command_shortcuts.browser.channel_next.cmd"), - } - } - - var buffer bytes.Buffer - for _, element := range shortcutIds { - buffer.WriteString(c.T(element, osDependentWords)) - } - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: buffer.String()} -} diff --git a/api/command_shrug.go b/api/command_shrug.go deleted file mode 100644 index 899fcab33..000000000 --- a/api/command_shrug.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package api - -import ( - "github.com/mattermost/platform/model" -) - -type ShrugProvider struct { -} - -const ( - CMD_SHRUG = "shrug" -) - -func init() { - RegisterCommandProvider(&ShrugProvider{}) -} - -func (me *ShrugProvider) GetTrigger() string { - return CMD_SHRUG -} - -func (me *ShrugProvider) GetCommand(c *Context) *model.Command { - return &model.Command{ - Trigger: CMD_SHRUG, - AutoComplete: true, - AutoCompleteDesc: c.T("api.command_shrug.desc"), - AutoCompleteHint: c.T("api.command_shrug.hint"), - DisplayName: c.T("api.command_shrug.name"), - } -} - -func (me *ShrugProvider) DoCommand(c *Context, args *model.CommandArgs, message string) *model.CommandResponse { - rmsg := `¯\\\_(ツ)\_/¯` - if len(message) > 0 { - rmsg = message + " " + rmsg - } - - return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, Text: rmsg} -} diff --git a/api/context.go b/api/context.go index 21989f775..9a707c968 100644 --- a/api/context.go +++ b/api/context.go @@ -102,6 +102,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { now := time.Now() l4g.Debug("%v", r.URL.Path) + if metrics := einterfaces.GetMetricsInterface(); metrics != nil && h.isApi { + metrics.IncrementHttpRequest() + } + c := &Context{} c.T, c.Locale = utils.GetTranslationsAndLocale(w, r) c.RequestId = model.NewId() @@ -250,8 +254,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if h.isApi && einterfaces.GetMetricsInterface() != nil { - einterfaces.GetMetricsInterface().IncrementHttpRequest() - if r.URL.Path != model.API_URL_SUFFIX_V3+"/users/websocket" { elapsed := float64(time.Since(now)) / float64(time.Second) einterfaces.GetMetricsInterface().ObserveHttpRequestDuration(elapsed) diff --git a/api/file.go b/api/file.go index afc0ddbd8..12219a2dc 100644 --- a/api/file.go +++ b/api/file.go @@ -316,5 +316,5 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { return } - w.Write([]byte(model.StringToJson(app.GeneratePublicLink(c.GetSiteURL(), info)))) + w.Write([]byte(model.StringToJson(app.GeneratePublicLinkV3(c.GetSiteURL(), info)))) } diff --git a/api/post.go b/api/post.go index f5b551218..4944ad28f 100644 --- a/api/post.go +++ b/api/post.go @@ -38,6 +38,8 @@ func InitPost() { BaseRoutes.NeedPost.Handle("/before/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsBefore)).Methods("GET") BaseRoutes.NeedPost.Handle("/after/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsAfter)).Methods("GET") BaseRoutes.NeedPost.Handle("/get_file_infos", ApiUserRequired(getFileInfosForPost)).Methods("GET") + BaseRoutes.NeedPost.Handle("/pin", ApiUserRequired(pinPost)).Methods("POST") + BaseRoutes.NeedPost.Handle("/unpin", ApiUserRequired(unpinPost)).Methods("POST") } func createPost(c *Context, w http.ResponseWriter, r *http.Request) { @@ -91,6 +93,59 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(rpost.ToJson())) } +func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinned bool) { + params := mux.Vars(r) + + channelId := params["channel_id"] + if len(channelId) != 26 { + c.SetInvalidParam("savedIsPinnedPost", "channelId") + return + } + + postId := params["post_id"] + if len(postId) != 26 { + c.SetInvalidParam("savedIsPinnedPost", "postId") + return + } + + pchan := app.Srv.Store.Post().Get(postId) + + var oldPost *model.Post + if result := <-pchan; result.Err != nil { + c.Err = result.Err + return + } else { + oldPost = result.Data.(*model.PostList).Posts[postId] + newPost := &model.Post{} + *newPost = *oldPost + newPost.IsPinned = isPinned + + if result := <-app.Srv.Store.Post().Update(newPost, oldPost); result.Err != nil { + c.Err = result.Err + return + } else { + rpost := result.Data.(*model.Post) + + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", rpost.ChannelId, "", nil) + message.Add("post", rpost.ToJson()) + + go app.Publish(message) + + app.InvalidateCacheForChannelPosts(rpost.ChannelId) + + w.Write([]byte(rpost.ToJson())) + } + } +} + +func pinPost(c *Context, w http.ResponseWriter, r *http.Request) { + saveIsPinnedPost(c, w, r, true) +} + +func unpinPost(c *Context, w http.ResponseWriter, r *http.Request) { + saveIsPinnedPost(c, w, r, false) +} + func getFlaggedPosts(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) diff --git a/api/post_test.go b/api/post_test.go index 39ef2f6c0..5546e7165 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -997,51 +997,62 @@ func TestDeletePosts(t *testing.T) { } -// func TestEmailMention(t *testing.T) { -// th := Setup().InitBasic() -// Client := th.BasicClient -// channel1 := th.BasicChannel -// Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id)) - -// th.LoginBasic2() -// //Set the notification properties -// data := make(map[string]string) -// data["user_id"] = th.BasicUser2.Id -// data["email"] = "true" -// data["desktop"] = "all" -// data["desktop_sound"] = "false" -// data["comments"] = "any" -// Client.Must(Client.UpdateUserNotify(data)) - -// store.Must(app.Srv.Store.Preference().Save(&model.Preferences{{ -// UserId: th.BasicUser2.Id, -// Category: model.PREFERENCE_CATEGORY_NOTIFICATIONS, -// Name: model.PREFERENCE_NAME_EMAIL_INTERVAL, -// Value: "0", -// }})) - -// //Delete all the messages before create a mention post -// utils.DeleteMailBox(th.BasicUser2.Email) - -// //Send a mention message from user1 to user2 -// th.LoginBasic() -// time.Sleep(10 * time.Millisecond) -// post1 := &model.Post{ChannelId: channel1.Id, Message: "@" + th.BasicUser2.Username + " this is a test"} -// post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) - -// //Check if the email was send to the rigth email address and the mention -// if resultsMailbox, err := utils.GetMailBox(th.BasicUser2.Email); err != nil && !strings.ContainsAny(resultsMailbox[0].To[0], th.BasicUser2.Email) { -// t.Fatal("Wrong To recipient") -// } else { -// if resultsEmail, err := utils.GetMessageFromMailbox(th.BasicUser2.Email, resultsMailbox[0].ID); err == nil { -// if !strings.Contains(resultsEmail.Body.Text, post1.Message) { -// t.Log(resultsEmail.Body.Text) -// t.Fatal("Received wrong Message") -// } -// } -// } - -// } +func TestEmailMention(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + channel1 := th.BasicChannel + Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id)) + + th.LoginBasic2() + //Set the notification properties + data := make(map[string]string) + data["user_id"] = th.BasicUser2.Id + data["email"] = "true" + data["desktop"] = "all" + data["desktop_sound"] = "false" + data["comments"] = "any" + Client.Must(Client.UpdateUserNotify(data)) + + store.Must(app.Srv.Store.Preference().Save(&model.Preferences{{ + UserId: th.BasicUser2.Id, + Category: model.PREFERENCE_CATEGORY_NOTIFICATIONS, + Name: model.PREFERENCE_NAME_EMAIL_INTERVAL, + Value: "0", + }})) + + //Delete all the messages before create a mention post + utils.DeleteMailBox(th.BasicUser2.Email) + + //Send a mention message from user1 to user2 + th.LoginBasic() + time.Sleep(10 * time.Millisecond) + post1 := &model.Post{ChannelId: channel1.Id, Message: "@" + th.BasicUser2.Username + " this is a test"} + post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post) + + var resultsMailbox utils.JSONMessageHeaderInbucket + err := utils.RetryInbucket(5, func() error { + var err error + resultsMailbox, err = utils.GetMailBox(th.BasicUser2.Email) + return err + }) + if err != nil { + t.Log(err) + t.Log("No email was received, maybe due load on the server. Disabling this verification") + } + if err == nil && len(resultsMailbox) > 0 { + if !strings.ContainsAny(resultsMailbox[0].To[0], th.BasicUser2.Email) { + t.Fatal("Wrong To recipient") + } else { + if resultsEmail, err := utils.GetMessageFromMailbox(th.BasicUser2.Email, resultsMailbox[0].ID); err == nil { + if !strings.Contains(resultsEmail.Body.Text, post1.Message) { + t.Log(resultsEmail.Body.Text) + t.Fatal("Received wrong Message") + } + } + } + } + +} func TestFuzzyPosts(t *testing.T) { th := Setup().InitBasic() @@ -1367,3 +1378,49 @@ func TestGetOpenGraphMetadata(t *testing.T) { t.Fatal("should have failed with 501 - disabled link previews") } } + +func TestPinPost(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + + post := th.BasicPost + if rupost1, err := Client.PinPost(post.ChannelId, post.Id); err != nil { + t.Fatal(err) + } else { + if rupost1.Data.(*model.Post).IsPinned != true { + t.Fatal("failed to pin post") + } + } + + pinnedPost := th.PinnedPost + if rupost2, err := Client.PinPost(pinnedPost.ChannelId, pinnedPost.Id); err != nil { + t.Fatal(err) + } else { + if rupost2.Data.(*model.Post).IsPinned != true { + t.Fatal("pinning a post should be idempotent") + } + } +} + +func TestUnpinPost(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + + pinnedPost := th.PinnedPost + if rupost1, err := Client.UnpinPost(pinnedPost.ChannelId, pinnedPost.Id); err != nil { + t.Fatal(err) + } else { + if rupost1.Data.(*model.Post).IsPinned != false { + t.Fatal("failed to unpin post") + } + } + + post := th.BasicPost + if rupost2, err := Client.UnpinPost(post.ChannelId, post.Id); err != nil { + t.Fatal(err) + } else { + if rupost2.Data.(*model.Post).IsPinned != false { + t.Fatal("unpinning a post should be idempotent") + } + } +} diff --git a/api/user.go b/api/user.go index cac6aeade..24a9025e4 100644 --- a/api/user.go +++ b/api/user.go @@ -1538,11 +1538,11 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) { var profiles []*model.User var err *model.AppError if props.InChannelId != "" { - profiles, err = app.SearchUsersInChannel(props.InChannelId, props.Term, searchOptions) + profiles, err = app.SearchUsersInChannel(props.InChannelId, props.Term, searchOptions, c.IsSystemAdmin()) } else if props.NotInChannelId != "" { - profiles, err = app.SearchUsersNotInChannel(props.TeamId, props.NotInChannelId, props.Term, searchOptions) + profiles, err = app.SearchUsersNotInChannel(props.TeamId, props.NotInChannelId, props.Term, searchOptions, c.IsSystemAdmin()) } else { - profiles, err = app.SearchUsersInTeam(props.TeamId, props.Term, searchOptions) + profiles, err = app.SearchUsersInTeam(props.TeamId, props.Term, searchOptions, c.IsSystemAdmin()) } if err != nil { @@ -1550,10 +1550,6 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) { return } - for _, p := range profiles { - sanitizeProfile(c, p) - } - w.Write([]byte(model.UserListToJson(profiles))) } @@ -1604,20 +1600,12 @@ func autocompleteUsersInChannel(c *Context, w http.ResponseWriter, r *http.Reque searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true } - autocomplete, err := app.AutocompleteUsersInChannel(teamId, channelId, term, searchOptions) + autocomplete, err := app.AutocompleteUsersInChannel(teamId, channelId, term, searchOptions, c.IsSystemAdmin()) if err != nil { c.Err = err return } - for _, p := range autocomplete.InChannel { - sanitizeProfile(c, p) - } - - for _, p := range autocomplete.OutOfChannel { - sanitizeProfile(c, p) - } - w.Write([]byte(autocomplete.ToJson())) } @@ -1642,16 +1630,12 @@ func autocompleteUsersInTeam(c *Context, w http.ResponseWriter, r *http.Request) searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true } - autocomplete, err := app.AutocompleteUsersInTeam(teamId, term, searchOptions) + autocomplete, err := app.AutocompleteUsersInTeam(teamId, term, searchOptions, c.IsSystemAdmin()) if err != nil { c.Err = err return } - for _, p := range autocomplete.InTeam { - sanitizeProfile(c, p) - } - w.Write([]byte(autocomplete.ToJson())) } @@ -1670,14 +1654,10 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) { var profiles []*model.User var err *model.AppError - if profiles, err = app.SearchUsersInTeam("", term, searchOptions); err != nil { + if profiles, err = app.SearchUsersInTeam("", term, searchOptions, c.IsSystemAdmin()); err != nil { c.Err = err return } - for _, p := range profiles { - sanitizeProfile(c, p) - } - w.Write([]byte(model.UserListToJson(profiles))) } diff --git a/api/user_test.go b/api/user_test.go index 2288f2a62..1fdb3a290 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -1330,14 +1330,27 @@ func TestResetPassword(t *testing.T) { } //Check if the email was send to the rigth email address and the recovery key match - if resultsMailbox, err := utils.GetMailBox(user.Email); err != nil && !strings.ContainsAny(resultsMailbox[0].To[0], user.Email) { - t.Fatal("Wrong To recipient") - } else { - if resultsEmail, err := utils.GetMessageFromMailbox(user.Email, resultsMailbox[0].ID); err == nil { - if !strings.Contains(resultsEmail.Body.Text, recovery.Code) { - t.Log(resultsEmail.Body.Text) - t.Log(recovery.Code) - t.Fatal("Received wrong recovery code") + var resultsMailbox utils.JSONMessageHeaderInbucket + err := utils.RetryInbucket(5, func() error { + var err error + resultsMailbox, err = utils.GetMailBox(user.Email) + return err + }) + if err != nil { + t.Log(err) + t.Log("No email was received, maybe due load on the server. Disabling this verification") + } + + if err == nil && len(resultsMailbox) > 0 { + if !strings.ContainsAny(resultsMailbox[0].To[0], user.Email) { + t.Fatal("Wrong To recipient") + } else { + if resultsEmail, err := utils.GetMessageFromMailbox(user.Email, resultsMailbox[0].ID); err == nil { + if !strings.Contains(resultsEmail.Body.Text, recovery.Code) { + t.Log(resultsEmail.Body.Text) + t.Log(recovery.Code) + t.Fatal("Received wrong recovery code") + } } } } |