Merge branch 'master' into plugins-2
Этот коммит содержится в:
@@ -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
|
||||
|
||||
88
app/command_join_test.go
Обычный файл
88
app/command_join_test.go
Обычный файл
@@ -0,0 +1,88 @@
|
||||
// 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),
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_EXPERIMENTAL, map[string]interface{}{
|
||||
"client_side_cert_enable": *cfg.ExperimentalSettings.ClientSideCertEnable,
|
||||
"isdefault_client_side_cert_check": isDefault(*cfg.ExperimentalSettings.ClientSideCertCheck, model.CLIENT_SIDE_CERT_CHECK_PRIMARY_AUTH),
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_ANALYTICS, map[string]interface{}{
|
||||
"isdefault_max_users_for_statistics": isDefault(*cfg.AnalyticsSettings.MaxUsersForStatistics, model.ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS),
|
||||
})
|
||||
|
||||
@@ -54,7 +54,7 @@ const (
|
||||
MaxImageSize = 6048 * 4032 // 24 megapixels, roughly 36MB as a raw image
|
||||
IMAGE_THUMBNAIL_PIXEL_WIDTH = 120
|
||||
IMAGE_THUMBNAIL_PIXEL_HEIGHT = 100
|
||||
IMAGE_PREVIEW_PIXEL_WIDTH = 1024
|
||||
IMAGE_PREVIEW_PIXEL_WIDTH = 1920
|
||||
)
|
||||
|
||||
func (a *App) FileBackend() (utils.FileBackend, *model.AppError) {
|
||||
|
||||
@@ -209,6 +209,9 @@ func (a *App) BulkImport(fileReader io.Reader, dryRun bool, workers int) (*model
|
||||
scanner := bufio.NewScanner(fileReader)
|
||||
lineNumber := 0
|
||||
|
||||
a.Srv.Store.LockToMaster()
|
||||
defer a.Srv.Store.UnlockFromMaster()
|
||||
|
||||
errorsChan := make(chan LineImportWorkerError, (2*workers)+1) // size chosen to ensure it never gets filled up completely.
|
||||
var wg sync.WaitGroup
|
||||
var linesChan chan LineImportWorkerData
|
||||
|
||||
@@ -457,7 +457,13 @@ func (a *App) LoginByOAuth(service string, userData io.Reader, teamId string) (*
|
||||
return nil, model.NewAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
|
||||
map[string]interface{}{"Service": strings.Title(service)}, "", http.StatusNotImplemented)
|
||||
} else {
|
||||
authData = provider.GetAuthDataFromJson(bytes.NewReader(buf.Bytes()))
|
||||
authUser := provider.GetUserFromJson(bytes.NewReader(buf.Bytes()))
|
||||
|
||||
if authUser.AuthData != nil {
|
||||
authData = *authUser.AuthData
|
||||
} else {
|
||||
authData = ""
|
||||
}
|
||||
}
|
||||
|
||||
if len(authData) == 0 {
|
||||
|
||||
Ссылка в новой задаче
Block a user