summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/admin.go22
-rw-r--r--api/admin_test.go4
-rw-r--r--api/apitestlib.go21
-rw-r--r--api/auto_channels.go74
-rw-r--r--api/auto_constants.go36
-rw-r--r--api/auto_environment.go99
-rw-r--r--api/auto_posts.go103
-rw-r--r--api/auto_teams.go81
-rw-r--r--api/auto_users.go110
-rw-r--r--api/channel.go16
-rw-r--r--api/channel_test.go33
-rw-r--r--api/command.go434
-rw-r--r--api/command_away.go43
-rw-r--r--api/command_echo.go97
-rw-r--r--api/command_expand_collapse.go87
-rw-r--r--api/command_invite_people.go64
-rw-r--r--api/command_join.go57
-rw-r--r--api/command_loadtest.go439
-rw-r--r--api/command_logout.go48
-rw-r--r--api/command_me.go37
-rw-r--r--api/command_msg.go95
-rw-r--r--api/command_offline.go43
-rw-r--r--api/command_online.go43
-rw-r--r--api/command_shortcuts.go94
-rw-r--r--api/command_shrug.go42
-rw-r--r--api/context.go6
-rw-r--r--api/file.go2
-rw-r--r--api/post.go57
-rw-r--r--api/post_test.go147
-rw-r--r--api/user.go42
-rw-r--r--api/user_test.go82
-rw-r--r--api/websocket.go17
-rw-r--r--api/websocket_test.go30
33 files changed, 466 insertions, 2139 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 81df9b388..bace5df5c 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -984,8 +984,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")
}
@@ -1048,11 +1048,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")
}
@@ -1082,8 +1082,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))
}
}
@@ -2203,3 +2203,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 1305f192a..1b251eb53 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..afe60144d 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)
@@ -475,11 +530,11 @@ func getOpenGraphMetadata(c *Context, w http.ResponseWriter, r *http.Request) {
og := app.GetOpenGraphMetadata(url)
ogJSON, err := og.ToJSON()
+ openGraphDataCache.AddWithExpiresInSecs(props["url"], ogJSON, 3600) // Cache would expire after 1 hour
if err != nil {
w.Write([]byte(`{"url": ""}`))
return
}
- openGraphDataCache.AddWithExpiresInSecs(props["url"], ogJSON, 3600) // Cache would expire after 1 houre
w.Write(ogJSON)
}
diff --git a/api/post_test.go b/api/post_test.go
index 46ab9cb6c..6558aeb5b 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -1010,51 +1010,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()
@@ -1380,3 +1391,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..795e83a2a 100644
--- a/api/user.go
+++ b/api/user.go
@@ -1535,26 +1535,12 @@ 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)
- } else if props.NotInChannelId != "" {
- profiles, err = app.SearchUsersNotInChannel(props.TeamId, props.NotInChannelId, props.Term, searchOptions)
- } else {
- profiles, err = app.SearchUsersInTeam(props.TeamId, props.Term, searchOptions)
- }
-
- if err != nil {
+ if profiles, err := app.SearchUsers(props, searchOptions, c.IsSystemAdmin()); err != nil {
c.Err = err
return
+ } else {
+ w.Write([]byte(model.UserListToJson(profiles)))
}
-
- for _, p := range profiles {
- sanitizeProfile(c, p)
- }
-
- w.Write([]byte(model.UserListToJson(profiles)))
}
func getProfilesByIds(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -1604,20 +1590,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 +1620,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 +1644,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..cdbccc57e 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")
+ }
}
}
}
@@ -2427,6 +2440,59 @@ func TestSearchUsers(t *testing.T) {
}
if _, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username, NotInChannelId: th.BasicChannel.Id}); err == nil {
+ t.Fatal("should not have access")
+ }
+
+ userWithoutTeam := th.CreateUser(Client)
+ if result, err := Client.SearchUsers(model.UserSearch{Term: userWithoutTeam.Username}); err != nil {
+ t.Fatal(err)
+ } else {
+ users := result.Data.([]*model.User)
+
+ found := false
+ for _, user := range users {
+ if user.Id == userWithoutTeam.Id {
+ found = true
+ }
+ }
+
+ if !found {
+ t.Fatal("should have found user without team")
+ }
+ }
+
+ if result, err := Client.SearchUsers(model.UserSearch{Term: userWithoutTeam.Username, WithoutTeam: true}); err != nil {
+ t.Fatal(err)
+ } else {
+ users := result.Data.([]*model.User)
+
+ found := false
+ for _, user := range users {
+ if user.Id == userWithoutTeam.Id {
+ found = true
+ }
+ }
+
+ if !found {
+ t.Fatal("should have found user without team")
+ }
+ }
+
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username, WithoutTeam: true}); err != nil {
+ t.Fatal(err)
+ } else {
+ users := result.Data.([]*model.User)
+
+ found := false
+ for _, user := range users {
+ if user.Id == th.BasicUser.Id {
+ found = true
+ }
+ }
+
+ if found {
+ t.Fatal("should not have found user with team")
+ }
}
}
diff --git a/api/websocket.go b/api/websocket.go
index 5c0858910..2de9abb0a 100644
--- a/api/websocket.go
+++ b/api/websocket.go
@@ -5,6 +5,7 @@ package api
import (
"net/http"
+ "strings"
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/websocket"
@@ -19,11 +20,25 @@ func InitWebSocket() {
app.HubStart()
}
+type OriginCheckerProc func(*http.Request) bool
+
+func OriginChecker(r *http.Request) bool {
+ origin := r.Header.Get("Origin")
+ return *utils.Cfg.ServiceSettings.AllowCorsFrom == "*" || strings.Contains(origin, *utils.Cfg.ServiceSettings.AllowCorsFrom)
+}
+
func connect(c *Context, w http.ResponseWriter, r *http.Request) {
+
+ var originChecker OriginCheckerProc = nil
+
+ if len(*utils.Cfg.ServiceSettings.AllowCorsFrom) > 0 {
+ originChecker = OriginChecker
+ }
+
upgrader := websocket.Upgrader{
ReadBufferSize: model.SOCKET_MAX_MESSAGE_SIZE_KB,
WriteBufferSize: model.SOCKET_MAX_MESSAGE_SIZE_KB,
- CheckOrigin: nil,
+ CheckOrigin: originChecker,
}
ws, err := upgrader.Upgrade(w, r, nil)
diff --git a/api/websocket_test.go b/api/websocket_test.go
index ab2959b03..d3d8fc4b2 100644
--- a/api/websocket_test.go
+++ b/api/websocket_test.go
@@ -316,6 +316,7 @@ func TestCreateDirectChannelWithSocket(t *testing.T) {
func TestWebsocketOriginSecurity(t *testing.T) {
Setup().InitBasic()
+
url := "ws://localhost" + utils.Cfg.ServiceSettings.ListenAddress
// Should fail because origin doesn't match
@@ -333,6 +334,35 @@ func TestWebsocketOriginSecurity(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+
+ // Should succeed now because open CORS
+ *utils.Cfg.ServiceSettings.AllowCorsFrom = "*"
+ _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
+ "Origin": []string{"http://www.evil.com"},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Should succeed now because matching CORS
+ *utils.Cfg.ServiceSettings.AllowCorsFrom = "www.evil.com"
+ _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
+ "Origin": []string{"http://www.evil.com"},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Should fail because non-matching CORS
+ *utils.Cfg.ServiceSettings.AllowCorsFrom = "www.good.com"
+ _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{
+ "Origin": []string{"http://www.evil.com"},
+ })
+ if err == nil {
+ t.Fatal("Should have errored because Origin contain AllowCorsFrom")
+ }
+
+ *utils.Cfg.ServiceSettings.AllowCorsFrom = ""
}
func TestZZWebSocketTearDown(t *testing.T) {