summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/channel.go31
-rw-r--r--api/channel_test.go6
-rw-r--r--api4/api.go1
-rw-r--r--api4/apitestlib.go30
-rw-r--r--api4/channel.go45
-rw-r--r--api4/channel_test.go169
-rw-r--r--app/channel.go33
-rw-r--r--i18n/en.json6
-rw-r--r--model/client4.go19
-rw-r--r--store/sql_channel_store.go5
10 files changed, 309 insertions, 36 deletions
diff --git a/api/channel.go b/api/channel.go
index c5ec36d4b..33e083f14 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -6,7 +6,6 @@ package api
import (
"net/http"
"strconv"
- "strings"
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
@@ -48,9 +47,7 @@ func InitChannel() {
}
func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
-
channel := model.ChannelFromJson(r.Body)
-
if channel == nil {
c.SetInvalidParam("createChannel", "channel")
return
@@ -60,16 +57,6 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
channel.TeamId = c.TeamId
}
- if channel.Type == model.CHANNEL_DIRECT {
- c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.direct_channel.app_error", nil, "")
- return
- }
-
- if strings.Index(channel.Name, "__") > 0 {
- c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.invalid_character.app_error", nil, "")
- return
- }
-
if channel.Type == model.CHANNEL_OPEN && !app.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_PUBLIC_CHANNEL) {
c.SetPermissionError(model.PERMISSION_CREATE_PUBLIC_CHANNEL)
return
@@ -80,23 +67,7 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if channel.TeamId == c.TeamId {
-
- // Get total number of channels on current team
- if count, err := app.GetNumberOfChannelsOnTeam(channel.TeamId); err != nil {
- c.Err = model.NewLocAppError("createChannel", "api.channel.get_channels.error", nil, err.Error())
- return
- } else {
- if int64(count+1) > *utils.Cfg.TeamSettings.MaxChannelsPerTeam {
- c.Err = model.NewLocAppError("createChannel", "api.channel.create_channel.max_channel_limit.app_error", map[string]interface{}{"MaxChannelsPerTeam": *utils.Cfg.TeamSettings.MaxChannelsPerTeam}, "")
- return
- }
- }
- }
-
- channel.CreatorId = c.Session.UserId
-
- if sc, err := app.CreateChannel(channel, true); err != nil {
+ if sc, err := app.CreateChannelWithUser(channel, c.Session.UserId); err != nil {
c.Err = err
return
} else {
diff --git a/api/channel_test.go b/api/channel_test.go
index 450c5556e..6e6883047 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -55,7 +55,7 @@ func TestCreateChannel(t *testing.T) {
rchannel.Data.(*model.Channel).Id = ""
if _, err := Client.CreateChannel(rchannel.Data.(*model.Channel)); err != nil {
- if err.Message != "A channel with that URL already exists" {
+ if err.Id != "store.sql_channel.save_channel.exists.app_error" {
t.Fatal(err)
}
}
@@ -1768,7 +1768,9 @@ func TestAutocompleteChannels(t *testing.T) {
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
channel3 := &model.Channel{DisplayName: "BadChannelC", Name: "c" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: model.NewId()}
- channel3 = th.SystemAdminClient.Must(th.SystemAdminClient.CreateChannel(channel3)).Data.(*model.Channel)
+ if _, err := th.SystemAdminClient.CreateChannel(channel3); err == nil {
+ t.Fatal("channel must have valid team id")
+ }
channel4 := &model.Channel{DisplayName: "BadChannelD", Name: "d" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
channel4 = Client.Must(Client.CreateChannel(channel4)).Data.(*model.Channel)
diff --git a/api4/api.go b/api4/api.go
index ace5de30a..2293cdec5 100644
--- a/api4/api.go
+++ b/api4/api.go
@@ -139,6 +139,7 @@ func InitApi(full bool) {
InitUser()
InitTeam()
+ InitChannel()
// REMOVE CONDITION WHEN APIv3 REMOVED
if full {
diff --git a/api4/apitestlib.go b/api4/apitestlib.go
index 6b129dd8f..d5706bf2b 100644
--- a/api4/apitestlib.go
+++ b/api4/apitestlib.go
@@ -25,6 +25,7 @@ type TestHelper struct {
BasicUser2 *model.User
TeamAdminUser *model.User
BasicTeam *model.Team
+ BasicChannel *model.Channel
SystemAdminClient *model.Client4
SystemAdminUser *model.User
@@ -63,6 +64,7 @@ func (me *TestHelper) InitBasic() *TestHelper {
me.TeamAdminUser = me.CreateUser()
me.LoginTeamAdmin()
me.BasicTeam = me.CreateTeam()
+ me.BasicChannel = me.CreatePublicChannel()
me.BasicUser = me.CreateUser()
LinkUserToTeam(me.BasicUser, me.BasicTeam)
me.BasicUser2 = me.CreateUser()
@@ -128,6 +130,30 @@ func (me *TestHelper) CreateUserWithClient(client *model.Client4) *model.User {
return ruser
}
+func (me *TestHelper) CreatePublicChannel() *model.Channel {
+ return me.CreateChannelWithClient(me.Client, model.CHANNEL_OPEN)
+}
+
+func (me *TestHelper) CreatePrivateChannel() *model.Channel {
+ return me.CreateChannelWithClient(me.Client, model.CHANNEL_PRIVATE)
+}
+
+func (me *TestHelper) CreateChannelWithClient(client *model.Client4, channelType string) *model.Channel {
+ id := model.NewId()
+
+ channel := &model.Channel{
+ DisplayName: "dn_" + id,
+ Name: GenerateTestChannelName(),
+ Type: channelType,
+ TeamId: me.BasicTeam.Id,
+ }
+
+ utils.DisableDebugLogForTest()
+ rchannel, _ := client.CreateChannel(channel)
+ utils.EnableDebugLogForTest()
+ return rchannel
+}
+
func (me *TestHelper) LoginBasic() {
me.LoginBasicWithClient(me.Client)
}
@@ -194,6 +220,10 @@ func GenerateTestTeamName() string {
return "faketeam" + model.NewId()
}
+func GenerateTestChannelName() string {
+ return "fakechannel" + model.NewId()
+}
+
func VerifyUserEmail(userId string) {
store.Must(app.Srv.Store.User().VerifyEmail(userId))
}
diff --git a/api4/channel.go b/api4/channel.go
new file mode 100644
index 000000000..2ce9e23e5
--- /dev/null
+++ b/api4/channel.go
@@ -0,0 +1,45 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api4
+
+import (
+ "net/http"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/app"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+)
+
+func InitChannel() {
+ l4g.Debug(utils.T("api.channel.init.debug"))
+
+ BaseRoutes.Channels.Handle("", ApiSessionRequired(createChannel)).Methods("POST")
+}
+
+func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
+ channel := model.ChannelFromJson(r.Body)
+ if channel == nil {
+ c.SetInvalidParam("channel")
+ return
+ }
+
+ if channel.Type == model.CHANNEL_OPEN && !app.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_PUBLIC_CHANNEL) {
+ c.SetPermissionError(model.PERMISSION_CREATE_PUBLIC_CHANNEL)
+ return
+ }
+
+ if channel.Type == model.CHANNEL_PRIVATE && !app.SessionHasPermissionToTeam(c.Session, channel.TeamId, model.PERMISSION_CREATE_PRIVATE_CHANNEL) {
+ c.SetPermissionError(model.PERMISSION_CREATE_PRIVATE_CHANNEL)
+ return
+ }
+
+ if sc, err := app.CreateChannelWithUser(channel, c.Session.UserId); err != nil {
+ c.Err = err
+ return
+ } else {
+ c.LogAudit("name=" + channel.Name)
+ w.Write([]byte(sc.ToJson()))
+ }
+}
diff --git a/api4/channel_test.go b/api4/channel_test.go
new file mode 100644
index 000000000..e3d0a85bd
--- /dev/null
+++ b/api4/channel_test.go
@@ -0,0 +1,169 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api4
+
+import (
+ "net/http"
+ "strconv"
+ "testing"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+)
+
+func TestCreateChannel(t *testing.T) {
+ th := Setup().InitBasic().InitSystemAdmin()
+ Client := th.Client
+ team := th.BasicTeam
+
+ channel := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ private := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
+
+ rchannel, resp := Client.CreateChannel(channel)
+ CheckNoError(t, resp)
+
+ if rchannel.Name != channel.Name {
+ t.Fatal("names did not match")
+ }
+
+ if rchannel.DisplayName != channel.DisplayName {
+ t.Fatal("display names did not match")
+ }
+
+ if rchannel.TeamId != channel.TeamId {
+ t.Fatal("team ids did not match")
+ }
+
+ rprivate, resp := Client.CreateChannel(private)
+ CheckNoError(t, resp)
+
+ if rprivate.Name != private.Name {
+ t.Fatal("names did not match")
+ }
+
+ if rprivate.Type != model.CHANNEL_PRIVATE {
+ t.Fatal("wrong channel type")
+ }
+
+ if rprivate.CreatorId != th.BasicUser.Id {
+ t.Fatal("wrong creator id")
+ }
+
+ _, resp = Client.CreateChannel(channel)
+ CheckErrorMessage(t, resp, "store.sql_channel.save_channel.exists.app_error")
+ CheckBadRequestStatus(t, resp)
+
+ direct := &model.Channel{DisplayName: "Test API Name", Name: GenerateTestChannelName(), Type: model.CHANNEL_DIRECT, TeamId: team.Id}
+ _, resp = Client.CreateChannel(direct)
+ CheckErrorMessage(t, resp, "api.channel.create_channel.direct_channel.app_error")
+ CheckBadRequestStatus(t, resp)
+
+ Client.Logout()
+ _, resp = Client.CreateChannel(channel)
+ CheckUnauthorizedStatus(t, resp)
+
+ userNotOnTeam := th.CreateUser()
+ Client.Login(userNotOnTeam.Email, userNotOnTeam.Password)
+
+ _, resp = Client.CreateChannel(channel)
+ CheckForbiddenStatus(t, resp)
+
+ _, resp = Client.CreateChannel(private)
+ CheckForbiddenStatus(t, resp)
+
+ th.LoginBasic()
+
+ // Check permissions with policy config changes
+ isLicensed := utils.IsLicensed
+ license := utils.License
+ restrictPublicChannel := *utils.Cfg.TeamSettings.RestrictPublicChannelManagement
+ restrictPrivateChannel := *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement
+ defer func() {
+ *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = restrictPublicChannel
+ *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = restrictPrivateChannel
+ utils.IsLicensed = isLicensed
+ utils.License = license
+ utils.SetDefaultRolesBasedOnConfig()
+ }()
+ *utils.Cfg.TeamSettings.RestrictPublicChannelManagement = model.PERMISSIONS_ALL
+ *utils.Cfg.TeamSettings.RestrictPrivateChannelManagement = model.PERMISSIONS_ALL
+ utils.SetDefaultRolesBasedOnConfig()
+ utils.IsLicensed = true
+ utils.License = &model.License{Features: &model.Features{}}
+ utils.License.Features.SetDefaults()
+
+ channel.Name = GenerateTestChannelName()
+ _, resp = Client.CreateChannel(channel)
+ CheckNoError(t, resp)
+
+ private.Name = GenerateTestChannelName()
+ _, resp = Client.CreateChannel(private)
+ CheckNoError(t, resp)
+
+ *utils.Cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_TEAM_ADMIN
+ *utils.Cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_TEAM_ADMIN
+ utils.SetDefaultRolesBasedOnConfig()
+
+ _, resp = Client.CreateChannel(channel)
+ CheckForbiddenStatus(t, resp)
+
+ _, resp = Client.CreateChannel(private)
+ CheckForbiddenStatus(t, resp)
+
+ th.LoginTeamAdmin()
+
+ channel.Name = GenerateTestChannelName()
+ _, resp = Client.CreateChannel(channel)
+ CheckNoError(t, resp)
+
+ private.Name = GenerateTestChannelName()
+ _, resp = Client.CreateChannel(private)
+ CheckNoError(t, resp)
+
+ channel.Name = GenerateTestChannelName()
+ _, resp = th.SystemAdminClient.CreateChannel(channel)
+ CheckNoError(t, resp)
+
+ private.Name = GenerateTestChannelName()
+ _, resp = th.SystemAdminClient.CreateChannel(private)
+ CheckNoError(t, resp)
+
+ *utils.Cfg.TeamSettings.RestrictPublicChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN
+ *utils.Cfg.TeamSettings.RestrictPrivateChannelCreation = model.PERMISSIONS_SYSTEM_ADMIN
+ utils.SetDefaultRolesBasedOnConfig()
+
+ th.LoginBasic()
+
+ _, resp = Client.CreateChannel(channel)
+ CheckForbiddenStatus(t, resp)
+
+ _, resp = Client.CreateChannel(private)
+ CheckForbiddenStatus(t, resp)
+
+ th.LoginTeamAdmin()
+
+ _, resp = Client.CreateChannel(channel)
+ CheckForbiddenStatus(t, resp)
+
+ _, resp = Client.CreateChannel(private)
+ CheckForbiddenStatus(t, resp)
+
+ channel.Name = GenerateTestChannelName()
+ _, resp = th.SystemAdminClient.CreateChannel(channel)
+ CheckNoError(t, resp)
+
+ private.Name = GenerateTestChannelName()
+ _, resp = th.SystemAdminClient.CreateChannel(private)
+ CheckNoError(t, resp)
+
+ if r, err := Client.DoApiPost("/channels", "garbage"); err == nil {
+ t.Fatal("should have errored")
+ } else {
+ if r.StatusCode != http.StatusBadRequest {
+ t.Log("actual: " + strconv.Itoa(r.StatusCode))
+ t.Log("expected: " + strconv.Itoa(http.StatusBadRequest))
+ t.Fatal("wrong status code")
+ }
+ }
+}
diff --git a/app/channel.go b/app/channel.go
index 818e241b7..aba65143b 100644
--- a/app/channel.go
+++ b/app/channel.go
@@ -6,6 +6,7 @@ package app
import (
"fmt"
"net/http"
+ "strings"
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/model"
@@ -129,6 +130,38 @@ func JoinDefaultChannels(teamId string, user *model.User, channelRole string) *m
return err
}
+func CreateChannelWithUser(channel *model.Channel, userId string) (*model.Channel, *model.AppError) {
+ if channel.Type == model.CHANNEL_DIRECT {
+ return nil, model.NewAppError("CreateChannelWithUser", "api.channel.create_channel.direct_channel.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if strings.Index(channel.Name, "__") > 0 {
+ return nil, model.NewAppError("CreateChannelWithUser", "api.channel.create_channel.invalid_character.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if len(channel.TeamId) == 0 {
+ return nil, model.NewAppError("CreateChannelWithUser", "app.channel.create_channel.no_team_id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ // Get total number of channels on current team
+ if count, err := GetNumberOfChannelsOnTeam(channel.TeamId); err != nil {
+ return nil, err
+ } else {
+ if int64(count+1) > *utils.Cfg.TeamSettings.MaxChannelsPerTeam {
+ return nil, model.NewAppError("CreateChannelWithUser", "api.channel.create_channel.max_channel_limit.app_error", map[string]interface{}{"MaxChannelsPerTeam": *utils.Cfg.TeamSettings.MaxChannelsPerTeam}, "", http.StatusBadRequest)
+ }
+ }
+
+ channel.CreatorId = userId
+
+ rchannel, err := CreateChannel(channel, true)
+ if err != nil {
+ return nil, err
+ }
+
+ return rchannel, nil
+}
+
func CreateChannel(channel *model.Channel, addMember bool) (*model.Channel, *model.AppError) {
if result := <-Srv.Store.Channel().Save(channel); result.Err != nil {
return nil, result.Err
diff --git a/i18n/en.json b/i18n/en.json
index 59ed5583c..cd109f860 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -208,6 +208,10 @@
"translation": "Cannot create more than {{.MaxChannelsPerTeam}} channels for current team"
},
{
+ "id": "app.channel.create_channel.no_team_id.app_error",
+ "translation": "Must specify the team ID to create a channel"
+ },
+ {
"id": "api.channel.create_default_channels.off_topic",
"translation": "Off-Topic"
},
@@ -4569,7 +4573,7 @@
},
{
"id": "store.sql_channel.save_channel.exists.app_error",
- "translation": "A channel with that URL already exists"
+ "translation": "A channel with that name already exists on the same team"
},
{
"id": "store.sql_channel.save_channel.limit.app_error",
diff --git a/model/client4.go b/model/client4.go
index f0adfd382..c82f5ce0e 100644
--- a/model/client4.go
+++ b/model/client4.go
@@ -60,6 +60,14 @@ func (c *Client4) GetTeamsRoute() string {
return fmt.Sprintf("/teams")
}
+func (c *Client4) GetChannelsRoute() string {
+ return fmt.Sprintf("/channels")
+}
+
+func (c *Client4) GetChannelRoute(channelId string) string {
+ return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId)
+}
+
func (c *Client4) GetTeamRoute(teamId string) string {
return fmt.Sprintf(c.GetTeamsRoute()+"/%v", teamId)
}
@@ -236,7 +244,16 @@ func (c *Client4) CreateTeam(team *Team) (*Team, *Response) {
}
// Channel Section
-// to be filled in..
+
+// CreateChannel creates a channel based on the provided channel struct.
+func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) {
+ if r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson()); err != nil {
+ return nil, &Response{StatusCode: r.StatusCode, Error: err}
+ } else {
+ defer closeBody(r)
+ return ChannelFromJson(r.Body), BuildResponse(r)
+ }
+}
// Post Section
// to be filled in..
diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go
index 7e90a6d27..a8474be80 100644
--- a/store/sql_channel_store.go
+++ b/store/sql_channel_store.go
@@ -6,6 +6,7 @@ package store
import (
"database/sql"
"fmt"
+ "net/http"
"strconv"
"strings"
@@ -223,7 +224,7 @@ func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *mo
if dupChannel.DeleteAt > 0 {
result.Err = model.NewLocAppError("SqlChannelStore.Save", "store.sql_channel.save_channel.previously.app_error", nil, "id="+channel.Id+", "+err.Error())
} else {
- result.Err = model.NewLocAppError("SqlChannelStore.Save", CHANNEL_EXISTS_ERROR, nil, "id="+channel.Id+", "+err.Error())
+ result.Err = model.NewAppError("SqlChannelStore.Save", CHANNEL_EXISTS_ERROR, nil, "id="+channel.Id+", "+err.Error(), http.StatusBadRequest)
result.Data = &dupChannel
}
} else {
@@ -427,7 +428,7 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string) StoreChannel
result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error())
} else {
if len(*data) == 0 {
- result.Err = model.NewLocAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId+", userId="+userId)
+ result.Err = model.NewAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId+", userId="+userId, http.StatusBadRequest)
} else {
result.Data = data
}