mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Merge branch 'master' into plugins-2
This commit is contained in:
109
api4/channel.go
109
api4/channel.go
@@ -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
|
||||
} else {
|
||||
c.LogAudit("")
|
||||
w.Write([]byte(rchannel.ToJson()))
|
||||
}
|
||||
|
||||
err = c.App.FillInChannelProps(rchannel)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
|
||||
err = c.App.FillInChannelsProps(channels)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
return
|
||||
} else {
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, channels.Etag())
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
|
||||
if c.HandleEtag(channels.Etag(), "Get Channels", w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
88
app/command_join_test.go
Normal file
88
app/command_join_test.go
Normal file
@@ -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 {
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
"WebserverMode": "gzip",
|
||||
"EnableCustomEmoji": false,
|
||||
"EnableEmojiPicker": true,
|
||||
"EnableGifPicker": true,
|
||||
"GfycatApiKey": "",
|
||||
"GfycatApiSecret": "",
|
||||
"RestrictCustomEmojiCreation": "all",
|
||||
"RestrictPostDelete": "all",
|
||||
"AllowEditPost": "always",
|
||||
|
||||
@@ -10,9 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type OauthProvider interface {
|
||||
GetIdentifier() string
|
||||
GetUserFromJson(data io.Reader) *model.User
|
||||
GetAuthDataFromJson(data io.Reader) string
|
||||
}
|
||||
|
||||
var oauthProviders = make(map[string]OauthProvider)
|
||||
|
||||
2940
i18n/de.json
2940
i18n/de.json
File diff suppressed because it is too large
Load Diff
128
i18n/en.json
128
i18n/en.json
@@ -99,6 +99,14 @@
|
||||
"id": "api.channel.add_user_to_channel.type.app_error",
|
||||
"translation": "Can not add user to this channel type"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.change_channel_privacy.private_to_public",
|
||||
"translation": "This channel has been converted to a Public Channel and can be joined by any team member."
|
||||
},
|
||||
{
|
||||
"id": "api.channel.change_channel_privacy.public_to_private",
|
||||
"translation": "This channel has been converted to a Private Channel."
|
||||
},
|
||||
{
|
||||
"id": "api.channel.convert_channel_to_private.default_channel_error",
|
||||
"translation": "This default channel cannot be converted into a private channel."
|
||||
@@ -1534,66 +1542,6 @@
|
||||
"id": "api.slackimport.slack_import.zip.app_error",
|
||||
"translation": "Unable to open the Slack export zip file.\r\n"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.rollback_transaction.app_error",
|
||||
"translation": "Failed to rollback the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.select.app_error",
|
||||
"translation": "Failed to retrieve the users"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.update.app_error",
|
||||
"translation": "Failed to update the user"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.rollback_transaction.app_error",
|
||||
"translation": "Failed to rollback the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.select.app_error",
|
||||
"translation": "Failed to retrieve the team members"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.update.app_error",
|
||||
"translation": "Failed to update the team member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.rollback_transaction.app_error",
|
||||
"translation": "Failed to rollback the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.select.app_error",
|
||||
"translation": "Failed to retrieve the channel members"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.update.app_error",
|
||||
"translation": "Failed to update the channel member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "api.status.user_not_found.app_error",
|
||||
"translation": "User not found"
|
||||
@@ -5126,6 +5074,26 @@
|
||||
"id": "store.sql_channel.analytics_type_count.app_error",
|
||||
"translation": "We couldn't get channel type counts"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.rollback_transaction.app_error",
|
||||
"translation": "Failed to rollback the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.select.app_error",
|
||||
"translation": "Failed to retrieve the channel members"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.clear_all_custom_role_assignments.update.app_error",
|
||||
"translation": "Failed to update the channel member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.delete.channel.app_error",
|
||||
"translation": "We couldn't delete the channel"
|
||||
@@ -6110,6 +6078,26 @@
|
||||
"id": "store.sql_team.analytics_team_count.app_error",
|
||||
"translation": "We couldn't count the teams"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.rollback_transaction.app_error",
|
||||
"translation": "Failed to rollback the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.select.app_error",
|
||||
"translation": "Failed to retrieve the team members"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.clear_all_custom_role_assignments.update.app_error",
|
||||
"translation": "Failed to update the team member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.get.find.app_error",
|
||||
"translation": "We couldn't find the existing team"
|
||||
@@ -6266,6 +6254,26 @@
|
||||
"id": "store.sql_user.analytics_unique_user_count.app_error",
|
||||
"translation": "We couldn't get the unique user count"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.open_transaction.app_error",
|
||||
"translation": "Failed to begin the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.rollback_transaction.app_error",
|
||||
"translation": "Failed to rollback the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.select.app_error",
|
||||
"translation": "Failed to retrieve the users"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.clear_all_custom_role_assignments.update.app_error",
|
||||
"translation": "Failed to update the user"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_user.get.app_error",
|
||||
"translation": "We encountered an error finding the account"
|
||||
|
||||
2940
i18n/es.json
2940
i18n/es.json
File diff suppressed because it is too large
Load Diff
2940
i18n/fr.json
2940
i18n/fr.json
File diff suppressed because it is too large
Load Diff
2942
i18n/it.json
2942
i18n/it.json
File diff suppressed because it is too large
Load Diff
2946
i18n/ja.json
2946
i18n/ja.json
File diff suppressed because it is too large
Load Diff
2942
i18n/ko.json
2942
i18n/ko.json
File diff suppressed because it is too large
Load Diff
2942
i18n/nl.json
2942
i18n/nl.json
File diff suppressed because it is too large
Load Diff
2942
i18n/pl.json
2942
i18n/pl.json
File diff suppressed because it is too large
Load Diff
2940
i18n/pt-BR.json
2940
i18n/pt-BR.json
File diff suppressed because it is too large
Load Diff
2942
i18n/ru.json
2942
i18n/ru.json
File diff suppressed because it is too large
Load Diff
2944
i18n/tr.json
2944
i18n/tr.json
File diff suppressed because it is too large
Load Diff
2940
i18n/zh-CN.json
2940
i18n/zh-CN.json
File diff suppressed because it is too large
Load Diff
2940
i18n/zh-TW.json
2940
i18n/zh-TW.json
File diff suppressed because it is too large
Load Diff
@@ -95,6 +95,8 @@ func (workers *Workers) Start() *Workers {
|
||||
}
|
||||
|
||||
func (workers *Workers) handleConfigChange(oldConfig *model.Config, newConfig *model.Config) {
|
||||
mlog.Debug("Workers received config change.")
|
||||
|
||||
if workers.DataRetention != nil {
|
||||
if (!*oldConfig.DataRetentionSettings.EnableMessageDeletion && !*oldConfig.DataRetentionSettings.EnableFileDeletion) && (*newConfig.DataRetentionSettings.EnableMessageDeletion || *newConfig.DataRetentionSettings.EnableFileDeletion) {
|
||||
go workers.DataRetention.Run()
|
||||
|
||||
@@ -32,21 +32,22 @@ const (
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Header string `json:"header"`
|
||||
Purpose string `json:"purpose"`
|
||||
LastPostAt int64 `json:"last_post_at"`
|
||||
TotalMsgCount int64 `json:"total_msg_count"`
|
||||
ExtraUpdateAt int64 `json:"extra_update_at"`
|
||||
CreatorId string `json:"creator_id"`
|
||||
SchemeId *string `json:"scheme_id"`
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Header string `json:"header"`
|
||||
Purpose string `json:"purpose"`
|
||||
LastPostAt int64 `json:"last_post_at"`
|
||||
TotalMsgCount int64 `json:"total_msg_count"`
|
||||
ExtraUpdateAt int64 `json:"extra_update_at"`
|
||||
CreatorId string `json:"creator_id"`
|
||||
SchemeId *string `json:"scheme_id"`
|
||||
Props map[string]interface{} `json:"props" db:"-"`
|
||||
}
|
||||
|
||||
type ChannelPatch struct {
|
||||
@@ -163,6 +164,18 @@ func (o *Channel) Patch(patch *ChannelPatch) {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) MakeNonNil() {
|
||||
if o.Props == nil {
|
||||
o.Props = make(map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Channel) AddProp(key string, value interface{}) {
|
||||
o.MakeNonNil()
|
||||
|
||||
o.Props[key] = value
|
||||
}
|
||||
|
||||
func GetDMNameFromIds(userId1, userId2 string) string {
|
||||
if userId1 > userId2 {
|
||||
return userId2 + "__" + userId1
|
||||
|
||||
28
model/channel_mentions.go
Normal file
28
model/channel_mentions.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var channelMentionRegexp = regexp.MustCompile(`\B~[a-zA-Z0-9\-_]+`)
|
||||
|
||||
func ChannelMentions(message string) []string {
|
||||
var names []string
|
||||
|
||||
if strings.Contains(message, "~") {
|
||||
alreadyMentioned := make(map[string]bool)
|
||||
for _, match := range channelMentionRegexp.FindAllString(message, -1) {
|
||||
name := match[1:]
|
||||
if !alreadyMentioned[name] {
|
||||
names = append(names, name)
|
||||
alreadyMentioned[name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
@@ -210,6 +210,9 @@ type ServiceSettings struct {
|
||||
WebserverMode *string
|
||||
EnableCustomEmoji *bool
|
||||
EnableEmojiPicker *bool
|
||||
EnableGifPicker *bool
|
||||
GfycatApiKey *string
|
||||
GfycatApiSecret *string
|
||||
RestrictCustomEmojiCreation *string
|
||||
RestrictPostDelete *string
|
||||
AllowEditPost *string
|
||||
@@ -413,6 +416,18 @@ func (s *ServiceSettings) SetDefaults() {
|
||||
s.EnableEmojiPicker = NewBool(true)
|
||||
}
|
||||
|
||||
if s.EnableGifPicker == nil {
|
||||
s.EnableGifPicker = NewBool(true)
|
||||
}
|
||||
|
||||
if s.GfycatApiKey == nil {
|
||||
s.GfycatApiKey = NewString("")
|
||||
}
|
||||
|
||||
if s.GfycatApiSecret == nil {
|
||||
s.GfycatApiSecret = NewString("")
|
||||
}
|
||||
|
||||
if s.RestrictCustomEmojiCreation == nil {
|
||||
s.RestrictCustomEmojiCreation = NewString(RESTRICT_EMOJI_CREATION_ALL)
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func userFromGitLabUser(glu *GitLabUser) *model.User {
|
||||
user.FirstName = glu.Name
|
||||
}
|
||||
user.Email = glu.Email
|
||||
userId := strconv.FormatInt(glu.Id, 10)
|
||||
userId := glu.getAuthData()
|
||||
user.AuthData = &userId
|
||||
user.AuthService = model.USER_AUTH_SERVICE_GITLAB
|
||||
|
||||
@@ -90,10 +90,6 @@ func (glu *GitLabUser) getAuthData() string {
|
||||
return strconv.FormatInt(glu.Id, 10)
|
||||
}
|
||||
|
||||
func (m *GitLabProvider) GetIdentifier() string {
|
||||
return model.USER_AUTH_SERVICE_GITLAB
|
||||
}
|
||||
|
||||
func (m *GitLabProvider) GetUserFromJson(data io.Reader) *model.User {
|
||||
glu := gitLabUserFromJson(data)
|
||||
if glu.IsValid() {
|
||||
@@ -102,13 +98,3 @@ func (m *GitLabProvider) GetUserFromJson(data io.Reader) *model.User {
|
||||
|
||||
return &model.User{}
|
||||
}
|
||||
|
||||
func (m *GitLabProvider) GetAuthDataFromJson(data io.Reader) string {
|
||||
glu := gitLabUserFromJson(data)
|
||||
|
||||
if glu.IsValid() {
|
||||
return glu.getAuthData()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
@@ -343,20 +342,8 @@ func PostPatchFromJson(data io.Reader) *PostPatch {
|
||||
return &post
|
||||
}
|
||||
|
||||
var channelMentionRegexp = regexp.MustCompile(`\B~[a-zA-Z0-9\-_]+`)
|
||||
|
||||
func (o *Post) ChannelMentions() (names []string) {
|
||||
if strings.Contains(o.Message, "~") {
|
||||
alreadyMentioned := make(map[string]bool)
|
||||
for _, match := range channelMentionRegexp.FindAllString(o.Message, -1) {
|
||||
name := match[1:]
|
||||
if !alreadyMentioned[name] {
|
||||
names = append(names, name)
|
||||
alreadyMentioned[name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
func (o *Post) ChannelMentions() []string {
|
||||
return ChannelMentions(o.Message)
|
||||
}
|
||||
|
||||
func (r *PostActionIntegrationRequest) ToJson() string {
|
||||
|
||||
@@ -181,6 +181,14 @@ func (s *LayeredStore) Close() {
|
||||
s.DatabaseLayer.Close()
|
||||
}
|
||||
|
||||
func (s *LayeredStore) LockToMaster() {
|
||||
s.DatabaseLayer.LockToMaster()
|
||||
}
|
||||
|
||||
func (s *LayeredStore) UnlockFromMaster() {
|
||||
s.DatabaseLayer.UnlockFromMaster()
|
||||
}
|
||||
|
||||
func (s *LayeredStore) DropAllTables() {
|
||||
s.DatabaseLayer.DropAllTables()
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ func (s SqlChannelStore) CreateIndexesIfNotExists() {
|
||||
s.CreateIndexIfNotExists("idx_channelmembers_channel_id", "ChannelMembers", "ChannelId")
|
||||
s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId")
|
||||
|
||||
s.CreateFullTextIndexIfNotExists("idx_channels_txt", "Channels", "Name, DisplayName")
|
||||
s.CreateFullTextIndexIfNotExists("idx_channel_search_txt", "Channels", "Name, DisplayName, Purpose")
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) store.StoreChannel {
|
||||
@@ -1573,7 +1573,7 @@ func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) s
|
||||
|
||||
func (s SqlChannelStore) buildLIKEClause(term string) (likeClause, likeTerm string) {
|
||||
likeTerm = term
|
||||
searchColumns := "Name, DisplayName"
|
||||
searchColumns := "Name, DisplayName, Purpose"
|
||||
|
||||
// These chars must be removed from the like query.
|
||||
for _, c := range ignoreLikeSearchChar {
|
||||
@@ -1608,7 +1608,7 @@ func (s SqlChannelStore) buildFulltextClause(term string) (fulltextClause, fullt
|
||||
// Copy the terms as we will need to prepare them differently for each search type.
|
||||
fulltextTerm = term
|
||||
|
||||
searchColumns := "Name, DisplayName"
|
||||
searchColumns := "Name, DisplayName, Purpose"
|
||||
|
||||
// These chars must be treated as spaces in the fulltext query.
|
||||
for _, c := range spaceFulltextSearchChar {
|
||||
|
||||
@@ -65,6 +65,8 @@ type SqlStore interface {
|
||||
RemoveIndexIfExists(indexName string, tableName string) bool
|
||||
GetAllConns() []*gorp.DbMap
|
||||
Close()
|
||||
LockToMaster()
|
||||
UnlockFromMaster()
|
||||
Team() store.TeamStore
|
||||
Channel() store.ChannelStore
|
||||
Post() store.PostStore
|
||||
|
||||
@@ -105,6 +105,7 @@ type SqlSupplier struct {
|
||||
searchReplicas []*gorp.DbMap
|
||||
oldStores SqlSupplierOldStores
|
||||
settings *model.SqlSettings
|
||||
lockedToMaster bool
|
||||
}
|
||||
|
||||
func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlSupplier {
|
||||
@@ -283,7 +284,7 @@ func (ss *SqlSupplier) GetSearchReplica() *gorp.DbMap {
|
||||
}
|
||||
|
||||
func (ss *SqlSupplier) GetReplica() *gorp.DbMap {
|
||||
if len(ss.settings.DataSourceReplicas) == 0 {
|
||||
if len(ss.settings.DataSourceReplicas) == 0 || ss.lockedToMaster {
|
||||
return ss.GetMaster()
|
||||
}
|
||||
|
||||
@@ -801,6 +802,14 @@ func (ss *SqlSupplier) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (ss *SqlSupplier) LockToMaster() {
|
||||
ss.lockedToMaster = true
|
||||
}
|
||||
|
||||
func (ss *SqlSupplier) UnlockFromMaster() {
|
||||
ss.lockedToMaster = false
|
||||
}
|
||||
|
||||
func (ss *SqlSupplier) Team() store.TeamStore {
|
||||
return ss.oldStores.team
|
||||
}
|
||||
|
||||
@@ -446,6 +446,8 @@ func UpgradeDatabaseToVersion50(sqlStore SqlStore) {
|
||||
sqlStore.GetMaster().Exec("UPDATE Roles SET SchemeManaged=false WHERE Name NOT IN ('system_user', 'system_admin', 'team_user', 'team_admin', 'channel_user', 'channel_admin')")
|
||||
sqlStore.CreateColumnIfNotExists("IncomingWebhooks", "ChannelLocked", "boolean", "boolean", "0")
|
||||
|
||||
sqlStore.RemoveIndexIfExists("idx_channels_txt", "Channels")
|
||||
|
||||
saveSchemaVersion(sqlStore, VERSION_5_0_0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ type Store interface {
|
||||
Plugin() PluginStore
|
||||
MarkSystemRanUnitTests()
|
||||
Close()
|
||||
LockToMaster()
|
||||
UnlockFromMaster()
|
||||
DropAllTables()
|
||||
TotalMasterDbConnections() int
|
||||
TotalReadDbConnections() int
|
||||
|
||||
@@ -1708,6 +1708,14 @@ func testChannelStoreSearchMore(t *testing.T, ss store.Store) {
|
||||
o8.Type = model.CHANNEL_PRIVATE
|
||||
store.Must(ss.Channel().Save(&o8, -1))
|
||||
|
||||
o9 := model.Channel{}
|
||||
o9.TeamId = o1.TeamId
|
||||
o9.DisplayName = "Channel With Purpose"
|
||||
o9.Purpose = "This can now be searchable!"
|
||||
o9.Name = "with-purpose"
|
||||
o9.Type = model.CHANNEL_OPEN
|
||||
store.Must(ss.Channel().Save(&o9, -1))
|
||||
|
||||
if result := <-ss.Channel().SearchMore(m1.UserId, o1.TeamId, "ChannelA"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
@@ -1773,6 +1781,19 @@ func testChannelStoreSearchMore(t *testing.T, ss store.Store) {
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-ss.Channel().SearchMore(m1.UserId, o1.TeamId, "now searchable"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
channels := result.Data.(*model.ChannelList)
|
||||
if len(*channels) != 1 {
|
||||
t.Fatal("should return 1 channel")
|
||||
}
|
||||
|
||||
if (*channels)[0].Name != o9.Name {
|
||||
t.Fatal("wrong channel returned")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Disabling this check as it will fail on PostgreSQL as we have "liberalised" channel matching to deal with
|
||||
// Full-Text Stemming Limitations.
|
||||
@@ -1884,6 +1905,14 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
|
||||
o11.Type = model.CHANNEL_OPEN
|
||||
store.Must(ss.Channel().Save(&o11, -1))
|
||||
|
||||
o12 := model.Channel{}
|
||||
o12.TeamId = o1.TeamId
|
||||
o12.DisplayName = "Channel With Purpose"
|
||||
o12.Purpose = "This can now be searchable!"
|
||||
o12.Name = "with-purpose"
|
||||
o12.Type = model.CHANNEL_OPEN
|
||||
store.Must(ss.Channel().Save(&o12, -1))
|
||||
|
||||
for name, search := range map[string]func(teamId string, term string) store.StoreChannel{
|
||||
"AutocompleteInTeam": ss.Channel().AutocompleteInTeam,
|
||||
"SearchInTeam": ss.Channel().SearchInTeam,
|
||||
@@ -1986,6 +2015,19 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
|
||||
t.Fatal("wrong channel returned")
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-search(o1.TeamId, "now searchable"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
channels := result.Data.(*model.ChannelList)
|
||||
if len(*channels) != 1 {
|
||||
t.Fatal("should return 1 channel")
|
||||
}
|
||||
|
||||
if (*channels)[0].Name != o12.Name {
|
||||
t.Fatal("wrong channel returned")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +200,11 @@ func (_m *LayeredStoreDatabaseLayer) License() store.LicenseStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// LockToMaster provides a mock function with given fields:
|
||||
func (_m *LayeredStoreDatabaseLayer) LockToMaster() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// MarkSystemRanUnitTests provides a mock function with given fields:
|
||||
func (_m *LayeredStoreDatabaseLayer) MarkSystemRanUnitTests() {
|
||||
_m.Called()
|
||||
@@ -851,6 +856,11 @@ func (_m *LayeredStoreDatabaseLayer) TotalSearchDbConnections() int {
|
||||
return r0
|
||||
}
|
||||
|
||||
// UnlockFromMaster provides a mock function with given fields:
|
||||
func (_m *LayeredStoreDatabaseLayer) UnlockFromMaster() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// User provides a mock function with given fields:
|
||||
func (_m *LayeredStoreDatabaseLayer) User() store.UserStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -411,6 +411,11 @@ func (_m *SqlStore) License() store.LicenseStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// LockToMaster provides a mock function with given fields:
|
||||
func (_m *SqlStore) LockToMaster() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// MarkSystemRanUnitTests provides a mock function with given fields:
|
||||
func (_m *SqlStore) MarkSystemRanUnitTests() {
|
||||
_m.Called()
|
||||
@@ -706,6 +711,11 @@ func (_m *SqlStore) TotalSearchDbConnections() int {
|
||||
return r0
|
||||
}
|
||||
|
||||
// UnlockFromMaster provides a mock function with given fields:
|
||||
func (_m *SqlStore) UnlockFromMaster() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// User provides a mock function with given fields:
|
||||
func (_m *SqlStore) User() store.UserStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -198,6 +198,11 @@ func (_m *Store) License() store.LicenseStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// LockToMaster provides a mock function with given fields:
|
||||
func (_m *Store) LockToMaster() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// MarkSystemRanUnitTests provides a mock function with given fields:
|
||||
func (_m *Store) MarkSystemRanUnitTests() {
|
||||
_m.Called()
|
||||
@@ -437,6 +442,11 @@ func (_m *Store) TotalSearchDbConnections() int {
|
||||
return r0
|
||||
}
|
||||
|
||||
// UnlockFromMaster provides a mock function with given fields:
|
||||
func (_m *Store) UnlockFromMaster() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// User provides a mock function with given fields:
|
||||
func (_m *Store) User() store.UserStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -77,6 +77,8 @@ func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
||||
}
|
||||
func (s *Store) MarkSystemRanUnitTests() { /* do nothing */ }
|
||||
func (s *Store) Close() { /* do nothing */ }
|
||||
func (s *Store) LockToMaster() { /* do nothing */ }
|
||||
func (s *Store) UnlockFromMaster() { /* do nothing */ }
|
||||
func (s *Store) DropAllTables() { /* do nothing */ }
|
||||
func (s *Store) TotalMasterDbConnections() int { return 1 }
|
||||
func (s *Store) TotalReadDbConnections() int { return 1 }
|
||||
|
||||
@@ -555,6 +555,9 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L
|
||||
props["SQLDriverName"] = *c.SqlSettings.DriverName
|
||||
|
||||
props["EnableEmojiPicker"] = strconv.FormatBool(*c.ServiceSettings.EnableEmojiPicker)
|
||||
props["EnableGifPicker"] = strconv.FormatBool(*c.ServiceSettings.EnableGifPicker)
|
||||
props["GfycatApiKey"] = *c.ServiceSettings.GfycatApiKey
|
||||
props["GfycatApiSecret"] = *c.ServiceSettings.GfycatApiSecret
|
||||
props["RestrictCustomEmojiCreation"] = *c.ServiceSettings.RestrictCustomEmojiCreation
|
||||
props["MaxFileSize"] = strconv.FormatInt(*c.FileSettings.MaxFileSize, 10)
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
c.Err.IsOAuth = false
|
||||
}
|
||||
|
||||
if IsApiCall(c.App, r) || len(r.Header.Get("X-Mobile-App")) > 0 {
|
||||
if IsApiCall(c.App, r) || IsWebhookCall(c.App, r) || len(r.Header.Get("X-Mobile-App")) > 0 {
|
||||
w.WriteHeader(c.Err.StatusCode)
|
||||
w.Write([]byte(c.Err.ToJson()))
|
||||
} else {
|
||||
|
||||
@@ -73,7 +73,13 @@ func Handle404(a *app.App, w http.ResponseWriter, r *http.Request) {
|
||||
func IsApiCall(a *app.App, r *http.Request) bool {
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
return strings.Index(r.URL.Path, path.Join(subpath, "api")+"/") == 0
|
||||
return strings.HasPrefix(r.URL.Path, path.Join(subpath, "api")+"/")
|
||||
}
|
||||
|
||||
func IsWebhookCall(a *app.App, r *http.Request) bool {
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
return strings.HasPrefix(r.URL.Path, path.Join(subpath, "hooks")+"/")
|
||||
}
|
||||
|
||||
func ReturnStatusOK(w http.ResponseWriter) {
|
||||
|
||||
Reference in New Issue
Block a user