diff options
-rw-r--r-- | api/channel.go | 59 | ||||
-rw-r--r-- | api/channel_test.go | 11 | ||||
-rw-r--r-- | api/command.go | 2 | ||||
-rw-r--r-- | api/command_test.go | 2 | ||||
-rw-r--r-- | api/team.go | 12 | ||||
-rw-r--r-- | api/team_test.go | 16 | ||||
-rw-r--r-- | api/templates/post_body.html | 2 | ||||
-rw-r--r-- | api/user.go | 15 | ||||
-rw-r--r-- | manualtesting/manual_testing.go | 2 | ||||
-rw-r--r-- | web/react/components/channel_header.jsx | 7 | ||||
-rw-r--r-- | web/react/components/post_body.jsx | 2 | ||||
-rw-r--r-- | web/react/components/post_list.jsx | 13 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 1 | ||||
-rw-r--r-- | web/web.go | 2 |
14 files changed, 110 insertions, 36 deletions
diff --git a/api/channel.go b/api/channel.go index d3f6ca2de..c0c2d1548 100644 --- a/api/channel.go +++ b/api/channel.go @@ -57,7 +57,7 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - if sc, err := CreateChannel(c, channel, r.URL.Path, true); err != nil { + if sc, err := CreateChannel(c, channel, true); err != nil { c.Err = err return } else { @@ -65,7 +65,7 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) { } } -func CreateChannel(c *Context, channel *model.Channel, path string, addMember bool) (*model.Channel, *model.AppError) { +func CreateChannel(c *Context, channel *model.Channel, addMember bool) (*model.Channel, *model.AppError) { if result := <-Srv.Store.Channel().Save(channel); result.Err != nil { return nil, result.Err } else { @@ -100,7 +100,7 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - if sc, err := CreateDirectChannel(c, userId, r.URL.Path); err != nil { + if sc, err := CreateDirectChannel(c, userId); err != nil { c.Err = err return } else { @@ -108,7 +108,7 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) { } } -func CreateDirectChannel(c *Context, otherUserId string, path string) (*model.Channel, *model.AppError) { +func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model.AppError) { if len(otherUserId) != 26 { return nil, model.NewAppError("CreateDirectChannel", "Invalid other user id ", otherUserId) } @@ -132,7 +132,7 @@ func CreateDirectChannel(c *Context, otherUserId string, path string) (*model.Ch return nil, model.NewAppError("CreateDirectChannel", "Invalid other user id ", otherUserId) } - if sc, err := CreateChannel(c, channel, path, true); err != nil { + if sc, err := CreateChannel(c, channel, true); err != nil { return nil, err } else { cm := &model.ChannelMember{ChannelId: sc.Id, UserId: otherUserId, @@ -146,6 +146,23 @@ func CreateDirectChannel(c *Context, otherUserId string, path string) (*model.Ch } } +func CreateDefaultChannels(c *Context, teamId string) ([]*model.Channel, *model.AppError) { + townSquare := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: teamId} + + if _, err := CreateChannel(c, townSquare, false); err != nil { + return nil, err + } + + offTopic := &model.Channel{DisplayName: "Off-Topic", Name: "off-topic", Type: model.CHANNEL_OPEN, TeamId: teamId} + + if _, err := CreateChannel(c, offTopic, false); err != nil { + return nil, err + } + + channels := []*model.Channel{townSquare, offTopic} + return channels, nil +} + func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { channel := model.ChannelFromJson(r.Body) @@ -303,7 +320,7 @@ func joinChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) channelId := params["id"] - JoinChannel(c, channelId, r.URL.Path) + JoinChannel(c, channelId, "") if c.Err != nil { return @@ -314,7 +331,7 @@ func joinChannel(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(result))) } -func JoinChannel(c *Context, channelId string, path string) { +func JoinChannel(c *Context, channelId string, role string) { sc := Srv.Store.Channel().Get(channelId) uc := Srv.Store.User().Get(c.Session.UserId) @@ -340,7 +357,7 @@ func JoinChannel(c *Context, channelId string, path string) { } if channel.Type == model.CHANNEL_OPEN { - cm := &model.ChannelMember{ChannelId: channel.Id, UserId: c.Session.UserId, NotifyLevel: model.CHANNEL_NOTIFY_ALL} + cm := &model.ChannelMember{ChannelId: channel.Id, UserId: c.Session.UserId, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: role} if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil { c.Err = cmresult.Err @@ -363,6 +380,32 @@ func JoinChannel(c *Context, channelId string, path string) { } } +func JoinDefaultChannels(c *Context, user *model.User, channelRole string) *model.AppError { + // We don't call JoinChannel here since c.Session is not populated on user creation + + var err *model.AppError = nil + + if result := <-Srv.Store.Channel().GetByName(user.TeamId, "town-square"); result.Err != nil { + err = result.Err + } else { + cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole} + if cmResult := <-Srv.Store.Channel().SaveMember(cm); cmResult.Err != nil { + err = cmResult.Err + } + } + + if result := <-Srv.Store.Channel().GetByName(user.TeamId, "off-topic"); result.Err != nil { + err = result.Err + } else { + cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole} + if cmResult := <-Srv.Store.Channel().SaveMember(cm); cmResult.Err != nil { + err = cmResult.Err + } + } + + return err +} + func leaveChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) diff --git a/api/channel_test.go b/api/channel_test.go index 2e2e3683a..dfae840dc 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -35,8 +35,15 @@ func TestCreateChannel(t *testing.T) { } rget := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) - if rget.Channels[0].Name != channel.Name { - t.Fatal("full name didn't match") + nameMatch := false + for _, c := range rget.Channels { + if c.Name == channel.Name { + nameMatch = true + } + } + + if !nameMatch { + t.Fatal("Did not create channel with correct name") } if _, err := Client.CreateChannel(rchannel.Data.(*model.Channel)); err == nil { diff --git a/api/command.go b/api/command.go index aedbe07cc..810a8a07e 100644 --- a/api/command.go +++ b/api/command.go @@ -197,7 +197,7 @@ func joinCommand(c *Context, command *model.Command) bool { return false } - JoinChannel(c, v.Id, "/command") + JoinChannel(c, v.Id, "") if c.Err != nil { return false diff --git a/api/command_test.go b/api/command_test.go index d3b0da455..5b7734628 100644 --- a/api/command_test.go +++ b/api/command_test.go @@ -129,7 +129,7 @@ func TestJoinCommands(t *testing.T) { c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) - if len(c1.Channels) != 3 { // 3 because of town-square and direct + if len(c1.Channels) != 4 { // 4 because of town-square, off-topic and direct t.Fatal("didn't join channel") } diff --git a/api/team.go b/api/team.go index 24b7ec9e1..15e4e2c17 100644 --- a/api/team.go +++ b/api/team.go @@ -146,10 +146,8 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { } else { rteam := result.Data.(*model.Team) - channel := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: rteam.Id} - - if _, err := CreateChannel(c, channel, r.URL.Path, false); err != nil { - c.Err = err + if _, err := CreateDefaultChannels(c, rteam.Id); err != nil { + c.Err = nil return } @@ -197,10 +195,8 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) { } else { rteam := result.Data.(*model.Team) - channel := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: rteam.Id} - - if _, err := CreateChannel(c, channel, r.URL.Path, false); err != nil { - c.Err = err + if _, err := CreateDefaultChannels(c, rteam.Id); err != nil { + c.Err = nil return } diff --git a/api/team_test.go b/api/team_test.go index 042c0a2e9..bb77d43a0 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -55,6 +55,11 @@ func TestCreateFromSignupTeam(t *testing.T) { } } + c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) + if len(c1.Channels) != 2 { + t.Fatal("default channels not created") + } + ts.Data = "garbage" _, err = Client.CreateTeamFromSignup(&ts) if err == nil { @@ -71,6 +76,17 @@ func TestCreateTeam(t *testing.T) { t.Fatal(err) } + user := &model.User{TeamId: rteam.Data.(*model.Team).Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) + Srv.Store.User().VerifyEmail(user.Id) + + Client.LoginByEmail(team.Domain, user.Email, "pwd") + + c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) + if len(c1.Channels) != 2 { + t.Fatal("default channels not created") + } + if rteam.Data.(*model.Team).Name != team.Name { t.Fatal("full name didn't match") } diff --git a/api/templates/post_body.html b/api/templates/post_body.html index 663ec66d2..069cdf1fb 100644 --- a/api/templates/post_body.html +++ b/api/templates/post_body.html @@ -20,7 +20,7 @@ <h2 style="font-weight: normal; margin-top: 10px;">You were mentioned</h2> <p>CHANNEL: {{.Props.ChannelName}}<br>{{.Props.SenderName}} - {{.Props.Hour}}:{{.Props.Minute}} GMT, {{.Props.Month}} {{.Props.Day}}<br><pre style="text-align:left;font-family: 'Lato', sans-serif;">{{.Props.PostMessage}}</pre></p> <p style="margin: 20px 0 15px"> - <a href="{{.Props.TeamLink}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Go To Channel</a> + <a href="{{.Props.TeamLink}}" style="background: #2389D7; display: inline-block; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 170px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Go To Channel</a> </p> </td> </tr> diff --git a/api/user.go b/api/user.go index f8382cf2f..292d2b61b 100644 --- a/api/user.go +++ b/api/user.go @@ -176,21 +176,16 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { } else { ruser := result.Data.(*model.User) - // Do not error if user cannot be added to the town-square channel - if cresult := <-Srv.Store.Channel().GetByName(team.Id, "town-square"); cresult.Err != nil { - l4g.Error("Failed to get town-square err=%v", cresult.Err) - } else { - cm := &model.ChannelMember{ChannelId: cresult.Data.(*model.Channel).Id, UserId: ruser.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole} - if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil { - l4g.Error("Failed to add member town-square err=%v", cmresult.Err) - } + // Soft error if there is an issue joining the default channels + if err := JoinDefaultChannels(c, ruser, channelRole); err != nil { + l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } //fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { - l4g.Error("Failed to get town-square err=%v", cresult.Err) + l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl) @@ -198,7 +193,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { ruser.Sanitize(map[string]bool{}) - //This message goes to every channel, so the channelId is irrelevant + // This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) store.PublishAndForget(message) diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go index ead441108..929f7ab5d 100644 --- a/manualtesting/manual_testing.go +++ b/manualtesting/manual_testing.go @@ -78,7 +78,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) { createdTeam := result.Data.(*model.Team) channel := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: createdTeam.Id} - if _, err := api.CreateChannel(c, channel, r.URL.Path, false); err != nil { + if _, err := api.CreateChannel(c, channel, false); err != nil { c.Err = err return } diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 428d3ed81..39e3e767a 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -44,8 +44,13 @@ var ExtraMembers = React.createClass({ var count = this.props.members.length == 0 ? "-" : this.props.members.length; count = this.props.members.length > 19 ? "20+" : count; var data_content = ""; + var sortedMembers = this.state.extra_info.members; - this.props.members.forEach(function(m) { + sortedMembers.sort(function(a,b) { + return a.username.localeCompare(b.username); + }) + + sortedMembers.forEach(function(m) { data_content += "<div style='white-space: nowrap'>" + m.username + "</div>"; }); diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 3079917ec..7d5ef4d33 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -102,7 +102,7 @@ module.exports = React.createClass({ images.push(filenames[i]); } else if (i < Constants.MAX_DISPLAY_FILES) { postFiles.push( - <div className="post-image__column custom-file" key={fileInfo.name}> + <div className="post-image__column custom-file" key={fileInfo.name+i}> <a href={fileInfo.path+"."+fileInfo.ext} download={fileInfo.name+"."+fileInfo.ext}> <div className={"file-icon "+utils.getIconClassName(type)}/> </a> diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 37e3faef2..e37de8d88 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -356,6 +356,7 @@ module.exports = React.createClass({ var ui_name = channel.display_name var members = ChannelStore.getCurrentExtraInfo().members; var creator_name = ""; + var userStyle = { color: UserStore.getCurrentUser().props.theme } for (var i = 0; i < members.length; i++) { if (members[i].roles.indexOf('admin') > -1) { @@ -382,8 +383,18 @@ module.exports = React.createClass({ </p> </div> ); + } else if (channel.name === Constants.OFFTOPIC_CHANNEL) { + more_messages = ( + <div className="channel-intro"> + <h4 className="channel-intro-title">Welcome</h4> + <p> + {"This is the start of " + ui_name + ", a channel for conversations you’d prefer out of more focused channels."} + <br/> + <a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={ui_name} data-channelid={channel.id}><i className="fa fa-pencil"></i>Set a description</a> + </p> + </div> + ); } else { - var userStyle = { color: UserStore.getCurrentUser().props.theme } var ui_type = channel.type === 'P' ? "private group" : "channel"; more_messages = ( <div className="channel-intro"> diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index e5f42c8a0..a5124afe2 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -51,6 +51,7 @@ module.exports = { MAX_UPLOAD_FILES: 5, MAX_FILE_SIZE: 50000000, // 50 MB DEFAULT_CHANNEL: 'town-square', + OFFTOPIC_CHANNEL: 'off-topic', POST_CHUNK_SIZE: 60, RESERVED_DOMAINS: [ "www", diff --git a/web/web.go b/web/web.go index 7357124b5..443a75916 100644 --- a/web/web.go +++ b/web/web.go @@ -285,7 +285,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { otherUserId = ids[0] } - if sc, err := api.CreateDirectChannel(c, otherUserId, r.URL.Path); err != nil { + if sc, err := api.CreateDirectChannel(c, otherUserId); err != nil { api.Handle404(w, r) return } else { |