summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2015-09-22 01:15:41 -0700
committer=Corey Hulen <corey@hulen.com>2015-09-22 01:15:41 -0700
commit08a3acbb44b043b9bb56f9b96e91432352d06d1a (patch)
treeceb451571691d0530ff2c97a033fcc206c42d3ea
parentf05a2c03d5dbf5b0b7d09148a37d2325012b309f (diff)
downloadchat-08a3acbb44b043b9bb56f9b96e91432352d06d1a.tar.gz
chat-08a3acbb44b043b9bb56f9b96e91432352d06d1a.tar.bz2
chat-08a3acbb44b043b9bb56f9b96e91432352d06d1a.zip
Adding team settings to admin console
-rw-r--r--api/admin_test.go4
-rw-r--r--api/api_test.go1
-rw-r--r--api/context.go2
-rw-r--r--api/file.go2
-rw-r--r--api/post.go70
-rw-r--r--api/post_test.go92
-rw-r--r--api/team.go25
-rw-r--r--api/team_test.go76
-rw-r--r--api/user.go16
-rw-r--r--api/user_test.go1
-rw-r--r--config/config.json11
-rw-r--r--model/config.go9
-rw-r--r--model/team.go1
-rw-r--r--store/sql_store.go29
-rw-r--r--store/sql_team_store.go1
-rw-r--r--store/sql_user_store_test.go2
-rw-r--r--utils/config.go5
-rw-r--r--web/react/components/admin_console/admin_controller.jsx6
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx9
-rw-r--r--web/react/components/admin_console/image_settings.jsx34
-rw-r--r--web/react/components/admin_console/team_settings.jsx257
-rw-r--r--web/react/components/view_image.jsx2
-rw-r--r--web/web.go2
23 files changed, 344 insertions, 313 deletions
diff --git a/api/admin_test.go b/api/admin_test.go
index c74fbf6e5..ad7ac08f8 100644
--- a/api/admin_test.go
+++ b/api/admin_test.go
@@ -83,7 +83,7 @@ func TestGetConfig(t *testing.T) {
} else {
cfg := result.Data.(*model.Config)
- if len(cfg.ServiceSettings.SiteName) == 0 {
+ if len(cfg.TeamSettings.SiteName) == 0 {
t.Fatal()
}
}
@@ -117,7 +117,7 @@ func TestSaveConfig(t *testing.T) {
} else {
cfg := result.Data.(*model.Config)
- if len(cfg.ServiceSettings.SiteName) == 0 {
+ if len(cfg.TeamSettings.SiteName) == 0 {
t.Fatal()
}
}
diff --git a/api/api_test.go b/api/api_test.go
index 642db581e..490f8ab5b 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -14,6 +14,7 @@ var Client *model.Client
func Setup() {
if Srv == nil {
utils.LoadConfig("config.json")
+ utils.Cfg.TeamSettings.MaxUsersPerTeam = 50
NewServer()
StartServer()
InitApi()
diff --git a/api/context.go b/api/context.go
index 02716bb33..c4684221d 100644
--- a/api/context.go
+++ b/api/context.go
@@ -471,7 +471,7 @@ func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request)
m := make(map[string]string)
m["Message"] = err.Message
m["Details"] = err.DetailedError
- m["SiteName"] = utils.Cfg.ServiceSettings.SiteName
+ m["SiteName"] = utils.Cfg.TeamSettings.SiteName
m["SiteURL"] = SiteURL
w.WriteHeader(err.StatusCode)
diff --git a/api/file.go b/api/file.go
index 69303f5f8..61d0df413 100644
--- a/api/file.go
+++ b/api/file.go
@@ -447,7 +447,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !utils.Cfg.TeamSettings.AllowPublicLink {
+ if !utils.Cfg.ImageSettings.EnablePublicLink {
c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "")
c.Err.StatusCode = http.StatusForbidden
}
diff --git a/api/post.go b/api/post.go
index 21bc35b97..4294ae03c 100644
--- a/api/post.go
+++ b/api/post.go
@@ -25,7 +25,6 @@ func InitPost(r *mux.Router) {
sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter()
sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST")
- sr.Handle("/valet_create", ApiUserRequired(createValetPost)).Methods("POST")
sr.Handle("/update", ApiUserRequired(updatePost)).Methods("POST")
sr.Handle("/posts/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequiredActivity(getPosts, false)).Methods("GET")
sr.Handle("/posts/{time:[0-9]+}", ApiUserRequiredActivity(getPostsSince, false)).Methods("GET")
@@ -60,75 +59,6 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
-func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) {
- tchan := Srv.Store.Team().Get(c.Session.TeamId)
-
- post := model.PostFromJson(r.Body)
- if post == nil {
- c.SetInvalidParam("createValetPost", "post")
- return
- }
-
- cchan := Srv.Store.Channel().CheckOpenChannelPermissions(c.Session.TeamId, post.ChannelId)
-
- // Any one with access to the team can post as valet to any open channel
- if !c.HasPermissionsToChannel(cchan, "createValetPost") {
- return
- }
-
- // Make sure this team has the valet feature enabled
- if tResult := <-tchan; tResult.Err != nil {
- c.Err = model.NewAppError("createValetPost", "Could not find the team for this session, team_id="+c.Session.TeamId, "")
- return
- } else {
- if !tResult.Data.(*model.Team).AllowValet {
- c.Err = model.NewAppError("createValetPost", "The valet feature is currently turned off. Please contact your team administrator for details.", "")
- c.Err.StatusCode = http.StatusNotImplemented
- return
- }
- }
-
- if rp, err := CreateValetPost(c, post); err != nil {
- c.Err = err
-
- if strings.Contains(c.Err.Message, "parameter") {
- c.Err.StatusCode = http.StatusBadRequest
- }
-
- return
- } else {
- w.Write([]byte(rp.ToJson()))
- }
-}
-
-func CreateValetPost(c *Context, post *model.Post) (*model.Post, *model.AppError) {
- post.Hashtags, _ = model.ParseHashtags(post.Message)
-
- post.Filenames = []string{} // no files allowed in valet posts yet
-
- if result := <-Srv.Store.User().GetByUsername(c.Session.TeamId, "valet"); result.Err != nil {
- // if the bot doesn't exist, create it
- if tresult := <-Srv.Store.Team().Get(c.Session.TeamId); tresult.Err != nil {
- return nil, tresult.Err
- } else {
- post.UserId = (CreateValet(c, tresult.Data.(*model.Team))).Id
- }
- } else {
- post.UserId = result.Data.(*model.User).Id
- }
-
- var rpost *model.Post
- if result := <-Srv.Store.Post().Save(post); result.Err != nil {
- return nil, result.Err
- } else {
- rpost = result.Data.(*model.Post)
- }
-
- fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL())
-
- return rpost, nil
-}
-
func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.Post, *model.AppError) {
var pchan store.StoreChannel
if len(post.RootId) > 0 {
diff --git a/api/post_test.go b/api/post_test.go
index 4cccfd62a..358611240 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -123,98 +123,6 @@ func TestCreatePost(t *testing.T) {
}
}
-func TestCreateValetPost(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)
-
- team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
- team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
-
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
- user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user1.Id))
-
- 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, user1.Email, "pwd")
-
- channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
- channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
-
- channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
- channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
-
- if utils.Cfg.TeamSettings.AllowValetDefault {
- post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
- rpost1, err := Client.CreateValetPost(post1)
- if err != nil {
- t.Fatal(err)
- }
-
- if rpost1.Data.(*model.Post).Message != post1.Message {
- t.Fatal("message didn't match")
- }
-
- if rpost1.Data.(*model.Post).Hashtags != "#hashtag" {
- t.Fatal("hashtag didn't match")
- }
-
- post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
- rpost2, err := Client.CreateValetPost(post2)
- if err != nil {
- t.Fatal(err)
- }
-
- post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: rpost2.Data.(*model.Post).Id}
- _, err = Client.CreateValetPost(post3)
- if err != nil {
- t.Fatal(err)
- }
-
- post4 := &model.Post{ChannelId: "junk", Message: "a" + model.NewId() + "a"}
- _, err = Client.CreateValetPost(post4)
- if err.StatusCode != http.StatusForbidden {
- t.Fatal("Should have been forbidden")
- }
-
- Client.LoginByEmail(team.Name, user2.Email, "pwd")
- post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
- _, err = Client.CreateValetPost(post5)
- if err != nil {
- t.Fatal(err)
- }
-
- user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
- user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user3.Id))
-
- Client.LoginByEmail(team2.Name, user3.Email, "pwd")
-
- channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
- channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
-
- post6 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
- _, err = Client.CreateValetPost(post6)
- if err.StatusCode != http.StatusForbidden {
- t.Fatal("Should have been forbidden")
- }
-
- if _, err = Client.DoApiPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
- t.Fatal("should have been an error")
- }
- } else {
- post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
- _, err := Client.CreateValetPost(post1)
- if err.StatusCode != http.StatusNotImplemented {
- t.Fatal("Should have failed with 501 - Not Implemented")
- }
- }
-}
-
func TestUpdatePost(t *testing.T) {
Setup()
diff --git a/api/team.go b/api/team.go
index 4531c83b9..8802208f7 100644
--- a/api/team.go
+++ b/api/team.go
@@ -60,7 +60,6 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
subjectPage.Props["SiteURL"] = c.GetSiteURL()
bodyPage := NewServerTemplatePage("signup_team_body")
bodyPage.Props["SiteURL"] = c.GetSiteURL()
- bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink
props := make(map[string]string)
props["email"] = email
@@ -124,8 +123,6 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
- team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault
-
if result := <-Srv.Store.Team().Save(team); result.Err != nil {
c.Err = result.Err
return
@@ -207,8 +204,6 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault
-
if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil {
c.Err = result.Err
return
@@ -228,13 +223,6 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if teamSignup.Team.AllowValet {
- CreateValet(c, rteam)
- if c.Err != nil {
- return
- }
- }
-
InviteMembers(c, rteam, ruser, teamSignup.Invites)
teamSignup.Team = *rteam
@@ -286,13 +274,6 @@ func CreateTeam(c *Context, team *model.Team) *model.Team {
return nil
}
- if rteam.AllowValet {
- CreateValet(c, rteam)
- if c.Err != nil {
- return nil
- }
- }
-
return rteam
}
}
@@ -301,7 +282,7 @@ func isTreamCreationAllowed(c *Context, email string) bool {
email = strings.ToLower(email)
- if utils.Cfg.TeamSettings.DisableTeamCreation {
+ if !utils.Cfg.TeamSettings.EnableTeamCreation {
c.Err = model.NewAppError("isTreamCreationAllowed", "Team creation has been disabled. Please ask your systems administrator for details.", "")
return false
}
@@ -567,8 +548,6 @@ func updateValetFeature(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- allowValet := allowValetStr == "true"
-
teamId := props["team_id"]
if len(teamId) > 0 && len(teamId) != 26 {
c.SetInvalidParam("updateValetFeature", "team_id")
@@ -597,8 +576,6 @@ func updateValetFeature(c *Context, w http.ResponseWriter, r *http.Request) {
team = tResult.Data.(*model.Team)
}
- team.AllowValet = allowValet
-
if result := <-Srv.Store.Team().Update(team); result.Err != nil {
c.Err = result.Err
return
diff --git a/api/team_test.go b/api/team_test.go
index 4f1b9e5f0..48c73c638 100644
--- a/api/team_test.go
+++ b/api/team_test.go
@@ -330,79 +330,3 @@ func TestGetMyTeam(t *testing.T) {
}
}
}
-
-func TestUpdateValetFeature(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: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"}
- user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user.Id))
-
- 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))
-
- team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
- team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
-
- user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
- user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user3.Id))
-
- Client.LoginByEmail(team.Name, user2.Email, "pwd")
-
- data := make(map[string]string)
- data["allow_valet"] = "true"
- if _, err := Client.UpdateValetFeature(data); err == nil {
- t.Fatal("Should have errored, not admin")
- }
-
- Client.LoginByEmail(team.Name, user.Email, "pwd")
-
- data["allow_valet"] = ""
- if _, err := Client.UpdateValetFeature(data); err == nil {
- t.Fatal("Should have errored, empty allow_valet field")
- }
-
- data["allow_valet"] = "true"
- if _, err := Client.UpdateValetFeature(data); err != nil {
- t.Fatal(err)
- }
-
- rteam := Client.Must(Client.GetMyTeam("")).Data.(*model.Team)
- if rteam.AllowValet != true {
- t.Fatal("Should have errored - allow valet property not updated")
- }
-
- data["team_id"] = "junk"
- if _, err := Client.UpdateValetFeature(data); err == nil {
- t.Fatal("Should have errored, junk team id")
- }
-
- data["team_id"] = "12345678901234567890123456"
- if _, err := Client.UpdateValetFeature(data); err == nil {
- t.Fatal("Should have errored, bad team id")
- }
-
- data["team_id"] = team.Id
- data["allow_valet"] = "false"
- if _, err := Client.UpdateValetFeature(data); err != nil {
- t.Fatal(err)
- }
-
- rteam = Client.Must(Client.GetMyTeam("")).Data.(*model.Team)
- if rteam.AllowValet != false {
- t.Fatal("Should have errored - allow valet property not updated")
- }
-
- Client.LoginByEmail(team2.Name, user3.Email, "pwd")
-
- data["team_id"] = team.Id
- data["allow_valet"] = "true"
- if _, err := Client.UpdateValetFeature(data); err == nil {
- t.Fatal("Should have errored, not part of team")
- }
-}
diff --git a/api/user.go b/api/user.go
index 7f4eb6c2d..ba5323d77 100644
--- a/api/user.go
+++ b/api/user.go
@@ -155,19 +155,13 @@ func IsVerifyHashRequired(user *model.User, team *model.Team, hash string) bool
return shouldVerifyHash
}
-func CreateValet(c *Context, team *model.Team) *model.User {
- valet := &model.User{}
- valet.TeamId = team.Id
- valet.Email = utils.Cfg.EmailSettings.FeedbackEmail
- valet.EmailVerified = true
- valet.Username = model.BOT_USERNAME
- valet.Password = model.NewId()
-
- return CreateUser(c, team, valet)
-}
-
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
+ if !utils.Cfg.TeamSettings.EnableUserCreation {
+ c.Err = model.NewAppError("CreateUser", "User creation has been disabled. Please ask your systems administrator for details.", "")
+ return nil
+ }
+
channelRole := ""
if team.Email == user.Email {
user.Roles = model.ROLE_TEAM_ADMIN
diff --git a/api/user_test.go b/api/user_test.go
index 8342f37f6..7451cb615 100644
--- a/api/user_test.go
+++ b/api/user_test.go
@@ -952,6 +952,7 @@ func TestUserUpdateNotify(t *testing.T) {
}
func TestFuzzyUserCreate(t *testing.T) {
+ Setup()
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
diff --git a/config/config.json b/config/config.json
index 8baa09859..12926bee5 100644
--- a/config/config.json
+++ b/config/config.json
@@ -1,6 +1,5 @@
{
"ServiceSettings": {
- "SiteName": "Mattermost",
"Mode": "dev",
"AllowTesting": false,
"UseSSL": false,
@@ -16,12 +15,11 @@
"GoogleDeveloperKey": ""
},
"TeamSettings": {
- "MaxUsersPerTeam": 150,
- "AllowPublicLink": true,
- "AllowValetDefault": false,
- "TourLink": "",
+ "SiteName": "Mattermost",
+ "MaxUsersPerTeam": 50,
"DefaultThemeColor": "#2389D7",
- "DisableTeamCreation": false,
+ "EnableTeamCreation": true,
+ "EnableUserCreation": true,
"RestrictCreationToDomains": ""
},
"SqlSettings": {
@@ -44,6 +42,7 @@
"ImageSettings": {
"DriverName": "local",
"Directory": "./data/",
+ "EnablePublicLink": true,
"ThumbnailWidth": 120,
"ThumbnailHeight": 100,
"PreviewWidth": 1024,
diff --git a/model/config.go b/model/config.go
index 3da068d8d..876c36e98 100644
--- a/model/config.go
+++ b/model/config.go
@@ -20,7 +20,6 @@ const (
)
type ServiceSettings struct {
- SiteName string
Mode string
AllowTesting bool
UseSSL bool
@@ -68,6 +67,7 @@ type LogSettings struct {
type ImageSettings struct {
DriverName string
Directory string
+ EnablePublicLink bool
ThumbnailWidth uint
ThumbnailHeight uint
PreviewWidth uint
@@ -113,12 +113,11 @@ type PrivacySettings struct {
}
type TeamSettings struct {
+ SiteName string
MaxUsersPerTeam int
- AllowPublicLink bool
- AllowValetDefault bool
- TourLink string
DefaultThemeColor string
- DisableTeamCreation bool
+ EnableTeamCreation bool
+ EnableUserCreation bool
RestrictCreationToDomains string
}
diff --git a/model/team.go b/model/team.go
index 8b4f82830..0d740dde2 100644
--- a/model/team.go
+++ b/model/team.go
@@ -27,7 +27,6 @@ type Team struct {
Type string `json:"type"`
CompanyName string `json:"company_name"`
AllowedDomains string `json:"allowed_domains"`
- AllowValet bool `json:"allow_valet"`
}
type Invites struct {
diff --git a/store/sql_store.go b/store/sql_store.go
index 7f3b555f1..7f3c59164 100644
--- a/store/sql_store.go
+++ b/store/sql_store.go
@@ -311,26 +311,21 @@ func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string,
}
}
-// func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
+func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
-// // XXX TODO FIXME this should be removed after 0.6.0
-// if utils.Cfg.SqlSettings.DriverName == "postgres" {
-// return false
-// }
-
-// if !ss.DoesColumnExist(tableName, columnName) {
-// return false
-// }
+ if !ss.DoesColumnExist(tableName, columnName) {
+ return false
+ }
-// _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
-// if err != nil {
-// l4g.Critical("Failed to drop column %v", err)
-// time.Sleep(time.Second)
-// panic("Failed to drop column " + err.Error())
-// }
+ _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
+ if err != nil {
+ l4g.Critical("Failed to drop column %v", err)
+ time.Sleep(time.Second)
+ panic("Failed to drop column " + err.Error())
+ }
-// return true
-// }
+ return true
+}
// func (ss SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
diff --git a/store/sql_team_store.go b/store/sql_team_store.go
index d2148c2e3..3d644e577 100644
--- a/store/sql_team_store.go
+++ b/store/sql_team_store.go
@@ -28,6 +28,7 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
}
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
+ s.RemoveColumnIfExists("Teams", "AllowValet")
}
func (s SqlTeamStore) CreateIndexesIfNotExists() {
diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go
index ddd7e5bb8..466da2845 100644
--- a/store/sql_user_store_test.go
+++ b/store/sql_user_store_test.go
@@ -42,7 +42,7 @@ func TestUserStoreSave(t *testing.T) {
t.Fatal("should be unique username")
}
- for i := 0; i < 150; i++ {
+ for i := 0; i < 50; i++ {
u1.Id = ""
u1.Email = model.NewId()
u1.Username = model.NewId()
diff --git a/utils/config.go b/utils/config.go
index 4a5746830..45f62dc19 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -173,7 +173,7 @@ func getClientProperties(c *model.Config) map[string]string {
props["BuildDate"] = model.BuildDate
props["BuildHash"] = model.BuildHash
- props["SiteName"] = c.ServiceSettings.SiteName
+ props["SiteName"] = c.TeamSettings.SiteName
props["AnalyticsUrl"] = c.ServiceSettings.AnalyticsUrl
props["EnableOAuthServiceProvider"] = strconv.FormatBool(c.ServiceSettings.EnableOAuthServiceProvider)
props["SegmentDeveloperKey"] = c.ServiceSettings.SegmentDeveloperKey
@@ -186,11 +186,10 @@ func getClientProperties(c *model.Config) map[string]string {
props["AllowSignUpWithGitLab"] = strconv.FormatBool(c.GitLabSettings.Allow)
props["ShowEmailAddress"] = strconv.FormatBool(c.PrivacySettings.ShowEmailAddress)
- props["AllowPublicLink"] = strconv.FormatBool(c.TeamSettings.AllowPublicLink)
+ props["EnablePublicLink"] = strconv.FormatBool(c.ImageSettings.EnablePublicLink)
props["ProfileHeight"] = fmt.Sprintf("%v", c.ImageSettings.ProfileHeight)
props["ProfileWidth"] = fmt.Sprintf("%v", c.ImageSettings.ProfileWidth)
- props["ProfileWidth"] = fmt.Sprintf("%v", c.ImageSettings.ProfileWidth)
return props
}
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
index 491dbd754..72b5d5c9d 100644
--- a/web/react/components/admin_console/admin_controller.jsx
+++ b/web/react/components/admin_console/admin_controller.jsx
@@ -14,6 +14,8 @@ var PrivacySettingsTab = require('./privacy_settings.jsx');
var RateSettingsTab = require('./rate_settings.jsx');
var GitLabSettingsTab = require('./gitlab_settings.jsx');
var SqlSettingsTab = require('./sql_settings.jsx');
+var TeamSettingsTab = require('./team_settings.jsx');
+
export default class AdminController extends React.Component {
constructor(props) {
@@ -24,7 +26,7 @@ export default class AdminController extends React.Component {
this.state = {
config: null,
- selected: 'sql_settings'
+ selected: 'team_settings'
};
}
@@ -68,6 +70,8 @@ export default class AdminController extends React.Component {
tab = <GitLabSettingsTab config={this.state.config} />;
} else if (this.state.selected === 'sql_settings') {
tab = <SqlSettingsTab config={this.state.config} />;
+ } else if (this.state.selected === 'team_settings') {
+ tab = <TeamSettingsTab config={this.state.config} />;
}
}
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
index deb064015..2b7159e1d 100644
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ b/web/react/components/admin_console/admin_sidebar.jsx
@@ -41,6 +41,15 @@ export default class AdminSidebar extends React.Component {
<li>
<a
href='#'
+ className={this.isSelected('team_settings')}
+ onClick={this.handleClick.bind(this, 'team_settings')}
+ >
+ {'Team Settings'}
+ </a>
+ </li>
+ <li>
+ <a
+ href='#'
className={this.isSelected('sql_settings')}
onClick={this.handleClick.bind(this, 'sql_settings')}
>
diff --git a/web/react/components/admin_console/image_settings.jsx b/web/react/components/admin_console/image_settings.jsx
index 9a7de266d..c0cbb5aa6 100644
--- a/web/react/components/admin_console/image_settings.jsx
+++ b/web/react/components/admin_console/image_settings.jsx
@@ -39,6 +39,7 @@ export default class ImageSettings extends React.Component {
config.ImageSettings.AmazonS3SecretAccessKey = React.findDOMNode(this.refs.AmazonS3SecretAccessKey).value;
config.ImageSettings.AmazonS3Bucket = React.findDOMNode(this.refs.AmazonS3Bucket).value;
config.ImageSettings.AmazonS3Region = React.findDOMNode(this.refs.AmazonS3Region).value;
+ config.ImageSettings.EnablePublicLink = React.findDOMNode(this.refs.EnablePublicLink).checked;
var thumbnailWidth = 120;
if (!isNaN(parseInt(React.findDOMNode(this.refs.ThumbnailWidth).value, 10))) {
@@ -391,6 +392,39 @@ export default class ImageSettings extends React.Component {
</div>
<div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnablePublicLink'
+ >
+ {'Share Public File Link: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnablePublicLink'
+ value='true'
+ ref='EnablePublicLink'
+ defaultChecked={this.props.config.ImageSettings.EnablePublicLink}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnablePublicLink'
+ value='false'
+ defaultChecked={!this.props.config.ImageSettings.EnablePublicLink}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'Allow users to share public links to files and images.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
diff --git a/web/react/components/admin_console/team_settings.jsx b/web/react/components/admin_console/team_settings.jsx
new file mode 100644
index 000000000..fefc0e936
--- /dev/null
+++ b/web/react/components/admin_console/team_settings.jsx
@@ -0,0 +1,257 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var Client = require('../../utils/client.jsx');
+var AsyncClient = require('../../utils/async_client.jsx');
+
+export default class TeamSettings extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+
+ this.state = {
+ saveNeeded: false,
+ serverError: null
+ };
+ }
+
+ handleChange() {
+ var s = {saveNeeded: true, serverError: this.state.serverError};
+ this.setState(s);
+ }
+
+ handleSubmit(e) {
+ e.preventDefault();
+ $('#save-button').button('loading');
+
+ var config = this.props.config;
+ config.TeamSettings.SiteName = React.findDOMNode(this.refs.SiteName).value.trim();
+ config.TeamSettings.DefaultThemeColor = React.findDOMNode(this.refs.DefaultThemeColor).value.trim();
+ config.TeamSettings.RestrictCreationToDomains = React.findDOMNode(this.refs.RestrictCreationToDomains).value.trim();
+ config.TeamSettings.EnableTeamCreation = React.findDOMNode(this.refs.EnableTeamCreation).checked;
+ config.TeamSettings.EnableUserCreation = React.findDOMNode(this.refs.EnableUserCreation).checked;
+
+ var MaxUsersPerTeam = 50;
+ if (!isNaN(parseInt(React.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
+ MaxUsersPerTeam = parseInt(React.findDOMNode(this.refs.MaxUsersPerTeam).value, 10);
+ }
+ config.TeamSettings.MaxUsersPerTeam = MaxUsersPerTeam;
+ React.findDOMNode(this.refs.MaxUsersPerTeam).value = MaxUsersPerTeam;
+
+ Client.saveConfig(
+ config,
+ () => {
+ AsyncClient.getConfig();
+ this.setState({
+ serverError: null,
+ saveNeeded: false
+ });
+ $('#save-button').button('reset');
+ },
+ (err) => {
+ this.setState({
+ serverError: err.message,
+ saveNeeded: true
+ });
+ $('#save-button').button('reset');
+ }
+ );
+ }
+
+ render() {
+ var serverError = '';
+ if (this.state.serverError) {
+ serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
+ }
+
+ var saveClass = 'btn';
+ if (this.state.saveNeeded) {
+ saveClass = 'btn btn-primary';
+ }
+
+ return (
+ <div className='wrapper--fixed'>
+
+ <h3>{'Team Settings'}</h3>
+ <form
+ className='form-horizontal'
+ role='form'
+ >
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='SiteName'
+ >
+ {'Site Name:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='SiteName'
+ ref='SiteName'
+ placeholder='Ex "Mattermost"'
+ defaultValue={this.props.config.TeamSettings.SiteName}
+ onChange={this.handleChange}
+ />
+ <p className='help-text'>{'Name of service shown in login screens and UI.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='MaxUsersPerTeam'
+ >
+ {'Max Users Per Team:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='MaxUsersPerTeam'
+ ref='MaxUsersPerTeam'
+ placeholder='Ex "25"'
+ defaultValue={this.props.config.TeamSettings.MaxUsersPerTeam}
+ onChange={this.handleChange}
+ />
+ <p className='help-text'>{'Maximum number of users per team.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='DefaultThemeColor'
+ >
+ {'Default Theme Color:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='DefaultThemeColor'
+ ref='DefaultThemeColor'
+ placeholder='Ex "#2389D7"'
+ defaultValue={this.props.config.TeamSettings.DefaultThemeColor}
+ onChange={this.handleChange}
+ />
+ <p className='help-text'>{'Default theme color for team sites.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnableTeamCreation'
+ >
+ {'Enable Team Creation: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableTeamCreation'
+ value='true'
+ ref='EnableTeamCreation'
+ defaultChecked={this.props.config.TeamSettings.EnableTeamCreation}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableTeamCreation'
+ value='false'
+ defaultChecked={!this.props.config.TeamSettings.EnableTeamCreation}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'When false the ability to create teams is disabled. The create team button displays error when pressed.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='EnableUserCreation'
+ >
+ {'Enable User Creation: '}
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableUserCreation'
+ value='true'
+ ref='EnableUserCreation'
+ defaultChecked={this.props.config.TeamSettings.EnableUserCreation}
+ onChange={this.handleChange}
+ />
+ {'true'}
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableUserCreation'
+ value='false'
+ defaultChecked={!this.props.config.TeamSettings.EnableUserCreation}
+ onChange={this.handleChange}
+ />
+ {'false'}
+ </label>
+ <p className='help-text'>{'When false the ability to create accounts is disabled. The create account button displays error when pressed.'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='RestrictCreationToDomains'
+ >
+ {'Restrict Creation To Domains:'}
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='RestrictCreationToDomains'
+ ref='RestrictCreationToDomains'
+ placeholder='Ex "corp.mattermost.com, mattermost.org"'
+ defaultValue={this.props.config.TeamSettings.RestrictCreationToDomains}
+ onChange={this.handleChange}
+ />
+ <p className='help-text'>{'Teams can only be created from a specific domain (e.g. "mattermost.org") or list of comma-separated domains (e.g. "corp.mattermost.com, mattermost.org").'}</p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <div className='col-sm-12'>
+ {serverError}
+ <button
+ disabled={!this.state.saveNeeded}
+ type='submit'
+ className={saveClass}
+ onClick={this.handleSubmit}
+ id='save-button'
+ data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Saving Config...'}
+ >
+ {'Save'}
+ </button>
+ </div>
+ </div>
+
+ </form>
+ </div>
+ );
+ }
+}
+
+TeamSettings.propTypes = {
+ config: React.PropTypes.object
+};
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index a37eb6775..dafcdd9f9 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -300,7 +300,7 @@ export default class ViewImageModal extends React.Component {
}
var publicLink = '';
- if (global.window.config.AllowPublicLink === 'true') {
+ if (global.window.config.EnablePublicLink === 'true') {
publicLink = (
<div>
<a
diff --git a/web/web.go b/web/web.go
index 2264d5053..86769dd54 100644
--- a/web/web.go
+++ b/web/web.go
@@ -25,7 +25,7 @@ type HtmlTemplatePage api.Page
func NewHtmlTemplatePage(templateName string, title string) *HtmlTemplatePage {
if len(title) > 0 {
- title = utils.Cfg.ServiceSettings.SiteName + " - " + title
+ title = utils.Cfg.TeamSettings.SiteName + " - " + title
}
props := make(map[string]string)