diff options
-rw-r--r-- | api/channel_test.go | 2 | ||||
-rw-r--r-- | api/slackimport.go | 41 | ||||
-rw-r--r-- | api/slackimport_test.go | 40 | ||||
-rw-r--r-- | i18n/en.json | 16 | ||||
-rw-r--r-- | model/channel.go | 20 | ||||
-rw-r--r-- | model/channel_test.go | 7 | ||||
-rw-r--r-- | store/sql_channel_store.go | 2 | ||||
-rw-r--r-- | store/sql_upgrade.go | 5 | ||||
-rw-r--r-- | webapp/components/edit_channel_purpose_modal.jsx | 2 | ||||
-rw-r--r-- | webapp/components/new_channel_modal.jsx | 2 |
10 files changed, 121 insertions, 16 deletions
diff --git a/api/channel_test.go b/api/channel_test.go index 83bb732dd..d05cd495b 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -546,7 +546,7 @@ func TestUpdateChannelPurpose(t *testing.T) { } data["channel_id"] = channel1.Id - data["channel_purpose"] = strings.Repeat("a", 150) + data["channel_purpose"] = strings.Repeat("a", 350) if _, err := Client.UpdateChannelPurpose(data); err == nil { t.Fatal("should have errored on bad channel purpose") } diff --git a/api/slackimport.go b/api/slackimport.go index 759514553..90e92cf0a 100644 --- a/api/slackimport.go +++ b/api/slackimport.go @@ -7,15 +7,17 @@ import ( "archive/zip" "bytes" "encoding/json" - l4g "github.com/alecthomas/log4go" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" "io" "mime/multipart" "path/filepath" "regexp" "strconv" "strings" + "unicode/utf8" + + l4g "github.com/alecthomas/log4go" + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" ) type SlackChannel struct { @@ -63,6 +65,14 @@ type SlackAttachment struct { Fields []map[string]interface{} `json:"fields"` } +func truncateRunes(s string, i int) string { + runes := []rune(s) + if len(runes) > i { + return string(runes[:i]) + } + return s +} + func SlackConvertTimeStamp(ts string) int64 { timeString := strings.SplitN(ts, ".", 2)[0] @@ -368,6 +378,30 @@ func addSlackUsersToChannel(members []string, users map[string]*model.User, chan } } +func SlackSanitiseChannelProperties(channel model.Channel) model.Channel { + if utf8.RuneCountInString(channel.DisplayName) > model.CHANNEL_DISPLAY_NAME_MAX_RUNES { + l4g.Warn("api.slackimport.slack_sanitise_channel_properties.display_name_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}) + channel.DisplayName = truncateRunes(channel.DisplayName, model.CHANNEL_DISPLAY_NAME_MAX_RUNES) + } + + if len(channel.Name) > model.CHANNEL_NAME_MAX_LENGTH { + l4g.Warn("api.slackimport.slack_sanitise_channel_properties.name_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}) + channel.Name = channel.Name[0:model.CHANNEL_NAME_MAX_LENGTH] + } + + if utf8.RuneCountInString(channel.Purpose) > model.CHANNEL_PURPOSE_MAX_RUNES { + l4g.Warn("api.slackimport.slack_sanitise_channel_properties.purpose_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}) + channel.Purpose = truncateRunes(channel.Purpose, model.CHANNEL_PURPOSE_MAX_RUNES) + } + + if utf8.RuneCountInString(channel.Header) > model.CHANNEL_HEADER_MAX_RUNES { + l4g.Warn("api.slackimport.slack_sanitise_channel_properties.header_too_long.warn", map[string]interface{}{"ChannelName": channel.DisplayName}) + channel.Header = truncateRunes(channel.Header, model.CHANNEL_HEADER_MAX_RUNES) + } + + return channel +} + func SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[string][]SlackPost, users map[string]*model.User, uploads map[string]*zip.File, botUser *model.User, log *bytes.Buffer) map[string]*model.Channel { // Write Header log.WriteString(utils.T("api.slackimport.slack_add_channels.added")) @@ -383,6 +417,7 @@ func SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[str Purpose: sChannel.Purpose["value"], Header: sChannel.Topic["value"], } + newChannel = SlackSanitiseChannelProperties(newChannel) mChannel := ImportChannel(&newChannel) if mChannel == nil { // Maybe it already exists? diff --git a/api/slackimport_test.go b/api/slackimport_test.go index 81b79b3d1..d78424ac0 100644 --- a/api/slackimport_test.go +++ b/api/slackimport_test.go @@ -4,7 +4,9 @@ package api import ( + "github.com/mattermost/platform/model" "os" + "strings" "testing" ) @@ -177,3 +179,41 @@ func TestSlackParsePosts(t *testing.T) { t.Fatalf("Unexpected number of posts: %v", len(posts)) } } + +func TestSlackSanitiseChannelProperties(t *testing.T) { + c1 := model.Channel{ + DisplayName: "display-name", + Name: "name", + Purpose: "The channel purpose", + Header: "The channel header", + } + + c1s := SlackSanitiseChannelProperties(c1) + if c1.DisplayName != c1s.DisplayName || c1.Name != c1s.Name || c1.Purpose != c1s.Purpose || c1.Header != c1s.Header { + t.Fatalf("Unexpected alterations to the channel properties.") + } + + c2 := model.Channel{ + DisplayName: strings.Repeat("abcdefghij", 7), + Name: strings.Repeat("abcdefghij", 7), + Purpose: strings.Repeat("0123456789", 30), + Header: strings.Repeat("0123456789", 120), + } + + c2s := SlackSanitiseChannelProperties(c2) + if c2s.DisplayName != strings.Repeat("abcdefghij", 6)+"abcd" { + t.Fatalf("Unexpected alterations to the channel properties: %v", c2s.DisplayName) + } + + if c2s.Name != strings.Repeat("abcdefghij", 6)+"abcd" { + t.Fatalf("Unexpected alterations to the channel properties: %v", c2s.Name) + } + + if c2s.Purpose != strings.Repeat("0123456789", 25) { + t.Fatalf("Unexpected alterations to the channel properties: %v", c2s.Purpose) + } + + if c2s.Header != strings.Repeat("0123456789", 102)+"0123" { + t.Fatalf("Unexpected alterations to the channel properties: %v", c2s.Header) + } +} diff --git a/i18n/en.json b/i18n/en.json index 40259a814..53a1939be 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1672,6 +1672,22 @@ "translation": "Error parsing some slack posts. Import may work anyway." }, { + "id": "api.slackimport.slack_sanitise_channel_properties.display_name_too_long.warn", + "translation": "Slack Importer: Channel {{.ChannelName}} has a display name which is too long. It will be truncated when imported." + }, + { + "id": "api.slackimport.slack_sanitise_channel_properties.header_too_long.warn", + "translation": "Slack Importer: Channel {{.ChannelName}} has a header which is too long. It will be truncated when imported." + }, + { + "id": "api.slackimport.slack_sanitise_channel_properties.name_too_long.warn", + "translation": "Slack Importer: Channel {{.ChannelName}} has a name which is too long. It will be truncated when imported." + }, + { + "id": "api.slackimport.slack_sanitise_channel_properties.purpose_too_long.warn", + "translation": "Slack Importer: Channel {{.ChannelName}} has a purpose which is too long. It will be truncated when imported." + }, + { "id": "api.status.init.debug", "translation": "Initializing status api routes" }, diff --git a/model/channel.go b/model/channel.go index 7dee079c5..2ad257ccc 100644 --- a/model/channel.go +++ b/model/channel.go @@ -10,10 +10,14 @@ import ( ) const ( - CHANNEL_OPEN = "O" - CHANNEL_PRIVATE = "P" - CHANNEL_DIRECT = "D" - DEFAULT_CHANNEL = "town-square" + CHANNEL_OPEN = "O" + CHANNEL_PRIVATE = "P" + CHANNEL_DIRECT = "D" + DEFAULT_CHANNEL = "town-square" + CHANNEL_DISPLAY_NAME_MAX_RUNES = 64 + CHANNEL_NAME_MAX_LENGTH = 64 + CHANNEL_HEADER_MAX_RUNES = 1024 + CHANNEL_PURPOSE_MAX_RUNES = 250 ) type Channel struct { @@ -75,11 +79,11 @@ func (o *Channel) IsValid() *AppError { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+o.Id) } - if utf8.RuneCountInString(o.DisplayName) > 64 { + if utf8.RuneCountInString(o.DisplayName) > CHANNEL_DISPLAY_NAME_MAX_RUNES { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) } - if len(o.Name) > 64 { + if len(o.Name) > CHANNEL_NAME_MAX_LENGTH { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id) } @@ -91,11 +95,11 @@ func (o *Channel) IsValid() *AppError { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) } - if utf8.RuneCountInString(o.Header) > 1024 { + if utf8.RuneCountInString(o.Header) > CHANNEL_HEADER_MAX_RUNES { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+o.Id) } - if utf8.RuneCountInString(o.Purpose) > 128 { + if utf8.RuneCountInString(o.Purpose) > CHANNEL_PURPOSE_MAX_RUNES { return NewLocAppError("Channel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+o.Id) } diff --git a/model/channel_test.go b/model/channel_test.go index 590417cfe..af4e3c16d 100644 --- a/model/channel_test.go +++ b/model/channel_test.go @@ -78,7 +78,7 @@ func TestChannelIsValid(t *testing.T) { t.Fatal(err) } - o.Purpose = strings.Repeat("01234567890", 20) + o.Purpose = strings.Repeat("01234567890", 30) if err := o.IsValid(); err == nil { t.Fatal("should be invalid") } @@ -87,6 +87,11 @@ func TestChannelIsValid(t *testing.T) { if err := o.IsValid(); err != nil { t.Fatal(err) } + + o.Purpose = strings.Repeat("0123456789", 25) + if err := o.IsValid(); err != nil { + t.Fatal(err) + } } func TestChannelPreSave(t *testing.T) { diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 8b77ab6ff..5107a0bd8 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -38,7 +38,7 @@ func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore { table.ColMap("Name").SetMaxSize(64) table.SetUniqueTogether("Name", "TeamId") table.ColMap("Header").SetMaxSize(1024) - table.ColMap("Purpose").SetMaxSize(128) + table.ColMap("Purpose").SetMaxSize(250) table.ColMap("CreatorId").SetMaxSize(26) tablem := db.AddTableWithName(model.ChannelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId") diff --git a/store/sql_upgrade.go b/store/sql_upgrade.go index 7ee7ea199..824d0c3f0 100644 --- a/store/sql_upgrade.go +++ b/store/sql_upgrade.go @@ -201,6 +201,11 @@ func UpgradeDatabaseToVersion35(sqlStore *SqlStore) { // The rest of the migration from Filenames -> FileIds is done lazily in api.GetFileInfosForPost sqlStore.CreateColumnIfNotExists("Posts", "FileIds", "varchar(150)", "varchar(150)", "[]") + // Increase maximum length of the Channel table Purpose column. + if sqlStore.GetMaxLengthOfColumnIfExists("Channels", "Purpose") != "250" { + sqlStore.AlterColumnTypeIfExists("Channels", "Purpose", "varchar(250)", "varchar(250)") + } + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // UNCOMMENT WHEN WE DO RELEASE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/webapp/components/edit_channel_purpose_modal.jsx b/webapp/components/edit_channel_purpose_modal.jsx index bfb4d181a..7ba2eff2c 100644 --- a/webapp/components/edit_channel_purpose_modal.jsx +++ b/webapp/components/edit_channel_purpose_modal.jsx @@ -155,7 +155,7 @@ export default class EditChannelPurposeModal extends React.Component { ref='purpose' className='form-control no-resize' rows='6' - maxLength='128' + maxLength='250' defaultValue={this.props.channel.purpose} onKeyDown={this.handleKeyDown} /> diff --git a/webapp/components/new_channel_modal.jsx b/webapp/components/new_channel_modal.jsx index 31ed15306..4122c3bfb 100644 --- a/webapp/components/new_channel_modal.jsx +++ b/webapp/components/new_channel_modal.jsx @@ -283,7 +283,7 @@ class NewChannelModal extends React.Component { ref='channel_purpose' rows='4' placeholder={this.props.intl.formatMessage({id: 'channel_modal.purpose'})} - maxLength='128' + maxLength='250' value={this.props.channelData.purpose} onChange={this.handleChange} tabIndex='2' |