Unverified Commit 88c5e469 authored by JoramWilander's avatar JoramWilander

Merge branch 'master' into plugins-2

parents d7976549 437f9f5b
......@@ -209,13 +209,20 @@ func patchChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if rchannel, err := c.App.PatchChannel(oldChannel, patch, c.Session.UserId); err != nil {
rchannel, err := c.App.PatchChannel(oldChannel, patch, c.Session.UserId)
if err != nil {
c.Err = err
return
}
err = c.App.FillInChannelProps(rchannel)
if err != nil {
c.Err = err
return
} else {
c.LogAudit("")
w.Write([]byte(rchannel.ToJson()))
}
c.LogAudit("")
w.Write([]byte(rchannel.ToJson()))
}
func restoreChannel(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -361,6 +368,12 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
err = c.App.FillInChannelProps(channel)
if err != nil {
c.Err = err
return
}
w.Write([]byte(channel.ToJson()))
}
......@@ -444,13 +457,19 @@ func getPublicChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request
return
}
if channels, err := c.App.GetPublicChannelsForTeam(c.Params.TeamId, c.Params.Page*c.Params.PerPage, c.Params.PerPage); err != nil {
channels, err := c.App.GetPublicChannelsForTeam(c.Params.TeamId, c.Params.Page*c.Params.PerPage, c.Params.PerPage)
if err != nil {
c.Err = err
return
} else {
w.Write([]byte(channels.ToJson()))
}
err = c.App.FillInChannelsProps(channels)
if err != nil {
c.Err = err
return
}
w.Write([]byte(channels.ToJson()))
}
func getDeletedChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -464,13 +483,19 @@ func getDeletedChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Reques
return
}
if channels, err := c.App.GetDeletedChannels(c.Params.TeamId, c.Params.Page*c.Params.PerPage, c.Params.PerPage); err != nil {
channels, err := c.App.GetDeletedChannels(c.Params.TeamId, c.Params.Page*c.Params.PerPage, c.Params.PerPage)
if err != nil {
c.Err = err
return
} else {
w.Write([]byte(channels.ToJson()))
}
err = c.App.FillInChannelsProps(channels)
if err != nil {
c.Err = err
return
}
w.Write([]byte(channels.ToJson()))
}
func getPublicChannelsByIdsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -497,12 +522,19 @@ func getPublicChannelsByIdsForTeam(c *Context, w http.ResponseWriter, r *http.Re
return
}
if channels, err := c.App.GetPublicChannelsByIdsForTeam(c.Params.TeamId, channelIds); err != nil {
channels, err := c.App.GetPublicChannelsByIdsForTeam(c.Params.TeamId, channelIds)
if err != nil {
c.Err = err
return
}
err = c.App.FillInChannelsProps(channels)
if err != nil {
c.Err = err
return
} else {
w.Write([]byte(channels.ToJson()))
}
w.Write([]byte(channels.ToJson()))
}
func getChannelsForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -521,15 +553,24 @@ func getChannelsForTeamForUser(c *Context, w http.ResponseWriter, r *http.Reques
return
}
if channels, err := c.App.GetChannelsForUser(c.Params.TeamId, c.Params.UserId); err != nil {
channels, err := c.App.GetChannelsForUser(c.Params.TeamId, c.Params.UserId)
if err != nil {
c.Err = err
return
} else if c.HandleEtag(channels.Etag(), "Get Channels", w, r) {
}
if c.HandleEtag(channels.Etag(), "Get Channels", w, r) {
return
} else {
w.Header().Set(model.HEADER_ETAG_SERVER, channels.Etag())
w.Write([]byte(channels.ToJson()))
}
err = c.App.FillInChannelsProps(channels)
if err != nil {
c.Err = err
return
}
w.Header().Set(model.HEADER_ETAG_SERVER, channels.Etag())
w.Write([]byte(channels.ToJson()))
}
func autocompleteChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -545,12 +586,15 @@ func autocompleteChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Requ
name := r.URL.Query().Get("name")
if channels, err := c.App.AutocompleteChannels(c.Params.TeamId, name); err != nil {
channels, err := c.App.AutocompleteChannels(c.Params.TeamId, name)
if err != nil {
c.Err = err
return
} else {
w.Write([]byte(channels.ToJson()))
}
// Don't fill in channels props, since unused by client and potentially expensive.
w.Write([]byte(channels.ToJson()))
}
func searchChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -570,12 +614,15 @@ func searchChannelsForTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if channels, err := c.App.SearchChannels(c.Params.TeamId, props.Term); err != nil {
channels, err := c.App.SearchChannels(c.Params.TeamId, props.Term)
if err != nil {
c.Err = err
return
} else {
w.Write([]byte(channels.ToJson()))
}
// Don't fill in channels props, since unused by client and potentially expensive.
w.Write([]byte(channels.ToJson()))
}
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
......@@ -638,6 +685,12 @@ func getChannelByName(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
err = c.App.FillInChannelProps(channel)
if err != nil {
c.Err = err
return
}
w.Write([]byte(channel.ToJson()))
}
......@@ -660,6 +713,12 @@ func getChannelByNameForTeamName(c *Context, w http.ResponseWriter, r *http.Requ
return
}
err = c.App.FillInChannelProps(channel)
if err != nil {
c.Err = err
return
}
w.Write([]byte(channel.ToJson()))
}
......
......@@ -1127,15 +1127,8 @@ func closeBody(r *http.Response) {
type MattermostTestProvider struct {
}
func (m *MattermostTestProvider) GetIdentifier() string {
return model.SERVICE_GITLAB
}
func (m *MattermostTestProvider) GetUserFromJson(data io.Reader) *model.User {
return model.UserFromJson(data)
}
func (m *MattermostTestProvider) GetAuthDataFromJson(data io.Reader) string {
authData := model.UserFromJson(data)
return authData.Email
user := model.UserFromJson(data)
user.AuthData = &user.Email
return user
}
......@@ -206,6 +206,9 @@ func New(options ...Option) (outApp *App, outErr error) {
}
app.initJobs()
app.AddLicenseListener(func() {
app.initJobs()
})
subpath, err := utils.GetSubpathFromConfig(app.Config())
if err != nil {
......
......@@ -397,13 +397,13 @@ func (a *App) UpdateChannelPrivacy(oldChannel *model.Channel, user *model.User)
}
func (a *App) postChannelPrivacyMessage(user *model.User, channel *model.Channel) *model.AppError {
privacy := (map[string]string{
model.CHANNEL_OPEN: "private_to_public",
model.CHANNEL_PRIVATE: "public_to_private",
message := (map[string]string{
model.CHANNEL_OPEN: utils.T("api.channel.change_channel_privacy.private_to_public"),
model.CHANNEL_PRIVATE: utils.T("api.channel.change_channel_privacy.public_to_private"),
})[channel.Type]
post := &model.Post{
ChannelId: channel.Id,
Message: utils.T("api.channel.change_channel_privacy." + privacy),
Message: message,
Type: model.POST_CHANGE_CHANNEL_PRIVACY,
UserId: user.Id,
Props: model.StringInterface{
......@@ -1591,3 +1591,67 @@ func (a *App) ToggleMuteChannel(channelId string, userId string) *model.ChannelM
a.Srv.Store.Channel().UpdateMember(member)
return member
}
func (a *App) FillInChannelProps(channel *model.Channel) *model.AppError {
return a.FillInChannelsProps(&model.ChannelList{channel})
}
func (a *App) FillInChannelsProps(channelList *model.ChannelList) *model.AppError {
// Group the channels by team and call GetChannelsByNames just once per team.
channelsByTeam := make(map[string]model.ChannelList)
for _, channel := range *channelList {
channelsByTeam[channel.TeamId] = append(channelsByTeam[channel.TeamId], channel)
}
for teamId, channelList := range channelsByTeam {
allChannelMentions := make(map[string]bool)
channelMentions := make(map[*model.Channel][]string, len(channelList))
// Collect mentions across the channels so as to query just once for this team.
for _, channel := range channelList {
channelMentions[channel] = model.ChannelMentions(channel.Header)
for _, channelMention := range channelMentions[channel] {
allChannelMentions[channelMention] = true
}
}
allChannelMentionNames := make([]string, 0, len(allChannelMentions))
for channelName := range allChannelMentions {
allChannelMentionNames = append(allChannelMentionNames, channelName)
}
if len(allChannelMentionNames) > 0 {
mentionedChannels, err := a.GetChannelsByNames(allChannelMentionNames, teamId)
if err != nil {
return err
}
mentionedChannelsByName := make(map[string]*model.Channel)
for _, channel := range mentionedChannels {
mentionedChannelsByName[channel.Name] = channel
}
for _, channel := range channelList {
channelMentionsProp := make(map[string]interface{}, len(channelMentions[channel]))
for _, channelMention := range channelMentions[channel] {
if mentioned, ok := mentionedChannelsByName[channelMention]; ok {
if mentioned.Type == model.CHANNEL_OPEN {
channelMentionsProp[mentioned.Name] = map[string]interface{}{
"display_name": mentioned.DisplayName,
}
}
}
}
if len(channelMentionsProp) > 0 {
channel.AddProp("channel_mentions", channelMentionsProp)
} else if channel.Props != nil {
delete(channel.Props, "channel_mentions")
}
}
}
}
return nil
}
......@@ -9,6 +9,7 @@ import (
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPermanentDeleteChannel(t *testing.T) {
......@@ -399,3 +400,208 @@ func TestAppUpdateChannelScheme(t *testing.T) {
t.Fatal("Wrong Channel SchemeId")
}
}
func TestFillInChannelProps(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
channelPublic1, err := th.App.CreateChannel(&model.Channel{DisplayName: "Public 1", Name: "public1", Type: model.CHANNEL_OPEN, TeamId: th.BasicTeam.Id}, false)
require.Nil(t, err)
defer th.App.PermanentDeleteChannel(channelPublic1)
channelPublic2, err := th.App.CreateChannel(&model.Channel{DisplayName: "Public 2", Name: "public2", Type: model.CHANNEL_OPEN, TeamId: th.BasicTeam.Id}, false)
require.Nil(t, err)
defer th.App.PermanentDeleteChannel(channelPublic2)
channelPrivate, err := th.App.CreateChannel(&model.Channel{DisplayName: "Private", Name: "private", Type: model.CHANNEL_PRIVATE, TeamId: th.BasicTeam.Id}, false)
require.Nil(t, err)
defer th.App.PermanentDeleteChannel(channelPrivate)
otherTeamId := model.NewId()
otherTeam := &model.Team{
DisplayName: "dn_" + otherTeamId,
Name: "name" + otherTeamId,
Email: "success+" + otherTeamId + "@simulator.amazonses.com",
Type: model.TEAM_OPEN,
}
otherTeam, err = th.App.CreateTeam(otherTeam)
require.Nil(t, err)
defer th.App.PermanentDeleteTeam(otherTeam)
channelOtherTeam, err := th.App.CreateChannel(&model.Channel{DisplayName: "Other Team Channel", Name: "other-team", Type: model.CHANNEL_OPEN, TeamId: otherTeam.Id}, false)
require.Nil(t, err)
defer th.App.PermanentDeleteChannel(channelOtherTeam)
// Note that purpose is intentionally plaintext below.
t.Run("single channels", func(t *testing.T) {
testCases := []struct {
Description string
Channel *model.Channel
ExpectedChannelProps map[string]interface{}
}{
{
"channel on basic team without references",
&model.Channel{
TeamId: th.BasicTeam.Id,
Header: "No references",
Purpose: "No references",
},
nil,
},
{
"channel on basic team",
&model.Channel{
TeamId: th.BasicTeam.Id,
Header: "~public1, ~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
map[string]interface{}{
"channel_mentions": map[string]interface{}{
"public1": map[string]interface{}{
"display_name": "Public 1",
},
},
},
},
{
"channel on other team",
&model.Channel{
TeamId: otherTeam.Id,
Header: "~public1, ~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
map[string]interface{}{
"channel_mentions": map[string]interface{}{
"other-team": map[string]interface{}{
"display_name": "Other Team Channel",
},
},
},
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
err = th.App.FillInChannelProps(testCase.Channel)
require.Nil(t, err)
assert.Equal(t, testCase.ExpectedChannelProps, testCase.Channel.Props)
})
}
})
t.Run("multiple channels", func(t *testing.T) {
testCases := []struct {
Description string
Channels *model.ChannelList
ExpectedChannelProps map[string]interface{}
}{
{
"single channel on basic team",
&model.ChannelList{
{
Name: "test",
TeamId: th.BasicTeam.Id,
Header: "~public1, ~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
},
map[string]interface{}{
"test": map[string]interface{}{
"channel_mentions": map[string]interface{}{
"public1": map[string]interface{}{
"display_name": "Public 1",
},
},
},
},
},
{
"multiple channels on basic team",
&model.ChannelList{
{
Name: "test",
TeamId: th.BasicTeam.Id,
Header: "~public1, ~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
{
Name: "test2",
TeamId: th.BasicTeam.Id,
Header: "~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
{
Name: "test3",
TeamId: th.BasicTeam.Id,
Header: "No references",
Purpose: "No references",
},
},
map[string]interface{}{
"test": map[string]interface{}{
"channel_mentions": map[string]interface{}{
"public1": map[string]interface{}{
"display_name": "Public 1",
},
},
},
"test2": map[string]interface{}(nil),
"test3": map[string]interface{}(nil),
},
},
{
"multiple channels across teams",
&model.ChannelList{
{
Name: "test",
TeamId: th.BasicTeam.Id,
Header: "~public1, ~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
{
Name: "test2",
TeamId: otherTeam.Id,
Header: "~private, ~other-team",
Purpose: "~public2, ~private, ~other-team",
},
{
Name: "test3",
TeamId: th.BasicTeam.Id,
Header: "No references",
Purpose: "No references",
},
},
map[string]interface{}{
"test": map[string]interface{}{
"channel_mentions": map[string]interface{}{
"public1": map[string]interface{}{
"display_name": "Public 1",
},
},
},
"test2": map[string]interface{}{
"channel_mentions": map[string]interface{}{
"other-team": map[string]interface{}{
"display_name": "Other Team Channel",
},
},
},
"test3": map[string]interface{}(nil),
},
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
err = th.App.FillInChannelsProps(testCase.Channels)
require.Nil(t, err)
for _, channel := range *testCase.Channels {
assert.Equal(t, testCase.ExpectedChannelProps[channel.Name], channel.Props)
}
})
}
})
}
......@@ -6,6 +6,7 @@ package app
import (
"github.com/mattermost/mattermost-server/model"
goi18n "github.com/nicksnyder/go-i18n/i18n"
"strings"
)
type JoinProvider struct {
......@@ -34,12 +35,18 @@ func (me *JoinProvider) GetCommand(a *App, T goi18n.TranslateFunc) *model.Comman
}
func (me *JoinProvider) DoCommand(a *App, args *model.CommandArgs, message string) *model.CommandResponse {
if result := <-a.Srv.Store.Channel().GetByName(args.TeamId, message, true); result.Err != nil {
channelName := message
if strings.HasPrefix(message, "~") {
channelName = message[1:]
}
if result := <-a.Srv.Store.Channel().GetByName(args.TeamId, channelName, true); result.Err != nil {
return &model.CommandResponse{Text: args.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.Name == channelName {
allowed := false
if (channel.Type == model.CHANNEL_PRIVATE && a.SessionHasPermissionToChannel(args.Session, channel.Id, model.PERMISSION_READ_CHANNEL)) || channel.Type == model.CHANNEL_OPEN {
allowed = true
......
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/nicksnyder/go-i18n/i18n"
"github.com/stretchr/testify/assert"
)
func TestJoinCommandNoChannel(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
if testing.Short() {
t.SkipNow()
}
cmd := &JoinProvider{}
resp := cmd.DoCommand(th.App, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser2.Id,
SiteURL: "http://test.url",
TeamId: th.BasicTeam.Id,
}, "asdsad")
assert.Equal(t, "api.command_join.list.app_error", resp.Text)
}
func TestJoinCommandForExistingChannel(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
if testing.Short() {
t.SkipNow()
}
channel2, _ := th.App.CreateChannel(&model.Channel{
DisplayName: "AA",
Name: "aa" + model.NewId() + "a",
Type: model.CHANNEL_OPEN,
TeamId: th.BasicTeam.Id,
CreatorId: th.BasicUser.Id,
}, false)
cmd := &JoinProvider{}
resp := cmd.DoCommand(th.App, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser2.Id,
SiteURL: "http://test.url",
TeamId: th.BasicTeam.Id,
}, channel2.Name)
assert.Equal(t, "", resp.Text)
assert.Equal(t, "http://test.url/"+th.BasicTeam.Name+"/channels/"+channel2.Name, resp.GotoLocation)
}
func TestJoinCommandWithTilde(t *testing.T) {
th := Setup().InitBasic()
defer th.TearDown()
if testing.Short() {
t.SkipNow()
}
channel2, _ := th.App.CreateChannel(&model.Channel{
DisplayName: "AA",
Name: "aa" + model.NewId() + "a",
Type: model.CHANNEL_OPEN,
TeamId: th.BasicTeam.Id,
CreatorId: th.BasicUser.Id,
}, false)
cmd := &JoinProvider{}
resp := cmd.DoCommand(th.App, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser2.Id,
SiteURL: "http://test.url",
TeamId: th.BasicTeam.Id,
}, "~"+channel2.Name)
assert.Equal(t, "", resp.Text)
assert.Equal(t, "http://test.url/"+th.BasicTeam.Name+"/channels/"+channel2.Name, resp.GotoLocation)
}
......@@ -36,6 +36,7 @@ const (
TRACK_CONFIG_WEBRTC = "config_webrtc"
TRACK_CONFIG_SUPPORT = "config_support"
TRACK_CONFIG_NATIVEAPP = "config_nativeapp"
TRACK_CONFIG_EXPERIMENTAL = "config_experimental"
TRACK_CONFIG_ANALYTICS = "config_analytics"
TRACK_CONFIG_ANNOUNCEMENT = "config_announcement"
TRACK_CONFIG_ELASTICSEARCH = "config_elasticsearch"
......@@ -252,6 +253,7 @@ func (a *App) trackConfig() {
"allow_cookies_for_subdomains": *cfg.ServiceSettings.AllowCookiesForSubdomains,
"enable_api_team_deletion": *cfg.ServiceSettings.EnableAPITeamDeletion,
"experimental_enable_hardened_mode": *cfg.ServiceSettings.ExperimentalEnableHardenedMode,
"experimental_limit_client_config": *cfg.ServiceSettings.ExperimentalLimitClientConfig,
})
a.SendDiagnostic(TRACK_CONFIG_TEAM, map[string]interface{}{
......@@ -475,6 +477,11 @@ func (a *App) trackConfig() {
"isdefault_turn_uri": isDefault(*cfg.WebrtcSettings.TurnURI, model.WEBRTC_SETTINGS_DEFAULT_TURN_URI),
})