Merge pull request #1199 from hmhealey/plt600
PLT-600 Renamed channel description to channel header and added channel purpose field
Этот коммит содержится в:
@@ -22,7 +22,8 @@ func InitChannel(r *mux.Router) {
|
||||
sr.Handle("/create", ApiUserRequired(createChannel)).Methods("POST")
|
||||
sr.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST")
|
||||
sr.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST")
|
||||
sr.Handle("/update_desc", ApiUserRequired(updateChannelDesc)).Methods("POST")
|
||||
sr.Handle("/update_header", ApiUserRequired(updateChannelHeader)).Methods("POST")
|
||||
sr.Handle("/update_purpose", ApiUserRequired(updateChannelPurpose)).Methods("POST")
|
||||
sr.Handle("/update_notify_props", ApiUserRequired(updateNotifyProps)).Methods("POST")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/", ApiUserRequiredActivity(getChannel, false)).Methods("GET")
|
||||
sr.Handle("/{id:[A-Za-z0-9]+}/extra_info", ApiUserRequired(getChannelExtraInfo)).Methods("GET")
|
||||
@@ -124,7 +125,7 @@ func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model
|
||||
channel.Name = model.GetDMNameFromIds(otherUserId, c.Session.UserId)
|
||||
|
||||
channel.TeamId = c.Session.TeamId
|
||||
channel.Description = ""
|
||||
channel.Header = ""
|
||||
channel.Type = model.CHANNEL_DIRECT
|
||||
|
||||
if uresult := <-uc; uresult.Err != nil {
|
||||
@@ -209,7 +210,8 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
oldChannel.Description = channel.Description
|
||||
oldChannel.Header = channel.Header
|
||||
oldChannel.Purpose = channel.Purpose
|
||||
|
||||
if len(channel.DisplayName) > 0 {
|
||||
oldChannel.DisplayName = channel.DisplayName
|
||||
@@ -233,18 +235,18 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func updateChannelDesc(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
channelId := props["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateChannelDesc", "channel_id")
|
||||
c.SetInvalidParam("updateChannelHeader", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelDesc := props["channel_description"]
|
||||
if len(channelDesc) > 1024 {
|
||||
c.SetInvalidParam("updateChannelDesc", "channel_description")
|
||||
channelHeader := props["channel_header"]
|
||||
if len(channelHeader) > 1024 {
|
||||
c.SetInvalidParam("updateChannelHeader", "channel_header")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -261,11 +263,54 @@ func updateChannelDesc(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
// Don't need to do anything channel member, just wanted to confirm it exists
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelDesc") {
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelHeader") {
|
||||
return
|
||||
}
|
||||
|
||||
channel.Description = channelDesc
|
||||
channel.Header = channelHeader
|
||||
|
||||
if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil {
|
||||
c.Err = ucresult.Err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("name=" + channel.Name)
|
||||
w.Write([]byte(channel.ToJson()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
channelId := props["channel_id"]
|
||||
if len(channelId) != 26 {
|
||||
c.SetInvalidParam("updateChannelPurpose", "channel_id")
|
||||
return
|
||||
}
|
||||
|
||||
channelPurpose := props["channel_purpose"]
|
||||
if len(channelPurpose) > 1024 {
|
||||
c.SetInvalidParam("updateChannelPurpose", "channel_purpose")
|
||||
return
|
||||
}
|
||||
|
||||
sc := Srv.Store.Channel().Get(channelId)
|
||||
cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId)
|
||||
|
||||
if cresult := <-sc; cresult.Err != nil {
|
||||
c.Err = cresult.Err
|
||||
return
|
||||
} else if cmcresult := <-cmc; cmcresult.Err != nil {
|
||||
c.Err = cmcresult.Err
|
||||
return
|
||||
} else {
|
||||
channel := cresult.Data.(*model.Channel)
|
||||
// Don't need to do anything channel member, just wanted to confirm it exists
|
||||
|
||||
if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelPurpose") {
|
||||
return
|
||||
}
|
||||
|
||||
channel.Purpose = channelPurpose
|
||||
|
||||
if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil {
|
||||
c.Err = ucresult.Err
|
||||
|
||||
@@ -61,8 +61,8 @@ func BenchmarkCreateDirectChannel(b *testing.B) {
|
||||
|
||||
func BenchmarkUpdateChannel(b *testing.B) {
|
||||
var (
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
CHANNEL_DESCRIPTION_LEN = 50
|
||||
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
|
||||
CHANNEL_HEADER_LEN = 50
|
||||
)
|
||||
team, _, _ := SetupBenchmark()
|
||||
|
||||
@@ -73,7 +73,7 @@ func BenchmarkUpdateChannel(b *testing.B) {
|
||||
}
|
||||
|
||||
for i := range channels {
|
||||
channels[i].Description = utils.RandString(CHANNEL_DESCRIPTION_LEN, utils.ALPHANUMERIC)
|
||||
channels[i].Header = utils.RandString(CHANNEL_HEADER_LEN, utils.ALPHANUMERIC)
|
||||
}
|
||||
|
||||
// Benchmark Start
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -173,12 +174,17 @@ func TestUpdateChannel(t *testing.T) {
|
||||
|
||||
Client.AddChannelMember(channel1.Id, userTeamAdmin.Id)
|
||||
|
||||
desc := "a" + model.NewId() + "a"
|
||||
upChannel1 := &model.Channel{Id: channel1.Id, Description: desc}
|
||||
header := "a" + model.NewId() + "a"
|
||||
purpose := "a" + model.NewId() + "a"
|
||||
upChannel1 := &model.Channel{Id: channel1.Id, Header: header, Purpose: purpose}
|
||||
upChannel1 = Client.Must(Client.UpdateChannel(upChannel1)).Data.(*model.Channel)
|
||||
|
||||
if upChannel1.Description != desc {
|
||||
t.Fatal("Channel admin failed to update desc")
|
||||
if upChannel1.Header != header {
|
||||
t.Fatal("Channel admin failed to update header")
|
||||
}
|
||||
|
||||
if upChannel1.Purpose != purpose {
|
||||
t.Fatal("Channel admin failed to update purpose")
|
||||
}
|
||||
|
||||
if upChannel1.DisplayName != channel1.DisplayName {
|
||||
@@ -187,12 +193,17 @@ func TestUpdateChannel(t *testing.T) {
|
||||
|
||||
Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
|
||||
|
||||
desc = "b" + model.NewId() + "b"
|
||||
upChannel1 = &model.Channel{Id: channel1.Id, Description: desc}
|
||||
header = "b" + model.NewId() + "b"
|
||||
purpose = "b" + model.NewId() + "b"
|
||||
upChannel1 = &model.Channel{Id: channel1.Id, Header: header, Purpose: purpose}
|
||||
upChannel1 = Client.Must(Client.UpdateChannel(upChannel1)).Data.(*model.Channel)
|
||||
|
||||
if upChannel1.Description != desc {
|
||||
t.Fatal("Team admin failed to update desc")
|
||||
if upChannel1.Header != header {
|
||||
t.Fatal("Team admin failed to update header")
|
||||
}
|
||||
|
||||
if upChannel1.Purpose != purpose {
|
||||
t.Fatal("Team admin failed to update purpose")
|
||||
}
|
||||
|
||||
if upChannel1.DisplayName != channel1.DisplayName {
|
||||
@@ -203,7 +214,7 @@ func TestUpdateChannel(t *testing.T) {
|
||||
data := rget.Data.(*model.ChannelList)
|
||||
for _, c := range data.Channels {
|
||||
if c.Name == model.DEFAULT_CHANNEL {
|
||||
c.Description = "new desc"
|
||||
c.Header = "new header"
|
||||
if _, err := Client.UpdateChannel(c); err == nil {
|
||||
t.Fatal("should have errored on updating default channel")
|
||||
}
|
||||
@@ -218,7 +229,7 @@ func TestUpdateChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChannelDesc(t *testing.T) {
|
||||
func TestUpdateChannelHeader(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
@@ -235,36 +246,33 @@ func TestUpdateChannelDesc(t *testing.T) {
|
||||
|
||||
data := make(map[string]string)
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_description"] = "new desc"
|
||||
data["channel_header"] = "new header"
|
||||
|
||||
var upChannel1 *model.Channel
|
||||
if result, err := Client.UpdateChannelDesc(data); err != nil {
|
||||
if result, err := Client.UpdateChannelHeader(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
upChannel1 = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
if upChannel1.Description != data["channel_description"] {
|
||||
t.Fatal("Failed to update desc")
|
||||
if upChannel1.Header != data["channel_header"] {
|
||||
t.Fatal("Failed to update header")
|
||||
}
|
||||
|
||||
data["channel_id"] = "junk"
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
if _, err := Client.UpdateChannelHeader(data); err == nil {
|
||||
t.Fatal("should have errored on junk channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
if _, err := Client.UpdateChannelHeader(data); err == nil {
|
||||
t.Fatal("should have errored on non-existent channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_description"] = ""
|
||||
for i := 0; i < 1050; i++ {
|
||||
data["channel_description"] += "a"
|
||||
}
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
t.Fatal("should have errored on bad channel desc")
|
||||
data["channel_header"] = strings.Repeat("a", 1050)
|
||||
if _, err := Client.UpdateChannelHeader(data); err == nil {
|
||||
t.Fatal("should have errored on bad channel header")
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
@@ -274,9 +282,68 @@ func TestUpdateChannelDesc(t *testing.T) {
|
||||
Client.LoginByEmail(team.Name, user2.Email, "pwd")
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_description"] = "new desc"
|
||||
if _, err := Client.UpdateChannelDesc(data); err == nil {
|
||||
t.Fatal("should have errored non-channel member trying to update desc")
|
||||
data["channel_header"] = "new header"
|
||||
if _, err := Client.UpdateChannelHeader(data); err == nil {
|
||||
t.Fatal("should have errored non-channel member trying to update header")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChannelPurpose(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_purpose"] = "new purpose"
|
||||
|
||||
var upChannel1 *model.Channel
|
||||
if result, err := Client.UpdateChannelPurpose(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
upChannel1 = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
if upChannel1.Purpose != data["channel_purpose"] {
|
||||
t.Fatal("Failed to update purpose")
|
||||
}
|
||||
|
||||
data["channel_id"] = "junk"
|
||||
if _, err := Client.UpdateChannelPurpose(data); err == nil {
|
||||
t.Fatal("should have errored on junk channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = "12345678901234567890123456"
|
||||
if _, err := Client.UpdateChannelPurpose(data); err == nil {
|
||||
t.Fatal("should have errored on non-existent channel id")
|
||||
}
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_purpose"] = strings.Repeat("a", 150)
|
||||
if _, err := Client.UpdateChannelPurpose(data); err == nil {
|
||||
t.Fatal("should have errored on bad channel purpose")
|
||||
}
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user2.Email, "pwd")
|
||||
|
||||
data["channel_id"] = channel1.Id
|
||||
data["channel_purpose"] = "new purpose"
|
||||
if _, err := Client.UpdateChannelPurpose(data); err == nil {
|
||||
t.Fatal("should have errored non-channel member trying to update purpose")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ func SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[str
|
||||
Type: model.CHANNEL_OPEN,
|
||||
DisplayName: sChannel.Name,
|
||||
Name: SlackConvertChannelName(sChannel.Name),
|
||||
Description: sChannel.Topic["value"],
|
||||
Purpose: sChannel.Topic["value"],
|
||||
}
|
||||
mChannel := ImportChannel(&newChannel)
|
||||
if mChannel == nil {
|
||||
|
||||
@@ -24,7 +24,8 @@ type Channel struct {
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
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"`
|
||||
@@ -89,8 +90,12 @@ func (o *Channel) IsValid() *AppError {
|
||||
return NewAppError("Channel.IsValid", "Invalid type", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Description) > 1024 {
|
||||
return NewAppError("Channel.IsValid", "Invalid description", "id="+o.Id)
|
||||
if len(o.Header) > 1024 {
|
||||
return NewAppError("Channel.IsValid", "Invalid header", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.Purpose) > 128 {
|
||||
return NewAppError("Channel.IsValid", "Invalid purpose", "id="+o.Id)
|
||||
}
|
||||
|
||||
if len(o.CreatorId) > 26 {
|
||||
|
||||
@@ -67,6 +67,26 @@ func TestChannelIsValid(t *testing.T) {
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o.Header = strings.Repeat("01234567890", 100)
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Header = "1234"
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o.Purpose = strings.Repeat("01234567890", 20)
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Purpose = "1234"
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelPreSave(t *testing.T) {
|
||||
|
||||
@@ -452,8 +452,17 @@ func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateChannelDesc(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/channels/update_desc", MapToJson(data)); err != nil {
|
||||
func (c *Client) UpdateChannelHeader(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/channels/update_header", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) UpdateChannelPurpose(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/channels/update_purpose", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
|
||||
@@ -25,7 +25,8 @@ func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore {
|
||||
table.ColMap("DisplayName").SetMaxSize(64)
|
||||
table.ColMap("Name").SetMaxSize(64)
|
||||
table.SetUniqueTogether("Name", "TeamId")
|
||||
table.ColMap("Description").SetMaxSize(1024)
|
||||
table.ColMap("Header").SetMaxSize(1024)
|
||||
table.ColMap("Purpose").SetMaxSize(128)
|
||||
table.ColMap("CreatorId").SetMaxSize(26)
|
||||
|
||||
tablem := db.AddTableWithName(model.ChannelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId")
|
||||
@@ -83,6 +84,11 @@ func (s SqlChannelStore) UpgradeSchemaIfNeeded() {
|
||||
|
||||
s.RemoveColumnIfExists("ChannelMembers", "NotifyLevel")
|
||||
}
|
||||
|
||||
// BEGIN REMOVE AFTER 1.2.0
|
||||
s.RenameColumnIfExists("Channels", "Description", "Header", "varchar(1024)")
|
||||
s.CreateColumnIfNotExists("Channels", "Purpose", "varchar(1024)", "varchar(1024)", "")
|
||||
// END REMOVE AFTER 1.2.0
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) CreateIndexesIfNotExists() {
|
||||
|
||||
@@ -364,27 +364,26 @@ func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) boo
|
||||
return true
|
||||
}
|
||||
|
||||
// func (ss SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
|
||||
func (ss SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
|
||||
if !ss.DoesColumnExist(tableName, oldColumnName) {
|
||||
return false
|
||||
}
|
||||
|
||||
// // XXX TODO FIXME this should be removed after 0.6.0
|
||||
// if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
|
||||
// return false
|
||||
// }
|
||||
var err error
|
||||
if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
|
||||
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType)
|
||||
} else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
|
||||
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName)
|
||||
}
|
||||
|
||||
// if !ss.DoesColumnExist(tableName, oldColumnName) {
|
||||
// return false
|
||||
// }
|
||||
if err != nil {
|
||||
l4g.Critical("Failed to rename column %v", err)
|
||||
time.Sleep(time.Second)
|
||||
panic("Failed to drop column " + err.Error())
|
||||
}
|
||||
|
||||
// _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType)
|
||||
|
||||
// if err != nil {
|
||||
// l4g.Critical("Failed to rename column %v", err)
|
||||
// time.Sleep(time.Second)
|
||||
// panic("Failed to drop column " + err.Error())
|
||||
// }
|
||||
|
||||
// return true
|
||||
// }
|
||||
return true
|
||||
}
|
||||
|
||||
func (ss SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) {
|
||||
ss.createIndexIfNotExists(indexName, tableName, columnName, INDEX_TYPE_DEFAULT)
|
||||
|
||||
@@ -90,8 +90,9 @@ export default class AccessHistoryModal extends React.Component {
|
||||
case '/channels/update':
|
||||
currentAuditDesc = 'Updated the ' + channelName + ' channel/group name';
|
||||
break;
|
||||
case '/channels/update_desc':
|
||||
currentAuditDesc = 'Updated the ' + channelName + ' channel/group description';
|
||||
case '/channels/update_desc': // support the old path
|
||||
case '/channels/update_header':
|
||||
currentAuditDesc = 'Updated the ' + channelName + ' channel/group header';
|
||||
break;
|
||||
default:
|
||||
let userIdField = [];
|
||||
|
||||
@@ -11,6 +11,7 @@ const TextFormatting = require('../utils/text_formatting.jsx');
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
const MessageWrapper = require('./message_wrapper.jsx');
|
||||
const PopoverListMembers = require('./popover_list_members.jsx');
|
||||
const EditChannelPurposeModal = require('./edit_channel_purpose_modal.jsx');
|
||||
|
||||
const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
@@ -27,7 +28,9 @@ export default class ChannelHeader extends React.Component {
|
||||
this.handleLeave = this.handleLeave.bind(this);
|
||||
this.searchMentions = this.searchMentions.bind(this);
|
||||
|
||||
this.state = this.getStateFromStores();
|
||||
const state = this.getStateFromStores();
|
||||
state.showEditChannelPurposeModal = false;
|
||||
this.state = state;
|
||||
}
|
||||
getStateFromStores() {
|
||||
return {
|
||||
@@ -110,11 +113,11 @@ export default class ChannelHeader extends React.Component {
|
||||
bSize='large'
|
||||
placement='bottom'
|
||||
className='description'
|
||||
onMouseOver={() => this.refs.descriptionOverlay.show()}
|
||||
onMouseOut={() => this.refs.descriptionOverlay.hide()}
|
||||
onMouseOver={() => this.refs.headerOverlay.show()}
|
||||
onMouseOut={() => this.refs.headerOverlay.hide()}
|
||||
>
|
||||
<MessageWrapper
|
||||
message={channel.description}
|
||||
message={channel.header}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
@@ -144,7 +147,7 @@ export default class ChannelHeader extends React.Component {
|
||||
if (isDirect) {
|
||||
dropdownContents.push(
|
||||
<li
|
||||
key='edit_description_direct'
|
||||
key='edit_header_direct'
|
||||
role='presentation'
|
||||
>
|
||||
<a
|
||||
@@ -152,11 +155,11 @@ export default class ChannelHeader extends React.Component {
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#edit_channel'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
Set Channel Description...
|
||||
Set Channel Header...
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -216,7 +219,7 @@ export default class ChannelHeader extends React.Component {
|
||||
|
||||
dropdownContents.push(
|
||||
<li
|
||||
key='set_channel_description'
|
||||
key='set_channel_header'
|
||||
role='presentation'
|
||||
>
|
||||
<a
|
||||
@@ -224,11 +227,25 @@ export default class ChannelHeader extends React.Component {
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#edit_channel'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
Set {channelTerm} Description...
|
||||
Set {channelTerm} Header...
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
dropdownContents.push(
|
||||
<li
|
||||
key='set_channel_purpose'
|
||||
role='presentation'
|
||||
>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={() => this.setState({showEditChannelPurposeModal: true})}
|
||||
>
|
||||
Set {channelTerm} Purpose...
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
@@ -307,84 +324,91 @@ export default class ChannelHeader extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<table className='channel-header alt'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<div className='channel-header__info'>
|
||||
<div className='dropdown'>
|
||||
<div>
|
||||
<table className='channel-header alt'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<div className='channel-header__info'>
|
||||
<div className='dropdown'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
id='channel_header_dropdown'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<strong className='heading'>{channelTitle} </strong>
|
||||
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon' />
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu'
|
||||
role='menu'
|
||||
aria-labelledby='channel_header_dropdown'
|
||||
>
|
||||
{dropdownContents}
|
||||
</ul>
|
||||
</div>
|
||||
<OverlayTrigger
|
||||
trigger={['hover', 'focus']}
|
||||
placement='bottom'
|
||||
overlay={popoverContent}
|
||||
ref='headerOverlay'
|
||||
>
|
||||
<div
|
||||
onClick={TextFormatting.handleClick}
|
||||
className='description'
|
||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(channel.header, {singleline: true, mentionHighlight: false})}}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<PopoverListMembers
|
||||
members={this.state.users}
|
||||
channelId={channel.id}
|
||||
/>
|
||||
</th>
|
||||
<th className='search-bar__container'><NavbarSearchBox /></th>
|
||||
<th>
|
||||
<div className='dropdown channel-header__links'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
id='channel_header_dropdown'
|
||||
id='channel_header_right_dropdown'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<strong className='heading'>{channelTitle} </strong>
|
||||
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon' />
|
||||
<span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} />
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu'
|
||||
className='dropdown-menu dropdown-menu-right'
|
||||
role='menu'
|
||||
aria-labelledby='channel_header_dropdown'
|
||||
aria-labelledby='channel_header_right_dropdown'
|
||||
>
|
||||
{dropdownContents}
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={this.searchMentions}
|
||||
>
|
||||
Recent Mentions
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<OverlayTrigger
|
||||
trigger={['hover', 'focus']}
|
||||
placement='bottom'
|
||||
overlay={popoverContent}
|
||||
ref='descriptionOverlay'
|
||||
>
|
||||
<div
|
||||
onClick={TextFormatting.handleClick}
|
||||
className='description'
|
||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(channel.description, {singleline: true, mentionHighlight: false})}}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<PopoverListMembers
|
||||
members={this.state.users}
|
||||
channelId={channel.id}
|
||||
/>
|
||||
</th>
|
||||
<th className='search-bar__container'><NavbarSearchBox /></th>
|
||||
<th>
|
||||
<div className='dropdown channel-header__links'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
id='channel_header_right_dropdown'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} />
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu dropdown-menu-right'
|
||||
role='menu'
|
||||
aria-labelledby='channel_header_right_dropdown'
|
||||
>
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={this.searchMentions}
|
||||
>
|
||||
Recent Mentions
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<EditChannelPurposeModal
|
||||
show={this.state.showEditChannelPurposeModal}
|
||||
onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
|
||||
channel={channel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class EditChannelModal extends React.Component {
|
||||
this.onShow = this.onShow.bind(this);
|
||||
|
||||
this.state = {
|
||||
description: '',
|
||||
header: '',
|
||||
title: '',
|
||||
channelId: '',
|
||||
serverError: ''
|
||||
@@ -28,32 +28,32 @@ export default class EditChannelModal extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
data.channel_description = this.state.description.trim();
|
||||
data.channel_header = this.state.header.trim();
|
||||
|
||||
Client.updateChannelDesc(data,
|
||||
function handleUpdateSuccess() {
|
||||
Client.updateChannelHeader(data,
|
||||
() => {
|
||||
this.setState({serverError: ''});
|
||||
AsyncClient.getChannel(this.state.channelId);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||
}.bind(this),
|
||||
function handleUpdateError(err) {
|
||||
if (err.message === 'Invalid channel_description parameter') {
|
||||
this.setState({serverError: 'This description is too long, please enter a shorter one'});
|
||||
},
|
||||
(err) => {
|
||||
if (err.message === 'Invalid channel_header parameter') {
|
||||
this.setState({serverError: 'This channel header is too long, please enter a shorter one'});
|
||||
} else {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
}
|
||||
handleUserInput(e) {
|
||||
this.setState({description: e.target.value});
|
||||
this.setState({header: e.target.value});
|
||||
}
|
||||
handleClose() {
|
||||
this.setState({description: '', serverError: ''});
|
||||
this.setState({header: '', serverError: ''});
|
||||
}
|
||||
onShow(e) {
|
||||
const button = e.relatedTarget;
|
||||
this.setState({description: $(button).attr('data-desc'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
|
||||
this.setState({header: $(button).attr('data-header'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
|
||||
}
|
||||
componentDidMount() {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
@@ -73,7 +73,7 @@ export default class EditChannelModal extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
Edit Description
|
||||
Edit Header
|
||||
</h4>
|
||||
);
|
||||
if (this.state.title) {
|
||||
@@ -82,7 +82,7 @@ export default class EditChannelModal extends React.Component {
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
Edit Description for <span className='name'>{this.state.title}</span>
|
||||
Edit Header for <span className='name'>{this.state.title}</span>
|
||||
</h4>
|
||||
);
|
||||
}
|
||||
@@ -113,9 +113,8 @@ export default class EditChannelModal extends React.Component {
|
||||
<textarea
|
||||
className='form-control no-resize'
|
||||
rows='6'
|
||||
ref='channelDesc'
|
||||
maxLength='1024'
|
||||
value={this.state.description}
|
||||
value={this.state.header}
|
||||
onChange={this.handleUserInput}
|
||||
/>
|
||||
{serverError}
|
||||
|
||||
118
web/react/components/edit_channel_purpose_modal.jsx
Обычный файл
118
web/react/components/edit_channel_purpose_modal.jsx
Обычный файл
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const AsyncClient = require('../utils/async_client.jsx');
|
||||
const Client = require('../utils/client.jsx');
|
||||
const Modal = ReactBootstrap.Modal;
|
||||
|
||||
export default class EditChannelPurposeModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleHide = this.handleHide.bind(this);
|
||||
this.handleSave = this.handleSave.bind(this);
|
||||
|
||||
this.state = {serverError: ''};
|
||||
}
|
||||
|
||||
handleHide() {
|
||||
this.setState({serverError: ''});
|
||||
|
||||
if (this.props.onModalDismissed) {
|
||||
this.props.onModalDismissed();
|
||||
}
|
||||
}
|
||||
|
||||
handleSave() {
|
||||
if (!this.props.channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
channel_id: this.props.channel.id,
|
||||
channel_purpose: ReactDOM.findDOMNode(this.refs.purpose).value.trim()
|
||||
};
|
||||
|
||||
Client.updateChannelPurpose(data,
|
||||
() => {
|
||||
AsyncClient.getChannel(this.props.channel.id);
|
||||
|
||||
this.handleHide();
|
||||
},
|
||||
(err) => {
|
||||
if (err.message === 'Invalid channel_purpose parameter') {
|
||||
this.setState({serverError: 'This channel purpose is too long, please enter a shorter one'});
|
||||
} else {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let serverError = null;
|
||||
if (this.state.serverError) {
|
||||
serverError = (
|
||||
<div className='form-group has-error'>
|
||||
<br/>
|
||||
<label className='control-label'>{this.state.serverError}</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let title = <span>{'Edit Purpose'}</span>;
|
||||
if (this.props.channel.display_name) {
|
||||
title = <span>{'Edit Purpose for '}<span className='name'>{this.props.channel.display_name}</span></span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className='modal-edit-channel-purpose'
|
||||
show={this.props.show}
|
||||
onHide={this.handleHide}
|
||||
>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>
|
||||
{title}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<textarea
|
||||
ref='purpose'
|
||||
className='form-control no-resize'
|
||||
rows='6'
|
||||
maxLength='128'
|
||||
defaultValue={this.props.channel.purpose}
|
||||
/>
|
||||
{serverError}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleHide}
|
||||
>
|
||||
{'Cancel'}
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-primary'
|
||||
onClick={this.handleSave}
|
||||
>
|
||||
{'Save'}
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditChannelPurposeModal.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
channel: React.PropTypes.object,
|
||||
onModalDismissed: React.PropTypes.func.isRequired
|
||||
};
|
||||
@@ -109,7 +109,7 @@ export default class MoreChannels extends React.Component {
|
||||
<tr key={channel.id}>
|
||||
<td>
|
||||
<p className='more-name'>{channel.display_name}</p>
|
||||
<p className='more-description'>{channel.description}</p>
|
||||
<p className='more-purpose'>{channel.purpose}</p>
|
||||
</td>
|
||||
<td className='td--action'>
|
||||
{joinButton}
|
||||
|
||||
@@ -8,6 +8,7 @@ var ChannelStore = require('../stores/channel_store.jsx');
|
||||
var TeamStore = require('../stores/team_store.jsx');
|
||||
var MessageWrapper = require('./message_wrapper.jsx');
|
||||
var NotifyCounts = require('./notify_counts.jsx');
|
||||
const EditChannelPurposeModal = require('./edit_channel_purpose_modal.jsx');
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
|
||||
var Constants = require('../utils/constants.jsx');
|
||||
@@ -26,7 +27,9 @@ export default class Navbar extends React.Component {
|
||||
this.createCollapseButtons = this.createCollapseButtons.bind(this);
|
||||
this.createDropdown = this.createDropdown.bind(this);
|
||||
|
||||
this.state = this.getStateFromStores();
|
||||
const state = this.getStateFromStores();
|
||||
state.showEditChannelPurposeModal = false;
|
||||
this.state = state;
|
||||
}
|
||||
getStateFromStores() {
|
||||
return {
|
||||
@@ -106,22 +109,35 @@ export default class Navbar extends React.Component {
|
||||
</li>
|
||||
);
|
||||
|
||||
var setChannelDescriptionOption = (
|
||||
var setChannelHeaderOption = (
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#edit_channel'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
Set Channel Description...
|
||||
Set Channel Header...
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
||||
var setChannelPurposeOption = null;
|
||||
if (!isDirect) {
|
||||
setChannelPurposeOption = (
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={() => this.setState({showEditChannelPurposeModal: true})}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
var addMembersOption;
|
||||
var leaveChannelOption;
|
||||
if (!isDirect && !ChannelStore.isDefault(channel)) {
|
||||
@@ -249,7 +265,8 @@ export default class Navbar extends React.Component {
|
||||
{viewInfoOption}
|
||||
{addMembersOption}
|
||||
{manageMembersOption}
|
||||
{setChannelDescriptionOption}
|
||||
{setChannelHeaderOption}
|
||||
{setChannelPurposeOption}
|
||||
{notificationPreferenceOption}
|
||||
{renameChannelOption}
|
||||
{deleteChannelOption}
|
||||
@@ -335,10 +352,10 @@ export default class Navbar extends React.Component {
|
||||
<Popover
|
||||
bsStyle='info'
|
||||
placement='bottom'
|
||||
id='description-popover'
|
||||
id='header-popover'
|
||||
>
|
||||
<MessageWrapper
|
||||
message={channel.description}
|
||||
message={channel.header}
|
||||
options={{singleline: true, mentionHighlight: false}}
|
||||
/>
|
||||
</Popover>
|
||||
@@ -360,20 +377,20 @@ export default class Navbar extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (channel.description.length === 0) {
|
||||
if (channel.header.length === 0) {
|
||||
popoverContent = (
|
||||
<Popover
|
||||
bsStyle='info'
|
||||
placement='bottom'
|
||||
id='description-popover'
|
||||
id='header-popover'
|
||||
>
|
||||
<div>
|
||||
{'No channel description yet.'}
|
||||
{'No channel header yet.'}
|
||||
<br/>
|
||||
<a
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
data-target='#edit_channel'
|
||||
@@ -392,17 +409,24 @@ export default class Navbar extends React.Component {
|
||||
var channelMenuDropdown = this.createDropdown(channel, channelTitle, isAdmin, isDirect, popoverContent);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className='navbar navbar-default navbar-fixed-top'
|
||||
role='navigation'
|
||||
>
|
||||
<div className='container-fluid theme'>
|
||||
<div className='navbar-header'>
|
||||
{collapseButtons}
|
||||
{channelMenuDropdown}
|
||||
<div>
|
||||
<nav
|
||||
className='navbar navbar-default navbar-fixed-top'
|
||||
role='navigation'
|
||||
>
|
||||
<div className='container-fluid theme'>
|
||||
<div className='navbar-header'>
|
||||
{collapseButtons}
|
||||
{channelMenuDropdown}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
<EditChannelPurposeModal
|
||||
show={this.state.showEditChannelPurposeModal}
|
||||
onModalDismissed={() => this.setState({showEditChannelPurposeModal: false})}
|
||||
channel={channel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export default class NewChannelFlow extends React.Component {
|
||||
flowState: SHOW_NEW_CHANNEL,
|
||||
channelDisplayName: '',
|
||||
channelName: '',
|
||||
channelDescription: '',
|
||||
channelPurpose: '',
|
||||
nameModified: false
|
||||
};
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export default class NewChannelFlow extends React.Component {
|
||||
flowState: SHOW_NEW_CHANNEL,
|
||||
channelDisplayName: '',
|
||||
channelName: '',
|
||||
channelDescription: '',
|
||||
channelPurpose: '',
|
||||
nameModified: false
|
||||
});
|
||||
}
|
||||
@@ -65,7 +65,7 @@ export default class NewChannelFlow extends React.Component {
|
||||
|
||||
const cu = UserStore.getCurrentUser();
|
||||
channel.team_id = cu.team_id;
|
||||
channel.description = this.state.channelDescription;
|
||||
channel.purpose = this.state.channelPurpose;
|
||||
channel.type = this.state.channelType;
|
||||
|
||||
Client.createChannel(channel,
|
||||
@@ -109,7 +109,7 @@ export default class NewChannelFlow extends React.Component {
|
||||
channelDataChanged(data) {
|
||||
this.setState({
|
||||
channelDisplayName: data.displayName,
|
||||
channelDescription: data.description
|
||||
channelPurpose: data.purpose
|
||||
});
|
||||
if (!this.state.nameModified) {
|
||||
this.setState({channelName: Utils.cleanUpUrlable(data.displayName.trim())});
|
||||
@@ -119,7 +119,7 @@ export default class NewChannelFlow extends React.Component {
|
||||
const channelData = {
|
||||
name: this.state.channelName,
|
||||
displayName: this.state.channelDisplayName,
|
||||
description: this.state.channelDescription
|
||||
purpose: this.state.channelPurpose
|
||||
};
|
||||
|
||||
let showChannelModal = false;
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class NewChannelModal extends React.Component {
|
||||
handleChange() {
|
||||
const newData = {
|
||||
displayName: ReactDOM.findDOMNode(this.refs.display_name).value,
|
||||
description: ReactDOM.findDOMNode(this.refs.channel_desc).value
|
||||
purpose: ReactDOM.findDOMNode(this.refs.channel_purpose).value
|
||||
};
|
||||
this.props.onDataChanged(newData);
|
||||
}
|
||||
@@ -136,22 +136,22 @@ export default class NewChannelModal extends React.Component {
|
||||
</div>
|
||||
<div className='form-group less'>
|
||||
<div className='col-sm-3'>
|
||||
<label className='form__label control-label'>{'Description'}</label>
|
||||
<label className='form__label control-label'>{'Purpose'}</label>
|
||||
<label className='form__label light'>{'(optional)'}</label>
|
||||
</div>
|
||||
<div className='col-sm-9'>
|
||||
<textarea
|
||||
className='form-control no-resize'
|
||||
ref='channel_desc'
|
||||
ref='channel_purpose'
|
||||
rows='4'
|
||||
placeholder='Description'
|
||||
maxLength='1024'
|
||||
value={this.props.channelData.description}
|
||||
placeholder='Purpose'
|
||||
maxLength='128'
|
||||
value={this.props.channelData.purpose}
|
||||
onChange={this.handleChange}
|
||||
tabIndex='2'
|
||||
/>
|
||||
<p className='input__help'>
|
||||
{'Description helps others decide whether to join this channel.'}
|
||||
{`Describe how this ${channelTerm} should be used.`}
|
||||
</p>
|
||||
{serverError}
|
||||
</div>
|
||||
|
||||
@@ -358,11 +358,11 @@ export default class PostList extends React.Component {
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#edit_channel'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
<i className='fa fa-pencil'></i>{'Set a description'}
|
||||
<i className='fa fa-pencil'></i>{'Set a header'}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
@@ -413,11 +413,11 @@ export default class PostList extends React.Component {
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#edit_channel'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
<i className='fa fa-pencil'></i>{'Set a description'}
|
||||
<i className='fa fa-pencil'></i>{'Set a header'}
|
||||
</a>
|
||||
<a
|
||||
className='intro-links'
|
||||
@@ -479,11 +479,11 @@ export default class PostList extends React.Component {
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-target='#edit_channel'
|
||||
data-desc={channel.description}
|
||||
data-header={channel.header}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
>
|
||||
<i className='fa fa-pencil'></i>{'Set a description'}
|
||||
<i className='fa fa-pencil'></i>{'Set a header'}
|
||||
</a>
|
||||
<a
|
||||
className='intro-links'
|
||||
|
||||
@@ -589,21 +589,38 @@ export function updateChannel(channel, success, error) {
|
||||
track('api', 'api_channels_update');
|
||||
}
|
||||
|
||||
export function updateChannelDesc(data, success, error) {
|
||||
export function updateChannelHeader(data, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/channels/update_desc',
|
||||
url: '/api/v1/channels/update_header',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('updateChannelDesc', xhr, status, err);
|
||||
var e = handleError('updateChannelHeader', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
|
||||
track('api', 'api_channels_desc');
|
||||
track('api', 'api_channels_header');
|
||||
}
|
||||
|
||||
export function updateChannelPurpose(data, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/channels/update_purpose',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('updateChannelPurpose', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
|
||||
track('api', 'api_channels_purpose');
|
||||
}
|
||||
|
||||
export function updateNotifyProps(data, success, error) {
|
||||
|
||||
@@ -376,7 +376,7 @@
|
||||
@include opacity(0.8);
|
||||
}
|
||||
|
||||
.more-description {
|
||||
.more-purpose {
|
||||
@include opacity(0.7);
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user