summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--README.md25
-rw-r--r--api/api.go4
-rw-r--r--api/api_test.go6
-rw-r--r--api/auto_constants.go4
-rw-r--r--api/auto_teams.go16
-rw-r--r--api/auto_users.go8
-rw-r--r--api/channel.go5
-rw-r--r--api/channel_benchmark_test.go4
-rw-r--r--api/channel_test.go144
-rw-r--r--api/command.go14
-rw-r--r--api/command_test.go20
-rw-r--r--api/context.go90
-rw-r--r--api/file.go6
-rw-r--r--api/file_test.go28
-rw-r--r--api/post.go68
-rw-r--r--api/post_test.go98
-rw-r--r--api/server.go3
-rw-r--r--api/sharding.go79
-rw-r--r--api/team.go243
-rw-r--r--api/team_test.go94
-rw-r--r--api/templates/email_change_body.html6
-rw-r--r--api/templates/email_change_subject.html2
-rw-r--r--api/templates/find_teams_body.html4
-rw-r--r--api/templates/invite_body.html6
-rw-r--r--api/templates/invite_subject.html2
-rw-r--r--api/templates/password_change_body.html6
-rw-r--r--api/templates/password_change_subject.html2
-rw-r--r--api/templates/post_body.html4
-rw-r--r--api/templates/post_subject.html2
-rw-r--r--api/templates/reset_body.html6
-rw-r--r--api/templates/signup_team_body.html4
-rw-r--r--api/templates/verify_body.html4
-rw-r--r--api/templates/verify_subject.html2
-rw-r--r--api/templates/welcome_body.html8
-rw-r--r--api/user.go88
-rw-r--r--api/user_test.go168
-rw-r--r--api/web_conn.go2
-rw-r--r--api/web_hub.go38
-rw-r--r--api/web_socket_test.go13
-rw-r--r--api/web_team_hub.go40
-rw-r--r--config/config.json14
-rw-r--r--config/config_docker.json14
-rw-r--r--manualtesting/manual_testing.go12
-rw-r--r--model/channel_extra.go5
-rw-r--r--model/client.go20
-rw-r--r--model/team.go14
-rw-r--r--model/team_signup_test.go2
-rw-r--r--model/team_test.go14
-rw-r--r--model/user.go44
-rw-r--r--model/user_test.go64
-rw-r--r--model/utils.go8
-rw-r--r--model/utils_test.go8
-rw-r--r--scripts/README_DEV.md4
-rw-r--r--store/redis.go75
-rw-r--r--store/redis_test.go59
-rw-r--r--store/sql_channel_store.go2
-rw-r--r--store/sql_channel_store_test.go4
-rw-r--r--store/sql_post_store.go5
-rw-r--r--store/sql_store.go148
-rw-r--r--store/sql_team_store.go27
-rw-r--r--store/sql_team_store_test.go40
-rw-r--r--store/sql_user_store.go48
-rw-r--r--store/store.go5
-rw-r--r--utils/config.go33
-rw-r--r--utils/config_test.go15
-rw-r--r--utils/mail.go13
-rw-r--r--web/react/components/channel_header.jsx127
-rw-r--r--web/react/components/create_comment.jsx3
-rw-r--r--web/react/components/create_post.jsx2
-rw-r--r--web/react/components/delete_channel_modal.jsx2
-rw-r--r--web/react/components/delete_post_modal.jsx4
-rw-r--r--web/react/components/edit_channel_modal.jsx2
-rw-r--r--web/react/components/edit_post_modal.jsx7
-rw-r--r--web/react/components/get_link_modal.jsx2
-rw-r--r--web/react/components/loading_screen.jsx24
-rw-r--r--web/react/components/login.jsx130
-rw-r--r--web/react/components/member_list_item.jsx5
-rw-r--r--web/react/components/member_list_team.jsx12
-rw-r--r--web/react/components/mention.jsx14
-rw-r--r--web/react/components/mention_list.jsx146
-rw-r--r--web/react/components/more_channels.jsx15
-rw-r--r--web/react/components/msg_typing.jsx2
-rw-r--r--web/react/components/navbar.jsx231
-rw-r--r--web/react/components/new_channel.jsx8
-rw-r--r--web/react/components/password_reset.jsx79
-rw-r--r--web/react/components/post.jsx25
-rw-r--r--web/react/components/post_body.jsx17
-rw-r--r--web/react/components/post_info.jsx7
-rw-r--r--web/react/components/post_list.jsx128
-rw-r--r--web/react/components/post_right.jsx33
-rw-r--r--web/react/components/rename_channel_modal.jsx6
-rw-r--r--web/react/components/rename_team_modal.jsx8
-rw-r--r--web/react/components/search_bar.jsx39
-rw-r--r--web/react/components/search_results.jsx78
-rw-r--r--web/react/components/sidebar.jsx140
-rw-r--r--web/react/components/sidebar_header.jsx65
-rw-r--r--web/react/components/sidebar_right_menu.jsx4
-rw-r--r--web/react/components/signup_team.jsx11
-rw-r--r--web/react/components/signup_team_complete.jsx141
-rw-r--r--web/react/components/signup_user_complete.jsx45
-rw-r--r--web/react/components/team_members.jsx2
-rw-r--r--web/react/components/user_profile.jsx4
-rw-r--r--web/react/components/user_settings.jsx130
-rw-r--r--web/react/components/view_image.jsx9
-rw-r--r--web/react/pages/channel.jsx10
-rw-r--r--web/react/pages/home.jsx7
-rw-r--r--web/react/pages/login.jsx4
-rw-r--r--web/react/pages/password_reset.jsx4
-rw-r--r--web/react/pages/signup_team_complete.jsx4
-rw-r--r--web/react/stores/browser_store.jsx153
-rw-r--r--web/react/stores/channel_store.jsx117
-rw-r--r--web/react/stores/error_store.jsx11
-rw-r--r--web/react/stores/post_store.jsx65
-rw-r--r--web/react/stores/socket_store.jsx2
-rw-r--r--web/react/stores/team_store.jsx25
-rw-r--r--web/react/stores/user_store.jsx153
-rw-r--r--web/react/utils/client.jsx33
-rw-r--r--web/react/utils/constants.jsx2
-rw-r--r--web/react/utils/utils.jsx118
-rw-r--r--web/sass-files/sass/partials/_base.scss10
-rw-r--r--web/sass-files/sass/partials/_files.scss3
-rw-r--r--web/sass-files/sass/partials/_headers.scss61
-rw-r--r--web/sass-files/sass/partials/_loading.scss50
-rw-r--r--web/sass-files/sass/partials/_mentions.scss13
-rw-r--r--web/sass-files/sass/partials/_modal.scss3
-rw-r--r--web/sass-files/sass/partials/_navbar.scss1
-rw-r--r--web/sass-files/sass/partials/_popover.scss9
-rw-r--r--web/sass-files/sass/partials/_post.scss15
-rw-r--r--web/sass-files/sass/partials/_post_right.scss7
-rw-r--r--web/sass-files/sass/partials/_responsive.scss19
-rw-r--r--web/sass-files/sass/partials/_search.scss13
-rw-r--r--web/sass-files/sass/partials/_settings.scss4
-rw-r--r--web/sass-files/sass/partials/_sidebar--right.scss8
-rw-r--r--web/sass-files/sass/partials/_signup.scss4
-rw-r--r--web/sass-files/sass/partials/_variables.scss8
-rw-r--r--web/sass-files/sass/styles.scss3
-rw-r--r--web/static/config/config.js4
-rw-r--r--web/static/images/dropdown-icon.pngbin2877 -> 0 bytes
-rw-r--r--web/templates/channel.html2
-rw-r--r--web/templates/head.html6
-rw-r--r--web/templates/home.html2
-rw-r--r--web/templates/login.html4
-rw-r--r--web/templates/password_reset.html2
-rw-r--r--web/templates/signup_team.html3
-rw-r--r--web/templates/signup_team_complete.html2
-rw-r--r--web/templates/signup_team_confirm.html1
-rw-r--r--web/templates/signup_user_complete.html2
-rw-r--r--web/web.go145
149 files changed, 2303 insertions, 2589 deletions
diff --git a/Makefile b/Makefile
index 724497d91..589521c37 100644
--- a/Makefile
+++ b/Makefile
@@ -41,17 +41,23 @@ build:
install:
@go get $(GOFLAGS) github.com/tools/godep
- @if [ $(shell docker ps | grep -ci mattermost-mysql) -eq 0 ]; then \
+ @if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 0 ]; then \
echo restoring go libs using godep; \
$(GOPATH)/bin/godep restore; \
echo starting mattermost-mysql; \
docker run --name mattermost-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mostest \
-e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql > /dev/null; \
+ elif [ $(shell docker ps | grep -ci mattermost-mysql) -eq 0 ]; then \
+ echo restarting mattermost-mysql; \
+ docker start mattermost-mysql > /dev/null; \
fi
- @if [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \
+ @if [ $(shell docker ps -a | grep -ci mattermost-redis) -eq 0 ]; then \
echo starting mattermost-redis; \
docker run --name mattermost-redis -p 6379:6379 -d redis > /dev/null; \
+ elif [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \
+ echo restarting mattermost-redis; \
+ docker start mattermost-redis > /dev/null; \
fi
@cd web/react/ && npm install
diff --git a/README.md b/README.md
index 04cbf718d..65a3e7c66 100644
--- a/README.md
+++ b/README.md
@@ -108,26 +108,10 @@ AWS Elastic Beanstalk Setup (Docker)
11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next.
12. Environment tags my be left blank. Press next.
13. You will be asked to review your information. Press Launch.
- 14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later.
-
-2. Map a wildcard domain to the new elastic beanstalk application
- 15. From the AWS console select route 53
- 16. From the sidebar select Hosted Zones
- 17. Select the domain you want to use or create a new one.
- 18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 1.13.
- 19. Save the record set
-
-3. Set the environment variable "MATTERMOST\_DOMAIN" to the domain you mapped above (example.com not www.example.com)
- 20. Return the Elastic Beanstalk from the AWS console.
- 21. Select the environment you created.
- 22. Select configuration from the sidebar.
- 23. Click the gear beside software configuration.
- 24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com.
- 25. Select apply.
4. Try it out!
- 26. Return to the dashboard on the sidebar and wait for beanstalk update the environment.
- 27. Try it out by entering the domain you mapped into your browser.
+ 14. Wait for beanstalk to update the environment.
+ 15. Try it out by entering the domain of the form \*.elasticbeanstalk.com found at the top of the dashboard into your browser. You can also map your own domain if you wish.
Contributing
------------
@@ -137,8 +121,5 @@ To contribute to this open source project please review the Mattermost Contribut
License
-------
-Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details.
-
-As an exception, Admin Tools and Configuration Files are are made available under the terms of the Apache License, version 2.0. See LICENSE.txt for more information.
-
+Mattermost is licensed under an "Apache-wrapped AGPL" model, which means you can run and link to the system using Configuration Files and Admin Tools licensed under Apache, version 2.0, as described in the LICENSE file, as an explicit exception to the terms of the GNU Affero General Public License (AGPL) that applies to most of the remaining source files. See individual files for details.
diff --git a/api/api.go b/api/api.go
index 70e1b64ae..25f3376c6 100644
--- a/api/api.go
+++ b/api/api.go
@@ -16,10 +16,10 @@ var ServerTemplates *template.Template
type ServerTemplatePage Page
-func NewServerTemplatePage(templateName, teamUrl string) *ServerTemplatePage {
+func NewServerTemplatePage(templateName, teamURL string) *ServerTemplatePage {
props := make(map[string]string)
props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl
- return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamUrl: teamUrl, Props: props}
+ return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamURL: teamURL, Props: props}
}
func (me *ServerTemplatePage) Render() string {
diff --git a/api/api_test.go b/api/api_test.go
index b407c2a5e..0c2e57891 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -24,12 +24,12 @@ func Setup() {
func SetupBenchmark() (*model.Team, *model.User, *model.Channel) {
Setup()
- team := &model.Team{Name: "Benchmark Team", Domain: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN}
+ team := &model.Team{DisplayName: "Benchmark Team", Name: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
- user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", FullName: "Mr. Benchmarker", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", Nickname: "Mr. Benchmarker", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
channel := &model.Channel{DisplayName: "Benchmark Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
diff --git a/api/auto_constants.go b/api/auto_constants.go
index 3f8831055..f80f15f2d 100644
--- a/api/auto_constants.go
+++ b/api/auto_constants.go
@@ -12,8 +12,8 @@ const (
USER_PASSWORD = "passwd"
CHANNEL_TYPE = model.CHANNEL_OPEN
FUZZ_USER_EMAIL_PREFIX_LEN = 10
- BTEST_TEAM_NAME = "TestTeam"
- BTEST_TEAM_DOMAIN_NAME = "z-z-testdomaina"
+ BTEST_TEAM_DISPLAY_NAME = "TestTeam"
+ BTEST_TEAM_NAME = "z-z-testdomaina"
BTEST_TEAM_EMAIL = "test@nowhere.com"
BTEST_TEAM_TYPE = model.TEAM_OPEN
BTEST_USER_NAME = "Mr. Testing Tester"
diff --git a/api/auto_teams.go b/api/auto_teams.go
index 2fe826774..e5c772b4c 100644
--- a/api/auto_teams.go
+++ b/api/auto_teams.go
@@ -39,22 +39,22 @@ func NewAutoTeamCreator(client *model.Client) *AutoTeamCreator {
func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) {
var teamEmail string
+ var teamDisplayName string
var teamName string
- var teamDomain string
if cfg.Fuzzy {
teamEmail = utils.FuzzEmail()
+ teamDisplayName = utils.FuzzName()
teamName = utils.FuzzName()
- teamDomain = utils.FuzzName()
} else {
teamEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset)
- teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset)
- teamDomain = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId()
+ teamDisplayName = utils.RandomName(cfg.NameLength, cfg.NameCharset)
+ teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId()
}
team := &model.Team{
- Name: teamName,
- Domain: teamDomain,
- Email: teamEmail,
- Type: model.TEAM_OPEN,
+ DisplayName: teamDisplayName,
+ Name: teamName,
+ Email: teamEmail,
+ Type: model.TEAM_OPEN,
}
result, err := cfg.client.CreateTeam(team)
diff --git a/api/auto_users.go b/api/auto_users.go
index 493ec5c17..39ed201b9 100644
--- a/api/auto_users.go
+++ b/api/auto_users.go
@@ -33,15 +33,15 @@ func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator {
// Basic test team and user so you always know one
func CreateBasicUser(client *model.Client) *model.AppError {
- result, _ := client.FindTeamByDomain(BTEST_TEAM_DOMAIN_NAME, true)
+ result, _ := client.FindTeamByName(BTEST_TEAM_NAME, true)
if result.Data.(bool) == false {
- newteam := &model.Team{Name: BTEST_TEAM_NAME, Domain: BTEST_TEAM_DOMAIN_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE}
+ newteam := &model.Team{DisplayName: BTEST_TEAM_DISPLAY_NAME, Name: BTEST_TEAM_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE}
result, err := client.CreateTeam(newteam)
if err != nil {
return err
}
basicteam := result.Data.(*model.Team)
- newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, FullName: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD}
+ newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD}
result, err = client.CreateUser(newuser, "")
if err != nil {
return err
@@ -65,7 +65,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
user := &model.User{
TeamId: cfg.teamID,
Email: userEmail,
- FullName: userName,
+ Nickname: userName,
Password: USER_PASSWORD}
result, err := cfg.client.CreateUser(user, "")
diff --git a/api/channel.go b/api/channel.go
index 88db27def..4d8dbad09 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -8,7 +8,6 @@ import (
"fmt"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
- "github.com/mattermost/platform/store"
"net/http"
"strings"
)
@@ -542,7 +541,7 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED)
message.Add("channel_id", id)
- store.PublishAndForget(message)
+ PublishAndForget(message)
result := make(map[string]string)
result["id"] = id
@@ -657,7 +656,7 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED)
- store.PublishAndForget(message)
+ PublishAndForget(message)
<-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id)
w.Write([]byte(cm.ToJson()))
diff --git a/api/channel_benchmark_test.go b/api/channel_benchmark_test.go
index 461a7ed3a..881638176 100644
--- a/api/channel_benchmark_test.go
+++ b/api/channel_benchmark_test.go
@@ -138,10 +138,10 @@ func BenchmarkJoinChannel(b *testing.B) {
}
// Secondary test user to join channels created by primary test user
- user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", FullName: "That Guy", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", Nickname: "That Guy", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
// Benchmark Start
b.ResetTimer()
diff --git a/api/channel_test.go b/api/channel_test.go
index ed0554693..ae7781302 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -14,17 +14,17 @@ import (
func TestCreateChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
channel := model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
rchannel, err := Client.CreateChannel(&channel)
@@ -94,18 +94,18 @@ func TestCreateChannel(t *testing.T) {
func TestCreateDirectChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
data := make(map[string]string)
data["user_id"] = user2.Id
@@ -149,23 +149,23 @@ func TestCreateDirectChannel(t *testing.T) {
func TestUpdateChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
+ userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id))
- userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id))
- userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
userStd.Roles = ""
- Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userChannelAdmin.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)
@@ -184,7 +184,7 @@ func TestUpdateChannel(t *testing.T) {
t.Fatal("Channel admin failed to skip displayName")
}
- Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
desc = "b" + model.NewId() + "b"
upChannel1 = &model.Channel{Id: channel1.Id, Description: desc}
@@ -210,7 +210,7 @@ func TestUpdateChannel(t *testing.T) {
}
}
- Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
+ Client.LoginByEmail(team.Name, userStd.Email, "pwd")
if _, err := Client.UpdateChannel(upChannel1); err == nil {
t.Fatal("Standard User should have failed to update")
@@ -220,14 +220,14 @@ func TestUpdateChannel(t *testing.T) {
func TestUpdateChannelDesc(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ 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)
@@ -266,11 +266,11 @@ func TestUpdateChannelDesc(t *testing.T) {
t.Fatal("should have errored on bad channel desc")
}
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
data["channel_id"] = channel1.Id
data["channel_description"] = "new desc"
@@ -282,14 +282,14 @@ func TestUpdateChannelDesc(t *testing.T) {
func TestGetChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ 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)
@@ -324,14 +324,14 @@ func TestGetChannel(t *testing.T) {
func TestGetMoreChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.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)
@@ -339,11 +339,11 @@ func TestGetMoreChannel(t *testing.T) {
channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
rget := Client.Must(Client.GetMoreChannels(""))
data := rget.Data.(*model.ChannelList)
@@ -368,14 +368,14 @@ func TestGetMoreChannel(t *testing.T) {
func TestJoinChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.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)
@@ -383,11 +383,11 @@ func TestJoinChannel(t *testing.T) {
channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
Client.Must(Client.JoinChannel(channel1.Id))
@@ -399,10 +399,10 @@ func TestJoinChannel(t *testing.T) {
data["user_id"] = user1.Id
rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
- user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
- Client.LoginByEmail(team.Domain, user3.Email, "pwd")
+ Client.LoginByEmail(team.Name, user3.Email, "pwd")
if _, err := Client.JoinChannel(rchannel.Id); err == nil {
t.Fatal("shoudn't be able to join direct channel")
@@ -412,14 +412,14 @@ func TestJoinChannel(t *testing.T) {
func TestLeaveChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.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)
@@ -427,11 +427,11 @@ func TestLeaveChannel(t *testing.T) {
channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
Client.Must(Client.JoinChannel(channel1.Id))
@@ -461,25 +461,25 @@ func TestLeaveChannel(t *testing.T) {
func TestDeleteChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
+ userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id))
- userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id))
- Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd")
channelMadeByCA := &model.Channel{DisplayName: "C Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)
- Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userTeamAdmin.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)
@@ -500,11 +500,11 @@ func TestDeleteChannel(t *testing.T) {
t.Fatal("should have failed to post to deleted channel")
}
- userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
- Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
+ Client.LoginByEmail(team.Name, userStd.Email, "pwd")
if _, err := Client.JoinChannel(channel1.Id); err == nil {
t.Fatal("should have failed to join deleted channel")
@@ -531,14 +531,14 @@ func TestDeleteChannel(t *testing.T) {
func TestGetChannelExtraInfo(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ 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)
@@ -552,19 +552,19 @@ func TestGetChannelExtraInfo(t *testing.T) {
func TestAddChannelMember(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.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)
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
@@ -591,13 +591,13 @@ func TestAddChannelMember(t *testing.T) {
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil {
t.Fatal("Should have errored, user not in channel")
}
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
Client.Must(Client.DeleteChannel(channel2.Id))
@@ -610,30 +610,30 @@ func TestAddChannelMember(t *testing.T) {
func TestRemoveChannelMember(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
+ userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id))
- userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id))
- Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd")
channelMadeByCA := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id))
- Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userTeamAdmin.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)
- userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
@@ -660,13 +660,13 @@ func TestRemoveChannelMember(t *testing.T) {
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
- Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
+ Client.LoginByEmail(team.Name, userStd.Email, "pwd")
if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil {
t.Fatal("Should have errored, user not channel admin")
}
- Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
+ Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
Client.Must(Client.AddChannelMember(channel2.Id, userStd.Id))
Client.Must(Client.DeleteChannel(channel2.Id))
@@ -680,14 +680,14 @@ func TestRemoveChannelMember(t *testing.T) {
func TestUpdateNotifyLevel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ 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)
@@ -746,10 +746,10 @@ func TestUpdateNotifyLevel(t *testing.T) {
t.Fatal("Should have errored - bad notify level")
}
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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)
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
data["channel_id"] = channel1.Id
data["user_id"] = user2.Id
@@ -762,14 +762,14 @@ func TestUpdateNotifyLevel(t *testing.T) {
func TestFuzzyChannel(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
// Strings that should pass as acceptable channel names
var fuzzyStringsPass = []string{
diff --git a/api/command.go b/api/command.go
index 810a8a07e..f051bd42e 100644
--- a/api/command.go
+++ b/api/command.go
@@ -27,8 +27,6 @@ var commands = []commandHandler{
func InitCommand(r *mux.Router) {
l4g.Debug("Initializing command api routes")
r.Handle("/command", ApiUserRequired(command)).Methods("POST")
-
- hub.Start()
}
func command(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -323,14 +321,14 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
}
}
- client := model.NewClient(c.TeamUrl + "/api/v1")
+ client := model.NewClient(c.GetSiteURL() + "/api/v1")
if doTeams {
if err := CreateBasicUser(client); err != nil {
l4g.Error("Failed to create testing enviroment")
return true
}
- client.LoginByEmail(BTEST_TEAM_DOMAIN_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
+ client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
enviroment, err := CreateTestEnviromentWithTeams(
client,
utils.Range{numTeams, numTeams},
@@ -344,7 +342,7 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
} else {
l4g.Info("Testing enviroment created")
for i := 0; i < len(enviroment.Teams); i++ {
- l4g.Info("Team Created: " + enviroment.Teams[i].Domain)
+ l4g.Info("Team Created: " + enviroment.Teams[i].Name)
l4g.Info("\t User to login: " + enviroment.Enviroments[i].Users[0].Email + ", " + USER_PASSWORD)
}
}
@@ -383,7 +381,7 @@ func loadTestUsersCommand(c *Context, command *model.Command) bool {
if err == false {
usersr = utils.Range{10, 15}
}
- client := model.NewClient(c.TeamUrl + "/api/v1")
+ client := model.NewClient(c.GetSiteURL() + "/api/v1")
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
userCreator.Fuzzy = doFuzz
userCreator.CreateTestUsers(usersr)
@@ -413,7 +411,7 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool {
if err == false {
channelsr = utils.Range{20, 30}
}
- client := model.NewClient(c.TeamUrl + "/api/v1")
+ client := model.NewClient(c.GetSiteURL() + "/api/v1")
client.MockSession(c.Session.Id)
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
channelCreator.Fuzzy = doFuzz
@@ -465,7 +463,7 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool {
}
}
- client := model.NewClient(c.TeamUrl + "/api/v1")
+ client := model.NewClient(c.GetSiteURL() + "/api/v1")
client.MockSession(c.Session.Id)
testPoster := NewAutoPostCreator(client, command.ChannelId)
testPoster.Fuzzy = doFuzz
diff --git a/api/command_test.go b/api/command_test.go
index 624c445e6..a58ef9be5 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -12,14 +12,14 @@ import (
func TestSuggestRootCommands(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
if _, err := Client.Command("", "", true); err == nil {
t.Fatal("Should fail")
@@ -55,14 +55,14 @@ func TestSuggestRootCommands(t *testing.T) {
func TestLogoutCommands(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command)
if rs1.GotoLocation != "/logout" {
@@ -73,14 +73,14 @@ func TestLogoutCommands(t *testing.T) {
func TestJoinCommands(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -90,7 +90,7 @@ func TestJoinCommands(t *testing.T) {
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
Client.Must(Client.LeaveChannel(channel2.Id))
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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(user1.Id))
diff --git a/api/context.go b/api/context.go
index bea0fbeff..ac9dffcbc 100644
--- a/api/context.go
+++ b/api/context.go
@@ -17,12 +17,14 @@ import (
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
type Context struct {
- Session model.Session
- RequestId string
- IpAddress string
- TeamUrl string
- Path string
- Err *model.AppError
+ Session model.Session
+ RequestId string
+ IpAddress string
+ Path string
+ Err *model.AppError
+ teamURLValid bool
+ teamURL string
+ siteURL string
}
type Page struct {
@@ -30,32 +32,36 @@ type Page struct {
Title string
SiteName string
FeedbackEmail string
- TeamUrl string
+ TeamURL string
Props map[string]string
}
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
- return &handler{h, false, false, true, false}
+ return &handler{h, false, false, true, false, false}
}
func AppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
- return &handler{h, false, false, false, false}
+ return &handler{h, false, false, false, false, false}
+}
+
+func AppHandlerIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
+ return &handler{h, false, false, false, false, true}
}
func ApiUserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
- return &handler{h, true, false, true, true}
+ return &handler{h, true, false, true, true, false}
}
func ApiUserRequiredActivity(h func(*Context, http.ResponseWriter, *http.Request), isUserActivity bool) http.Handler {
- return &handler{h, true, false, true, isUserActivity}
+ return &handler{h, true, false, true, isUserActivity, false}
}
func UserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
- return &handler{h, true, false, false, false}
+ return &handler{h, true, false, false, false, false}
}
func ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
- return &handler{h, true, true, true, false}
+ return &handler{h, true, true, true, false, false}
}
type handler struct {
@@ -64,6 +70,7 @@ type handler struct {
requireSystemAdmin bool
isApi bool
isUserActivity bool
+ isTeamIndependent bool
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -73,7 +80,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := &Context{}
c.RequestId = model.NewId()
c.IpAddress = GetIpAddress(r)
- c.Path = r.URL.Path
protocol := "http"
@@ -90,7 +96,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
- c.TeamUrl = protocol + "://" + r.Host
+ c.setSiteURL(protocol + "://" + r.Host)
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version)
@@ -135,6 +141,15 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
+ if h.isApi || h.isTeamIndependent {
+ c.setTeamURL(c.GetSiteURL(), false)
+ c.Path = r.URL.Path
+ } else {
+ splitURL := strings.Split(r.URL.Path, "/")
+ c.setTeamURL(protocol+"://"+r.Host+"/"+splitURL[1], true)
+ c.Path = "/" + strings.Join(splitURL[2:], "/")
+ }
+
if c.Err == nil && h.requireUser {
c.UserRequired()
}
@@ -165,7 +180,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(c.Err.ToJson()))
} else {
if c.Err.StatusCode == http.StatusUnauthorized {
- http.Redirect(w, r, "/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
+ http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
} else {
RenderWebError(c.Err, w, r)
}
@@ -265,6 +280,16 @@ func (c *Context) IsSystemAdmin() bool {
return false
}
+func (c *Context) IsTeamAdmin(userId string) bool {
+ if uresult := <-Srv.Store.User().Get(userId); uresult.Err != nil {
+ c.Err = uresult.Err
+ return false
+ } else {
+ user := uresult.Data.(*model.User)
+ return strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId
+ }
+}
+
func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
sessionCache.Remove(c.Session.Id)
@@ -289,6 +314,39 @@ func (c *Context) SetUnknownError(where string, details string) {
c.Err = model.NewAppError(where, "An unknown error has occured. Please contact support.", details)
}
+func (c *Context) setTeamURL(url string, valid bool) {
+ c.teamURL = url
+ c.teamURLValid = valid
+}
+
+func (c *Context) setTeamURLFromSession() {
+ if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil {
+ c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true)
+ }
+}
+
+func (c *Context) setSiteURL(url string) {
+ c.siteURL = url
+}
+
+func (c *Context) GetTeamURLFromTeam(team *model.Team) string {
+ return c.GetSiteURL() + "/" + team.Name
+}
+
+func (c *Context) GetTeamURL() string {
+ if !c.teamURLValid {
+ c.setTeamURLFromSession()
+ if !c.teamURLValid {
+ l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent")
+ }
+ }
+ return c.teamURL
+}
+
+func (c *Context) GetSiteURL() string {
+ return c.siteURL
+}
+
func GetIpAddress(r *http.Request) string {
address := r.Header.Get(model.HEADER_FORWARDED)
if len(address) == 0 {
diff --git a/api/file.go b/api/file.go
index 0e08567d6..362cdf896 100644
--- a/api/file.go
+++ b/api/file.go
@@ -13,9 +13,9 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/nfnt/resize"
+ _ "golang.org/x/image/bmp"
"image"
_ "image/gif"
- _ "golang.org/x/image/bmp"
"image/jpeg"
"io"
"net/http"
@@ -115,7 +115,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- fileUrl := c.TeamUrl + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename)
+ fileUrl := c.GetSiteURL() + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename)
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
}
@@ -363,7 +363,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
- url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.TeamUrl, getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)
+ url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)
if !c.HasPermissionsToChannel(cchan, "getPublicLink") {
return
diff --git a/api/file_test.go b/api/file_test.go
index e334fd6e5..79ee03c77 100644
--- a/api/file_test.go
+++ b/api/file_test.go
@@ -24,14 +24,14 @@ import (
func TestUploadFile(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ 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)
@@ -111,14 +111,14 @@ func TestUploadFile(t *testing.T) {
func TestGetFile(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ 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)
@@ -173,10 +173,10 @@ func TestGetFile(t *testing.T) {
t.Fatal("file get failed")
}
- team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := &model.User{TeamId: team2.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))
@@ -187,7 +187,7 @@ func TestGetFile(t *testing.T) {
data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
- Client.LoginByEmail(team2.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team2.Name, user2.Email, "pwd")
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr != nil {
t.Fatal(downErr)
@@ -258,18 +258,18 @@ func TestGetFile(t *testing.T) {
func TestGetPublicLink(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user1.Email, "pwd")
+ 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)
@@ -344,7 +344,7 @@ func TestGetPublicLink(t *testing.T) {
t.Fatal("Should have errored - bad file path")
}
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
data["filename"] = filenames[0]
if _, err := Client.GetPublicLink(data); err == nil {
t.Fatal("should have errored, user not member of channel")
diff --git a/api/post.go b/api/post.go
index 650f47062..fb9fdd1ef 100644
--- a/api/post.go
+++ b/api/post.go
@@ -11,6 +11,7 @@ import (
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"net/http"
+ "path/filepath"
"strconv"
"strings"
"time"
@@ -121,7 +122,7 @@ func CreateValetPost(c *Context, post *model.Post) (*model.Post, *model.AppError
rpost = result.Data.(*model.Post)
}
- fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl)
+ fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL())
return rpost, nil
}
@@ -200,14 +201,14 @@ func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.P
} else {
rpost = result.Data.(*model.Post)
- fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl)
+ fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL())
}
return rpost, nil
}
-func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
+func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) {
go func() {
// Get a list of user names (to be used as keywords) and ids for the given team
@@ -297,14 +298,11 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
for _, k := range splitKeys {
keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
}
+ }
- // If turned on, add the user's case sensitive first name
- if profile.NotifyProps["first_name"] == "true" {
- splitName := strings.Split(profile.FullName, " ")
- if len(splitName) > 0 && splitName[0] != "" {
- keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id)
- }
- }
+ // If turned on, add the user's case sensitive first name
+ if profile.NotifyProps["first_name"] == "true" {
+ keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
}
// Add @all to keywords if user has them turned on
@@ -364,20 +362,22 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
mentionedUsers = append(mentionedUsers, k)
}
- var teamName string
+ var teamDisplayName string
+ var teamURL string
if result := <-tchan; result.Err != nil {
l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err)
return
} else {
- teamName = result.Data.(*model.Team).Name
+ teamDisplayName = result.Data.(*model.Team).DisplayName
+ teamURL = siteURL + "/" + result.Data.(*model.Team).Name
}
// Build and send the emails
location, _ := time.LoadLocation("UTC")
tm := time.Unix(post.CreateAt/1000, 0).In(location)
- subjectPage := NewServerTemplatePage("post_subject", teamUrl)
- subjectPage.Props["TeamName"] = teamName
+ subjectPage := NewServerTemplatePage("post_subject", teamURL)
+ subjectPage.Props["TeamDisplayName"] = teamDisplayName
subjectPage.Props["SubjectText"] = subjectText
subjectPage.Props["Month"] = tm.Month().String()[:3]
subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
@@ -394,11 +394,9 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
continue
}
- firstName := strings.Split(profileMap[id].FullName, " ")[0]
-
- bodyPage := NewServerTemplatePage("post_body", teamUrl)
- bodyPage.Props["FullName"] = firstName
- bodyPage.Props["TeamName"] = teamName
+ bodyPage := NewServerTemplatePage("post_body", teamURL)
+ bodyPage.Props["Nickname"] = profileMap[id].FirstName
+ bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["ChannelName"] = channelName
bodyPage.Props["BodyText"] = bodyText
bodyPage.Props["SenderName"] = senderName
@@ -407,7 +405,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
bodyPage.Props["Month"] = tm.Month().String()[:3]
bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
- bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name
+ bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
@@ -437,11 +435,24 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
message.Add("post", post.ToJson())
+
+ if len(post.Filenames) != 0 {
+ message.Add("otherFile", "true")
+
+ for _, filename := range post.Filenames {
+ ext := filepath.Ext(filename)
+ if model.IsFileExtImage(ext) {
+ message.Add("image", "true")
+ break
+ }
+ }
+ }
+
if len(mentionedUsers) != 0 {
message.Add("mentions", model.ArrayToJson(mentionedUsers))
}
- store.PublishAndForget(message)
+ PublishAndForget(message)
}()
}
@@ -507,7 +518,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
message.Add("channel_id", rpost.ChannelId)
message.Add("message", rpost.Message)
- store.PublishAndForget(message)
+ PublishAndForget(message)
w.Write([]byte(rpost.ToJson()))
}
@@ -620,16 +631,17 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
pchan := Srv.Store.Post().Get(postId)
- if !c.HasPermissionsToChannel(cchan, "deletePost") {
- return
- }
-
if result := <-pchan; result.Err != nil {
c.Err = result.Err
return
} else {
+
post := result.Data.(*model.PostList).Posts[postId]
+ if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(post.UserId) {
+ return
+ }
+
if post == nil {
c.SetInvalidParam("deletePost", "postId")
return
@@ -641,7 +653,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if post.UserId != c.Session.UserId {
+ if post.UserId != c.Session.UserId && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
c.Err.StatusCode = http.StatusForbidden
return
@@ -656,7 +668,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
message.Add("post_id", post.Id)
message.Add("channel_id", post.ChannelId)
- store.PublishAndForget(message)
+ PublishAndForget(message)
result := make(map[string]string)
result["id"] = postId
diff --git a/api/post_test.go b/api/post_test.go
index 3af1874c2..e245366ca 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -15,21 +15,21 @@ import (
func TestCreatePost(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user1.Email, "pwd")
+ 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)
@@ -96,18 +96,18 @@ func TestCreatePost(t *testing.T) {
t.Fatal("Should have been forbidden")
}
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
_, err = Client.CreatePost(post7)
if err.StatusCode != http.StatusForbidden {
t.Fatal("Should have been forbidden")
}
- user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user3.Email, "pwd")
+ 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)
@@ -126,21 +126,21 @@ func TestCreatePost(t *testing.T) {
func TestCreateValetPost(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user1.Email, "pwd")
+ 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)
@@ -181,18 +181,18 @@ func TestCreateValetPost(t *testing.T) {
t.Fatal("Should have been forbidden")
}
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user3.Email, "pwd")
+ 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)
@@ -218,21 +218,21 @@ func TestCreateValetPost(t *testing.T) {
func TestUpdatePost(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user1.Email, "pwd")
+ 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)
@@ -289,14 +289,14 @@ func TestUpdatePost(t *testing.T) {
func TestGetPosts(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -354,14 +354,14 @@ func TestGetPosts(t *testing.T) {
func TestSearchPosts(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -400,14 +400,14 @@ func TestSearchPosts(t *testing.T) {
func TestSearchHashtagPosts(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -431,14 +431,14 @@ func TestSearchHashtagPosts(t *testing.T) {
func TestGetPostsCache(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -480,14 +480,18 @@ func TestGetPostsCache(t *testing.T) {
func TestDeletePosts(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ userAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
+ userAdmin = Client.Must(Client.CreateUser(userAdmin, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(userAdmin.Id))
+
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -522,21 +526,29 @@ func TestDeletePosts(t *testing.T) {
r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList)
if len(r2.Posts) != 4 {
- t.Fatal("should have returned 5 items")
+ t.Fatal("should have returned 4 items")
}
+
+ time.Sleep(10 * time.Millisecond)
+ post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
+ post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
+
+ Client.LoginByEmail(team.Name, userAdmin.Email, "pwd")
+
+ Client.Must(Client.DeletePost(channel1.Id, post4.Id))
}
func TestEmailMention(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", FullName: "Bob Bobby", Password: "pwd"}
+ user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", Nickname: "Bob Bobby", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -551,14 +563,14 @@ func TestEmailMention(t *testing.T) {
func TestFuzzyPosts(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.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)
diff --git a/api/server.go b/api/server.go
index 58986a8d4..3163f79f5 100644
--- a/api/server.go
+++ b/api/server.go
@@ -28,7 +28,6 @@ func NewServer() {
Srv = &Server{}
Srv.Server = manners.NewServer()
Srv.Store = store.NewSqlStore()
- store.RedisClient()
Srv.Router = mux.NewRouter()
Srv.Router.NotFoundHandler = http.HandlerFunc(Handle404)
@@ -54,7 +53,7 @@ func StopServer() {
Srv.Server.Shutdown <- true
Srv.Store.Close()
- store.RedisClose()
+ hub.Stop()
l4g.Info("Server stopped")
}
diff --git a/api/sharding.go b/api/sharding.go
new file mode 100644
index 000000000..569d3a232
--- /dev/null
+++ b/api/sharding.go
@@ -0,0 +1,79 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+/*
+func createSubDomain(subDomain string, target string) {
+
+ if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
+ return
+ }
+
+ creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
+ r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
+
+ rr := route53.ResourceRecord{
+ Value: aws.String(target),
+ }
+
+ rrs := make([]*route53.ResourceRecord, 1)
+ rrs[0] = &rr
+
+ change := route53.Change{
+ Action: aws.String("CREATE"),
+ ResourceRecordSet: &route53.ResourceRecordSet{
+ Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)),
+ TTL: aws.Long(300),
+ Type: aws.String("CNAME"),
+ ResourceRecords: rrs,
+ },
+ }
+
+ changes := make([]*route53.Change, 1)
+ changes[0] = &change
+
+ r53req := &route53.ChangeResourceRecordSetsInput{
+ HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
+ ChangeBatch: &route53.ChangeBatch{
+ Changes: changes,
+ },
+ }
+
+ if _, err := r53.ChangeResourceRecordSets(r53req); err != nil {
+ l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err)
+ return
+ }
+}
+
+func doesSubDomainExist(subDomain string) bool {
+
+ // if it's configured for testing then skip this step
+ if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
+ return false
+ }
+
+ creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
+ r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
+
+ r53req := &route53.ListResourceRecordSetsInput{
+ HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
+ MaxItems: aws.String("1"),
+ StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)),
+ }
+
+ if result, err := r53.ListResourceRecordSets(r53req); err != nil {
+ l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err)
+ return true
+ } else {
+
+ for _, v := range result.ResourceRecordSets {
+ if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+*/
diff --git a/api/team.go b/api/team.go
index c4a0ca181..1145e6e81 100644
--- a/api/team.go
+++ b/api/team.go
@@ -6,8 +6,6 @@ package api
import (
l4g "code.google.com/p/log4go"
"fmt"
- "github.com/awslabs/aws-sdk-go/aws"
- "github.com/awslabs/aws-sdk-go/service/route53"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
@@ -24,11 +22,11 @@ func InitTeam(r *mux.Router) {
sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST")
sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST")
sr.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST")
- sr.Handle("/find_team_by_domain", ApiAppHandler(findTeamByDomain)).Methods("POST")
+ sr.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST")
sr.Handle("/find_teams", ApiAppHandler(findTeams)).Methods("POST")
sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST")
sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST")
- sr.Handle("/update_name", ApiUserRequired(updateTeamName)).Methods("POST")
+ sr.Handle("/update_name", ApiUserRequired(updateTeamDisplayName)).Methods("POST")
sr.Handle("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST")
sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET")
}
@@ -37,38 +35,38 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
m := model.MapFromJson(r.Body)
email := strings.ToLower(strings.TrimSpace(m["email"]))
- name := strings.TrimSpace(m["name"])
+ displayName := strings.TrimSpace(m["display_name"])
if len(email) == 0 {
c.SetInvalidParam("signupTeam", "email")
return
}
- if len(name) == 0 {
- c.SetInvalidParam("signupTeam", "name")
+ if len(displayName) == 0 {
+ c.SetInvalidParam("signupTeam", "display_name")
return
}
- subjectPage := NewServerTemplatePage("signup_team_subject", c.TeamUrl)
- bodyPage := NewServerTemplatePage("signup_team_body", c.TeamUrl)
+ subjectPage := NewServerTemplatePage("signup_team_subject", c.GetSiteURL())
+ bodyPage := NewServerTemplatePage("signup_team_body", c.GetSiteURL())
bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink
props := make(map[string]string)
props["email"] = email
- props["name"] = name
+ props["display_name"] = displayName
props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
- bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash))
+ bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash))
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
c.Err = err
return
}
- if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
+ if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV || utils.Cfg.EmailSettings.ByPassEmail {
m["follow_link"] = bodyPage.Props["Link"]
}
@@ -119,25 +117,16 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- found := FindTeamByDomain(c, teamSignup.Team.Domain, "true")
+ found := FindTeamByName(c, teamSignup.Team.Name, "true")
if c.Err != nil {
return
}
if found {
- c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Domain)
+ c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Name)
return
}
- if IsBetaDomain(r) {
- for key, value := range utils.Cfg.ServiceSettings.Shards {
- if strings.Index(r.Host, key) == 0 {
- createSubDomain(teamSignup.Team.Domain, value)
- break
- }
- }
- }
-
teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault
if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil {
@@ -166,7 +155,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
- InviteMembers(rteam, ruser, teamSignup.Invites)
+ InviteMembers(c, rteam, ruser, teamSignup.Invites)
teamSignup.Team = *rteam
teamSignup.User = *ruser
@@ -211,87 +200,14 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
-func doesSubDomainExist(subDomain string) bool {
-
- // if it's configured for testing then skip this step
- if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
- return false
- }
-
- creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
- r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
-
- r53req := &route53.ListResourceRecordSetsInput{
- HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
- MaxItems: aws.String("1"),
- StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)),
- }
-
- if result, err := r53.ListResourceRecordSets(r53req); err != nil {
- l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err)
- return true
- } else {
-
- for _, v := range result.ResourceRecordSets {
- if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) {
- return true
- }
- }
- }
-
- return false
-}
-
-func createSubDomain(subDomain string, target string) {
-
- if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
- return
- }
-
- creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
- r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
-
- rr := route53.ResourceRecord{
- Value: aws.String(target),
- }
-
- rrs := make([]*route53.ResourceRecord, 1)
- rrs[0] = &rr
-
- change := route53.Change{
- Action: aws.String("CREATE"),
- ResourceRecordSet: &route53.ResourceRecordSet{
- Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)),
- TTL: aws.Long(300),
- Type: aws.String("CNAME"),
- ResourceRecords: rrs,
- },
- }
-
- changes := make([]*route53.Change, 1)
- changes[0] = &change
-
- r53req := &route53.ChangeResourceRecordSetsInput{
- HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
- ChangeBatch: &route53.ChangeBatch{
- Changes: changes,
- },
- }
-
- if _, err := r53.ChangeResourceRecordSets(r53req); err != nil {
- l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err)
- return
- }
-}
-
-func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) {
+func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) {
m := model.MapFromJson(r.Body)
- domain := strings.ToLower(strings.TrimSpace(m["domain"]))
+ name := strings.ToLower(strings.TrimSpace(m["name"]))
all := strings.ToLower(strings.TrimSpace(m["all"]))
- found := FindTeamByDomain(c, domain, all)
+ found := FindTeamByName(c, name, all)
if c.Err != nil {
return
@@ -304,56 +220,25 @@ func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
-func FindTeamByDomain(c *Context, domain string, all string) bool {
+func FindTeamByName(c *Context, name string, all string) bool {
- if domain == "" || len(domain) > 64 {
- c.SetInvalidParam("findTeamByDomain", "domain")
+ if name == "" || len(name) > 64 {
+ c.SetInvalidParam("findTeamByName", "domain")
return false
}
- if model.IsReservedDomain(domain) {
- c.Err = model.NewAppError("findTeamByDomain", "This URL is unavailable. Please try another.", "d="+domain)
+ if model.IsReservedTeamName(name) {
+ c.Err = model.NewAppError("findTeamByName", "This URL is unavailable. Please try another.", "name="+name)
return false
}
- if all == "false" {
- if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
- return false
- } else {
- return true
- }
- } else {
- if doesSubDomainExist(domain) {
- return true
- }
-
- protocol := "http"
-
- if utils.Cfg.ServiceSettings.UseSSL {
- protocol = "https"
- }
-
- for key, _ := range utils.Cfg.ServiceSettings.Shards {
- url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain)
-
- if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 {
- url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain)
- }
-
- client := model.NewClient(url)
-
- if result, err := client.FindTeamByDomain(domain, false); err != nil {
- c.Err = err
- return false
- } else {
- if result.Data.(bool) {
- return true
- }
- }
- }
-
+ if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
return false
+ } else {
+ return true
}
+
+ return false
}
func findTeams(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -376,7 +261,7 @@ func findTeams(c *Context, w http.ResponseWriter, r *http.Request) {
s := make([]string, 0, len(teams))
for _, v := range teams {
- s = append(s, v.Domain)
+ s = append(s, v.Name)
}
w.Write([]byte(model.ArrayToJson(s)))
@@ -394,33 +279,8 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- protocol := "http"
-
- if utils.Cfg.ServiceSettings.UseSSL {
- protocol = "https"
- }
-
- subjectPage := NewServerTemplatePage("find_teams_subject", c.TeamUrl)
- bodyPage := NewServerTemplatePage("find_teams_body", c.TeamUrl)
-
- for key, _ := range utils.Cfg.ServiceSettings.Shards {
- url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain)
-
- if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 {
- url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain)
- }
-
- client := model.NewClient(url)
-
- if result, err := client.FindTeams(email); err != nil {
- l4g.Error("An error occured while finding teams at %v err=%v", key, err)
- } else {
- data := result.Data.([]string)
- for _, domain := range data {
- bodyPage.Props[fmt.Sprintf("%v://%v.%v", protocol, domain, utils.Cfg.ServiceSettings.Domain)] = ""
- }
- }
- }
+ subjectPage := NewServerTemplatePage("find_teams_subject", c.GetSiteURL())
+ bodyPage := NewServerTemplatePage("find_teams_body", c.GetSiteURL())
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("An error occured while sending an email in emailTeams err=%v", err)
@@ -470,29 +330,18 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
ia = append(ia, invite["email"])
}
- InviteMembers(team, user, ia)
+ InviteMembers(c, team, user, ia)
w.Write([]byte(invites.ToJson()))
}
-func InviteMembers(team *model.Team, user *model.User, invites []string) {
+func InviteMembers(c *Context, team *model.Team, user *model.User, invites []string) {
for _, invite := range invites {
if len(invite) > 0 {
- teamUrl := ""
- if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
- teamUrl = "http://localhost:8065"
- } else if utils.Cfg.ServiceSettings.UseSSL {
- teamUrl = fmt.Sprintf("https://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain)
- } else {
- teamUrl = fmt.Sprintf("http://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain)
- }
+ teamURL := ""
+ teamURL = c.GetTeamURLFromTeam(team)
- sender := ""
- if len(strings.TrimSpace(user.FullName)) == 0 {
- sender = user.Username
- } else {
- sender = user.FullName
- }
+ sender := user.GetDisplayName()
senderRole := ""
if strings.Contains(user.Roles, model.ROLE_ADMIN) || strings.Contains(user.Roles, model.ROLE_SYSTEM_ADMIN) {
@@ -501,11 +350,11 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
senderRole = "member"
}
- subjectPage := NewServerTemplatePage("invite_subject", teamUrl)
+ subjectPage := NewServerTemplatePage("invite_subject", teamURL)
subjectPage.Props["SenderName"] = sender
- subjectPage.Props["TeamName"] = team.Name
- bodyPage := NewServerTemplatePage("invite_body", teamUrl)
- bodyPage.Props["TeamName"] = team.Name
+ subjectPage.Props["TeamDisplayName"] = team.DisplayName
+ bodyPage := NewServerTemplatePage("invite_body", teamURL)
+ bodyPage.Props["TeamDisplayName"] = team.DisplayName
bodyPage.Props["SenderName"] = sender
bodyPage.Props["SenderStatus"] = senderRole
@@ -514,12 +363,12 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
props := make(map[string]string)
props["email"] = invite
props["id"] = team.Id
+ props["display_name"] = team.DisplayName
props["name"] = team.Name
- props["domain"] = team.Domain
props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
- bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", teamUrl, url.QueryEscape(data), url.QueryEscape(hash))
+ bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash))
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"])
@@ -532,35 +381,35 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
}
}
-func updateTeamName(c *Context, w http.ResponseWriter, r *http.Request) {
+func updateTeamDisplayName(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
new_name := props["new_name"]
if len(new_name) == 0 {
- c.SetInvalidParam("updateTeamName", "new_name")
+ c.SetInvalidParam("updateTeamDisplayName", "new_name")
return
}
teamId := props["team_id"]
if len(teamId) > 0 && len(teamId) != 26 {
- c.SetInvalidParam("updateTeamName", "team_id")
+ c.SetInvalidParam("updateTeamDisplayName", "team_id")
return
} else if len(teamId) == 0 {
teamId = c.Session.TeamId
}
- if !c.HasPermissionsToTeam(teamId, "updateTeamName") {
+ if !c.HasPermissionsToTeam(teamId, "updateTeamDisplayName") {
return
}
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
- c.Err = model.NewAppError("updateTeamName", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
+ c.Err = model.NewAppError("updateTeamDisplayName", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
- if result := <-Srv.Store.Team().UpdateName(new_name, c.Session.TeamId); result.Err != nil {
+ if result := <-Srv.Store.Team().UpdateDisplayName(new_name, c.Session.TeamId); result.Err != nil {
c.Err = result.Err
return
}
diff --git a/api/team_test.go b/api/team_test.go
index 2bf3219e4..2723eff57 100644
--- a/api/team_test.go
+++ b/api/team_test.go
@@ -32,8 +32,8 @@ func TestCreateFromSignupTeam(t *testing.T) {
data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
- user := model.User{Email: props["email"], FullName: "Corey Hulen", Password: "hello"}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ user := model.User{Email: props["email"], Nickname: "Corey Hulen", Password: "hello"}
ts := model.TeamSignup{Team: team, User: user, Invites: []string{"corey@test.com"}, Data: data, Hash: hash}
@@ -42,7 +42,7 @@ func TestCreateFromSignupTeam(t *testing.T) {
t.Fatal(err)
}
- if rts.Data.(*model.TeamSignup).Team.Name != team.Name {
+ if rts.Data.(*model.TeamSignup).Team.DisplayName != team.DisplayName {
t.Fatal("full name didn't match")
}
@@ -71,24 +71,24 @@ func TestCreateFromSignupTeam(t *testing.T) {
func TestCreateTeam(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, err := Client.CreateTeam(&team)
if err != nil {
t.Fatal(err)
}
- user := &model.User{TeamId: rteam.Data.(*model.Team).Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := &model.User{TeamId: rteam.Data.(*model.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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, 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 {
+ if rteam.Data.(*model.Team).DisplayName != team.DisplayName {
t.Fatal("full name didn't match")
}
@@ -111,10 +111,10 @@ func TestCreateTeam(t *testing.T) {
func TestFindTeamByEmail(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
@@ -122,7 +122,7 @@ func TestFindTeamByEmail(t *testing.T) {
t.Fatal(err)
} else {
domains := r1.Data.([]string)
- if domains[0] != team.Domain {
+ if domains[0] != team.Name {
t.Fatal(domains)
}
}
@@ -139,14 +139,14 @@ XXXXXX investigate and fix failing test
func TestFindTeamByDomain(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- if r1, err := Client.FindTeamByDomain(team.Domain, false); err != nil {
+ if r1, err := Client.FindTeamByDomain(team.Name, false); err != nil {
t.Fatal(err)
} else {
val := r1.Data.(bool)
@@ -155,7 +155,7 @@ func TestFindTeamByDomain(t *testing.T) {
}
}
- if r1, err := Client.FindTeamByDomain(team.Domain, true); err != nil {
+ if r1, err := Client.FindTeamByDomain(team.Name, true); err != nil {
t.Fatal(err)
} else {
val := r1.Data.(bool)
@@ -179,10 +179,10 @@ func TestFindTeamByDomain(t *testing.T) {
func TestFindTeamByEmailSend(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
@@ -203,14 +203,14 @@ func TestFindTeamByEmailSend(t *testing.T) {
func TestInviteMembers(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
invite := make(map[string]string)
invite["email"] = model.NewId() + "corey@test.com"
@@ -229,54 +229,54 @@ func TestInviteMembers(t *testing.T) {
}
}
-func TestUpdateTeamName(t *testing.T) {
+func TestUpdateTeamDisplayName(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
data := make(map[string]string)
data["new_name"] = "NewName"
- if _, err := Client.UpdateTeamName(data); err == nil {
+ if _, err := Client.UpdateTeamDisplayName(data); err == nil {
t.Fatal("Should have errored, not admin")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
data["new_name"] = ""
- if _, err := Client.UpdateTeamName(data); err == nil {
+ if _, err := Client.UpdateTeamDisplayName(data); err == nil {
t.Fatal("Should have errored, empty name")
}
data["new_name"] = "NewName"
- if _, err := Client.UpdateTeamName(data); err != nil {
+ if _, err := Client.UpdateTeamDisplayName(data); err != nil {
t.Fatal(err)
}
// No GET team web service, so hard to confirm here that team name updated
data["team_id"] = "junk"
- if _, err := Client.UpdateTeamName(data); err == nil {
+ if _, err := Client.UpdateTeamDisplayName(data); err == nil {
t.Fatal("Should have errored, junk team id")
}
data["team_id"] = "12345678901234567890123456"
- if _, err := Client.UpdateTeamName(data); err == nil {
+ if _, err := Client.UpdateTeamDisplayName(data); err == nil {
t.Fatal("Should have errored, bad team id")
}
data["team_id"] = team.Id
data["new_name"] = "NewNameAgain"
- if _, err := Client.UpdateTeamName(data); err != nil {
+ if _, err := Client.UpdateTeamDisplayName(data); err != nil {
t.Fatal(err)
}
// No GET team web service, so hard to confirm here that team name updated
@@ -285,17 +285,17 @@ func TestUpdateTeamName(t *testing.T) {
func TestFuzzyTeamCreate(t *testing.T) {
for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ {
- testName := "Name"
+ testDisplayName := "Name"
testEmail := "test@nowhere.com"
if i < len(utils.FUZZY_STRINGS_NAMES) {
- testName = utils.FUZZY_STRINGS_NAMES[i]
+ testDisplayName = utils.FUZZY_STRINGS_NAMES[i]
}
if i < len(utils.FUZZY_STRINGS_EMAILS) {
testEmail = utils.FUZZY_STRINGS_EMAILS[i]
}
- team := model.Team{Name: testName, Domain: "z-z-" + model.NewId() + "a", Email: testEmail, Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: testDisplayName, Name: "z-z-" + model.NewId() + "a", Email: testEmail, Type: model.TEAM_OPEN}
_, err := Client.CreateTeam(&team)
if err != nil {
@@ -307,22 +307,22 @@ func TestFuzzyTeamCreate(t *testing.T) {
func TestGetMyTeam(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
- Client.LoginByEmail(team.Domain, user.Email, user.Password)
+ Client.LoginByEmail(team.Name, user.Email, user.Password)
if result, err := Client.GetMyTeam(""); err != nil {
t.Fatal("Failed to get user")
} else {
- if result.Data.(*model.Team).Name != team.Name {
+ if result.Data.(*model.Team).DisplayName != team.DisplayName {
t.Fatal("team names did not match")
}
- if result.Data.(*model.Team).Domain != team.Domain {
+ if result.Data.(*model.Team).Name != team.Name {
t.Fatal("team domains did not match")
}
if result.Data.(*model.Team).Type != team.Type {
@@ -334,25 +334,25 @@ func TestGetMyTeam(t *testing.T) {
func TestUpdateValetFeature(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
data := make(map[string]string)
data["allow_valet"] = "true"
@@ -360,7 +360,7 @@ func TestUpdateValetFeature(t *testing.T) {
t.Fatal("Should have errored, not admin")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
data["allow_valet"] = ""
if _, err := Client.UpdateValetFeature(data); err == nil {
@@ -398,7 +398,7 @@ func TestUpdateValetFeature(t *testing.T) {
t.Fatal("Should have errored - allow valet property not updated")
}
- Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
+ Client.LoginByEmail(team2.Name, user3.Email, "pwd")
data["team_id"] = team.Id
data["allow_valet"] = "true"
diff --git a/api/templates/email_change_body.html b/api/templates/email_change_body.html
index dffe589cd..f8f3845e7 100644
--- a/api/templates/email_change_body.html
+++ b/api/templates/email_change_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your email</h2>
- <p>You updated your email for {{.Props.TeamName}} on {{ .TeamUrl }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
+ <p>You updated your email for {{.Props.TeamDisplayName}} on {{ .TeamURL }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/email_change_subject.html b/api/templates/email_change_subject.html
index 612dfcbe7..5690b148a 100644
--- a/api/templates/email_change_subject.html
+++ b/api/templates/email_change_subject.html
@@ -1 +1 @@
-{{define "email_change_subject"}}You updated your email for {{.Props.TeamName}} on {{ .Props.Domain }}{{end}}
+{{define "email_change_subject"}}You updated your email for {{.Props.TeamDisplayName}} on {{ .Props.Domain }}{{end}}
diff --git a/api/templates/find_teams_body.html b/api/templates/find_teams_body.html
index d8b582b8a..6eaaf56e0 100644
--- a/api/templates/find_teams_body.html
+++ b/api/templates/find_teams_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -42,7 +42,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/invite_body.html b/api/templates/invite_body.html
index 8be2ac0df..46189fae5 100644
--- a/api/templates/invite_body.html
+++ b/api/templates/invite_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2>
- <p>{{.Props.TeamName}} started using {{.SiteName}}.<br> The team {{.Props.SenderStatus}} <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamName}}</strong>.</p>
+ <p>{{.Props.TeamDisplayName}} started using {{.SiteName}}.<br> The team {{.Props.SenderStatus}} <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamDisplayName}}</strong>.</p>
<p style="margin: 20px 0 15px">
<a href="{{.Props.Link}}" 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;">Join Team</a>
</p>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/invite_subject.html b/api/templates/invite_subject.html
index 4be15e343..6a1e57dcc 100644
--- a/api/templates/invite_subject.html
+++ b/api/templates/invite_subject.html
@@ -1 +1 @@
-{{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamName }} Team on {{.SiteName}}{{end}}
+{{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamDisplayName }} Team on {{.SiteName}}{{end}}
diff --git a/api/templates/password_change_body.html b/api/templates/password_change_body.html
index f6499d46d..515c0a7d9 100644
--- a/api/templates/password_change_body.html
+++ b/api/templates/password_change_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your password</h2>
- <p>You updated your password for {{.Props.TeamName}} on {{ .TeamUrl }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
+ <p>You updated your password for {{.Props.TeamDisplayName}} on {{ .TeamURL }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/password_change_subject.html b/api/templates/password_change_subject.html
index 57422c692..55daefdb1 100644
--- a/api/templates/password_change_subject.html
+++ b/api/templates/password_change_subject.html
@@ -1 +1 @@
-{{define "password_change_subject"}}You updated your password for {{.Props.TeamName}} on {{ .SiteName }}{{end}}
+{{define "password_change_subject"}}You updated your password for {{.Props.TeamDisplayName}} on {{ .SiteName }}{{end}}
diff --git a/api/templates/post_body.html b/api/templates/post_body.html
index 069cdf1fb..41a29d020 100644
--- a/api/templates/post_body.html
+++ b/api/templates/post_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/post_subject.html b/api/templates/post_subject.html
index 32df31018..8ebc9550b 100644
--- a/api/templates/post_subject.html
+++ b/api/templates/post_subject.html
@@ -1 +1 @@
-{{define "post_subject"}}[{{.Props.TeamName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}} \ No newline at end of file
+{{define "post_subject"}}[{{.Props.TeamDisplayName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}}
diff --git a/api/templates/reset_body.html b/api/templates/reset_body.html
index 3a5d62ab4..af9f6b4e8 100644
--- a/api/templates/reset_body.html
+++ b/api/templates/reset_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You requested a password reset</h2>
- <p>To change your password, click below.<br>If you did not mean to reset your password, please ignore this email and your password will remain the same.</p>
+ <p>To change your password, click "Reset Password" below.<br>If you did not mean to reset your password, please ignore this email and your password will remain the same.</p>
<p style="margin: 20px 0 15px">
<a href="{{.Props.ResetUrl}}" 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;">Reset Password</a>
</p>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/signup_team_body.html b/api/templates/signup_team_body.html
index 6f8bbd92d..5a5ae4d47 100644
--- a/api/templates/signup_team_body.html
+++ b/api/templates/signup_team_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -40,7 +40,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/verify_body.html b/api/templates/verify_body.html
index 56d85572b..67ded9c20 100644
--- a/api/templates/verify_body.html
+++ b/api/templates/verify_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/verify_subject.html b/api/templates/verify_subject.html
index de0ef1d7a..a66150735 100644
--- a/api/templates/verify_subject.html
+++ b/api/templates/verify_subject.html
@@ -1 +1 @@
-{{define "verify_subject"}}[{{ .Props.TeamName }} {{ .SiteName }}] Email Verification{{end}} \ No newline at end of file
+{{define "verify_subject"}}[{{ .Props.TeamDisplayName }} {{ .SiteName }}] Email Verification{{end}}
diff --git a/api/templates/welcome_body.html b/api/templates/welcome_body.html
index fbc2e5551..7107bc2e0 100644
--- a/api/templates/welcome_body.html
+++ b/api/templates/welcome_body.html
@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
- <img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -17,8 +17,8 @@
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
- <h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamName}} team at {{.SiteName}}!</h2>
- <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamUrl}}">{{.SiteName}}</a>.</p>
+ <h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamDisplayName}} team at {{.SiteName}}!</h2>
+ <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamURL}}">{{.SiteName}}</a>.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/user.go b/api/user.go
index 483ae67b5..18c5e863a 100644
--- a/api/user.go
+++ b/api/user.go
@@ -181,14 +181,13 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
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")
-
+ //fireAndForgetWelcomeEmail(ruser.FirstName, 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 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)
+ FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.FirstName, ruser.Email, team.DisplayName, c.GetTeamURLFromTeam(team))
}
ruser.Sanitize(map[string]bool{})
@@ -196,19 +195,19 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
// 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)
+ PublishAndForget(message)
return ruser
}
}
-func fireAndForgetWelcomeEmail(name, email, teamName, link string) {
+func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link string) {
go func() {
subjectPage := NewServerTemplatePage("welcome_subject", link)
bodyPage := NewServerTemplatePage("welcome_body", link)
- bodyPage.Props["FullName"] = name
- bodyPage.Props["TeamName"] = teamName
+ bodyPage.Props["Nickname"] = name
+ bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -218,16 +217,16 @@ func fireAndForgetWelcomeEmail(name, email, teamName, link string) {
}()
}
-func FireAndForgetVerifyEmail(userId, name, email, teamName, teamUrl string) {
+func FireAndForgetVerifyEmail(userId, name, email, teamDisplayName, teamURL string) {
go func() {
- link := fmt.Sprintf("%s/verify?uid=%s&hid=%s", teamUrl, userId, model.HashPassword(userId))
+ link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s", teamURL, userId, model.HashPassword(userId))
- subjectPage := NewServerTemplatePage("verify_subject", teamUrl)
- subjectPage.Props["TeamName"] = teamName
- bodyPage := NewServerTemplatePage("verify_body", teamUrl)
- bodyPage.Props["FullName"] = name
- bodyPage.Props["TeamName"] = teamName
+ subjectPage := NewServerTemplatePage("verify_subject", teamURL)
+ subjectPage.Props["TeamDisplayName"] = teamDisplayName
+ bodyPage := NewServerTemplatePage("verify_body", teamURL)
+ bodyPage.Props["Nickname"] = name
+ bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["VerifyUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -251,10 +250,10 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
}
var team *model.Team
- if result.Data == nil && len(props["email"]) != 0 && len(props["domain"]) != 0 {
- extraInfo = props["email"] + " in " + props["domain"]
+ if result.Data == nil && len(props["email"]) != 0 && len(props["name"]) != 0 {
+ extraInfo = props["email"] + " in " + props["name"]
- if nr := <-Srv.Store.Team().GetByDomain(props["domain"]); nr.Err != nil {
+ if nr := <-Srv.Store.Team().GetByName(props["name"]); nr.Err != nil {
c.Err = nr.Err
return
} else {
@@ -293,7 +292,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !user.EmailVerified {
+ if !user.EmailVerified && !utils.Cfg.EmailSettings.ByPassEmail {
c.Err = model.NewAppError("login", "Login failed because email address has not been verified", extraInfo)
c.Err.StatusCode = http.StatusForbidden
return
@@ -517,7 +516,6 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) {
}
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
- w.Header().Set("Cache-Control", "max-age=120, public") // 2 mins
w.Write([]byte(model.UserMapToJson(profiles)))
return
}
@@ -729,6 +727,8 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ Srv.Store.User().UpdateLastPictureUpdate(c.Session.UserId)
+
c.LogAudit("")
}
@@ -756,7 +756,8 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
if tresult := <-Srv.Store.Team().Get(rusers[1].TeamId); tresult.Err != nil {
l4g.Error(tresult.Err.Message)
} else {
- fireAndForgetEmailChangeEmail(rusers[1].Email, tresult.Data.(*model.Team).Name, c.TeamUrl)
+ team := tresult.Data.(*model.Team)
+ fireAndForgetEmailChangeEmail(rusers[1].Email, team.DisplayName, c.GetTeamURLFromTeam(team))
}
}
@@ -827,7 +828,8 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
if tresult := <-tchan; tresult.Err != nil {
l4g.Error(tresult.Err.Message)
} else {
- fireAndForgetPasswordChangeEmail(user.Email, tresult.Data.(*model.Team).Name, c.TeamUrl, "using the settings menu")
+ team := tresult.Data.(*model.Team)
+ fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using the settings menu")
}
data := make(map[string]string)
@@ -986,14 +988,14 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- domain := props["domain"]
- if len(domain) == 0 {
- c.SetInvalidParam("sendPasswordReset", "domain")
+ name := props["name"]
+ if len(name) == 0 {
+ c.SetInvalidParam("sendPasswordReset", "name")
return
}
var team *model.Team
- if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
+ if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -1015,10 +1017,10 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.ResetSalt))
- link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash))
+ link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash))
- subjectPage := NewServerTemplatePage("reset_subject", c.TeamUrl)
- bodyPage := NewServerTemplatePage("reset_body", c.TeamUrl)
+ subjectPage := NewServerTemplatePage("reset_subject", c.GetTeamURLFromTeam(team))
+ bodyPage := NewServerTemplatePage("reset_body", c.GetTeamURLFromTeam(team))
bodyPage.Props["ResetUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -1060,16 +1062,16 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- domain := props["domain"]
- if len(domain) == 0 {
- c.SetInvalidParam("resetPassword", "domain")
+ name := props["name"]
+ if len(name) == 0 {
+ c.SetInvalidParam("resetPassword", "name")
return
}
c.LogAuditWithUserId(userId, "attempt")
var team *model.Team
- if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
+ if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -1108,19 +1110,19 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(userId, "success")
}
- fireAndForgetPasswordChangeEmail(user.Email, team.Name, c.TeamUrl, "using a reset password link")
+ fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using a reset password link")
props["new_password"] = ""
w.Write([]byte(model.MapToJson(props)))
}
-func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) {
+func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, method string) {
go func() {
- subjectPage := NewServerTemplatePage("password_change_subject", teamUrl)
- subjectPage.Props["TeamName"] = teamName
- bodyPage := NewServerTemplatePage("password_change_body", teamUrl)
- bodyPage.Props["TeamName"] = teamName
+ subjectPage := NewServerTemplatePage("password_change_subject", teamURL)
+ subjectPage.Props["TeamDisplayName"] = teamDisplayName
+ bodyPage := NewServerTemplatePage("password_change_body", teamURL)
+ bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["Method"] = method
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -1130,13 +1132,13 @@ func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) {
}()
}
-func fireAndForgetEmailChangeEmail(email, teamName, teamUrl string) {
+func fireAndForgetEmailChangeEmail(email, teamDisplayName, teamURL string) {
go func() {
- subjectPage := NewServerTemplatePage("email_change_subject", teamUrl)
- subjectPage.Props["TeamName"] = teamName
- bodyPage := NewServerTemplatePage("email_change_body", teamUrl)
- bodyPage.Props["TeamName"] = teamName
+ subjectPage := NewServerTemplatePage("email_change_subject", teamURL)
+ subjectPage.Props["TeamDisplayName"] = teamDisplayName
+ bodyPage := NewServerTemplatePage("email_change_body", teamURL)
+ bodyPage.Props["TeamDisplayName"] = teamDisplayName
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send update password email successfully err=%v", err)
diff --git a/api/user_test.go b/api/user_test.go
index f2b69aba4..e7a3f153a 100644
--- a/api/user_test.go
+++ b/api/user_test.go
@@ -25,18 +25,18 @@ import (
func TestCreateUser(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello"}
ruser, err := Client.CreateUser(&user, "")
if err != nil {
t.Fatal(err)
}
- if ruser.Data.(*model.User).FullName != user.FullName {
- t.Fatal("full name didn't match")
+ if ruser.Data.(*model.User).Nickname != user.Nickname {
+ t.Fatal("nickname didn't match")
}
if ruser.Data.(*model.User).Password != "" {
@@ -68,7 +68,7 @@ func TestCreateUser(t *testing.T) {
}
}
- user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello", Username: model.BOT_USERNAME}
+ user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello", Username: model.BOT_USERNAME}
if _, err := Client.CreateUser(&user2, ""); err == nil {
t.Fatal("Should have failed using reserved bot name")
@@ -82,10 +82,10 @@ func TestCreateUser(t *testing.T) {
func TestCreateUserAllowedDomains(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello"}
_, err := Client.CreateUser(&user, "")
if err == nil {
@@ -102,10 +102,10 @@ func TestCreateUserAllowedDomains(t *testing.T) {
func TestLogin(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
@@ -117,7 +117,7 @@ func TestLogin(t *testing.T) {
}
}
- if result, err := Client.LoginByEmail(team.Domain, user.Email, user.Password); err != nil {
+ if result, err := Client.LoginByEmail(team.Name, user.Email, user.Password); err != nil {
t.Fatal(err)
} else {
if result.Data.(*model.User).Email != user.Email {
@@ -125,11 +125,11 @@ func TestLogin(t *testing.T) {
}
}
- if _, err := Client.LoginByEmail(team.Domain, user.Email, user.Password+"invalid"); err == nil {
+ if _, err := Client.LoginByEmail(team.Name, user.Email, user.Password+"invalid"); err == nil {
t.Fatal("Invalid Password")
}
- if _, err := Client.LoginByEmail(team.Domain, "", user.Password); err == nil {
+ if _, err := Client.LoginByEmail(team.Name, "", user.Password); err == nil {
t.Fatal("should have failed")
}
@@ -142,10 +142,10 @@ func TestLogin(t *testing.T) {
Client.AuthToken = ""
- team2 := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE}
+ team2 := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE}
rteam2 := Client.Must(Client.CreateTeam(&team2))
- user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
if _, err := Client.CreateUserFromSignup(&user2, "junk", "1231312"); err == nil {
t.Fatal("Should have errored, signed up without hashed email")
@@ -154,14 +154,14 @@ func TestLogin(t *testing.T) {
props := make(map[string]string)
props["email"] = user2.Email
props["id"] = rteam2.Data.(*model.Team).Id
- props["name"] = rteam2.Data.(*model.Team).Name
+ props["display_name"] = rteam2.Data.(*model.Team).DisplayName
props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash)
- if _, err := Client.LoginByEmail(team2.Domain, ruser2.Data.(*model.User).Email, user2.Password); err != nil {
+ if _, err := Client.LoginByEmail(team2.Name, ruser2.Data.(*model.User).Email, user2.Password); err != nil {
t.Fatal("From verfied hash")
}
@@ -171,16 +171,16 @@ func TestLogin(t *testing.T) {
func TestSessions(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
deviceId := model.NewId()
- Client.LoginByEmailWithDevice(team.Domain, user.Email, user.Password, deviceId)
- Client.LoginByEmail(team.Domain, user.Email, user.Password)
+ Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId)
+ Client.LoginByEmail(team.Name, user.Email, user.Password)
r1, err := Client.GetSessions(ruser.Id)
if err != nil {
@@ -228,18 +228,18 @@ func TestSessions(t *testing.T) {
func TestGetUser(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
- user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser2, _ := Client.CreateUser(&user2, "")
store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id))
- Client.LoginByEmail(team.Domain, user.Email, user.Password)
+ Client.LoginByEmail(team.Name, user.Email, user.Password)
rId := ruser.Data.(*model.User).Id
if result, err := Client.GetUser(rId, ""); err != nil {
@@ -299,16 +299,16 @@ func TestGetUser(t *testing.T) {
func TestGetAudits(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
time.Sleep(100 * time.Millisecond)
- Client.LoginByEmail(team.Domain, user.Email, user.Password)
+ Client.LoginByEmail(team.Name, user.Email, user.Password)
time.Sleep(100 * time.Millisecond)
@@ -352,14 +352,14 @@ func TestUserCreateImage(t *testing.T) {
t.Fatal("Failed to create correct color")
}
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(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.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
Client.DoGet("/users/"+user.Id+"/image", "", "")
@@ -368,10 +368,10 @@ func TestUserCreateImage(t *testing.T) {
func TestUserUploadProfileImage(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(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))
@@ -384,7 +384,7 @@ func TestUserUploadProfileImage(t *testing.T) {
t.Fatal("Should have errored")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil {
t.Fatal("Should have errored")
@@ -465,12 +465,12 @@ func TestUserUploadProfileImage(t *testing.T) {
func TestUserUpdate(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
time1 := model.GetMillis()
- user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -478,7 +478,7 @@ func TestUserUpdate(t *testing.T) {
t.Fatal("Should have errored")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
time.Sleep(100 * time.Millisecond)
@@ -486,7 +486,7 @@ func TestUserUpdate(t *testing.T) {
time.Sleep(100 * time.Millisecond)
- user.FullName = "Jim Jimmy"
+ user.Nickname = "Jim Jimmy"
user.TeamId = "12345678901234567890123456"
user.LastActivityAt = time2
user.LastPingAt = time2
@@ -496,8 +496,8 @@ func TestUserUpdate(t *testing.T) {
if result, err := Client.UpdateUser(user); err != nil {
t.Fatal(err)
} else {
- if result.Data.(*model.User).FullName != "Jim Jimmy" {
- t.Fatal("FullName did not update properly")
+ if result.Data.(*model.User).Nickname != "Jim Jimmy" {
+ t.Fatal("Nickname did not update properly")
}
if result.Data.(*model.User).TeamId != team.Id {
t.Fatal("TeamId should not have updated")
@@ -526,13 +526,13 @@ func TestUserUpdate(t *testing.T) {
t.Fatal("Should have errored")
}
- user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
- user.FullName = "Tim Timmy"
+ user.Nickname = "Tim Timmy"
if _, err := Client.UpdateUser(user); err == nil {
t.Fatal("Should have errored")
@@ -542,10 +542,10 @@ func TestUserUpdate(t *testing.T) {
func TestUserUpdatePassword(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(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))
@@ -553,7 +553,7 @@ func TestUserUpdatePassword(t *testing.T) {
t.Fatal("Should have errored")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
if _, err := Client.UpdateUserPassword("123", "pwd", "newpwd"); err == nil {
t.Fatal("Should have errored")
@@ -584,14 +584,14 @@ func TestUserUpdatePassword(t *testing.T) {
t.Fatal("LastPasswordUpdate should have changed")
}
- if _, err := Client.LoginByEmail(team.Domain, user.Email, "newpwd"); err != nil {
+ if _, err := Client.LoginByEmail(team.Name, user.Email, "newpwd"); err != nil {
t.Fatal(err)
}
- user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil {
t.Fatal("Should have errored")
@@ -601,14 +601,14 @@ func TestUserUpdatePassword(t *testing.T) {
func TestUserUpdateRoles(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(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))
@@ -620,20 +620,20 @@ func TestUserUpdateRoles(t *testing.T) {
t.Fatal("Should have errored, not logged in")
}
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
if _, err := Client.UpdateUserRoles(data); err == nil {
t.Fatal("Should have errored, not admin")
}
- team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
+ user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.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.Domain, user3.Email, "pwd")
+ Client.LoginByEmail(team2.Name, user3.Email, "pwd")
data["user_id"] = user2.Id
@@ -641,7 +641,7 @@ func TestUserUpdateRoles(t *testing.T) {
t.Fatal("Should have errored, wrong team")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
data["user_id"] = "junk"
data["new_roles"] = "admin"
@@ -670,14 +670,14 @@ func TestUserUpdateRoles(t *testing.T) {
func TestUserUpdateActive(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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", FullName: "Corey Hulen", Password: "pwd"}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(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))
@@ -685,26 +685,26 @@ func TestUserUpdateActive(t *testing.T) {
t.Fatal("Should have errored, not logged in")
}
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
if _, err := Client.UpdateActive(user.Id, false); err == nil {
t.Fatal("Should have errored, not admin")
}
- team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"}
+ user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.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.Domain, user3.Email, "pwd")
+ Client.LoginByEmail(team2.Name, user3.Email, "pwd")
if _, err := Client.UpdateActive(user.Id, false); err == nil {
t.Fatal("Should have errored, wrong team")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
if _, err := Client.UpdateActive("junk", false); err == nil {
t.Fatal("Should have errored, bad id")
@@ -734,16 +734,16 @@ func TestUserUpdateActive(t *testing.T) {
func TestSendPasswordReset(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(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))
data := make(map[string]string)
data["email"] = user.Email
- data["domain"] = team.Domain
+ data["name"] = team.Name
if _, err := Client.SendPasswordReset(data); err != nil {
t.Fatal(err)
@@ -760,24 +760,24 @@ func TestSendPasswordReset(t *testing.T) {
}
data["email"] = user.Email
- data["domain"] = ""
+ data["name"] = ""
if _, err := Client.SendPasswordReset(data); err == nil {
- t.Fatal("Should have errored - no domain")
+ t.Fatal("Should have errored - no name")
}
- data["domain"] = "junk"
+ data["name"] = "junk"
if _, err := Client.SendPasswordReset(data); err == nil {
- t.Fatal("Should have errored - bad domain")
+ t.Fatal("Should have errored - bad name")
}
}
func TestResetPassword(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(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))
@@ -788,7 +788,7 @@ func TestResetPassword(t *testing.T) {
props["time"] = fmt.Sprintf("%v", model.GetMillis())
data["data"] = model.MapToJson(props)
data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.ServiceSettings.ResetSalt))
- data["domain"] = team.Domain
+ data["name"] = team.Name
if _, err := Client.ResetPassword(data); err != nil {
t.Fatal(err)
@@ -846,10 +846,10 @@ func TestResetPassword(t *testing.T) {
t.Fatal("Should have errored - bad domain")
}
- team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test2@nowhere.com", Type: model.TEAM_OPEN}
+ team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test2@nowhere.com", Type: model.TEAM_OPEN}
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
- data["domain"] = team2.Domain
+ data["domain"] = team2.Name
if _, err := Client.ResetPassword(data); err == nil {
t.Fatal("Should have errored - domain team doesn't match user team")
}
@@ -858,10 +858,10 @@ func TestResetPassword(t *testing.T) {
func TestUserUpdateNotify(t *testing.T) {
Setup()
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd", Roles: ""}
+ user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd", Roles: ""}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -875,7 +875,7 @@ func TestUserUpdateNotify(t *testing.T) {
t.Fatal("Should have errored - not logged in")
}
- Client.LoginByEmail(team.Domain, user.Email, "pwd")
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
if result, err := Client.UpdateUserNotify(data); err != nil {
t.Fatal(err)
@@ -926,7 +926,7 @@ func TestUserUpdateNotify(t *testing.T) {
func TestFuzzyUserCreate(t *testing.T) {
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ {
@@ -940,7 +940,7 @@ func TestFuzzyUserCreate(t *testing.T) {
testEmail = utils.FUZZY_STRINGS_EMAILS[i]
}
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + testEmail, FullName: testName, Password: "hello"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + testEmail, Nickname: testName, Password: "hello"}
_, err := Client.CreateUser(&user, "")
if err != nil {
@@ -952,14 +952,14 @@ func TestFuzzyUserCreate(t *testing.T) {
func TestStatuses(t *testing.T) {
Setup()
- team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
rteam, _ := Client.CreateTeam(&team)
- user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
- Client.LoginByEmail(team.Domain, user.Email, user.Password)
+ Client.LoginByEmail(team.Name, user.Email, user.Password)
r1, err := Client.GetStatuses()
if err != nil {
diff --git a/api/web_conn.go b/api/web_conn.go
index 751f6f407..0990de8ef 100644
--- a/api/web_conn.go
+++ b/api/web_conn.go
@@ -70,7 +70,7 @@ func (c *WebConn) readPump() {
} else {
msg.TeamId = c.TeamId
msg.UserId = c.UserId
- store.PublishAndForget(&msg)
+ PublishAndForget(&msg)
}
}
}
diff --git a/api/web_hub.go b/api/web_hub.go
index bf5fbb321..c7be19cac 100644
--- a/api/web_hub.go
+++ b/api/web_hub.go
@@ -5,12 +5,14 @@ package api
import (
l4g "code.google.com/p/log4go"
+ "github.com/mattermost/platform/model"
)
type Hub struct {
teamHubs map[string]*TeamHub
register chan *WebConn
unregister chan *WebConn
+ broadcast chan *model.Message
stop chan string
}
@@ -18,9 +20,16 @@ var hub = &Hub{
register: make(chan *WebConn),
unregister: make(chan *WebConn),
teamHubs: make(map[string]*TeamHub),
+ broadcast: make(chan *model.Message),
stop: make(chan string),
}
+func PublishAndForget(message *model.Message) {
+ go func() {
+ hub.Broadcast(message)
+ }()
+}
+
func (h *Hub) Register(webConn *WebConn) {
h.register <- webConn
}
@@ -29,8 +38,14 @@ func (h *Hub) Unregister(webConn *WebConn) {
h.unregister <- webConn
}
-func (h *Hub) Stop(teamId string) {
- h.stop <- teamId
+func (h *Hub) Broadcast(message *model.Message) {
+ if message != nil {
+ h.broadcast <- message
+ }
+}
+
+func (h *Hub) Stop() {
+ h.stop <- "all"
}
func (h *Hub) Start() {
@@ -53,18 +68,17 @@ func (h *Hub) Start() {
if nh, ok := h.teamHubs[c.TeamId]; ok {
nh.Unregister(c)
}
-
+ case msg := <-h.broadcast:
+ nh := h.teamHubs[msg.TeamId]
+ if nh != nil {
+ nh.broadcast <- msg
+ }
case s := <-h.stop:
- if len(s) == 0 {
- l4g.Debug("stopping all connections")
- for _, v := range h.teamHubs {
- v.Stop()
- }
- return
- } else if nh, ok := h.teamHubs[s]; ok {
- delete(h.teamHubs, s)
- nh.Stop()
+ l4g.Debug("stopping %v connections", s)
+ for _, v := range h.teamHubs {
+ v.Stop()
}
+ return
}
}
}()
diff --git a/api/web_socket_test.go b/api/web_socket_test.go
index 4cb49220f..161274ff7 100644
--- a/api/web_socket_test.go
+++ b/api/web_socket_test.go
@@ -17,13 +17,13 @@ func TestSocket(t *testing.T) {
Setup()
url := "ws://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1/websocket"
- team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ 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)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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))
- Client.LoginByEmail(team.Domain, user1.Email, "pwd")
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -39,10 +39,10 @@ func TestSocket(t *testing.T) {
t.Fatal(err)
}
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ 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.Domain, user2.Email, "pwd")
+ Client.LoginByEmail(team.Name, user2.Email, "pwd")
header2 := http.Header{}
header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)
@@ -115,9 +115,6 @@ func TestSocket(t *testing.T) {
}()
time.Sleep(2 * time.Second)
-
- hub.Stop(team.Id)
-
}
func TestZZWebSocketTearDown(t *testing.T) {
diff --git a/api/web_team_hub.go b/api/web_team_hub.go
index 7c7981e76..7a63b84d1 100644
--- a/api/web_team_hub.go
+++ b/api/web_team_hub.go
@@ -6,8 +6,6 @@ package api
import (
l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/model"
- "github.com/mattermost/platform/store"
- "strings"
)
type TeamHub struct {
@@ -43,43 +41,6 @@ func (h *TeamHub) Stop() {
}
func (h *TeamHub) Start() {
-
- pubsub := store.RedisClient().PubSub()
-
- go func() {
- defer func() {
- l4g.Debug("redis reader finished for teamId=%v", h.teamId)
- hub.Stop(h.teamId)
- }()
-
- l4g.Debug("redis reader starting for teamId=%v", h.teamId)
-
- err := pubsub.Subscribe(h.teamId)
- if err != nil {
- l4g.Error("Error while subscribing to redis %v %v", h.teamId, err)
- return
- }
-
- for {
- if payload, err := pubsub.ReceiveTimeout(REDIS_WAIT); err != nil {
- if strings.Contains(err.Error(), "i/o timeout") {
- if len(h.connections) == 0 {
- l4g.Debug("No active connections so sending stop %v", h.teamId)
- return
- }
- } else {
- return
- }
- } else {
- msg := store.GetMessageFromPayload(payload)
- if msg != nil {
- h.broadcast <- msg
- }
- }
- }
-
- }()
-
go func() {
for {
select {
@@ -110,7 +71,6 @@ func (h *TeamHub) Start() {
webCon.WebSocket.Close()
}
- pubsub.Close()
return
}
}
diff --git a/config/config.json b/config/config.json
index b0a019e8d..e6025ef51 100644
--- a/config/config.json
+++ b/config/config.json
@@ -9,7 +9,6 @@
},
"ServiceSettings": {
"SiteName": "Mattermost",
- "Domain": "xxxxxxmustbefilledin.com",
"Mode" : "dev",
"AllowTesting" : false,
"UseSSL": false,
@@ -31,19 +30,11 @@
"Trace": false,
"AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS"
},
- "RedisSettings": {
- "DataSource": "dockerhost:6379",
- "MaxOpenConns": 1000
- },
"AWSSettings": {
"S3AccessKeyId": "",
"S3SecretAccessKey": "",
"S3Bucket": "",
- "S3Region": "",
- "Route53AccessKeyId": "",
- "Route53SecretAccessKey": "",
- "Route53ZoneId": "",
- "Route53Region": ""
+ "S3Region": ""
},
"ImageSettings": {
"ThumbnailWidth": 200,
@@ -54,11 +45,12 @@
"ProfileHeight": 128
},
"EmailSettings": {
+ "ByPassEmail" : true,
"SMTPUsername": "",
"SMTPPassword": "",
"SMTPServer": "",
"UseTLS": false,
- "FeedbackEmail": "feedback@xxxxxxmustbefilledin.com",
+ "FeedbackEmail": "",
"FeedbackName": "",
"ApplePushServer": "",
"ApplePushCertPublic": "",
diff --git a/config/config_docker.json b/config/config_docker.json
index 85f0d9c73..9be837072 100644
--- a/config/config_docker.json
+++ b/config/config_docker.json
@@ -9,7 +9,6 @@
},
"ServiceSettings": {
"SiteName": "Mattermost",
- "Domain": "",
"Mode" : "dev",
"AllowTesting" : false,
"UseSSL": false,
@@ -31,19 +30,11 @@
"Trace": false,
"AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS"
},
- "RedisSettings": {
- "DataSource": "localhost:6379",
- "MaxOpenConns": 1000
- },
"AWSSettings": {
"S3AccessKeyId": "",
"S3SecretAccessKey": "",
"S3Bucket": "",
- "S3Region": "",
- "Route53AccessKeyId": "",
- "Route53SecretAccessKey": "",
- "Route53ZoneId": "",
- "Route53Region": ""
+ "S3Region": ""
},
"ImageSettings": {
"ThumbnailWidth": 200,
@@ -54,9 +45,10 @@
"ProfileHeight": 128
},
"EmailSettings": {
+ "ByPassEmail" : true,
"SMTPUsername": "",
"SMTPPassword": "",
- "SMTPServer": "localhost:25",
+ "SMTPServer": "",
"UseTLS": false,
"FeedbackEmail": "",
"FeedbackName": "",
diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go
index 929f7ab5d..f7408b814 100644
--- a/manualtesting/manual_testing.go
+++ b/manualtesting/manual_testing.go
@@ -57,17 +57,17 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
// Check for username parameter and create a user if present
username, ok1 := params["username"]
- teamname, ok2 := params["teamname"]
+ teamDisplayName, ok2 := params["teamname"]
var teamID string
var userID string
if ok1 && ok2 {
l4g.Info("Creating user and team")
// Create team for testing
team := &model.Team{
- Name: teamname[0],
- Domain: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
- Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
- Type: model.TEAM_OPEN,
+ DisplayName: teamDisplayName[0],
+ Name: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
+ Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
+ Type: model.TEAM_OPEN,
}
if result := <-api.Srv.Store.Team().Save(team); result.Err != nil {
@@ -90,7 +90,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
user := &model.User{
TeamId: teamID,
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
- FullName: username[0],
+ Nickname: username[0],
Password: api.USER_PASSWORD}
result, err := client.CreateUser(user, "")
diff --git a/model/channel_extra.go b/model/channel_extra.go
index a5c9acf71..3a918b524 100644
--- a/model/channel_extra.go
+++ b/model/channel_extra.go
@@ -10,7 +10,7 @@ import (
type ExtraMember struct {
Id string `json:"id"`
- FullName string `json:"full_name"`
+ Nickname string `json:"nickname"`
Email string `json:"email"`
Roles string `json:"roles"`
Username string `json:"username"`
@@ -20,9 +20,6 @@ func (o *ExtraMember) Sanitize(options map[string]bool) {
if len(options) == 0 || !options["email"] {
o.Email = ""
}
- if len(options) == 0 || !options["fullname"] {
- o.FullName = ""
- }
}
type ChannelExtra struct {
diff --git a/model/client.go b/model/client.go
index e22f2308e..ad1e4b5f1 100644
--- a/model/client.go
+++ b/model/client.go
@@ -100,10 +100,10 @@ func (c *Client) Must(result *Result, err *AppError) *Result {
return result
}
-func (c *Client) SignupTeam(email string, name string) (*Result, *AppError) {
+func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppError) {
m := make(map[string]string)
m["email"] = email
- m["name"] = name
+ m["display_name"] = displayName
if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil {
return nil, err
} else {
@@ -130,11 +130,11 @@ func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
}
}
-func (c *Client) FindTeamByDomain(domain string, allServers bool) (*Result, *AppError) {
+func (c *Client) FindTeamByName(name string, allServers bool) (*Result, *AppError) {
m := make(map[string]string)
- m["domain"] = domain
+ m["name"] = name
m["all"] = fmt.Sprintf("%v", allServers)
- if r, err := c.DoPost("/teams/find_team_by_domain", MapToJson(m)); err != nil {
+ if r, err := c.DoPost("/teams/find_team_by_name", MapToJson(m)); err != nil {
return nil, err
} else {
val := false
@@ -179,7 +179,7 @@ func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
}
}
-func (c *Client) UpdateTeamName(data map[string]string) (*Result, *AppError) {
+func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppError) {
if r, err := c.DoPost("/teams/update_name", MapToJson(data)); err != nil {
return nil, err
} else {
@@ -249,17 +249,17 @@ func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
return c.login(m)
}
-func (c *Client) LoginByEmail(domain string, email string, password string) (*Result, *AppError) {
+func (c *Client) LoginByEmail(name string, email string, password string) (*Result, *AppError) {
m := make(map[string]string)
- m["domain"] = domain
+ m["name"] = name
m["email"] = email
m["password"] = password
return c.login(m)
}
-func (c *Client) LoginByEmailWithDevice(domain string, email string, password string, deviceId string) (*Result, *AppError) {
+func (c *Client) LoginByEmailWithDevice(name string, email string, password string, deviceId string) (*Result, *AppError) {
m := make(map[string]string)
- m["domain"] = domain
+ m["name"] = name
m["email"] = email
m["password"] = password
m["device_id"] = deviceId
diff --git a/model/team.go b/model/team.go
index 5c66f3b1f..e7005625b 100644
--- a/model/team.go
+++ b/model/team.go
@@ -18,8 +18,8 @@ type Team struct {
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
+ DisplayName string `json:"display_name"`
Name string `json:"name"`
- Domain string `json:"domain"`
Email string `json:"email"`
Type string `json:"type"`
CompanyName string `json:"company_name"`
@@ -97,20 +97,20 @@ func (o *Team) IsValid() *AppError {
return NewAppError("Team.IsValid", "Invalid email", "id="+o.Id)
}
- if len(o.Name) > 64 {
+ if len(o.DisplayName) > 64 {
return NewAppError("Team.IsValid", "Invalid name", "id="+o.Id)
}
- if len(o.Domain) > 64 {
- return NewAppError("Team.IsValid", "Invalid domain", "id="+o.Id)
+ if len(o.Name) > 64 {
+ return NewAppError("Team.IsValid", "Invalid URL Identifier", "id="+o.Id)
}
- if IsReservedDomain(o.Domain) {
+ if IsReservedTeamName(o.Name) {
return NewAppError("Team.IsValid", "This URL is unavailable. Please try another.", "id="+o.Id)
}
- if !IsValidDomain(o.Domain) {
- return NewAppError("Team.IsValid", "Domain must be 4 or more lowercase alphanumeric characters", "id="+o.Id)
+ if !IsValidTeamName(o.Name) {
+ return NewAppError("Team.IsValid", "Name must be 4 or more lowercase alphanumeric characters", "id="+o.Id)
}
if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {
diff --git a/model/team_signup_test.go b/model/team_signup_test.go
index f3f74470b..eb2fbc69f 100644
--- a/model/team_signup_test.go
+++ b/model/team_signup_test.go
@@ -9,7 +9,7 @@ import (
)
func TestTeamSignupJson(t *testing.T) {
- team := Team{Id: NewId(), Name: NewId()}
+ team := Team{Id: NewId(), DisplayName: NewId()}
o := TeamSignup{Team: team, Data: "data"}
json := o.ToJson()
ro := TeamSignupFromJson(strings.NewReader(json))
diff --git a/model/team_test.go b/model/team_test.go
index 6261ed6bf..071b1a2e9 100644
--- a/model/team_test.go
+++ b/model/team_test.go
@@ -9,7 +9,7 @@ import (
)
func TestTeamJson(t *testing.T) {
- o := Team{Id: NewId(), Name: NewId()}
+ o := Team{Id: NewId(), DisplayName: NewId()}
json := o.ToJson()
ro := TeamFromJson(strings.NewReader(json))
@@ -46,18 +46,18 @@ func TestTeamIsValid(t *testing.T) {
}
o.Email = "corey@hulen.com"
- o.Name = strings.Repeat("01234567890", 20)
+ o.DisplayName = strings.Repeat("01234567890", 20)
if err := o.IsValid(); err == nil {
t.Fatal("should be invalid")
}
- o.Name = "1234"
- o.Domain = "ZZZZZZZ"
+ o.DisplayName = "1234"
+ o.Name = "ZZZZZZZ"
if err := o.IsValid(); err == nil {
t.Fatal("should be invalid")
}
- o.Domain = "zzzzz"
+ o.Name = "zzzzz"
o.Type = TEAM_OPEN
if err := o.IsValid(); err != nil {
t.Fatal(err)
@@ -65,12 +65,12 @@ func TestTeamIsValid(t *testing.T) {
}
func TestTeamPreSave(t *testing.T) {
- o := Team{Name: "test"}
+ o := Team{DisplayName: "test"}
o.PreSave()
o.Etag()
}
func TestTeamPreUpdate(t *testing.T) {
- o := Team{Name: "test"}
+ o := Team{DisplayName: "test"}
o.PreUpdate()
}
diff --git a/model/user.go b/model/user.go
index b94ceb899..727165b8c 100644
--- a/model/user.go
+++ b/model/user.go
@@ -37,7 +37,9 @@ type User struct {
AuthData string `json:"auth_data"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
- FullName string `json:"full_name"`
+ Nickname string `json:"nickname"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"`
Roles string `json:"roles"`
LastActivityAt int64 `json:"last_activity_at"`
LastPingAt int64 `json:"last_ping_at"`
@@ -45,6 +47,7 @@ type User struct {
Props StringMap `json:"props"`
NotifyProps StringMap `json:"notify_props"`
LastPasswordUpdate int64 `json:"last_password_update"`
+ LastPictureUpdate int64 `json:"last_picture_update"`
}
// IsValid validates the user and returns an error if it isn't configured
@@ -81,8 +84,16 @@ func (u *User) IsValid() *AppError {
return NewAppError("User.IsValid", "Invalid email", "user_id="+u.Id)
}
- if len(u.FullName) > 64 {
- return NewAppError("User.IsValid", "Invalid full name", "user_id="+u.Id)
+ if len(u.Nickname) > 64 {
+ return NewAppError("User.IsValid", "Invalid nickname", "user_id="+u.Id)
+ }
+
+ if len(u.FirstName) > 64 {
+ return NewAppError("User.IsValid", "Invalid first name", "user_id="+u.Id)
+ }
+
+ if len(u.LastName) > 64 {
+ return NewAppError("User.IsValid", "Invalid last name", "user_id="+u.Id)
}
return nil
@@ -151,7 +162,7 @@ func (u *User) SetDefaultNotifications() {
u.NotifyProps["first_name"] = "false"
u.NotifyProps["all"] = "true"
u.NotifyProps["channel"] = "true"
- splitName := strings.Split(u.FullName, " ")
+ splitName := strings.Split(u.Nickname, " ")
if len(splitName) > 0 && splitName[0] != "" {
u.NotifyProps["first_name"] = "true"
u.NotifyProps["mention_keys"] += "," + splitName[0]
@@ -190,7 +201,8 @@ func (u *User) Sanitize(options map[string]bool) {
u.Email = ""
}
if len(options) != 0 && !options["fullname"] {
- u.FullName = ""
+ u.FirstName = ""
+ u.LastName = ""
}
if len(options) != 0 && !options["skypeid"] {
// TODO - fill in when SkypeId is added to user model
@@ -225,6 +237,28 @@ func (u *User) AddNotifyProp(key string, value string) {
u.NotifyProps[key] = value
}
+func (u *User) GetFullName() string {
+ if u.FirstName != "" && u.LastName != "" {
+ return u.FirstName + " " + u.LastName
+ } else if u.FirstName != "" {
+ return u.FirstName
+ } else if u.LastName != "" {
+ return u.LastName
+ } else {
+ return ""
+ }
+}
+
+func (u *User) GetDisplayName() string {
+ if u.Nickname != "" {
+ return u.Nickname
+ } else if fullName := u.GetFullName(); fullName != "" {
+ return fullName
+ } else {
+ return u.Username
+ }
+}
+
// UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User {
decoder := json.NewDecoder(data)
diff --git a/model/user_test.go b/model/user_test.go
index df9ac19c2..a48c3f2e7 100644
--- a/model/user_test.go
+++ b/model/user_test.go
@@ -80,13 +80,73 @@ func TestUserIsValid(t *testing.T) {
}
user.Email = "test@nowhere.com"
- user.FullName = strings.Repeat("01234567890", 20)
+ user.Nickname = strings.Repeat("01234567890", 20)
if err := user.IsValid(); err == nil {
t.Fatal()
}
- user.FullName = ""
+ user.Nickname = ""
if err := user.IsValid(); err != nil {
t.Fatal(err)
}
+
+ user.FirstName = ""
+ user.LastName = ""
+ if err := user.IsValid(); err != nil {
+ t.Fatal(err)
+ }
+
+ user.FirstName = strings.Repeat("01234567890", 20)
+ if err := user.IsValid(); err == nil {
+ t.Fatal(err)
+ }
+
+ user.FirstName = ""
+ user.LastName = strings.Repeat("01234567890", 20)
+ if err := user.IsValid(); err == nil {
+ t.Fatal(err)
+ }
+}
+
+func TestUserGetFullName(t *testing.T) {
+ user := User{}
+
+ if fullName := user.GetFullName(); fullName != "" {
+ t.Fatal("Full name should be blank")
+ }
+
+ user.FirstName = "first"
+ if fullName := user.GetFullName(); fullName != "first" {
+ t.Fatal("Full name should be first name")
+ }
+
+ user.FirstName = ""
+ user.LastName = "last"
+ if fullName := user.GetFullName(); fullName != "last" {
+ t.Fatal("Full name should be last name")
+ }
+
+ user.FirstName = "first"
+ if fullName := user.GetFullName(); fullName != "first last" {
+ t.Fatal("Full name should be first name and last name")
+ }
+}
+
+func TestUserGetDisplayName(t *testing.T) {
+ user := User{Username: "user"}
+
+ if displayName := user.GetDisplayName(); displayName != "user" {
+ t.Fatal("Display name should be username")
+ }
+
+ user.FirstName = "first"
+ user.LastName = "last"
+ if displayName := user.GetDisplayName(); displayName != "first last" {
+ t.Fatal("Display name should be full name")
+ }
+
+ user.Nickname = "nickname"
+ if displayName := user.GetDisplayName(); displayName != "nickname" {
+ t.Fatal("Display name should be nickname")
+ }
}
diff --git a/model/utils.go b/model/utils.go
index 465901a09..38592b984 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -148,7 +148,7 @@ func IsValidEmail(email string) bool {
return false
}
-var reservedDomains = []string{
+var reservedName = []string{
"www",
"web",
"admin",
@@ -168,10 +168,10 @@ var reservedDomains = []string{
"api",
}
-func IsReservedDomain(s string) bool {
+func IsReservedTeamName(s string) bool {
s = strings.ToLower(s)
- for _, value := range reservedDomains {
+ for _, value := range reservedName {
if strings.Index(s, value) == 0 {
return true
}
@@ -180,7 +180,7 @@ func IsReservedDomain(s string) bool {
return false
}
-func IsValidDomain(s string) bool {
+func IsValidTeamName(s string) bool {
if !IsValidAlphaNum(s) {
return false
diff --git a/model/utils_test.go b/model/utils_test.go
index a9721042d..dbb448882 100644
--- a/model/utils_test.go
+++ b/model/utils_test.go
@@ -83,9 +83,9 @@ var domains = []struct {
{"test", true},
}
-func TestValidDomain(t *testing.T) {
+func TestValidTeamName(t *testing.T) {
for _, v := range domains {
- if IsValidDomain(v.value) != v.expected {
+ if IsValidTeamName(v.value) != v.expected {
t.Errorf("expect %v as %v", v.value, v.expected)
}
}
@@ -102,9 +102,9 @@ var tReservedDomains = []struct {
{"spin-punch-admin", false},
}
-func TestReservedDomain(t *testing.T) {
+func TestReservedTeamName(t *testing.T) {
for _, v := range tReservedDomains {
- if IsReservedDomain(v.value) != v.expected {
+ if IsReservedTeamName(v.value) != v.expected {
t.Errorf("expect %v as %v", v.value, v.expected)
}
}
diff --git a/scripts/README_DEV.md b/scripts/README_DEV.md
index 6a2dfc54d..be24daad9 100644
--- a/scripts/README_DEV.md
+++ b/scripts/README_DEV.md
@@ -10,7 +10,7 @@ DOCKER SETUP
3. Add a line to your /etc/hosts that goes `<Docker IP> dockerhost`
4. Run `boot2docker shellinit` and copy the export statements to your ~/.bash_profile
-Any issues? Please let us know on our forums at: http://bit.ly/1MY1kul
+Any issues? Please let us know on our forums at: http://forum.mattermost.org
GO SETUP
@@ -39,4 +39,4 @@ MATTERMOST SETUP
6. Then do `cd platform` and `make test`. Provided the test runs fine, you now have a complete build environment.
7. Use `make run` to run your code
-Any issues? Please let us know on our forums at: http://bit.ly/1MY1kul \ No newline at end of file
+Any issues? Please let us know on our forums at: http://forum.mattermost.org
diff --git a/store/redis.go b/store/redis.go
deleted file mode 100644
index 262040d43..000000000
--- a/store/redis.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package store
-
-import (
- l4g "code.google.com/p/log4go"
- "github.com/mattermost/platform/model"
- "github.com/mattermost/platform/utils"
- "gopkg.in/redis.v2"
- "strings"
- "time"
-)
-
-var client *redis.Client
-
-func RedisClient() *redis.Client {
-
- if client == nil {
-
- addr := utils.Cfg.RedisSettings.DataSource
-
- client = redis.NewTCPClient(&redis.Options{
- Addr: addr,
- Password: "",
- DB: 0,
- PoolSize: utils.Cfg.RedisSettings.MaxOpenConns,
- })
-
- l4g.Info("Pinging redis at '%v'", addr)
- pong, err := client.Ping().Result()
-
- if err != nil {
- l4g.Critical("Failed to open redis connection to '%v' err:%v", addr, err)
- time.Sleep(time.Second)
- panic("Failed to open redis connection " + err.Error())
- }
-
- if pong != "PONG" {
- l4g.Critical("Failed to ping redis connection to '%v' err:%v", addr, err)
- time.Sleep(time.Second)
- panic("Failed to open ping connection " + err.Error())
- }
- }
-
- return client
-}
-
-func RedisClose() {
- l4g.Info("Closing redis")
-
- if client != nil {
- client.Close()
- client = nil
- }
-}
-
-func PublishAndForget(message *model.Message) {
-
- go func() {
- c := RedisClient()
- result := c.Publish(message.TeamId, message.ToJson())
- if result.Err() != nil {
- l4g.Error("Failed to publish message err=%v, payload=%v", result.Err(), message.ToJson())
- }
- }()
-}
-
-func GetMessageFromPayload(m interface{}) *model.Message {
- if msg, found := m.(*redis.Message); found {
- return model.MessageFromJson(strings.NewReader(msg.Payload))
- } else {
- return nil
- }
-}
diff --git a/store/redis_test.go b/store/redis_test.go
deleted file mode 100644
index 11bd9ca6a..000000000
--- a/store/redis_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package store
-
-import (
- "fmt"
- "github.com/mattermost/platform/model"
- "github.com/mattermost/platform/utils"
- "testing"
-)
-
-func TestRedis(t *testing.T) {
- utils.LoadConfig("config.json")
-
- c := RedisClient()
-
- if c == nil {
- t.Fatal("should have a valid redis connection")
- }
-
- pubsub := c.PubSub()
- defer pubsub.Close()
-
- m := model.NewMessage(model.NewId(), model.NewId(), model.NewId(), model.ACTION_TYPING)
- m.Add("RootId", model.NewId())
-
- err := pubsub.Subscribe(m.TeamId)
- if err != nil {
- t.Fatal(err)
- }
-
- // should be the subscribe success message
- // lets gobble that up
- if _, err := pubsub.Receive(); err != nil {
- t.Fatal(err)
- }
-
- PublishAndForget(m)
-
- fmt.Println("here1")
-
- if msg, err := pubsub.Receive(); err != nil {
- t.Fatal(err)
- } else {
-
- rmsg := GetMessageFromPayload(msg)
-
- if m.TeamId != rmsg.TeamId {
- t.Fatal("Ids do not match")
- }
-
- if m.Props["RootId"] != rmsg.Props["RootId"] {
- t.Fatal("Ids do not match")
- }
- }
-
- RedisClose()
-}
diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go
index 5aa7f21f9..dbdfc16b1 100644
--- a/store/sql_channel_store.go
+++ b/store/sql_channel_store.go
@@ -360,7 +360,7 @@ func (s SqlChannelStore) GetExtraMembers(channelId string, limit int) StoreChann
result := StoreResult{}
var members []model.ExtraMember
- _, err := s.GetReplica().Select(&members, "SELECT Id, FullName, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = :ChannelId LIMIT :Limit", map[string]interface{}{"ChannelId": channelId, "Limit": limit})
+ _, err := s.GetReplica().Select(&members, "SELECT Id, Nickname, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = :ChannelId LIMIT :Limit", map[string]interface{}{"ChannelId": channelId, "Limit": limit})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetExtraMembers", "We couldn't get the extra info for channel members", "channel_id="+channelId+", "+err.Error())
} else {
diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go
index 3cb249b39..ae29bc2b3 100644
--- a/store/sql_channel_store_test.go
+++ b/store/sql_channel_store_test.go
@@ -200,13 +200,13 @@ func TestChannelMemberStore(t *testing.T) {
u1 := model.User{}
u1.TeamId = model.NewId()
u1.Email = model.NewId()
- u1.FullName = model.NewId()
+ u1.Nickname = model.NewId()
Must(store.User().Save(&u1))
u2 := model.User{}
u2.TeamId = model.NewId()
u2.Email = model.NewId()
- u2.FullName = model.NewId()
+ u2.Nickname = model.NewId()
Must(store.User().Save(&u2))
o1 := model.ChannelMember{}
diff --git a/store/sql_post_store.go b/store/sql_post_store.go
index 13e7b891d..56c174e4c 100644
--- a/store/sql_post_store.go
+++ b/store/sql_post_store.go
@@ -36,6 +36,11 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore {
}
func (s SqlPostStore) UpgradeSchemaIfNeeded() {
+
+ // These execs are for upgrading currently created databases to full utf8mb4 compliance
+ // Will be removed as seen fit for upgrading
+ s.GetMaster().Exec("ALTER TABLE Posts charset=utf8mb4")
+ s.GetMaster().Exec("ALTER TABLE Posts MODIFY COLUMN Message varchar(4000) CHARACTER SET utf8mb4")
}
func (s SqlPostStore) CreateIndexesIfNotExists() {
diff --git a/store/sql_store.go b/store/sql_store.go
index 543945605..216060dba 100644
--- a/store/sql_store.go
+++ b/store/sql_store.go
@@ -83,7 +83,14 @@ func NewSqlStore() Store {
func setupConnection(con_type string, driver string, dataSource string, maxIdle int, maxOpen int, trace bool) *gorp.DbMap {
- db, err := dbsql.Open(driver, dataSource)
+ charset := ""
+ if strings.Index(dataSource, "?") > -1 {
+ charset = "&charset=utf8mb4,utf8"
+ } else {
+ charset = "?charset=utf8mb4,utf8"
+ }
+
+ db, err := dbsql.Open(driver, dataSource+charset)
if err != nil {
l4g.Critical("Failed to open sql connection to '%v' err:%v", dataSource, err)
time.Sleep(time.Second)
@@ -106,7 +113,7 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
if driver == "sqlite3" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}}
} else if driver == "mysql" {
- dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}}
+ dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}}
} else if driver == "postgres" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}}
} else {
@@ -122,27 +129,9 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
return dbmap
}
-func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, colType string, defaultValue string) bool {
-
- var count int64
- var err error
-
- if utils.Cfg.SqlSettings.DriverName == "postgres" {
-
- count, err = ss.GetMaster().SelectInt(
- `SELECT
- COUNT(0) AS column_exists
- FROM
- information_schema.COLUMNS
- WHERE
- TABLE_NAME = $1
- AND COLUMN_NAME = $2`,
- tableName,
- columnName,
- )
- } else if utils.Cfg.SqlSettings.DriverName == "mysql" {
- count, err = ss.GetMaster().SelectInt(
- `SELECT
+func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool {
+ count, err := ss.GetMaster().SelectInt(
+ `SELECT
COUNT(0) AS column_exists
FROM
information_schema.COLUMNS
@@ -150,22 +139,24 @@ func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string,
TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ?
AND COLUMN_NAME = ?`,
- tableName,
- columnName,
- )
- }
-
+ tableName,
+ columnName,
+ )
if err != nil {
l4g.Critical("Failed to check if column exists %v", err)
time.Sleep(time.Second)
panic("Failed to check if column exists " + err.Error())
}
- if count > 0 {
+ return count > 0
+}
+
+func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool {
+ if ss.DoesColumnExist(tableName, columnName) {
return false
}
- _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + colType + " DEFAULT '" + defaultValue + "'")
+ _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + colType + " DEFAULT '" + defaultValue + "'" + " AFTER " + afterName)
if err != nil {
l4g.Critical("Failed to create column %v", err)
time.Sleep(time.Second)
@@ -176,31 +167,32 @@ func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string,
}
func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
- count, err := ss.GetMaster().SelectInt(
- `SELECT
- COUNT(0) AS column_exists
- FROM
- information_schema.COLUMNS
- WHERE
- TABLE_SCHEMA = DATABASE()
- AND TABLE_NAME = ?
- AND COLUMN_NAME = ?`,
- tableName,
- columnName,
- )
+ if !ss.DoesColumnExist(tableName, columnName) {
+ return false
+ }
+
+ _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
if err != nil {
- l4g.Critical("Failed to check if column exists %v", err)
+ l4g.Critical("Failed to drop column %v", err)
time.Sleep(time.Second)
- panic("Failed to check if column exists " + err.Error())
+ panic("Failed to drop column " + err.Error())
}
- if count == 0 {
+ return true
+}
+
+func (ss SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
+ if !ss.DoesColumnExist(tableName, oldColumnName) {
return false
}
- _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
+ _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType)
+
+ // when we eventually support PostgreSQL, we can use the following instead
+ //_, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName)
+
if err != nil {
- l4g.Critical("Failed to drop column %v", err)
+ l4g.Critical("Failed to rename column %v", err)
time.Sleep(time.Second)
panic("Failed to drop column " + err.Error())
}
@@ -217,55 +209,27 @@ func (ss SqlStore) CreateFullTextIndexIfNotExists(indexName string, tableName st
}
func (ss SqlStore) createIndexIfNotExists(indexName string, tableName string, columnName string, fullText bool) {
+ count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
+ if err != nil {
+ l4g.Critical("Failed to check index", err)
+ time.Sleep(time.Second)
+ panic("Failed to check index" + err.Error())
+ }
- if utils.Cfg.SqlSettings.DriverName == "postgres" {
- _, err := ss.GetMaster().SelectStr("SELECT to_regclass($1)", indexName)
- // It should fail if the index does not exist
- if err == nil {
- return
- }
-
- query := ""
- if fullText {
- query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + columnName + "))"
- } else {
- query = "CREATE INDEX " + indexName + " ON " + tableName + " (" + columnName + ")"
- }
-
- _, err = ss.GetMaster().Exec(query)
- if err != nil {
- l4g.Critical("Failed to create index %v", err)
- time.Sleep(time.Second)
- panic("Failed to create index " + err.Error())
- }
- } else if utils.Cfg.SqlSettings.DriverName == "mysql" {
-
- count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
- if err != nil {
- l4g.Critical("Failed to check index %v", err)
- time.Sleep(time.Second)
- panic("Failed to check index " + err.Error())
- }
-
- if count > 0 {
- return
- }
+ if count > 0 {
+ return
+ }
- fullTextIndex := ""
- if fullText {
- fullTextIndex = " FULLTEXT "
- }
+ fullTextIndex := ""
+ if fullText {
+ fullTextIndex = " FULLTEXT "
+ }
- _, err = ss.GetMaster().Exec("CREATE " + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + columnName + ")")
- if err != nil {
- l4g.Critical("Failed to create index %v", err)
- time.Sleep(time.Second)
- panic("Failed to create index " + err.Error())
- }
- } else {
- l4g.Critical("Failed to create index because of missing driver")
+ _, err = ss.GetMaster().Exec("CREATE " + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + columnName + ")")
+ if err != nil {
+ l4g.Critical("Failed to create index", err)
time.Sleep(time.Second)
- panic("Failed to create index because of missing driver")
+ panic("Failed to create index " + err.Error())
}
}
diff --git a/store/sql_team_store.go b/store/sql_team_store.go
index 193d0a62c..73d603151 100644
--- a/store/sql_team_store.go
+++ b/store/sql_team_store.go
@@ -17,8 +17,8 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id")
table.ColMap("Id").SetMaxSize(26)
- table.ColMap("Name").SetMaxSize(64)
- table.ColMap("Domain").SetMaxSize(64).SetUnique(true)
+ table.ColMap("DisplayName").SetMaxSize(64)
+ table.ColMap("Name").SetMaxSize(64).SetUnique(true)
table.ColMap("Email").SetMaxSize(128)
table.ColMap("CompanyName").SetMaxSize(64)
table.ColMap("AllowedDomains").SetMaxSize(500)
@@ -28,6 +28,15 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
}
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
+ defaultValue := "0"
+ if utils.Cfg.TeamSettings.AllowValetDefault {
+ defaultValue = "1"
+ }
+ s.CreateColumnIfNotExists("Teams", "AllowValet", "AllowedDomains", "tinyint(1)", defaultValue)
+ if !s.DoesColumnExist("Teams", "DisplayName") {
+ s.RenameColumnIfExists("Teams", "Name", "DisplayName", "varchar(64)")
+ s.RenameColumnIfExists("Teams", "Domain", "Name", "varchar(64)")
+ }
}
func (s SqlTeamStore) CreateIndexesIfNotExists() {
@@ -56,7 +65,7 @@ func (s SqlTeamStore) Save(team *model.Team) StoreChannel {
}
if err := s.GetMaster().Insert(team); err != nil {
- if IsUniqueConstraintError(err.Error(), "Domain", "teams_domain_key") {
+ if IsUniqueConstraintError(err.Error(), "Name", "teams_domain_key") {
result.Err = model.NewAppError("SqlTeamStore.Save", "A team with that domain already exists", "id="+team.Id+", "+err.Error())
} else {
result.Err = model.NewAppError("SqlTeamStore.Save", "We couldn't save the team", "id="+team.Id+", "+err.Error())
@@ -94,7 +103,7 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel {
} else {
oldTeam := oldResult.(*model.Team)
team.CreateAt = oldTeam.CreateAt
- team.Domain = oldTeam.Domain
+ team.Name = oldTeam.Name
if count, err := s.GetMaster().Update(team); err != nil {
result.Err = model.NewAppError("SqlTeamStore.Update", "We encounted an error updating the team", "id="+team.Id+", "+err.Error())
@@ -112,14 +121,14 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel {
return storeChannel
}
-func (s SqlTeamStore) UpdateName(name string, teamId string) StoreChannel {
+func (s SqlTeamStore) UpdateDisplayName(name string, teamId string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
- if _, err := s.GetMaster().Exec("UPDATE Teams SET Name = :Name WHERE Id = :Id", map[string]interface{}{"Name": name, "Id": teamId}); err != nil {
+ if _, err := s.GetMaster().Exec("UPDATE Teams SET DisplayName = :Name WHERE Id = :Id", map[string]interface{}{"Name": name, "Id": teamId}); err != nil {
result.Err = model.NewAppError("SqlTeamStore.UpdateName", "We couldn't update the team name", "team_id="+teamId)
} else {
result.Data = teamId
@@ -153,7 +162,7 @@ func (s SqlTeamStore) Get(id string) StoreChannel {
return storeChannel
}
-func (s SqlTeamStore) GetByDomain(domain string) StoreChannel {
+func (s SqlTeamStore) GetByName(name string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
@@ -161,8 +170,8 @@ func (s SqlTeamStore) GetByDomain(domain string) StoreChannel {
team := model.Team{}
- if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Domain = :Domain", map[string]interface{}{"Domain": domain}); err != nil {
- result.Err = model.NewAppError("SqlTeamStore.GetByDomain", "We couldn't find the existing team", "domain="+domain+", "+err.Error())
+ if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil {
+ result.Err = model.NewAppError("SqlTeamStore.GetByName", "We couldn't find the existing team", "name="+name+", "+err.Error())
}
result.Data = &team
diff --git a/store/sql_team_store_test.go b/store/sql_team_store_test.go
index bd1a7de2e..1f13e466c 100644
--- a/store/sql_team_store_test.go
+++ b/store/sql_team_store_test.go
@@ -13,8 +13,8 @@ func TestTeamStoreSave(t *testing.T) {
Setup()
o1 := model.Team{}
- o1.Name = "Name"
- o1.Domain = "a" + model.NewId() + "b"
+ o1.DisplayName = "DisplayName"
+ o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
@@ -36,8 +36,8 @@ func TestTeamStoreUpdate(t *testing.T) {
Setup()
o1 := model.Team{}
- o1.Name = "Name"
- o1.Domain = "a" + model.NewId() + "b"
+ o1.DisplayName = "DisplayName"
+ o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
if err := (<-store.Team().Save(&o1)).Err; err != nil {
@@ -61,25 +61,25 @@ func TestTeamStoreUpdate(t *testing.T) {
}
}
-func TestTeamStoreUpdateName(t *testing.T) {
+func TestTeamStoreUpdateDisplayName(t *testing.T) {
Setup()
o1 := &model.Team{}
- o1.Name = "Name"
- o1.Domain = "a" + model.NewId() + "b"
+ o1.DisplayName = "Display Name"
+ o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
o1 = (<-store.Team().Save(o1)).Data.(*model.Team)
- newName := "NewName"
+ newDisplayName := "NewDisplayName"
- if err := (<-store.Team().UpdateName(newName, o1.Id)).Err; err != nil {
+ if err := (<-store.Team().UpdateDisplayName(newDisplayName, o1.Id)).Err; err != nil {
t.Fatal(err)
}
ro1 := (<-store.Team().Get(o1.Id)).Data.(*model.Team)
- if ro1.Name != newName {
- t.Fatal("Name not updated")
+ if ro1.DisplayName != newDisplayName {
+ t.Fatal("DisplayName not updated")
}
}
@@ -87,8 +87,8 @@ func TestTeamStoreGet(t *testing.T) {
Setup()
o1 := model.Team{}
- o1.Name = "Name"
- o1.Domain = "a" + model.NewId() + "b"
+ o1.DisplayName = "DisplayName"
+ o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
Must(store.Team().Save(&o1))
@@ -106,12 +106,12 @@ func TestTeamStoreGet(t *testing.T) {
}
}
-func TestTeamStoreGetByDomain(t *testing.T) {
+func TestTeamStoreGetByName(t *testing.T) {
Setup()
o1 := model.Team{}
- o1.Name = "Name"
- o1.Domain = "a" + model.NewId() + "b"
+ o1.DisplayName = "DisplayName"
+ o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
@@ -119,7 +119,7 @@ func TestTeamStoreGetByDomain(t *testing.T) {
t.Fatal(err)
}
- if r1 := <-store.Team().GetByDomain(o1.Domain); r1.Err != nil {
+ if r1 := <-store.Team().GetByName(o1.Name); r1.Err != nil {
t.Fatal(r1.Err)
} else {
if r1.Data.(*model.Team).ToJson() != o1.ToJson() {
@@ -127,7 +127,7 @@ func TestTeamStoreGetByDomain(t *testing.T) {
}
}
- if err := (<-store.Team().GetByDomain("")).Err; err == nil {
+ if err := (<-store.Team().GetByName("")).Err; err == nil {
t.Fatal("Missing id should have failed")
}
}
@@ -136,8 +136,8 @@ func TestTeamStoreGetForEmail(t *testing.T) {
Setup()
o1 := model.Team{}
- o1.Name = "Name"
- o1.Domain = "a" + model.NewId() + "b"
+ o1.DisplayName = "DisplayName"
+ o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
Must(store.Team().Save(&o1))
diff --git a/store/sql_user_store.go b/store/sql_user_store.go
index 1eb20734b..5feef5e69 100644
--- a/store/sql_user_store.go
+++ b/store/sql_user_store.go
@@ -24,7 +24,9 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
table.ColMap("Password").SetMaxSize(128)
table.ColMap("AuthData").SetMaxSize(128)
table.ColMap("Email").SetMaxSize(128)
- table.ColMap("FullName").SetMaxSize(64)
+ table.ColMap("Nickname").SetMaxSize(64)
+ table.ColMap("FirstName").SetMaxSize(64)
+ table.ColMap("LastName").SetMaxSize(64)
table.ColMap("Roles").SetMaxSize(64)
table.ColMap("Props").SetMaxSize(4000)
table.ColMap("NotifyProps").SetMaxSize(2000)
@@ -35,9 +37,29 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
return us
}
-func (s SqlUserStore) UpgradeSchemaIfNeeded() {
+func (us SqlUserStore) UpgradeSchemaIfNeeded() {
+ us.CreateColumnIfNotExists("Users", "LastPictureUpdate", "LastPasswordUpdate", "bigint(20)", "0")
+
+ // migrating the FullName column to Nickname and adding the FirstName and LastName columns for MM-825
+ if us.RenameColumnIfExists("Users", "FullName", "Nickname", "varchar(64)") {
+ us.CreateColumnIfNotExists("Users", "FirstName", "Nickname", "varchar(64)", "")
+ us.CreateColumnIfNotExists("Users", "LastName", "FirstName", "varchar(64)", "")
+
+ // infer values of first and last name by splitting the previous full name
+ if _, err := us.GetMaster().Exec("UPDATE Users SET FirstName = SUBSTRING_INDEX(SUBSTRING_INDEX(Nickname, ' ', 1), ' ', -1)"); err != nil {
+ panic("Failed to set first name from nickname " + err.Error())
+ }
+
+ // only set the last name from full names that are comprised of multiple words (ie that have at least one space in them)
+ if _, err := us.GetMaster().Exec("Update Users SET LastName = SUBSTRING(Nickname, INSTR(Nickname, ' ') + 1) " +
+ "WHERE CHAR_LENGTH(REPLACE(Nickname, ' ', '')) < CHAR_LENGTH(Nickname)"); err != nil {
+ panic("Failed to set last name from nickname " + err.Error())
+ }
+ }
}
+//func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool {
+
func (us SqlUserStore) CreateIndexesIfNotExists() {
us.CreateIndexIfNotExists("idx_users_team_id", "Users", "TeamId")
us.CreateIndexIfNotExists("idx_users_email", "Users", "Email")
@@ -120,6 +142,7 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha
user.AuthData = oldUser.AuthData
user.Password = oldUser.Password
user.LastPasswordUpdate = oldUser.LastPasswordUpdate
+ user.LastPictureUpdate = oldUser.LastPictureUpdate
user.TeamId = oldUser.TeamId
user.LastActivityAt = oldUser.LastActivityAt
user.LastPingAt = oldUser.LastPingAt
@@ -150,6 +173,27 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha
return storeChannel
}
+func (us SqlUserStore) UpdateLastPictureUpdate(userId string) StoreChannel {
+ storeChannel := make(StoreChannel)
+
+ go func() {
+ result := StoreResult{}
+
+ curTime := model.GetMillis()
+
+ if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = ?, UpdateAt = ? WHERE Id = ?", curTime, curTime, userId); err != nil {
+ result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "We couldn't update the update_at", "user_id="+userId)
+ } else {
+ result.Data = userId
+ }
+
+ storeChannel <- result
+ close(storeChannel)
+ }()
+
+ return storeChannel
+}
+
func (us SqlUserStore) UpdateLastPingAt(userId string, time int64) StoreChannel {
storeChannel := make(StoreChannel)
diff --git a/store/store.go b/store/store.go
index 070ee0562..5b0e13fce 100644
--- a/store/store.go
+++ b/store/store.go
@@ -36,9 +36,9 @@ type Store interface {
type TeamStore interface {
Save(team *model.Team) StoreChannel
Update(team *model.Team) StoreChannel
- UpdateName(name string, teamId string) StoreChannel
+ UpdateDisplayName(name string, teamId string) StoreChannel
Get(id string) StoreChannel
- GetByDomain(domain string) StoreChannel
+ GetByName(name string) StoreChannel
GetTeamsForEmail(domain string) StoreChannel
}
@@ -77,6 +77,7 @@ type PostStore interface {
type UserStore interface {
Save(user *model.User) StoreChannel
Update(user *model.User, allowRoleUpdate bool) StoreChannel
+ UpdateLastPictureUpdate(userId string) StoreChannel
UpdateLastPingAt(userId string, time int64) StoreChannel
UpdateLastActivityAt(userId string, time int64) StoreChannel
UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel
diff --git a/utils/config.go b/utils/config.go
index eb2ae3050..efa4b263a 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -19,13 +19,11 @@ const (
type ServiceSettings struct {
SiteName string
- Domain string
Mode string
AllowTesting bool
UseSSL bool
Port string
Version string
- Shards map[string]string
InviteSalt string
PublicLinkSalt string
ResetSalt string
@@ -42,11 +40,6 @@ type SqlSettings struct {
AtRestEncryptKey string
}
-type RedisSettings struct {
- DataSource string
- MaxOpenConns int
-}
-
type LogSettings struct {
ConsoleEnable bool
ConsoleLevel string
@@ -57,14 +50,10 @@ type LogSettings struct {
}
type AWSSettings struct {
- S3AccessKeyId string
- S3SecretAccessKey string
- S3Bucket string
- S3Region string
- Route53AccessKeyId string
- Route53SecretAccessKey string
- Route53ZoneId string
- Route53Region string
+ S3AccessKeyId string
+ S3SecretAccessKey string
+ S3Bucket string
+ S3Region string
}
type ImageSettings struct {
@@ -77,6 +66,7 @@ type ImageSettings struct {
}
type EmailSettings struct {
+ ByPassEmail bool
SMTPUsername string
SMTPPassword string
SMTPServer string
@@ -112,7 +102,6 @@ type Config struct {
LogSettings LogSettings
ServiceSettings ServiceSettings
SqlSettings SqlSettings
- RedisSettings RedisSettings
AWSSettings AWSSettings
ImageSettings ImageSettings
EmailSettings EmailSettings
@@ -218,18 +207,10 @@ func LoadConfig(fileName string) {
panic("Error decoding configuration " + err.Error())
}
- // Grabs the domain from enviroment variable if not in configuration
- if config.ServiceSettings.Domain == "" {
- config.ServiceSettings.Domain = os.Getenv("MATTERMOST_DOMAIN")
- // If the enviroment variable is not set, use a default
- if config.ServiceSettings.Domain == "" {
- config.ServiceSettings.Domain = "localhost"
- }
- }
-
// Check for a valid email for feedback, if not then do feedback@domain
if _, err := mail.ParseAddress(config.EmailSettings.FeedbackEmail); err != nil {
- config.EmailSettings.FeedbackEmail = "feedback@" + config.ServiceSettings.Domain
+ config.EmailSettings.FeedbackEmail = "feedback@localhost"
+ l4g.Error("Misconfigured feedback email setting: %s", config.EmailSettings.FeedbackEmail)
}
configureLog(config.LogSettings)
diff --git a/utils/config_test.go b/utils/config_test.go
index 9067dc647..4d37b4e88 100644
--- a/utils/config_test.go
+++ b/utils/config_test.go
@@ -4,24 +4,9 @@
package utils
import (
- "os"
"testing"
)
func TestConfig(t *testing.T) {
LoadConfig("config.json")
}
-
-func TestEnvOverride(t *testing.T) {
- os.Setenv("MATTERMOST_DOMAIN", "testdomain.com")
-
- LoadConfig("config_docker.json")
- if Cfg.ServiceSettings.Domain != "testdomain.com" {
- t.Fail()
- }
-
- LoadConfig("config.json")
- if Cfg.ServiceSettings.Domain == "testdomain.com" {
- t.Fail()
- }
-}
diff --git a/utils/mail.go b/utils/mail.go
index 3cd37ffef..0fe7042b7 100644
--- a/utils/mail.go
+++ b/utils/mail.go
@@ -8,14 +8,14 @@ import (
"crypto/tls"
"fmt"
"github.com/mattermost/platform/model"
+ "html"
"net"
"net/mail"
"net/smtp"
- "html"
)
func CheckMailSettings() *model.AppError {
- if len(Cfg.EmailSettings.SMTPServer) == 0 {
+ if len(Cfg.EmailSettings.SMTPServer) == 0 || Cfg.EmailSettings.ByPassEmail {
return model.NewAppError("CheckMailSettings", "No email settings present, mail will not be sent", "")
}
conn, err := connectToSMTPServer()
@@ -79,6 +79,10 @@ func newSMTPClient(conn net.Conn) (*smtp.Client, *model.AppError) {
func SendMail(to, subject, body string) *model.AppError {
+ if len(Cfg.EmailSettings.SMTPServer) == 0 || Cfg.EmailSettings.ByPassEmail {
+ return nil
+ }
+
fromMail := mail.Address{"", Cfg.EmailSettings.FeedbackEmail}
toMail := mail.Address{"", to}
@@ -95,11 +99,6 @@ func SendMail(to, subject, body string) *model.AppError {
}
message += "\r\n<html><body>" + body + "</body></html>"
- if len(Cfg.EmailSettings.SMTPServer) == 0 {
- l4g.Warn("Skipping sending of email because EmailSettings are not configured")
- return nil
- }
-
conn, err1 := connectToSMTPServer()
if err1 != nil {
return err1
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 68de80228..30435dc08 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -1,6 +1,7 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+
var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_store.jsx');
@@ -16,7 +17,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-var ExtraMembers = React.createClass({
+var PopoverListMembers = React.createClass({
componentDidMount: function() {
var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj) {
@@ -35,30 +36,29 @@ var ExtraMembers = React.createClass({
$("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true});
$('body').on('click', function (e) {
- if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) {
+ if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) {
$("#member_popover").popover('hide');
}
});
-
},
+
render: function() {
- 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.props.members;
+ var popoverHtml = '';
+ var members = this.props.members;
+ var count = (members.length > 20) ? "20+" : (members.length || '-');
- if(sortedMembers) {
- sortedMembers.sort(function(a,b) {
+ if (members) {
+ members.sort(function(a,b) {
return a.username.localeCompare(b.username);
- })
+ });
- sortedMembers.forEach(function(m) {
- data_content += "<div style='white-space: nowrap'>" + m.username + "</div>";
+ members.forEach(function(m) {
+ popoverHtml += "<div class='text--nowrap'>" + m.username + "</div>";
});
}
return (
- <div style={{"cursor" : "pointer"}} id="member_popover" data-toggle="popover" data-content={data_content} data-original-title="Members" >
+ <div id="member_popover" data-toggle="popover" data-content={popoverHtml} data-original-title="Members" >
<div id="member_tooltip" data-toggle="tooltip" title="View Channel Members">
{count} <span className="glyphicon glyphicon-user" aria-hidden="true"></span>
</div>
@@ -78,6 +78,7 @@ function getStateFromStores() {
}
module.exports = React.createClass({
+ displayName: 'ChannelHeader',
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(this._onChange);
@@ -99,7 +100,7 @@ module.exports = React.createClass({
$(".channel-header__info .description").popover({placement : 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
},
_onSocketChange: function(msg) {
- if(msg.action === "new_user") {
+ if (msg.action === "new_user") {
AsyncClient.getChannelExtraInfo(true);
}
},
@@ -107,15 +108,14 @@ module.exports = React.createClass({
return getStateFromStores();
},
handleLeave: function(e) {
- var self = this;
Client.leaveChannel(this.state.channel.id,
function(data) {
var townsquare = ChannelStore.getByName('town-square');
utils.switchChannel(townsquare);
- }.bind(this),
+ },
function(err) {
AsyncClient.dispatchError(err, "handleLeave");
- }.bind(this)
+ }
);
},
searchMentions: function(e) {
@@ -131,52 +131,29 @@ module.exports = React.createClass({
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH_TERM,
term: terms,
- do_search: false
+ do_search: true,
+ is_mention_search: true
});
-
- Client.search(
- terms,
- function(data) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECIEVED_SEARCH,
- results: data,
- is_mention_search: true
- });
- },
- function(err) {
- dispatchError(err, "search");
- }
- );
},
+
render: function() {
if (this.state.channel == null) {
- return (
- <div></div>
- );
+ return null;
}
- var description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true});
- var popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>);
- var channelTitle = "";
- var channelName = this.state.channel.name;
+ var channel = this.state.channel;
+ var description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
+ var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
+ var channelTitle = channel.display_name;
var currentId = UserStore.getCurrentId();
var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1;
- var searchForm = <th className="search-bar__container"><NavbarSearchBox /></th>;
- var isDirect = false;
-
- if (this.state.channel.type === 'O') {
- channelTitle = this.state.channel.display_name;
- } else if (this.state.channel.type === 'P') {
- channelTitle = this.state.channel.display_name;
- } else if (this.state.channel.type === 'D') {
- isDirect = true;
+ var isDirect = (this.state.channel.type === 'D');
+
+ if (isDirect) {
if (this.state.users.length > 1) {
- if (this.state.users[0].id === UserStore.getCurrentId()) {
- channelTitle = <UserProfile userId={this.state.users[1].id} overwriteName={this.state.users[1].full_name ? this.state.users[1].full_name : this.state.users[1].username} />;
- } else {
- channelTitle = <UserProfile userId={this.state.users[0].id} overwriteName={this.state.users[0].full_name ? this.state.users[0].full_name : this.state.users[0].username} />;
- }
+ var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)];
+ channelTitle = <UserProfile userId={contact.id} overwriteName={contact.nickname || contact.username} />;
}
}
@@ -192,25 +169,28 @@ module.exports = React.createClass({
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={this.state.channel.id} href="#">View Info</a></li>
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
- { isAdmin ?
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={channel.id} href="#">View Info</a></li>
+ { !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
+ : null
+ }
+ { isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
- : ""
+ : null
}
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Set Channel Description...</a></li>
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Notification Preferences</a></li>
- { isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={this.state.channel.display_name} data-name={this.state.channel.name} data-channelid={this.state.channel.id}>Rename Channel...</a></li>
- : ""
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename Channel...</a></li>
+ : null
}
- { isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Delete Channel...</a></li>
- : ""
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
+ : null
}
- { channelName != Constants.DEFAULT_CHANNEL ?
+ { !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
- : ""
+ : null
}
</ul>
</div>
@@ -220,14 +200,13 @@ module.exports = React.createClass({
<a href="#"><strong className="heading">{channelTitle}</strong></a>
}
</th>
- <th><ExtraMembers members={this.state.users} channelId={this.state.channel.id} /></th>
- { searchForm }
+ <th><PopoverListMembers members={this.state.users} channelId={channel.id} /></th>
+ <th className="search-bar__container"><NavbarSearchBox /></th>
<th>
- <div className="dropdown" style={{"marginLeft":"5px", "marginRight":"10px"}}>
+ <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">
- <i className="fa fa-caret-down"></i>
- </a>
- <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_right_dropdown" style={{"left": "-150px"}}>
+ <span dangerouslySetInnerHTML={{__html: " <svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>"}} /> </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>
@@ -237,5 +216,3 @@ module.exports = React.createClass({
);
}
});
-
-
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 9e3feb25c..6ed0f0b34 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -43,7 +43,7 @@ module.exports = React.createClass({
client.createPost(post, ChannelStore.getCurrent(),
function(data) {
- this.setState({ messageText: '', submitting: false, post_error: null });
+ this.setState({ messageText: '', submitting: false, post_error: null, server_error: null });
this.clearPreviews();
AsyncClient.getPosts(true, this.props.channelId);
@@ -57,6 +57,7 @@ module.exports = React.createClass({
function(err) {
var state = {};
state.server_error = err.message;
+ state.submitting = false;
if (err.message === "Invalid RootId parameter") {
if ($('#post_deleted').length > 0) $('#post_deleted').modal('show');
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 0c23dcfac..d38a6798f 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -234,7 +234,7 @@ module.exports = React.createClass({
},
render: function() {
- var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
+ var server_error = this.state.server_error ? <div className='has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
var post_error = this.state.post_error ? <label className='control-label'>{this.state.post_error}</label> : null;
var limit_error = this.state.limit_error ? <div className='has-error'><label className='control-label'>{this.state.limit_error}</label></div> : null;
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
index a8c690789..e23a37740 100644
--- a/web/react/components/delete_channel_modal.jsx
+++ b/web/react/components/delete_channel_modal.jsx
@@ -12,7 +12,7 @@ module.exports = React.createClass({
Client.deleteChannel(this.state.channel_id,
function(data) {
AsyncClient.getChannels(true);
- window.location.href = '/channels/town-square';
+ window.location.href = '/';
}.bind(this),
function(err) {
AsyncClient.dispatchError(err, "handleDelete");
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
index fefac12d7..11970bc2b 100644
--- a/web/react/components/delete_post_modal.jsx
+++ b/web/react/components/delete_post_modal.jsx
@@ -56,13 +56,13 @@ module.exports = React.createClass({
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
var newState = {};
if(BrowserStore.getItem('edit_state_transfer')) {
- newState = JSON.parse(BrowserStore.getItem('edit_state_transfer'));
+ newState = BrowserStore.getItem('edit_state_transfer');
BrowserStore.removeItem('edit_state_transfer');
} else {
var button = e.relatedTarget;
newState = { title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments') };
}
- self.setState(newState)
+ self.setState(newState);
});
PostStore.addSelectedPostChangeListener(this._onChange);
},
diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx
index 255654fd5..c0818959a 100644
--- a/web/react/components/edit_channel_modal.jsx
+++ b/web/react/components/edit_channel_modal.jsx
@@ -43,7 +43,7 @@ module.exports = React.createClass({
<h4 className="modal-title" ref="title">Edit {this.state.title} Description</h4>
</div>
<div className="modal-body">
- <textarea className="form-control" style={{resize: "none"}} rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
+ <textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index d741e189b..21b75bb6e 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -4,6 +4,7 @@
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var Textbox = require('./textbox.jsx');
+var BrowserStore = require('../stores/browser_store.jsx');
module.exports = React.createClass({
handleEdit: function(e) {
@@ -13,14 +14,14 @@ module.exports = React.createClass({
if (updatedPost.message.length === 0) {
var tempState = this.state;
delete tempState.editText;
- BrowserStore.setItem('edit_state_transfer', JSON.stringify(tempState));
+ BrowserStore.setItem('edit_state_transfer', tempState);
$("#edit_post").modal('hide');
$("#delete_post").modal('show');
return;
}
- updatedPost.id = this.state.post_id
- updatedPost.channel_id = this.state.channel_id
+ updatedPost.id = this.state.post_id;
+ updatedPost.channel_id = this.state.channel_id;
Client.updatePost(updatedPost,
function(data) {
diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx
index bbfdce63a..af5314e64 100644
--- a/web/react/components/get_link_modal.jsx
+++ b/web/react/components/get_link_modal.jsx
@@ -38,7 +38,7 @@ module.exports = React.createClass({
<div className="modal-body">
<p>{"The link below is used for open " + strings.TeamPlural + " or if you allowed your " + strings.Team + " members to sign up using their " + strings.Company + " email addresses."}
</p>
- <textarea className="form-control" style={{resize: "none"}} readOnly="true" value={this.state.value}></textarea>
+ <textarea className="form-control no-resize" readOnly="true" value={this.state.value}></textarea>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
diff --git a/web/react/components/loading_screen.jsx b/web/react/components/loading_screen.jsx
new file mode 100644
index 000000000..5905e519b
--- /dev/null
+++ b/web/react/components/loading_screen.jsx
@@ -0,0 +1,24 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+module.exports = React.createClass({
+ displayName: "LoadingScreen",
+ propTypes: {
+ position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit'])
+ },
+ getDefaultProps: function() {
+ return { position: 'relative' };
+ },
+ render: function() {
+ return (
+ <div className="loading-screen" style={{position: this.props.position}}>
+ <div className="loading__content">
+ <h3>Loading</h3>
+ <div className="round round-1"></div>
+ <div className="round round-2"></div>
+ <div className="round round-3"></div>
+ </div>
+ </div>
+ );
+ }
+});
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 74c7d4065..71fefff5b 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -1,102 +1,21 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-
-
-
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
-
-
-var FindTeamDomain = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var state = { }
-
- var domain = this.refs.domain.getDOMNode().value.trim();
- if (!domain) {
- state.server_error = "A domain is required"
- this.setState(state);
- return;
- }
-
- if (!BrowserStore.isLocalStorageSupported()) {
- state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
- this.setState(state);
- return;
- }
-
- state.server_error = "";
- this.setState(state);
-
- client.findTeamByDomain(domain,
- function(data) {
- console.log(data);
- if (data) {
- window.location.href = window.location.protocol + "//" + domain + "." + utils.getDomainWithOutSub();
- }
- else {
- this.state.server_error = "We couldn't find your " + strings.Team + ".";
- this.setState(this.state);
- }
- }.bind(this),
- function(err) {
- this.state.server_error = err.message;
- this.setState(this.state);
- }.bind(this)
- );
- },
- getInitialState: function() {
- return { };
- },
- render: function() {
- var server_error = this.state.server_error ? <label className="control-label">{this.state.server_error}</label> : null;
-
- return (
- <div className="signup-team__container">
- <div>
- <span className="signup-team__name">{ config.SiteName }</span>
- <br/>
- <span className="signup-team__subdomain">Enter your {strings.Team}'s domain.</span>
- <br/>
- <br/>
- </div>
- <form onSubmit={this.handleSubmit}>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- { server_error }
- <input type="text" className="form-control" name="domain" ref="domain" placeholder="team domain" />
- </div>
- <div className="form-group">
- <button type="submit" className="btn btn-primary">Continue</button>
- </div>
- <div>
- <span>Don't remember your {strings.Team}'s domain? <a href="/find_team">Find it here</a></span>
- </div>
- <br/>
- <br/>
- <br/>
- <br/>
- <br/>
- <br/>
- <div>
- <span>{"Want to create your own " + strings.Team + "?"} <a href={utils.getHomeLink()} className="signup-team-login">Sign up now</a></span>
- </div>
- </form>
- </div>
- );
- }
-});
+var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var state = { }
- var domain = this.refs.domain.getDOMNode().value.trim();
- if (!domain) {
- state.server_error = "A domain is required"
+ var name = this.props.teamName
+ if (!name) {
+ state.server_error = "Bad team name"
this.setState(state);
return;
}
@@ -124,23 +43,22 @@ module.exports = React.createClass({
state.server_error = "";
this.setState(state);
- client.loginByEmail(domain, email, password,
+ client.loginByEmail(name, email, password,
function(data) {
- UserStore.setLastDomain(domain);
- UserStore.setLastEmail(email);
UserStore.setCurrentUser(data);
+ UserStore.setLastEmail(email);
var redirect = utils.getUrlParameter("redirect");
if (redirect) {
- window.location.href = decodeURI(redirect);
+ window.location.pathname = decodeURI(redirect);
} else {
- window.location.href = '/channels/town-square';
+ window.location.pathname = '/' + name + '/channels/town-square';
}
}.bind(this),
function(err) {
if (err.message == "Login failed because email address has not been verified") {
- window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email);
+ window.location.href = '/verify_email?name=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email);
return;
}
state.server_error = err.message;
@@ -161,35 +79,35 @@ module.exports = React.createClass({
priorEmail = decodeURIComponent(emailParam);
}
- var subDomainClass = "form-control hidden";
- var subDomain = utils.getSubDomain();
+ var teamDisplayName = this.props.teamDisplayName;
+ var teamName = this.props.teamName;
- if (utils.isTestDomain()) {
- subDomainClass = "form-control";
- subDomain = UserStore.getLastDomain();
- } else if (subDomain == "") {
- return (<FindTeamDomain />);
+ var focusEmail = false;
+ var focusPassword = false;
+ if (priorEmail != "") {
+ focusPassword = true;
+ } else {
+ focusEmail = true;
}
return (
<div className="signup-team__container">
<div>
- <span className="signup-team__name">{ subDomain }</span>
+ <span className="signup-team__name">{ teamDisplayName }</span>
<br/>
- <span className="signup-team__subdomain">{ utils.getDomainWithOutSub() }</span>
+ <span className="signup-team__subdomain">/{ teamName }/</span>
<br/>
<br/>
</div>
<form onSubmit={this.handleSubmit}>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
{ server_error }
- <input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
</div>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
- <input type="email" className="form-control" name="email" defaultValue={priorEmail} ref="email" placeholder="Email" />
+ <input autoFocus={focusEmail} type="email" className="form-control" name="email" defaultValue={priorEmail} ref="email" placeholder="Email" />
</div>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
- <input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
+ <input autoFocus={focusPassword} type="password" className="form-control" name="password" ref="password" placeholder="Password" />
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">Sign in</button>
@@ -198,10 +116,10 @@ module.exports = React.createClass({
<span><a href="/find_team">{"Find other " + strings.TeamPlural}</a></span>
</div>
<div className="form-group">
- <a href="/reset_password">I forgot my password</a>
+ <a href={"/" + teamName + "/reset_password"}>I forgot my password</a>
</div>
<div className="external-link">
- <span>{"Want to create your own " + strings.Team + "?"} <a href={utils.getHomeLink()} className="signup-team-login">Sign up now</a></span>
+ <span>{"Want to create your own " + strings.Team + "?"} <a href="/" className="signup-team-login">Sign up now</a></span>
</div>
</form>
</div>
diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx
index 357fd49a8..a5279909b 100644
--- a/web/react/components/member_list_item.jsx
+++ b/web/react/components/member_list_item.jsx
@@ -23,6 +23,7 @@ module.exports = React.createClass({
var member = this.props.member;
var isAdmin = this.props.isAdmin;
var isMemberAdmin = member.roles.indexOf("admin") > -1;
+ var timestamp = UserStore.getCurrentUser().update_at;
var invite;
if (member.invited && this.props.handleInvite) {
@@ -48,12 +49,12 @@ module.exports = React.createClass({
</div>
);
} else {
- invite = <div className="member-role text-capitalize" style={{marginRight: 15}}>{member.roles || 'Member'}</div>;
+ invite = <div className="member-role text-capitalize">{member.roles || 'Member'}<span className="caret hidden"></span></div>;
}
return (
<div className="row member-div">
- <img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image"} height="36" width="36" />
+ <img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image?time=" + timestamp} height="36" width="36" />
<span className="member-name">{member.username}</span>
<span className="member-email">{member.email}</span>
{ invite }
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
index cfb473e5e..cb48e5cc5 100644
--- a/web/react/components/member_list_team.jsx
+++ b/web/react/components/member_list_team.jsx
@@ -5,6 +5,7 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
+var utils = require('../utils/utils.jsx');
var MemberListTeamItem = React.createClass({
handleMakeMember: function() {
@@ -59,9 +60,10 @@ var MemberListTeamItem = React.createClass({
return {};
},
render: function() {
- var server_error = this.state.server_error ? <div style={{ clear: "both" }} className="has-error"><label className='has-error control-label'>{this.state.server_error}</label></div> : null;
+ var server_error = this.state.server_error ? <div className="has-error"><label className='has-error control-label'>{this.state.server_error}</label></div> : null;
var user = this.props.user;
- var currentRoles = "Member"
+ var currentRoles = "Member";
+ var timestamp = UserStore.getCurrentUser().update_at;
if (user.roles.length > 0) {
currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
@@ -83,9 +85,9 @@ var MemberListTeamItem = React.createClass({
return (
<div className="row member-div">
- <img className="post-profile-img pull-left" src={"/api/v1/users/" + user.id + "/image"} height="36" width="36" />
- <span className="member-name">{user.full_name.trim() ? user.full_name : user.username}</span>
- <span className="member-email">{user.full_name.trim() ? user.username : email}</span>
+ <img className="post-profile-img pull-left" src={"/api/v1/users/" + user.id + "/image?time=" + timestamp} height="36" width="36" />
+ <span className="member-name">{utils.getDisplayName(user)}</span>
+ <span className="member-email">{email}</span>
<div className="dropdown member-drop">
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
<span>{currentRoles} </span>
diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx
index 3c33ddf49..114dc183f 100644
--- a/web/react/components/mention.jsx
+++ b/web/react/components/mention.jsx
@@ -1,19 +1,27 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
+var UserStore = require("../stores/user_store.jsx");
module.exports = React.createClass({
handleClick: function() {
this.props.handleClick(this.props.username);
},
+ getInitialState: function() {
+ return null;
+ },
render: function() {
+ var self = this;
var icon;
- if (this.props.id != null) {
- icon = <span><img className="mention-img" src={"/api/v1/users/" + this.props.id + "/image"}/></span>;
+ var timestamp = UserStore.getCurrentUser().update_at;
+ if (this.props.id === "allmention" || this.props.id === "channelmention") {
+ icon = <span><i className="mention-img fa fa-users fa-2x"></i></span>;
+ } else if (this.props.id != null) {
+ icon = <span><img className="mention-img" src={"/api/v1/users/" + this.props.id + "/image?time=" + timestamp}/></span>;
} else {
icon = <span><i className="mention-img fa fa-users fa-2x"></i></span>;
}
return (
- <div className="mentions-name" onClick={this.handleClick}>
+ <div className={"mentions-name " + this.props.isFocused} id={this.props.id + "_mentions"} onClick={this.handleClick} onMouseEnter={this.props.handleMouseEnter}>
<div className="pull-left">{icon}</div>
<div className="pull-left mention-align"><span>@{this.props.username}</span><span className="mention-fullname">{this.props.secondary_text}</span></div>
</div>
diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx
index ba2c53612..71a6083d2 100644
--- a/web/react/components/mention_list.jsx
+++ b/web/react/components/mention_list.jsx
@@ -7,19 +7,48 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Mention = require('./mention.jsx');
var Constants = require('../utils/constants.jsx');
+var Utils = require('../utils/utils.jsx');
var ActionTypes = Constants.ActionTypes;
+var MAX_HEIGHT_LIST = 292;
+var MAX_ITEMS_IN_LIST = 25;
+var ITEM_HEIGHT = 36;
+
module.exports = React.createClass({
+ displayName: "MentionList",
componentDidMount: function() {
PostStore.addMentionDataChangeListener(this._onChange);
-
var self = this;
- $('body').on('keypress.mentionlist', '#'+this.props.id,
+
+ $('body').on('keydown.mentionlist', '#'+this.props.id,
function(e) {
- if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 13) {
+ if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) {
e.stopPropagation();
e.preventDefault();
- self.addFirstMention();
+ self.addCurrentMention();
+ }
+ else if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 38 || e.which === 40)) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ var tempSelectedMention = -1;
+ if (e.which === 38) {
+ if (self.getSelection(self.state.selectedMention - 1))
+ self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username });
+ else {
+ while (self.getSelection(++tempSelectedMention))
+ ; //Need to find the top of the list
+ self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username });
+ }
+ }
+ else if (e.which === 40) {
+ if (self.getSelection(self.state.selectedMention + 1))
+ self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username });
+ else
+ self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username });
+ }
+
+ self.scrollToMention(e.which, tempSelectedMention);
}
}
);
@@ -32,7 +61,28 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
PostStore.removeMentionDataChangeListener(this._onChange);
- $('body').off('keypress.mentionlist', '#'+this.props.id);
+ $('body').off('keydown.mentionlist', '#'+this.props.id);
+ },
+ componentDidUpdate: function() {
+ if (this.state.mentionText != "-1") {
+ if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) {
+ var tempSelectedMention = -1;
+ var foundMatch = false;
+ while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) {
+ if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) {
+ this.setState({ selectedMention: tempSelectedMention });
+ foundMatch = true;
+ break;
+ }
+ }
+ if (this.getSelection(0) && !foundMatch) {
+ this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username });
+ }
+ }
+ }
+ else if (this.state.selectedMention !== 0) {
+ this.setState({ selectedMention: 0, selectedUsername: "" });
+ }
},
_onChange: function(id, mentionText, excludeList) {
if (id !== this.props.id) return;
@@ -40,6 +90,7 @@ module.exports = React.createClass({
var newState = this.state;
if (mentionText != null) newState.mentionText = mentionText;
if (excludeList != null) newState.excludeUsers = excludeList;
+
this.setState(newState);
},
handleClick: function(name) {
@@ -51,6 +102,21 @@ module.exports = React.createClass({
this.setState({ mentionText: '-1' });
},
+ handleMouseEnter: function(listId) {
+ this.setState({ selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username });
+ },
+ getSelection: function(listId) {
+ if (!this.refs['mention' + listId])
+ return false;
+ else
+ return true;
+ },
+ addCurrentMention: function() {
+ if (!this.getSelection(this.state.selectedMention))
+ this.addFirstMention();
+ else
+ this.refs['mention' + this.state.selectedMention].handleClick();
+ },
addFirstMention: function() {
if (!this.refs.mention0) return;
this.refs.mention0.handleClick();
@@ -58,6 +124,23 @@ module.exports = React.createClass({
isEmpty: function() {
return (!this.refs.mention0);
},
+ scrollToMention: function(keyPressed, ifLoopUp) {
+ var direction = keyPressed === 38 ? "up" : "down";
+ var scrollAmount = 0;
+
+ if (direction === "up" && ifLoopUp !== -1)
+ scrollAmount = $("#mentionsbox").height() * 100; //Makes sure that it scrolls all the way to the bottom
+ else if (direction === "down" && this.state.selectedMention === 0)
+ scrollAmount = 0;
+ else if (direction === "up")
+ scrollAmount = "-=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
+ else if (direction === "down")
+ scrollAmount = "+=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
+
+ $("#mentionsbox").animate({
+ scrollTop: scrollAmount
+ }, 75);
+ },
alreadyMentioned: function(username) {
var excludeUsers = this.state.excludeUsers;
for (var i = 0; i < excludeUsers.length; i++) {
@@ -68,11 +151,12 @@ module.exports = React.createClass({
return false;
},
getInitialState: function() {
- return { excludeUsers: [], mentionText: "-1" };
+ return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" };
},
render: function() {
+ var self = this;
var mentionText = this.state.mentionText;
- if (mentionText === '-1') return (<div/>);
+ if (mentionText === '-1') return null;
var profiles = UserStore.getActiveOnlyProfiles();
var users = [];
@@ -82,14 +166,16 @@ module.exports = React.createClass({
var all = {};
all.username = "all";
- all.full_name = "";
+ all.nickname = "";
all.secondary_text = "Notifies everyone in the team";
+ all.id = "allmention";
users.push(all);
var channel = {};
channel.username = "channel";
- channel.full_name = "";
+ channel.nickname = "";
channel.secondary_text = "Notifies everyone in the channel";
+ channel.id = "channelmention";
users.push(channel);
users.sort(function(a,b) {
@@ -100,44 +186,42 @@ module.exports = React.createClass({
var mentions = {};
var index = 0;
- for (var i = 0; i < users.length; i++) {
- if (Object.keys(mentions).length >= 25) break;
+ for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) {
if (this.alreadyMentioned(users[i].username)) continue;
- var firstName = "", lastName = "";
- if (users[i].full_name.length > 0) {
- var splitName = users[i].full_name.split(' ');
- firstName = splitName[0].toLowerCase();
- lastName = splitName.length > 1 ? splitName[splitName.length-1].toLowerCase() : "";
- users[i].secondary_text = users[i].full_name;
- }
-
- if (firstName.lastIndexOf(mentionText,0) === 0
- || lastName.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) {
- mentions[i+1] = (
+ if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText,0) === 0)
+ || (users[i].last_name && users[i].last_name.lastIndexOf(mentionText,0) === 0) || users[i].username.lastIndexOf(mentionText,0) === 0) {
+ mentions[index] = (
<Mention
ref={'mention' + index}
username={users[i].username}
- secondary_text={users[i].secondary_text}
+ secondary_text={Utils.getFullName(users[i])}
id={users[i].id}
+ listId={index}
+ isFocused={this.state.selectedMention === index ? "mentions-focus" : ""}
+ handleMouseEnter={function(value) { return function() { self.handleMouseEnter(value); } }(index)}
handleClick={this.handleClick} />
);
index++;
}
}
+
var numMentions = Object.keys(mentions).length;
- if (numMentions < 1) return (<div/>);
+ if (numMentions < 1) return null;
- var height = (numMentions*36) + 4;
- var width = $('#'+this.props.id).parent().width();
- var bottom = $(window).height() - $('#'+this.props.id).offset().top;
- var left = $('#'+this.props.id).offset().left;
- var max_height = $('#'+this.props.id).offset().top - 10;
+ var $mention_tab = $('#'+this.props.id);
+ var maxHeight = Math.min(MAX_HEIGHT_LIST, $mention_tab.offset().top - 10);
+ var style = {
+ height: Math.min(maxHeight, (numMentions*ITEM_HEIGHT) + 4),
+ width: $mention_tab.parent().width(),
+ bottom: $(window).height() - $mention_tab.offset().top,
+ left: $mention_tab.offset().left
+ };
return (
- <div className="mentions--top" style={{height: height, width: width, bottom: bottom, left: left}}>
- <div ref="mentionlist" className="mentions-box" style={{height: height, width: width}}>
+ <div className="mentions--top" style={style}>
+ <div ref="mentionlist" className="mentions-box" id="mentionsbox">
{ mentions }
</div>
</div>
diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx
index c3ddc76f3..007476f9b 100644
--- a/web/react/components/more_channels.jsx
+++ b/web/react/components/more_channels.jsx
@@ -7,6 +7,7 @@ var client = require('../utils/client.jsx');
var asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
+var LoadingScreen = require('./loading_screen.jsx');
function getStateFromStores() {
return {
@@ -16,6 +17,8 @@ function getStateFromStores() {
}
module.exports = React.createClass({
+ displayName: "MoreChannelsModal",
+
componentDidMount: function() {
ChannelStore.addMoreChangeListener(this._onChange);
$(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) {
@@ -90,7 +93,7 @@ module.exports = React.createClass({
<p className="more-channel-name">{channel.display_name}</p>
<p className="more-channel-description">{channel.description}</p>
</td>
- <td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="pull-right btn btn-primary">Join</button></td>
+ <td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="btn btn-primary">Join</button></td>
</tr>
)
})}
@@ -100,15 +103,7 @@ module.exports = React.createClass({
<p className="primary-message">No more channels to join</p>
<p className="secondary-message">Click 'Create New Channel' to make a new one</p>
</div>)
- : <div ref="loadingscreen" className="loading-screen loading-screen--channel">
- <div className="loading__content">
- <h3>Loading</h3>
- <div id="round_1" className="round"></div>
- <div id="round_2" className="round"></div>
- <div id="round_3" className="round"></div>
- </div>
- </div>
- }
+ : <LoadingScreen /> }
{ server_error }
</div>
<div className="modal-footer">
diff --git a/web/react/components/msg_typing.jsx b/web/react/components/msg_typing.jsx
index a6953028f..aacb315dd 100644
--- a/web/react/components/msg_typing.jsx
+++ b/web/react/components/msg_typing.jsx
@@ -51,7 +51,7 @@ module.exports = React.createClass({
},
render: function() {
return (
- <span className="msg-typing">{ this.state.text }</span>
+ <span className="msg-typing">{ this.state.text }</span>
);
}
});
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 35f7d9044..34c65c34f 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -2,21 +2,21 @@
// See License.txt for license information.
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
-var Sidebar = require('./sidebar.jsx');
var UserStore = require('../stores/user_store.jsx');
-var SocketStore = require('../stores/socket_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
-var Constants = require('../utils/constants.jsx');
+var TeamStore = require('../stores/team_store.jsx');
+
var UserProfile = require('./user_profile.jsx');
var MessageWrapper = require('./message_wrapper.jsx');
+
+var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
+var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
function getCountsStateFromStores() {
-
var count = 0;
var channels = ChannelStore.getAll();
var members = ChannelStore.getAllMembers();
@@ -34,7 +34,7 @@ function getCountsStateFromStores() {
}
});
- return { count: count }
+ return { count: count };
}
var NotifyCounts = React.createClass({
@@ -54,99 +54,11 @@ var NotifyCounts = React.createClass({
return getCountsStateFromStores();
},
render: function() {
- if (this.state.count == 0) {
- return (<span></span>);
- }
- else {
- return (<span className="badge badge-notify">{ this.state.count }</span>);
- }
- }
-});
-
-var NavbarLoginForm = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var state = { }
-
- var domain = this.refs.domain.getDOMNode().value.trim();
- if (!domain) {
- state.server_error = "A domain is required"
- this.setState(state);
- return;
- }
-
- var email = this.refs.email.getDOMNode().value.trim();
- if (!email) {
- state.server_error = "An email is required"
- this.setState(state);
- return;
- }
-
- var password = this.refs.password.getDOMNode().value.trim();
- if (!password) {
- state.server_error = "A password is required"
- this.setState(state);
- return;
- }
-
- state.server_error = "";
- this.setState(state);
-
- client.loginByEmail(domain, email, password,
- function(data) {
- UserStore.setLastDomain(domain);
- UserStore.setLastEmail(email);
- UserStore.setCurrentUser(data);
-
- var redirect = utils.getUrlParameter("redirect");
- if (redirect) {
- window.location.href = decodeURI(redirect);
- } else {
- window.location.href = '/channels/town-square';
- }
-
- }.bind(this),
- function(err) {
- if (err.message == "Login failed because email address has not been verified") {
- window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email);
- return;
- }
- state.server_error = err.message;
- this.valid = false;
- this.setState(state);
- }.bind(this)
- );
- },
- getInitialState: function() {
- return { };
- },
- render: function() {
- var server_error = this.state.server_error ? <label className="control-label">{this.state.server_error}</label> : null;
-
- var subDomain = utils.getSubDomain();
- var subDomainClass = "form-control hidden";
-
- if (subDomain == "") {
- subDomain = UserStore.getLastDomain();
- subDomainClass = "form-control";
+ if (this.state.count) {
+ return <span className="badge badge-notify">{ this.state.count }</span>;
+ } else {
+ return null;
}
-
- return (
- <form className="navbar-form navbar-right" onSubmit={this.handleSubmit}>
- <a href="/find_team">Find your team</a>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- { server_error }
- <input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
- </div>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- <input type="text" className="form-control" name="email" defaultValue={UserStore.getLastEmail()} ref="email" placeholder="Email" />
- </div>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- <input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
- </div>
- <button type="submit" className="btn btn-default">Login</button>
- </form>
- );
}
});
@@ -159,13 +71,14 @@ function getStateFromStores() {
}
module.exports = React.createClass({
+ displayName: 'Navbar',
+
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(this._onChange);
- var self = this;
- $('.inner__wrap').click(self.hideSidebars);
+ $('.inner__wrap').click(this.hideSidebars);
- $('body').on('click.infopopover', function(e){
+ $('body').on('click.infopopover', function(e) {
if ($(e.target).attr('data-toggle') !== 'popover'
&& $(e.target).parents('.popover.in').length === 0) {
$('.info-popover').popover('hide');
@@ -181,13 +94,13 @@ module.exports = React.createClass({
},
handleLeave: function(e) {
client.leaveChannel(this.state.channel.id,
- function(data) {
+ function(data, text, req) {
AsyncClient.getChannels(true);
- window.location.href = '/channels/town-square';
+ window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square';
}.bind(this),
function(err) {
AsyncClient.dispatchError(err, "handleLeave");
- }.bind(this)
+ }
);
},
hideSidebars: function(e) {
@@ -204,7 +117,7 @@ module.exports = React.createClass({
});
if (e.target.className != 'navbar-toggle' && e.target.className != 'icon-bar') {
- $('.inner__wrap').removeClass('move--right').removeClass('move--left').removeClass('move--left-small');
+ $('.inner__wrap').removeClass('move--right move--left move--left-small');
$('.sidebar--left').removeClass('move--right');
$('.sidebar--right').removeClass('move--left');
$('.sidebar--menu').removeClass('move--left');
@@ -229,24 +142,22 @@ module.exports = React.createClass({
render: function() {
var currentId = UserStore.getCurrentId();
- var channelName = "";
var popoverContent = "";
- var channelTitle = this.props.teamName;
+ var channelTitle = this.props.teamDisplayName;
var isAdmin = false;
var isDirect = false;
var description = ""
+ var channel = this.state.channel;
- if (this.state.channel) {
- var channel = this.state.channel;
- description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true});
- popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>);
- channelName = this.state.channel.name;
+ if (channel) {
+ description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
+ popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
isAdmin = this.state.member.roles.indexOf("admin") > -1;
if (channel.type === 'O') {
- channelTitle = this.state.channel.display_name;
+ channelTitle = channel.display_name;
} else if (channel.type === 'P') {
- channelTitle = this.state.channel.display_name;
+ channelTitle = channel.display_name;
} else if (channel.type === 'D') {
isDirect = true;
if (this.state.users.length > 1) {
@@ -258,12 +169,11 @@ module.exports = React.createClass({
}
}
- if(this.state.channel.description.length == 0){
- popoverContent = React.renderToString(<div>No channel description yet. <br /><a href='#' data-toggle='modal' data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id} data-target='#edit_channel'>Click here</a> to add one.</div>);
+ if (channel.description.length == 0) {
+ popoverContent = React.renderToString(<div>No channel description yet. <br /><a href='#' data-toggle='modal' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id} data-target='#edit_channel'>Click here</a> to add one.</div>);
}
}
- var loginForm = currentId == null ? <NavbarLoginForm /> : null;
var navbar_collapse_button = currentId != null ? null :
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
<span className="sr-only">Toggle sidebar</span>
@@ -292,60 +202,55 @@ module.exports = React.createClass({
{ navbar_collapse_button }
{ sidebar_collapse_button }
{ right_sidebar_collapse_button }
- { !isDirect && this.state.channel ?
- <div className="navbar-brand">
- <div className="dropdown">
- <div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
- <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"></span>
- </a>
- <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
- { isAdmin ?
- <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
- : ""
- }
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Set Channel Description...</a></li>
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Notification Preferences</a></li>
- { isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={this.state.channel.display_name} data-name={this.state.channel.name} data-channelid={this.state.channel.id}>Rename Channel...</a></li>
- : ""
- }
- { isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
- <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Delete Channel...</a></li>
- : ""
- }
- { channelName != Constants.DEFAULT_CHANNEL ?
- <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
- : ""
- }
- </ul>
+ { !isDirect && channel ?
+ <div className="navbar-brand">
+ <div className="dropdown">
+ <div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
+ <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
+ <span className="heading">{channelTitle} </span>
+ <span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
+ </a>
+ <ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
+ { !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
+ : null
+ }
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
+ : null
+ }
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename Channel...</a></li>
+ : null
+ }
+ { isAdmin && !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
+ : null
+ }
+ { !ChannelStore.isDefault(channel) ?
+ <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
+ : null
+ }
+ </ul>
+ </div>
</div>
- </div>
- : "" }
- { isDirect && this.state.channel ?
+ : null
+ }
+ { isDirect && channel ?
<div className="navbar-brand">
- <strong>
- <a href="#"><strong className="heading">{channelTitle}</strong></a>
- </strong>
+ <a href="#" className="heading">{ channelTitle }</a>
</div>
- : "" }
- { !this.state.channel ?
+ : null }
+ { !channel ?
<div className="navbar-brand">
- <strong>
- <a href="/"><strong className="heading">{ channelTitle }</strong></a>
- </strong>
+ <a href="/" className="heading">{ channelTitle }</a>
</div>
: "" }
</div>
- <div className="collapse navbar-collapse" id="navbar-collapse-1">
- { loginForm }
- </div>
</div>
</nav>
);
-}
+ }
});
-
-
diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx
index 160241c1c..49e088458 100644
--- a/web/react/components/new_channel.jsx
+++ b/web/react/components/new_channel.jsx
@@ -6,6 +6,8 @@ var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
+var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
handleSubmit: function(e) {
@@ -60,13 +62,13 @@ module.exports = React.createClass({
var self = this;
client.createChannel(channel,
- function(data) {
+ function() {
this.refs.display_name.getDOMNode().value = "";
this.refs.channel_name.getDOMNode().value = "";
this.refs.channel_desc.getDOMNode().value = "";
$(self.refs.modal.getDOMNode()).modal('hide');
- window.location.href = "/channels/" + channel.name;
+ window.location = TeamStore.getCurrentTeamUrl() + "/channels/" + channel.name;
asyncClient.getChannels(true);
}.bind(this),
function(err) {
@@ -122,7 +124,7 @@ module.exports = React.createClass({
</div>
<div className="form-group">
<label className='control-label'>Description</label>
- <textarea className="form-control" style={{resize: "none"}} ref="channel_desc" rows="3" placeholder="Description" maxLength="1024"></textarea>
+ <textarea className="form-control no-resize" ref="channel_desc" rows="3" placeholder="Description" maxLength="1024"></textarea>
</div>
{ server_error }
</form>
diff --git a/web/react/components/password_reset.jsx b/web/react/components/password_reset.jsx
index 24566c7b1..b2edea620 100644
--- a/web/react/components/password_reset.jsx
+++ b/web/react/components/password_reset.jsx
@@ -10,13 +10,6 @@ SendResetPasswordLink = React.createClass({
e.preventDefault();
var state = {};
- var domain = this.refs.domain.getDOMNode().value.trim();
- if (!domain) {
- state.error = "A domain is required"
- this.setState(state);
- return;
- }
-
var email = this.refs.email.getDOMNode().value.trim();
if (!email) {
state.error = "Please enter a valid email address."
@@ -29,17 +22,17 @@ SendResetPasswordLink = React.createClass({
data = {};
data['email'] = email;
- data['domain'] = domain;
+ data['name'] = this.props.teamName;
client.sendPasswordReset(data,
- function(data) {
- this.setState({ error: null, update_text: <p>A password reset link has been sent to <b>{email}</b> for your <b>{this.props.teamName}</b> team on {config.SiteName}.com.</p>, more_update_text: "Please check your inbox." });
- $(this.refs.reset_form.getDOMNode()).hide();
- }.bind(this),
- function(err) {
- this.setState({ error: err.message, update_text: null, more_update_text: null });
- }.bind(this)
- );
+ function(data) {
+ this.setState({ error: null, update_text: <p>A password reset link has been sent to <b>{email}</b> for your <b>{this.props.teamDisplayName}</b> team on {window.location.hostname}.</p>, more_update_text: "Please check your inbox." });
+ $(this.refs.reset_form.getDOMNode()).hide();
+ }.bind(this),
+ function(err) {
+ this.setState({ error: err.message, update_text: null, more_update_text: null });
+ }.bind(this)
+ );
},
getInitialState: function() {
return {};
@@ -48,24 +41,13 @@ SendResetPasswordLink = React.createClass({
var update_text = this.state.update_text ? <div className="reset-form alert alert-success">{this.state.update_text}{this.state.more_update_text}</div> : null;
var error = this.state.error ? <div className="form-group has-error"><label className="control-label">{this.state.error}</label></div> : null;
- var subDomain = utils.getSubDomain();
- var subDomainClass = "form-control hidden";
-
- if (subDomain == "") {
- subDomain = UserStore.getLastDomain();
- subDomainClass = "form-control";
- }
-
return (
<div className="col-sm-12">
<div className="signup-team__container">
<h3>Password Reset</h3>
{ update_text }
<form onSubmit={this.handleSendLink} ref="reset_form">
- <p>{"To reset your password, enter the email address you used to sign up for " + this.props.teamName + "."}</p>
- <div className="form-group">
- <input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
- </div>
+ <p>{"To reset your password, enter the email address you used to sign up for " + this.props.teamDisplayName + "."}</p>
<div className={error ? 'form-group has-error' : 'form-group'}>
<input type="text" className="form-control" name="email" ref="email" placeholder="Email" />
</div>
@@ -83,13 +65,6 @@ ResetPassword = React.createClass({
e.preventDefault();
var state = {};
- var domain = this.refs.domain.getDOMNode().value.trim();
- if (!domain) {
- state.error = "A domain is required"
- this.setState(state);
- return;
- }
-
var password = this.refs.password.getDOMNode().value.trim();
if (!password || password.length < 5) {
state.error = "Please enter at least 5 characters."
@@ -104,41 +79,30 @@ ResetPassword = React.createClass({
data['new_password'] = password;
data['hash'] = this.props.hash;
data['data'] = this.props.data;
- data['domain'] = domain;
+ data['name'] = this.props.teamName;
client.resetPassword(data,
- function(data) {
- this.setState({ error: null, update_text: "Your password has been updated successfully." });
- }.bind(this),
- function(err) {
- this.setState({ error: err.message, update_text: null });
- }.bind(this)
- );
+ function(data) {
+ this.setState({ error: null, update_text: "Your password has been updated successfully." });
+ }.bind(this),
+ function(err) {
+ this.setState({ error: err.message, update_text: null });
+ }.bind(this)
+ );
},
getInitialState: function() {
return {};
},
render: function() {
- var update_text = this.state.update_text ? <div className="form-group"><br/><label className="control-label reset-form">{this.state.update_text} Click <a href="/login">here</a> to log in.</label></div> : null;
+ var update_text = this.state.update_text ? <div className="form-group"><br/><label className="control-label reset-form">{this.state.update_text} Click <a href={"/" + this.props.teamName + "/login"}>here</a> to log in.</label></div> : null;
var error = this.state.error ? <div className="form-group has-error"><label className="control-label">{this.state.error}</label></div> : null;
- var subDomain = this.props.domain != "" ? this.props.domain : utils.getSubDomain();
- var subDomainClass = "form-control hidden";
-
- if (subDomain == "") {
- subDomain = UserStore.getLastDomain();
- subDomainClass = "form-control";
- }
-
return (
<div className="col-sm-12">
<div className="signup-team__container">
<h3>Password Reset</h3>
<form onSubmit={this.handlePasswordReset}>
- <p>{"Enter a new password for your " + this.props.teamName + " " + config.SiteName + " account."}</p>
- <div className="form-group">
- <input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
- </div>
+ <p>{"Enter a new password for your " + this.props.teamDisplayName + " " + config.SiteName + " account."}</p>
<div className={error ? 'form-group has-error' : 'form-group'}>
<input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
</div>
@@ -161,14 +125,15 @@ module.exports = React.createClass({
if (this.props.isReset === "false") {
return (
<SendResetPasswordLink
+ teamDisplayName={this.props.teamDisplayName}
teamName={this.props.teamName}
/>
);
} else {
return (
<ResetPassword
+ teamDisplayName={this.props.teamDisplayName}
teamName={this.props.teamName}
- domain={this.props.domain}
hash={this.props.hash}
data={this.props.data}
/>
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
index 04b5ba082..e72a2d001 100644
--- a/web/react/components/post.jsx
+++ b/web/react/components/post.jsx
@@ -10,6 +10,7 @@ var UserStore = require('../stores/user_store.jsx');
var ActionTypes = Constants.ActionTypes;
module.exports = React.createClass({
+ displayName: "Post",
componentDidMount: function() {
$('.modal').on('show.bs.modal', function () {
$('.modal-body').css('overflow-y', 'auto');
@@ -19,7 +20,7 @@ module.exports = React.createClass({
handleCommentClick: function(e) {
e.preventDefault();
- data = {};
+ var data = {};
data.order = [this.props.post.id];
data.posts = this.props.posts;
@@ -33,6 +34,10 @@ module.exports = React.createClass({
results: null
});
},
+ forceUpdateInfo: function() {
+ this.refs.info.forceUpdate();
+ this.refs.header.forceUpdate();
+ },
getInitialState: function() {
return { };
},
@@ -48,7 +53,6 @@ module.exports = React.createClass({
var commentCount = 0;
var commentRootId = parentPost ? post.root_id : post.id;
- var rootUser = "";
for (var postId in posts) {
if (posts[postId].root_id == commentRootId) {
commentCount += 1;
@@ -57,12 +61,7 @@ module.exports = React.createClass({
var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null;
- if (this.props.sameRoot){
- rootUser = "same--root";
- }
- else {
- rootUser = "other--root";
- }
+ var rootUser = this.props.sameRoot ? "same--root" : "other--root";
var postType = "";
if (type != "Post"){
@@ -74,18 +73,20 @@ module.exports = React.createClass({
currentUserCss = "current--user";
}
+ var timestamp = UserStore.getCurrentUser().update_at;
+
return (
<div>
<div id={post.id} className={"post " + this.props.sameUser + " " + rootUser + " " + postType + " " + currentUserCss}>
{ !this.props.hideProfilePic ?
<div className="post-profile-img__container">
- <img className="post-profile-img" src={"/api/v1/users/" + post.user_id + "/image"} height="36" width="36" />
+ <img className="post-profile-img" src={"/api/v1/users/" + post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
- : "" }
+ : null }
<div className="post__content">
- <PostHeader post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
+ <PostHeader ref="header" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
<PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} />
- <PostInfo post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply="true" />
+ <PostInfo ref="info" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply="true" />
</div>
</div>
</div>
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 7d5ef4d33..d9678df30 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -71,11 +71,22 @@ module.exports = React.createClass({
name = <a className="theme" onClick={function(){ utils.searchForTerm(profile.username); }}>{profile.username}</a>;
}
- var message = parentPost.message;
+ var message = ""
+ if(parentPost.message) {
+ message = utils.replaceHtmlEntities(parentPost.message)
+ } else if (parentPost.filenames.length) {
+ message = parentPost.filenames[0].split('/').pop();
+
+ if (parentPost.filenames.length === 2) {
+ message += " plus 1 other file";
+ } else if (parentPost.filenames.length > 2) {
+ message += " plus " + (parentPost.filenames.length - 1) + " other files";
+ }
+ }
comment = (
<p className="post-link">
- <span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{utils.replaceHtmlEntities(message)}</a></span>
+ <span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{message}</a></span>
</p>
);
@@ -120,7 +131,7 @@ module.exports = React.createClass({
return (
<div className="post-body">
{ comment }
- <p key={post.Id+"_message"} className={postClass}><span>{inner}</span></p>
+ <p key={post.id+"_message"} className={postClass}><span>{inner}</span></p>
{ filenames && filenames.length > 0 ?
<div className="post-image__columns">
{ postFiles }
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index cf01747f0..d6422fe3a 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -11,6 +11,7 @@ module.exports = React.createClass({
render: function() {
var post = this.props.post;
var isOwner = UserStore.getCurrentId() == post.user_id;
+ var isAdmin = UserStore.getCurrentUser().roles.indexOf("admin") > -1
var type = "Post"
if (post.root_id.length > 0) {
@@ -30,13 +31,11 @@ module.exports = React.createClass({
<div className="dropdown">
{ isOwner || (this.props.allowReply === "true" && type != "Comment") ?
<div>
- <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
- [...]
- </a>
+ <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
{ isOwner ? <li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={post.message} data-postid={post.id} data-channelid={post.channel_id} data-comments={type === "Post" ? this.props.commentCount : 0}>Edit</a></li>
: "" }
- { isOwner ? <li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={post.id} data-channelid={post.channel_id} data-comments={type === "Post" ? this.props.commentCount : 0}>Delete</a></li>
+ { isOwner || isAdmin ? <li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={post.id} data-channelid={post.channel_id} data-comments={type === "Post" ? this.props.commentCount : 0}>Delete</a></li>
: "" }
{ this.props.allowReply === "true" ? <li role="presentation"><a className="reply-link theme" href="#" onClick={this.props.handleCommentClick}>Reply</a></li>
: "" }
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index d6dc9ce30..c058455ba 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -6,8 +6,8 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' );
var AsyncClient = require('../utils/async_client.jsx');
-var CreatePost = require('./create_post.jsx');
var Post = require('./post.jsx');
+var LoadingScreen = require('./loading_screen.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
@@ -26,37 +26,8 @@ function getStateFromStores() {
};
}
-function changeColor(col, amt) {
-
- var usePound = false;
-
- if (col[0] == "#") {
- col = col.slice(1);
- usePound = true;
- }
-
- var num = parseInt(col,16);
-
- var r = (num >> 16) + amt;
-
- if (r > 255) r = 255;
- else if (r < 0) r = 0;
-
- var b = ((num >> 8) & 0x00FF) + amt;
-
- if (b > 255) b = 255;
- else if (b < 0) b = 0;
-
- var g = (num & 0x0000FF) + amt;
-
- if (g > 255) g = 255;
- else if (g < 0) g = 0;
-
- return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6);
-
-}
-
module.exports = React.createClass({
+ displayName: "PostList",
scrollPosition: 0,
preventScrollTrigger: false,
gotMorePosts: false,
@@ -69,7 +40,7 @@ module.exports = React.createClass({
utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;');
utils.changeCss('div.theme', 'background-color:'+user.props.theme+';');
utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';');
- utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + changeColor(user.props.theme, -10) +';');
+ utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';');
utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';');
utils.changeCss('.mention', 'background: ' + user.props.theme+';');
utils.changeCss('.mention-link', 'color: ' + user.props.theme+';');
@@ -78,6 +49,7 @@ module.exports = React.createClass({
PostStore.addChangeListener(this._onChange);
ChannelStore.addChangeListener(this._onChange);
+ UserStore.addStatusesChangeListener(this._onTimeChange);
SocketStore.addChangeListener(this._onSocketChange);
$(".post-list-holder-by-time").perfectScrollbar();
@@ -157,28 +129,25 @@ module.exports = React.createClass({
componentWillUnmount: function() {
PostStore.removeChangeListener(this._onChange);
ChannelStore.removeChangeListener(this._onChange);
+ UserStore.removeStatusesChangeListener(this._onTimeChange);
SocketStore.removeChangeListener(this._onSocketChange);
$('body').off('click.userpopover');
},
resize: function() {
+ var post_holder = $(".post-list-holder-by-time")[0];
+ this.preventScrollTrigger = true;
if (this.gotMorePosts) {
this.gotMorePosts = false;
- var post_holder = $(".post-list-holder-by-time")[0];
- this.preventScrollTrigger = true;
$(post_holder).scrollTop($(post_holder).scrollTop() + (post_holder.scrollHeight-this.oldScrollHeight) );
- $(post_holder).perfectScrollbar('update');
} else {
- var post_holder = $(".post-list-holder-by-time")[0];
- this.preventScrollTrigger = true;
if ($("#new_message")[0] && !this.scrolledToNew) {
$(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63);
- $(post_holder).perfectScrollbar('update');
this.scrolledToNew = true;
} else {
$(post_holder).scrollTop(post_holder.scrollHeight);
- $(post_holder).perfectScrollbar('update');
}
}
+ $(post_holder).perfectScrollbar('update');
},
_onChange: function() {
var newState = getStateFromStores();
@@ -239,12 +208,8 @@ module.exports = React.createClass({
var index = post_list.order.indexOf(msg.props.post_id);
if (index > -1) post_list.order.splice(index, 1);
- var scrollSave = $(".post-list-holder-by-time").scrollTop();
-
this.setState({ post_list: post_list });
- $(".post-list-holder-by-time").scrollTop(scrollSave)
-
PostStore.storePosts(msg.channel_id, post_list);
} else {
AsyncClient.getPosts(true, msg.channel_id);
@@ -253,10 +218,16 @@ module.exports = React.createClass({
if (activeRootPostId === msg.props.post_id && UserStore.getCurrentId() != msg.user_id) {
$('#post_deleted').modal('show');
}
- } else if(msg.action == "new_user") {
+ } else if (msg.action == "new_user") {
AsyncClient.getProfiles();
}
},
+ _onTimeChange: function() {
+ for (var id in this.state.post_list.posts) {
+ if (!this.refs[id]) continue;
+ this.refs[id].forceUpdateInfo();
+ }
+ },
getMorePosts: function(e) {
e.preventDefault();
@@ -297,7 +268,7 @@ module.exports = React.createClass({
},
function(err) {
$(self.refs.loadmore.getDOMNode()).text("Load more messages");
- dispatchError(err, "getPosts");
+ AsyncClient.dispatchError(err, "getPosts");
}
);
},
@@ -338,16 +309,19 @@ module.exports = React.createClass({
var teammate = utils.getDirectTeammate(channel.id)
if (teammate) {
- var teammate_name = teammate.full_name.length > 0 ? teammate.full_name : teammate.username;
+ var teammate_name = teammate.nickname.length > 0 ? teammate.nickname : teammate.username;
more_messages = (
<div className="channel-intro">
<div className="post-profile-img__container channel-intro-img">
- <img className="post-profile-img" src={"/api/v1/users/" + teammate.id + "/image"} height="50" width="50" />
+ <img className="post-profile-img" src={"/api/v1/users/" + teammate.id + "/image?time=" + teammate.update_at} height="50" width="50" />
</div>
<div className="channel-intro-profile">
<strong><UserProfile userId={teammate.id} /></strong>
</div>
- <p className="channel-intro-text">{"This is the start of your private message history with " + teammate_name + "." }<br/>{"Private messages and files shared here are not shown to people outside this area."}</p>
+ <p className="channel-intro-text">
+ {"This is the start of your private message history with " + teammate_name + "." }<br/>
+ {"Private messages and files shared here are not shown to people outside this area."}
+ </p>
</div>
);
} else {
@@ -370,7 +344,7 @@ module.exports = React.createClass({
}
}
- if (channel.name === Constants.DEFAULT_CHANNEL) {
+ if (ChannelStore.isDefault(channel)) {
more_messages = (
<div className="channel-intro">
<h4 className="channel-intro-title">Welcome</h4>
@@ -410,7 +384,7 @@ module.exports = React.createClass({
{ channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
<br/>
<a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}><i className="fa fa-pencil"></i>Set a description</a>
- <a className="intro-links" style={userStyle} data-toggle="modal" data-target="#channel_invite"><i className="fa fa-user-plus"></i>Invite others to this {ui_type}</a>
+ <a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#channel_invite"><i className="fa fa-user-plus"></i>Invite others to this {ui_type}</a>
</p>
</div>
);
@@ -420,37 +394,35 @@ module.exports = React.createClass({
var postCtls = [];
- if (posts != undefined) {
+ if (posts) {
var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date();
- var currentPostDay = new Date();
+ var currentPostDay;
for (var i = order.length-1; i >= 0; i--) {
var post = posts[order[i]];
- var parentPost;
+ var parentPost = post.parent_id ? posts[post.parent_id] : null;
- if (post.parent_id) {
- parentPost = posts[post.parent_id];
- } else {
- parentPost = null;
- }
+ var sameUser = '';
+ var sameRoot = false;
+ var hideProfilePic = false;
+ var prevPost = (i < order.length - 1) ? posts[order[i + 1]] : null;
- var sameUser = i < order.length-1 && posts[order[i+1]].user_id === post.user_id && post.create_at - posts[order[i+1]].create_at <= 1000*60*5 ? "same--user" : "";
- var sameRoot = i < order.length-1 && post.root_id != "" && (posts[order[i+1]].id === post.root_id || posts[order[i+1]].root_id === post.root_id) ? true : false;
+ if (prevPost) {
+ sameUser = (prevPost.user_id === post.user_id) && (post.create_at - prevPost.create_at <= 1000*60*5) ? "same--user" : "";
+ sameRoot = utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id);
- // we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post
- var hideProfilePic = i < order.length-1 && posts[order[i+1]].user_id === post.user_id && posts[order[i+1]].root_id === '' && post.root_id === '';
+ // we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post
+ hideProfilePic = (prevPost.user_id === post.user_id) && !utils.isComment(prevPost) && !utils.isComment(post);
+ }
// check if it's the last comment in a consecutive string of comments on the same post
- var isLastComment = false;
- if (utils.isComment(post)) {
- // it is the last comment if it is last post in the channel or the next post has a different root post
- isLastComment = (i === 0 || posts[order[i-1]].root_id != post.root_id);
- }
+ // it is the last comment if it is last post in the channel or the next post has a different root post
+ var isLastComment = utils.isComment(post) && (i === 0 || posts[order[i-1]].root_id != post.root_id);
- var postCtl = <Post sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id} posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} />;
+ var postCtl = <Post ref={post.id} sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id} posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} />;
currentPostDay = utils.getDateForUnixTicks(post.create_at);
- if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) {
+ if (currentPostDay.toDateString() != previousPostDay.toDateString()) {
postCtls.push(
<div className="date-separator">
<hr className="separator__hr" />
@@ -469,20 +441,10 @@ module.exports = React.createClass({
);
}
postCtls.push(postCtl);
- previousPostDay = utils.getDateForUnixTicks(post.create_at);
+ previousPostDay = currentPostDay;
}
- }
- else {
- postCtls.push(
- <div ref="loadingscreen" className="loading-screen">
- <div className="loading__content">
- <h3>Loading</h3>
- <div id="round_1" className="round"></div>
- <div id="round_2" className="round"></div>
- <div id="round_3" className="round"></div>
- </div>
- </div>
- );
+ } else {
+ postCtls.push(<LoadingScreen position="absolute" />);
}
return (
@@ -497,5 +459,3 @@ module.exports = React.createClass({
);
}
});
-
-
diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx
index 115ee87d4..581a1abe9 100644
--- a/web/react/components/post_right.jsx
+++ b/web/react/components/post_right.jsx
@@ -43,7 +43,7 @@ RhsHeaderPost = React.createClass({
});
},
render: function() {
- var back = this.props.fromSearch ? <a href="#" onClick={this.handleBack} style={{color:"black"}}>{"< "}</a> : "";
+ var back = this.props.fromSearch ? <a href="#" onClick={this.handleBack} className="sidebar--right__back"><i className="fa fa-chevron-left"></i></a> : "";
return (
<div className="sidebar--right__header">
@@ -67,6 +67,8 @@ RootPost = React.createClass({
var message = utils.textToJsx(this.props.post.message);
var filenames = this.props.post.filenames;
var isOwner = UserStore.getCurrentId() == this.props.post.user_id;
+ var timestamp = UserStore.getProfile(this.props.post.user_id).update_at;
+ var channel = ChannelStore.get(this.props.post.channel_id);
var type = "Post";
if (this.props.post.root_id.length > 0) {
@@ -78,6 +80,10 @@ RootPost = React.createClass({
currentUserCss = "current--user";
}
+ if (channel) {
+ channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
+ }
+
if (filenames) {
var postFiles = [];
var images = [];
@@ -117,8 +123,9 @@ RootPost = React.createClass({
return (
<div className={"post post--root " + currentUserCss}>
+ <div className="post-right-channel__name">{ channelName }</div>
<div className="post-profile-img__container">
- <img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image"} height="36" width="36" />
+ <img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
<div className="post__content">
<ul className="post-header">
@@ -128,9 +135,7 @@ RootPost = React.createClass({
<div className="dropdown">
{ isOwner ?
<div>
- <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
- [...]
- </a>
+ <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={this.props.post.message} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id}>Edit</a></li>
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id} data-comments={this.props.commentCount}>Delete</a></li>
@@ -227,11 +232,12 @@ CommentPost = React.createClass({
}
var message = utils.textToJsx(this.props.post.message);
+ var timestamp = UserStore.getCurrentUser().update_at;
return (
<div className={commentClass + " " + currentUserCss}>
<div className="post-profile-img__container">
- <img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image"} height="36" width="36" />
+ <img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
<div className="post__content">
<ul className="post-header">
@@ -240,9 +246,7 @@ CommentPost = React.createClass({
<li className="post-header-col post-header__reply">
{ isOwner ?
<div className="dropdown" onClick={function(e){$('.post-list-holder-by-time').scrollTop($(".post-list-holder-by-time").scrollTop() + 50);}}>
- <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
- [...]
- </a>
+ <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={this.props.post.message} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id}>Edit</a></li>
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id} data-comments={0}>Delete</a></li>
@@ -282,6 +286,7 @@ module.exports = React.createClass({
componentDidMount: function() {
PostStore.addSelectedPostChangeListener(this._onChange);
PostStore.addChangeListener(this._onChangeAll);
+ UserStore.addStatusesChangeListener(this._onTimeChange);
this.resize();
var self = this;
$(window).resize(function(){
@@ -294,6 +299,7 @@ module.exports = React.createClass({
componentWillUnmount: function() {
PostStore.removeSelectedPostChangeListener(this._onChange);
PostStore.removeChangeListener(this._onChangeAll);
+ UserStore.removeStatusesChangeListener(this._onTimeChange);
},
_onChange: function() {
if (this.isMounted()) {
@@ -304,7 +310,6 @@ module.exports = React.createClass({
}
},
_onChangeAll: function() {
-
if (this.isMounted()) {
// if something was changed in the channel like adding a
@@ -333,6 +338,12 @@ module.exports = React.createClass({
this.setState(getStateFromStores());
}
},
+ _onTimeChange: function() {
+ for (var id in this.state.post_list.posts) {
+ if (!this.refs[id]) continue;
+ this.refs[id].forceUpdate();
+ }
+ },
getInitialState: function() {
return getStateFromStores();
},
@@ -392,7 +403,7 @@ module.exports = React.createClass({
<RootPost post={root_post} commentCount={posts_array.length}/>
<div className="post-right-comments-container">
{ posts_array.map(function(cpost) {
- return <CommentPost key={cpost.id} post={cpost} selected={ (cpost.id == selected_post.id) } />
+ return <CommentPost ref={cpost.id} key={cpost.id} post={cpost} selected={ (cpost.id == selected_post.id) } />
})}
</div>
<div className="post-create__container">
diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx
index b4ccb2937..2ae331626 100644
--- a/web/react/components/rename_channel_modal.jsx
+++ b/web/react/components/rename_channel_modal.jsx
@@ -6,6 +6,8 @@ var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
+var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
handleSubmit: function(e) {
@@ -60,12 +62,12 @@ module.exports = React.createClass({
return;
Client.updateChannel(channel,
- function(data) {
+ function(data, text, req) {
this.refs.display_name.getDOMNode().value = "";
this.refs.channel_name.getDOMNode().value = "";
$('#' + this.props.modalId).modal('hide');
- window.location.href = '/channels/' + this.state.channel_name;
+ window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + this.state.channel_name;
AsyncClient.getChannels(true);
}.bind(this),
function(err) {
diff --git a/web/react/components/rename_team_modal.jsx b/web/react/components/rename_team_modal.jsx
index 67a150b9d..a6da57b67 100644
--- a/web/react/components/rename_team_modal.jsx
+++ b/web/react/components/rename_team_modal.jsx
@@ -24,13 +24,13 @@ module.exports = React.createClass({
if (!valid)
return;
- if (this.props.teamName === name)
+ if (this.props.teamDisplayName === name)
return;
var data = {};
data["new_name"] = name;
- Client.updateTeamName(data,
+ Client.updateTeamDisplayName(data,
function(data) {
$('#rename_team_link').modal('hide');
window.location.reload();
@@ -47,11 +47,11 @@ module.exports = React.createClass({
componentDidMount: function() {
var self = this;
$(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) {
- self.setState({ name: self.props.teamName });
+ self.setState({ name: self.props.teamDisplayName });
});
},
getInitialState: function() {
- return { name: this.props.teamName };
+ return { name: this.props.teamDisplayName };
},
render: function() {
diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx
index cddb738f9..f21f0cd58 100644
--- a/web/react/components/search_bar.jsx
+++ b/web/react/components/search_bar.jsx
@@ -3,6 +3,7 @@
var client = require('../utils/client.jsx');
+var AsyncClient = require('../utils/async_client.jsx');
var PostStore = require('../stores/post_store.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var utils = require('../utils/utils.jsx');
@@ -10,14 +11,14 @@ var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
function getSearchTermStateFromStores() {
- term = PostStore.getSearchTerm();
- if (!term) term = "";
+ var term = PostStore.getSearchTerm() || '';
return {
search_term: term
};
}
module.exports = React.createClass({
+ displayName: 'SearchBar',
componentDidMount: function() {
PostStore.addSearchTermChangeListener(this._onChange);
},
@@ -58,14 +59,14 @@ module.exports = React.createClass({
e.target.select();
},
performSearch: function(terms, isMentionSearch) {
- if (terms.length > 0) {
- $("#search-spinner").removeClass("hidden");
+ if (terms.length) {
+ this.setState({isSearching: true});
client.search(
terms,
function(data) {
- $("#search-spinner").addClass("hidden");
- if(utils.isMobile()) {
- $('#search')[0].value = "";
+ this.setState({isSearching: false});
+ if (utils.isMobile()) {
+ React.findDOMNode(this.refs.search).value = '';
}
AppDispatcher.handleServerAction({
@@ -73,18 +74,17 @@ module.exports = React.createClass({
results: data,
is_mention_search: isMentionSearch
});
- },
+ }.bind(this),
function(err) {
- $("#search-spinner").addClass("hidden");
- dispatchError(err, "search");
- }
+ this.setState({isSearching: false});
+ AsyncClient.dispatchError(err, "search");
+ }.bind(this)
);
}
},
handleSubmit: function(e) {
e.preventDefault();
- terms = this.state.search_term.trim();
- this.performSearch(terms);
+ this.performSearch(this.state.search_term.trim());
},
getInitialState: function() {
return getSearchTermStateFromStores();
@@ -92,11 +92,18 @@ module.exports = React.createClass({
render: function() {
return (
<div>
- <div className="sidebar__collapse" onClick={this.handleClose}></div>
+ <div className="sidebar__collapse" onClick={this.handleClose}>Cancel</div>
<span className="glyphicon glyphicon-search sidebar__search-icon"></span>
<form role="form" className="search__form relative-div" onSubmit={this.handleSubmit}>
- <input type="text" className="form-control search-bar-box" ref="search" id="search" placeholder="Search" value={this.state.search_term} onFocus={this.handleUserFocus} onChange={this.handleUserInput} />
- <span id="search-spinner" className="glyphicon glyphicon-refresh glyphicon-refresh-animate hidden"></span>
+ <input
+ type="text"
+ ref="search"
+ className="form-control search-bar-box"
+ placeholder="Search"
+ value={this.state.search_term}
+ onFocus={this.handleUserFocus}
+ onChange={this.handleUserInput} />
+ {this.state.isSearching ? <span className={"glyphicon glyphicon-refresh glyphicon-refresh-animate"}></span> : null}
</form>
</div>
);
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
index 003a38b7e..643ad112b 100644
--- a/web/react/components/search_results.jsx
+++ b/web/react/components/search_results.jsx
@@ -8,12 +8,13 @@ var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' );
var SearchBox =require('./search_bar.jsx');
var utils = require('../utils/utils.jsx');
-var client =require('../utils/client.jsx');
+var client = require('../utils/client.jsx');
+var AsyncClient = require('../utils/async_client.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-RhsHeaderSearch = React.createClass({
+var RhsHeaderSearch = React.createClass({
handleClose: function(e) {
e.preventDefault();
@@ -32,13 +33,13 @@ RhsHeaderSearch = React.createClass({
return (
<div className="sidebar--right__header">
<span className="sidebar--right__title">{title}</span>
- <button type="button" className="sidebar--right__close" aria-label="Close" onClick={this.handleClose}></button>
+ <button type="button" className="sidebar--right__close" aria-label="Close" title="Close" onClick={this.handleClose}></button>
</div>
);
}
});
-SearchItem = React.createClass({
+var SearchItem = React.createClass({
handleClick: function(e) {
e.preventDefault();
@@ -62,39 +63,41 @@ SearchItem = React.createClass({
});
},
function(err) {
- dispatchError(err, "getPost");
+ AsyncClient.dispatchError(err, "getPost");
}
);
- var postChannel = ChannelStore.get(this.props.post.channel_id);
- var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : "";
+ var postChannel = ChannelStore.get(this.props.post.channel_id);
+ var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : "";
- utils.switchChannel(postChannel,teammate);
+ utils.switchChannel(postChannel, teammate);
},
+
render: function() {
var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch});
var channelName = "";
- var channel = ChannelStore.get(this.props.post.channel_id)
+ var channel = ChannelStore.get(this.props.post.channel_id);
+ var timestamp = UserStore.getCurrentUser().update_at;
if (channel) {
- if (channel.type === 'D') {
- channelName = "Private Message";
- } else {
- channelName = channel.display_name;
- }
+ channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
}
- return (
+ return (
<div className="search-item-container post" onClick={this.handleClick}>
<div className="search-channel__name">{ channelName }</div>
<div className="post-profile-img__container">
- <img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image"} height="36" width="36" />
+ <img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
<div className="post__content">
<ul className="post-header">
<li className="post-header-col"><strong><UserProfile userId={this.props.post.user_id} /></strong></li>
- <li className="post-header-col"><time className="search-item-time">{ utils.displayDate(this.props.post.create_at)+' '+utils.displayTime(this.props.post.create_at) }</time></li>
+ <li className="post-header-col">
+ <time className="search-item-time">
+ { utils.displayDate(this.props.post.create_at) + ' ' + utils.displayTime(this.props.post.create_at) }
+ </time>
+ </li>
</ul>
<div className="search-item-snippet"><span>{message}</span></div>
</div>
@@ -103,11 +106,13 @@ SearchItem = React.createClass({
}
});
+
function getStateFromStores() {
return { results: PostStore.getSearchResults() };
}
module.exports = React.createClass({
+ displayName: 'SearchResults',
componentDidMount: function() {
PostStore.addSearchChangeListener(this._onChange);
this.resize();
@@ -143,41 +148,24 @@ module.exports = React.createClass({
var results = this.state.results;
var currentId = UserStore.getCurrentId();
- var searchForm = currentId == null ? null : <SearchBox />;
-
- if (results == null) {
- return (
- <div className="sidebar--right__header">
- <div className="sidebar__heading">Search Results</div>
- </div>
- );
- }
+ var searchForm = currentId ? <SearchBox /> : null;
+ var noResults = (!results || !results.order || !results.order.length);
+ var searchTerm = PostStore.getSearchTerm();
- if (!results.order || results.order.length == 0) {
- return (
- <div className="sidebar--right__content">
- <div className="search-bar__container">{searchForm}</div>
- <div className="sidebar-right__body">
- <RhsHeaderSearch />
- <div id="search-items-container" className="search-items-container">
- <div className="sidebar--right__subheader">No results</div>
- </div>
- </div>
- </div>
- );
- }
-
- var self = this;
return (
<div className="sidebar--right__content">
<div className="search-bar__container sidebar--right__search-header">{searchForm}</div>
<div className="sidebar-right__body">
<RhsHeaderSearch isMentionSearch={this.props.isMentionSearch} />
<div id="search-items-container" className="search-items-container">
- {results.order.map(function(id) {
- var post = results.posts[id];
- return <SearchItem key={post.id} post={post} term={PostStore.getSearchTerm()} isMentionSearch={self.props.isMentionSearch} />
- })}
+
+ { noResults ? <div className="sidebar--right__subheader">No results</div>
+ : results.order.map(function(id) {
+ var post = results.posts[id];
+ return <SearchItem key={post.id} post={post} term={searchTerm} isMentionSearch={this.props.isMentionSearch} />
+ }, this)
+ }
+
</div>
</div>
</div>
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 2095978e8..3cf67e410 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -6,6 +6,7 @@ var ChannelStore = require('../stores/channel_store.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx');
@@ -13,93 +14,6 @@ var SearchBox = require('./search_bar.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-var SidebarLoginForm = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- var state = { }
-
- var domain = this.refs.domain.getDOMNode().value.trim();
- if (!domain) {
- state.server_error = "A domain is required"
- this.setState(state);
- return;
- }
-
- var email = this.refs.email.getDOMNode().value.trim();
- if (!email) {
- state.server_error = "An email is required"
- this.setState(state);
- return;
- }
-
- var password = this.refs.password.getDOMNode().value.trim();
- if (!password) {
- state.server_error = "A password is required"
- this.setState(state);
- return;
- }
-
- state.server_error = "";
- this.setState(state);
-
- client.loginByEmail(domain, email, password,
- function(data) {
- UserStore.setLastDomain(domain);
- UserStore.setLastEmail(email);
- UserStore.setCurrentUser(data);
-
- var redirect = utils.getUrlParameter("redirect");
- if (redirect) {
- window.location.href = decodeURI(redirect);
- } else {
- window.location.href = '/channels/town-square';
- }
-
- }.bind(this),
- function(err) {
- if (err.message == "Login failed because email address has not been verified") {
- window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email);
- return;
- }
- state.server_error = err.message;
- this.valid = false;
- this.setState(state);
- }.bind(this)
- );
- },
- getInitialState: function() {
- return { };
- },
- render: function() {
- var server_error = this.state.server_error ? <label className="control-label">{this.state.server_error}</label> : null;
-
- var subDomain = utils.getSubDomain();
- var subDomainClass = "form-control hidden";
-
- if (subDomain == "") {
- subDomain = UserStore.getLastDomain();
- subDomainClass = "form-control";
- }
-
- return (
- <form className="" onSubmit={this.handleSubmit}>
- <a href="/find_team">{"Find your " + strings.Team}</a>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- { server_error }
- <input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
- </div>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- <input type="text" className="form-control" name="email" defaultValue={UserStore.getLastEmail()} ref="email" placeholder="Email" />
- </div>
- <div className={server_error ? 'form-group has-error' : 'form-group'}>
- <input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
- </div>
- <button type="submit" className="btn btn-default">Login</button>
- </form>
- );
- }
-});
-
function getStateFromStores() {
var members = ChannelStore.getAllMembers();
var team_member_map = UserStore.getActiveOnlyProfiles();
@@ -131,7 +45,7 @@ function getStateFromStores() {
var channel = ChannelStore.getByName(channelName);
if (channel != null) {
- channel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username;
+ channel.display_name = utils.getDisplayName(teammate);
channel.teammate_username = teammate.username;
channel.status = UserStore.getStatus(teammate.id);
@@ -150,7 +64,7 @@ function getStateFromStores() {
var tempChannel = {};
tempChannel.fake = true;
tempChannel.name = channelName;
- tempChannel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username;
+ tempChannel.display_name = utils.getDisplayName(teammate);
tempChannel.status = UserStore.getStatus(teammate.id);
tempChannel.last_post_at = 0;
readDirectChannels.push(tempChannel);
@@ -192,7 +106,7 @@ function getStateFromStores() {
};
}
-var SidebarLoggedIn = React.createClass({
+module.exports = React.createClass({
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
UserStore.addChangeListener(this._onChange);
@@ -249,11 +163,27 @@ var SidebarLoggedIn = React.createClass({
var repRegex = new RegExp("<br>", "g");
var post = JSON.parse(msg.props.post);
- var msg = post.message.replace(repRegex, "\n").split("\n")[0].replace("<mention>", "").replace("</mention>", "");
+ var msgProps = msg.props;
+ var msg = post.message.replace(repRegex, "\n").replace(/\n+/g, " ").replace("<mention>", "").replace("</mention>", "");
+
if (msg.length > 50) {
msg = msg.substring(0,49) + "...";
}
- utils.notifyMe(title, username + " wrote: " + msg, channel);
+
+ if (msg.length === 0) {
+ if (msgProps.image) {
+ utils.notifyMe(title, username + " uploaded an image", channel);
+ }
+ else if (msgProps.otherFile) {
+ utils.notifyMe(title, username + " uploaded a file", channel);
+ }
+ else {
+ utils.notifyMe(title, username + " did something new", channel);
+ }
+ }
+ else {
+ utils.notifyMe(title, username + " wrote: " + msg, channel);
+ }
if (!user.notify_props || user.notify_props.desktop_sound === "true") {
utils.ding();
}
@@ -367,7 +297,7 @@ var SidebarLoggedIn = React.createClass({
);
} else {
return (
- <li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href={"/channels/"+channel.name}><span className="status" dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
+ <li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href={TeamStore.getCurrentTeamUrl() + "/channels/"+channel.name}><span className="status" dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
);
}
@@ -398,7 +328,7 @@ var SidebarLoggedIn = React.createClass({
}
return (
<div>
- <SidebarHeader teamName={this.props.teamName} teamType={this.props.teamType} />
+ <SidebarHeader teamDisplayName={this.props.teamDisplayName} teamType={this.props.teamType} />
<SearchBox />
<div className="nav-pills__container">
@@ -424,25 +354,3 @@ var SidebarLoggedIn = React.createClass({
);
}
});
-
-var SidebarLoggedOut = React.createClass({
- render: function() {
- return (
- <div>
- <SidebarHeader teamName={this.props.teamName} />
- <SidebarLoginForm />
- </div>
- );
- }
-});
-
-module.exports = React.createClass({
- render: function() {
- var currentId = UserStore.getCurrentId();
- if (currentId != null) {
- return <SidebarLoggedIn teamName={this.props.teamName} teamType={this.props.teamType} />;
- } else {
- return <SidebarLoggedOut teamName={this.props.teamName} />;
- }
- }
-});
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 0b59d2036..bab2897b6 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -37,17 +37,13 @@ var NavbarDropdown = React.createClass({
var invite_link = "";
var manage_link = "";
var rename_link = "";
- var currentUser = UserStore.getCurrentUser()
+ var currentUser = UserStore.getCurrentUser();
var isAdmin = false;
if (currentUser != null) {
isAdmin = currentUser.roles.indexOf("admin") > -1;
- invite_link = (
- <li>
- <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a>
- </li>
- );
+ invite_link = ( <li> <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a> </li>);
if (this.props.teamType == "O") {
team_link = (
@@ -59,43 +55,31 @@ var NavbarDropdown = React.createClass({
}
if (isAdmin) {
- manage_link = (
- <li>
- <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a>
- </li>
- );
- rename_link = (
- <li>
- <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a>
- </li>
- );
+ manage_link = ( <li> <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a> </li>);
+ rename_link = ( <li> <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a> </li>);
}
var teams = [];
+ teams.push(<li className="divider" key="div"></li>);
if (this.state.teams.length > 1) {
for (var i = 0; i < this.state.teams.length; i++) {
- var domain = this.state.teams[i];
-
- if (domain == utils.getSubDomain())
- continue;
+ var teamName = this.state.teams[i];
- if (teams.length == 0)
- teams.push(<li className="divider" key="div"></li>);
-
- teams.push(<li key={ domain }><a href={window.location.protocol + "//" + domain + "." + utils.getDomainWithOutSub() }>Switch to { domain }</a></li>);
+ teams.push(<li key={ teamName }><a href={window.location.origin + "/" + teamName }>Switch to { teamName }</a></li>);
}
}
+ teams.push(<li><a href={window.location.origin + "/signup_team" }>Create a New Team</a></li>);
return (
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
- <i className="dropdown__icon"></i>
+ <span className="dropdown__icon" dangerouslySetInnerHTML={{__html: " <svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>"}} />
</a>
<ul className="dropdown-menu" role="menu">
<li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li>
- { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : "" }
+ { isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : null }
{ invite_link }
{ team_link }
{ manage_link }
@@ -113,23 +97,32 @@ var NavbarDropdown = React.createClass({
});
module.exports = React.createClass({
- handleSubmit: function(e) {
- e.preventDefault();
- },
- getInitialState: function() {
- return { };
+ displayName: 'SidebarHeader',
+
+ getDefaultProps: function() {
+ return {
+ teamName: config.SiteName
+ };
},
+
render: function() {
- var teamName = this.props.teamName ? this.props.teamName : config.SiteName;
+ var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : config.SiteName;
+ var me = UserStore.getCurrentUser()
+ if (!me) {
+ return null;
+ }
return (
<div className="team__header theme">
- <a className="team__name" href="/channels/town-square">{ teamName }</a>
+ <a className="settings_link" href="#" data-toggle="modal" data-target="#user_settings1">
+ <img className="user__picture" src={"/api/v1/users/" + me.id + "/image?time=" + me.update_at} />
+ <div className="header__info">
+ <div className="user__name">{ '@' + me.username}</div>
+ <div className="team__name">{ teamDisplayName }</div>
+ </div>
+ </a>
<NavbarDropdown teamType={this.props.teamType} />
</div>
);
}
});
-
-
-
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index 22d1d9ad2..15306a499 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -49,12 +49,12 @@ module.exports = React.createClass({
}
var siteName = config.SiteName != null ? config.SiteName : "";
- var teamName = this.props.teamName ? this.props.teamName : siteName;
+ var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : siteName;
return (
<div>
<div className="team__header theme">
- <a className="team__name" href="/channels/town-square">{ teamName }</a>
+ <a className="team__name" href="/channels/town-square">{ teamDisplayName }</a>
</div>
<div className="nav-pills__container">
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
index 22086250c..cf982cc1e 100644
--- a/web/react/components/signup_team.jsx
+++ b/web/react/components/signup_team.jsx
@@ -20,8 +20,8 @@ module.exports = React.createClass({
state.email_error = "";
}
- team.name = this.refs.name.getDOMNode().value.trim();
- if (!team.name) {
+ team.display_name = this.refs.name.getDOMNode().value.trim();
+ if (!team.display_name) {
state.name_error = "This field is required";
state.inValid = true;
}
@@ -34,7 +34,7 @@ module.exports = React.createClass({
return;
}
- client.signupTeam(team.email, team.name,
+ client.signupTeam(team.email, team.display_name,
function(data) {
if (data["follow_link"]) {
window.location.href = data["follow_link"];
@@ -61,7 +61,7 @@ module.exports = React.createClass({
return (
<form role="form" onSubmit={this.handleSubmit}>
<div className={ email_error ? "form-group has-error" : "form-group" }>
- <input type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" />
+ <input autoFocus={true} type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" />
{ email_error }
</div>
<div className={ name_error ? "form-group has-error" : "form-group" }>
@@ -70,6 +70,9 @@ module.exports = React.createClass({
</div>
{ server_error }
<button className="btn btn-md btn-primary" type="submit">Sign up for Free</button>
+ <div className="form-group form-group--small">
+ <span><a href="/find_team">{"Find my " + strings.Team}</a></span>
+ </div>
</form>
);
}
diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx
index 500ee231e..9ceeb6324 100644
--- a/web/react/components/signup_team_complete.jsx
+++ b/web/react/components/signup_team_complete.jsx
@@ -15,7 +15,7 @@ WelcomePage = React.createClass({
return;
}
e.preventDefault();
- this.props.state.wizard = "team_name";
+ this.props.state.wizard = "team_display_name";
this.props.updateParent(this.props.state);
},
handleDiffEmail: function (e) {
@@ -57,6 +57,17 @@ WelcomePage = React.createClass({
getInitialState: function() {
return { use_diff: false };
},
+ handleKeyPress: function(event) {
+ if (event.keyCode == 13) {
+ this.submitNext(event);
+ }
+ },
+ componentWillMount: function() {
+ document.addEventListener("keyup", this.handleKeyPress, false);
+ },
+ componentWillUnmount: function() {
+ document.removeEventListener("keyup", this.handleKeyPress, false);
+ },
render: function() {
client.track('signup', 'signup_team_01_welcome');
@@ -77,7 +88,7 @@ WelcomePage = React.createClass({
<span className="black">{ this.props.state.team.email }</span><br />
</p>
<div className="form-group">
- <button className="btn-primary btn form-group" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
+ <button className="btn-primary btn form-group" type="submit" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
{ storage_error }
</div>
<hr />
@@ -92,15 +103,15 @@ WelcomePage = React.createClass({
{ email_error }
</div>
{ server_error }
- <button className="btn btn-md btn-primary" onClick={this.handleDiffSubmit} type="submit">Use this instead</button>
+ <button className="btn btn-md btn-primary" type="button" onClick={this.handleDiffSubmit} type="submit">Use this instead</button>
</div>
- <button onClick={this.handleDiffEmail} className={ this.state.use_diff ? "btn-default btn hidden" : "btn-default btn" }>Use a different address</button>
+ <button type="button" onClick={this.handleDiffEmail} className={ this.state.use_diff ? "btn-default btn hidden" : "btn-default btn" }>Use a different address</button>
</div>
);
}
});
-TeamNamePage = React.createClass({
+TeamDisplayNamePage = React.createClass({
submitBack: function (e) {
e.preventDefault();
this.props.state.wizard = "welcome";
@@ -109,19 +120,24 @@ TeamNamePage = React.createClass({
submitNext: function (e) {
e.preventDefault();
- var name = this.refs.name.getDOMNode().value.trim();
- if (!name) {
+ var display_name = this.refs.name.getDOMNode().value.trim();
+ if (!display_name) {
this.setState({name_error: "This field is required"});
return;
}
this.props.state.wizard = "team_url";
- this.props.state.team.name = name;
+ this.props.state.team.display_name = display_name;
this.props.updateParent(this.props.state);
},
getInitialState: function() {
return { };
},
+ handleFocus: function(e) {
+ e.preventDefault();
+
+ e.currentTarget.select();
+ },
render: function() {
client.track('signup', 'signup_team_02_name');
@@ -130,29 +146,31 @@ TeamNamePage = React.createClass({
return (
<div>
+ <form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>{utils.toTitleCase(strings.Team) + " Name"}</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
- <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.name} />
+ <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.display_name} autoFocus={true} onFocus={this.handleFocus} />
</div>
</div>
{ name_error }
</div>
<p>{"Your " + strings.Team + " name shows in menus and headings. It may include the name of your " + strings.Company + ", but it's not required."}</p>
- <button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
- <button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ <button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
+ <button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ </form>
</div>
);
}
});
-TeamUrlPage = React.createClass({
+TeamURLPage = React.createClass({
submitBack: function (e) {
e.preventDefault();
- this.props.state.wizard = "team_name";
+ this.props.state.wizard = "team_display_name";
this.props.updateParent(this.props.state);
},
submitNext: function (e) {
@@ -172,18 +190,18 @@ TeamUrlPage = React.createClass({
return;
}
else if (cleaned_name.length <= 3 || cleaned_name.length > 15) {
- this.setState({name_error: "Domain must be 4 or more characters up to a maximum of 15"})
+ this.setState({name_error: "Name must be 4 or more characters up to a maximum of 15"})
return;
}
- for (var index = 0; index < constants.RESERVED_DOMAINS.length; index++) {
- if (cleaned_name.indexOf(constants.RESERVED_DOMAINS[index]) == 0) {
- this.setState({name_error: "This Team URL name is unavailable"})
+ for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) {
+ if (cleaned_name.indexOf(constants.RESERVED_TEAM_NAMES[index]) == 0) {
+ this.setState({name_error: "This team name is unavailable"})
return;
}
}
- client.findTeamByDomain(name,
+ client.findTeamByName(name,
function(data) {
if (!data) {
if (config.AllowSignupDomainsWizard) {
@@ -193,7 +211,7 @@ TeamUrlPage = React.createClass({
this.props.state.team.type = 'O';
}
- this.props.state.team.domain = name;
+ this.props.state.team.name = name;
this.props.updateParent(this.props.state);
}
else {
@@ -210,6 +228,11 @@ TeamUrlPage = React.createClass({
getInitialState: function() {
return { };
},
+ handleFocus: function(e) {
+ e.preventDefault();
+
+ e.currentTarget.select();
+ },
render: function() {
client.track('signup', 'signup_team_03_url');
@@ -218,14 +241,15 @@ TeamUrlPage = React.createClass({
return (
<div>
+ <form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>{utils.toTitleCase(strings.Team) + " URL"}</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
<div className="input-group">
- <input type="text" ref="name" className="form-control text-right" placeholder="" maxLength="128" defaultValue={this.props.state.team.domain} />
- <span className="input-group-addon">.{ utils.getDomainWithOutSub() }</span>
+ <span className="input-group-addon">{ window.location.origin + "/" }</span>
+ <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.name} autoFocus={true} onFocus={this.handleFocus}/>
</div>
</div>
</div>
@@ -233,8 +257,9 @@ TeamUrlPage = React.createClass({
</div>
<p className="black">{"Pick something short and memorable for your " + strings.Team + "'s web address."}</p>
<p>{"Your " + strings.Team + " URL can only contain lowercase letters, numbers and dashes. Also, it needs to start with a letter and cannot end in a dash."}</p>
- <button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
- <button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ <button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
+ <button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ </form>
</div>
);
}
@@ -291,6 +316,7 @@ AllowedDomainsPage = React.createClass({
return (
<div>
+ <form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Email Domain</h2>
<p>
@@ -303,7 +329,7 @@ AllowedDomainsPage = React.createClass({
<div className="col-sm-9">
<div className="input-group">
<span className="input-group-addon">@</span>
- <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.allowed_domains} />
+ <input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.allowed_domains} autoFocus={true} onFocus={this.handleFocus}/>
</div>
</div>
</div>
@@ -313,8 +339,9 @@ AllowedDomainsPage = React.createClass({
<p>
<div className="checkbox"><label><input type="checkbox" ref="open_network" defaultChecked={this.props.state.team.type == 'O'} /> Allow anyone to signup to this domain without an invitation.</label></div>
</p>
- <button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
- <button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ <button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
+ <button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ </form>
</div>
);
}
@@ -356,7 +383,7 @@ EmailItem = React.createClass({
return (
<div className={ email_error ? "form-group has-error" : "form-group" }>
- <input type="email" ref="email" className="form-control" placeholder="Email Address" defaultValue={this.props.email} maxLength="128" />
+ <input autoFocus={this.props.focus} type="email" ref="email" className="form-control" placeholder="Email Address" defaultValue={this.props.email} maxLength="128" />
{ email_error }
</div>
);
@@ -424,16 +451,22 @@ SendInivtesPage = React.createClass({
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
- emails.push(<EmailItem key={i} ref={'email_' + i} email={this.props.state.invites[i]} />);
+ if (i == 0) {
+ emails.push(<EmailItem focus={true} key={i} ref={'email_' + i} email={this.props.state.invites[i]} />);
+ } else {
+ emails.push(<EmailItem focus={false} key={i} ref={'email_' + i} email={this.props.state.invites[i]} />);
+ }
}
return (
<div>
+ <form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Send Invitations</h2>
{ emails }
- <div className="form-group"><button className="btn-default btn" onClick={this.submitAddInvite}>Add Invitation</button></div>
- <div className="form btn-default-group"><button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;<button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button></div>
+ <div className="form-group"><button type="button" className="btn-default btn" onClick={this.submitAddInvite}>Add Invitation</button></div>
+ <div className="form btn-default-group"><button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button></div>
+ </form>
<p>{"If you'd prefer, you can send invitations after you finish setting up the "+ strings.Team + "."}</p>
<div><a href="#" onClick={this.submitSkip}>Skip this step</a></div>
</div>
@@ -477,20 +510,22 @@ UsernamePage = React.createClass({
return (
<div>
+ <form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a username</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
- <input type="text" ref="name" className="form-control" placeholder="" defaultValue={this.props.state.user.username} maxLength="128" />
+ <input autoFocus={true} type="text" ref="name" className="form-control" placeholder="" defaultValue={this.props.state.user.username} maxLength="128" />
</div>
</div>
{ name_error }
</div>
<p>{"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others."}</p>
<p>It can be made of lowercase letters and numbers.</p>
- <button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
- <button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ <button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
+ <button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
+ </form>
</div>
);
}
@@ -531,18 +566,11 @@ PasswordPage = React.createClass({
props.state.wizard = "finished";
props.updateParent(props.state, true);
- if (utils.isTestDomain()) {
- UserStore.setLastDomain(teamSignup.team.domain);
- UserStore.setLastEmail(teamSignup.team.email);
- window.location.href = window.location.protocol + '//' + utils.getDomainWithOutSub() + '/login?email=' + encodeURIComponent(teamSignup.team.email);
- }
- else {
- window.location.href = window.location.protocol + '//' + teamSignup.team.domain + '.' + utils.getDomainWithOutSub() + '/login?email=' + encodeURIComponent(teamSignup.team.email);
- }
+ window.location.href = window.location.origin + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email);
// client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password,
// function(data) {
- // UserStore.setLastDomain(teamSignup.team.domain);
+ // TeamStore.setLastName(teamSignup.team.domain);
// UserStore.setLastEmail(teamSignup.team.email);
// UserStore.setCurrentUser(data);
// window.location.href = '/channels/town-square';
@@ -570,13 +598,14 @@ PasswordPage = React.createClass({
return (
<div>
+ <form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a password</h2>
<p>You'll use your email address ({this.props.state.team.email}) and password to log into {config.SiteName}.</p>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
- <input type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
+ <input autoFocus={true} type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
</div>
</div>
{ name_error }
@@ -585,10 +614,11 @@ PasswordPage = React.createClass({
<label><input type="checkbox" ref="email_service" /> It's ok to send me occassional email with updates about the {config.SiteName} service.</label>
</div>
<div className="form-group">
- <button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
- <button className="btn-primary btn" id="finish-button" data-loading-text={"<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Creating "+strings.Team+"..."} onClick={this.submitNext}>Finish</button>
+ <button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
+ <button type="submit" className="btn-primary btn" id="finish-button" data-loading-text={"<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Creating "+strings.Team+"..."} onClick={this.submitNext}>Finish</button>
</div>
<p>By proceeding to create your account and use { config.SiteName }, you agree to our <a href={ config.TermsLink }>Terms of Service</a> and <a href={ config.PrivacyLink }>Privacy Policy</a>. If you do not agree, you cannot use {config.SiteName}.</p>
+ </form>
</div>
);
}
@@ -596,28 +626,23 @@ PasswordPage = React.createClass({
module.exports = React.createClass({
updateParent: function(state, skipSet) {
- BrowserStore.setGlobalItem(this.props.hash, JSON.stringify(state));
+ BrowserStore.setGlobalItem(this.props.hash, state);
if (!skipSet) {
this.setState(state);
}
},
getInitialState: function() {
- var props = null;
- try {
- props = JSON.parse(BrowserStore.getGlobalItem(this.props.hash));
- }
- catch(parse_error) {
- }
+ var props = BrowserStore.getGlobalItem(this.props.hash);
if (!props) {
props = {};
props.wizard = "welcome";
props.team = {};
props.team.email = this.props.email;
- props.team.name = this.props.name;
+ props.team.display_name = this.props.name;
props.team.company_name = this.props.name;
- props.team.domain = utils.cleanUpUrlable(this.props.name);
+ props.team.name = utils.cleanUpUrlable(this.props.name);
props.team.allowed_domains = "";
props.invites = [];
props.invites.push("");
@@ -628,19 +653,19 @@ module.exports = React.createClass({
props.data = this.props.data;
}
- return props ;
+ return props;
},
render: function() {
if (this.state.wizard == "welcome") {
return <WelcomePage state={this.state} updateParent={this.updateParent} />
}
- if (this.state.wizard == "team_name") {
- return <TeamNamePage state={this.state} updateParent={this.updateParent} />
+ if (this.state.wizard == "team_display_name") {
+ return <TeamDisplayNamePage state={this.state} updateParent={this.updateParent} />
}
if (this.state.wizard == "team_url") {
- return <TeamUrlPage state={this.state} updateParent={this.updateParent} />
+ return <TeamURLPage state={this.state} updateParent={this.updateParent} />
}
if (this.state.wizard == "allowed_domains") {
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index fb96cc99f..eed323d1f 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -17,7 +17,7 @@ module.exports = React.createClass({
return;
}
- var username_error = utils.isValidUsername(this.state.user.username)
+ var username_error = utils.isValidUsername(this.state.user.username);
if (username_error === "Cannot use a reserved word as a username.") {
this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""});
return;
@@ -46,25 +46,25 @@ module.exports = React.createClass({
function(data) {
client.track('signup', 'signup_user_02_complete');
- if (data.email_verified) {
- client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password,
- function(data) {
- UserStore.setLastDomain(this.props.domain);
- UserStore.setLastEmail(this.state.user.email);
- UserStore.setCurrentUser(data);
- if (this.props.hash > 0)
- BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"}));
- window.location.href = '/channels/town-square';
- }.bind(this),
- function(err) {
+ client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password,
+ function(data) {
+ UserStore.setLastEmail(this.state.user.email);
+ UserStore.setCurrentUser(data);
+ if (this.props.hash > 0)
+ {
+ BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"}));
+ }
+ window.location.href = '/';
+ }.bind(this),
+ function(err) {
+ if (err.message == "Login failed because email address has not been verified") {
+ window.location.href = "/verify_email?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain);
+ } else {
this.state.server_error = err.message;
this.setState(this.state);
- }.bind(this)
- );
- }
- else {
- window.location.href = "/verify?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain);
- }
+ }
+ }.bind(this)
+ );
}.bind(this),
function(err) {
this.state.server_error = err.message;
@@ -73,12 +73,7 @@ module.exports = React.createClass({
);
},
getInitialState: function() {
- var props = null;
- try {
- props = JSON.parse(BrowserStore.getGlobalItem(this.props.hash));
- }
- catch(parse_error) {
- }
+ var props = BrowserStore.getGlobalItem(this.props.hash);
if (!props) {
props = {};
@@ -91,7 +86,7 @@ module.exports = React.createClass({
props.original_email = this.props.email;
}
- return props ;
+ return props;
},
render: function() {
diff --git a/web/react/components/team_members.jsx b/web/react/components/team_members.jsx
index 6b978f88b..616fd2c99 100644
--- a/web/react/components/team_members.jsx
+++ b/web/react/components/team_members.jsx
@@ -57,7 +57,7 @@ module.exports = React.createClass({
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
- <h4 className="modal-title" id="myModalLabel">{this.props.teamName + " Members"}</h4>
+ <h4 className="modal-title" id="myModalLabel">{this.props.teamDisplayName + " Members"}</h4>
</div>
<div ref="modalBody" className="modal-body">
<div className="channel-settings">
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index 648960471..65f025919 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -53,7 +53,7 @@ module.exports = React.createClass({
var name = this.props.overwriteName ? this.props.overwriteName : this.state.profile.username;
- var data_content = "<img style='margin: 10px' src='/api/v1/users/" + this.state.profile.id + "/image' height='128' width='128' />";
+ var data_content = "<img class='user-popover__image' src='/api/v1/users/" + this.state.profile.id + "/image?time=" + this.state.profile.update_at + "' height='128' width='128' />";
if (!config.ShowEmail) {
data_content += "<div class='text-nowrap'>Email not shared</div>";
} else {
@@ -61,7 +61,7 @@ module.exports = React.createClass({
}
return (
- <div style={{"cursor" : "pointer", "display" : "inline-block"}} className="user-popover" id={"profile_" + this.uniqueId} data-toggle="popover" data-content={data_content} data-original-title={this.state.profile.username} >
+ <div className="user-popover" id={"profile_" + this.uniqueId} data-toggle="popover" data-content={data_content} data-original-title={this.state.profile.username} >
{ name }
</div>
);
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index b4c3747af..59c97c309 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -156,6 +156,8 @@ var NotificationsTab = React.createClass({
var self = this;
+ var user = this.props.user;
+
var desktopSection;
if (this.props.activeSection === 'desktop') {
var notifyActive = [false, false, false];
@@ -314,20 +316,14 @@ var NotificationsTab = React.createClass({
var keysSection;
if (this.props.activeSection === 'keys') {
- var user = this.props.user;
- var first_name = "";
- if (user.full_name.length > 0) {
- first_name = user.full_name.split(' ')[0];
- }
-
var inputs = [];
- if (first_name != "") {
+ if (user.first_name) {
inputs.push(
<div>
<div className="checkbox">
<label>
- <input type="checkbox" checked={this.state.first_name_key} onChange={function(e){self.updateFirstNameKey(e.target.checked);}}>{'Your case sensitive first name "' + first_name + '"'}</input>
+ <input type="checkbox" checked={this.state.first_name_key} onChange={function(e){self.updateFirstNameKey(e.target.checked);}}>{'Your case sensitive first name "' + user.first_name + '"'}</input>
</label>
</div>
</div>
@@ -396,14 +392,9 @@ var NotificationsTab = React.createClass({
);
} else {
var keys = [];
- if (this.state.first_name_key) {
- var first_name = "";
- var user = this.props.user;
- if (user.full_name.length > 0) first_name = user.full_name.split(' ')[0];
- if (first_name != "") keys.push(first_name);
- }
- if (this.state.username_key) keys.push(this.props.user.username);
- if (this.state.mention_key) keys.push('@'+this.props.user.username);
+ if (this.state.first_name_key) keys.push(user.first_name);
+ if (this.state.username_key) keys.push(user.username);
+ if (this.state.mention_key) keys.push('@'+user.username);
if (this.state.all_key) keys.push('@all');
if (this.state.channel_key) keys.push('@channel');
if (this.state.custom_keys.length > 0) keys = keys.concat(this.state.custom_keys.split(','));
@@ -560,7 +551,7 @@ var AuditTab = React.createClass({
<div className="user-settings">
<h3 className="tab-header">Activity Log</h3>
<div className="divider-dark first"/>
- <div className="table-responsive" style={{ maxWidth: "560px", maxHeight: "300px" }}>
+ <div className="table-responsive">
<table className="table-condensed small">
<thead>
<tr>
@@ -576,11 +567,11 @@ var AuditTab = React.createClass({
this.state.audits.map(function(value, index) {
return (
<tr key={ "" + index }>
- <td style={{ whiteSpace: "nowrap" }}>{ new Date(value.create_at).toLocaleString() }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.action.replace("/api/v1", "") }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.ip_address }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.session_id }</td>
- <td style={{ whiteSpace: "nowrap" }}>{ value.extra_info }</td>
+ <td className="text-nowrap">{ new Date(value.create_at).toLocaleString() }</td>
+ <td className="text-nowrap">{ value.action.replace("/api/v1", "") }</td>
+ <td className="text-nowrap">{ value.ip_address }</td>
+ <td className="text-nowrap">{ value.session_id }</td>
+ <td className="text-nowrap">{ value.extra_info }</td>
</tr>
);
}, this)
@@ -626,7 +617,7 @@ var SecurityTab = React.createClass({
client.updatePassword(data,
function(data) {
- this.updateSection("");
+ this.props.updateSection("");
AsyncClient.getMe();
this.setState({ current_password: '', new_password: '', confirm_password: '' });
}.bind(this),
@@ -752,6 +743,21 @@ var GeneralTab = React.createClass({
this.submitUser(user);
},
+ submitNickname: function(e) {
+ e.preventDefault();
+
+ var user = UserStore.getCurrentUser();
+ var nickname = this.state.nickname.trim();
+
+ if (user.nickname === nickname) {
+ this.setState({client_error: "You must submit a new nickname"})
+ return;
+ }
+
+ user.nickname = nickname;
+
+ this.submitUser(user);
+ },
submitName: function(e) {
e.preventDefault();
@@ -759,14 +765,13 @@ var GeneralTab = React.createClass({
var firstName = this.state.first_name.trim();
var lastName = this.state.last_name.trim();
- var fullName = firstName + ' ' + lastName;
-
- if (user.full_name === fullName) {
- this.setState({client_error: "You must submit a new name"})
+ if (user.first_name === firstName && user.last_name === lastName) {
+ this.setState({client_error: "You must submit a new first or last name"})
return;
}
- user.full_name = fullName;
+ user.first_name = firstName;
+ user.last_name = lastName;
this.submitUser(user);
},
@@ -820,6 +825,7 @@ var GeneralTab = React.createClass({
client.uploadProfileImage(formData,
function(data) {
this.submitActive = false;
+ AsyncClient.getMe();
window.location.reload();
}.bind(this),
function(err) {
@@ -838,6 +844,9 @@ var GeneralTab = React.createClass({
updateLastName: function(e) {
this.setState({ last_name: e.target.value});
},
+ updateNickname: function(e) {
+ this.setState({nickname: e.target.value});
+ },
updateEmail: function(e) {
this.setState({ email: e.target.value});
},
@@ -860,11 +869,7 @@ var GeneralTab = React.createClass({
getInitialState: function() {
var user = this.props.user;
- var splitStr = user.full_name.split(' ');
- var firstName = splitStr.shift();
- var lastName = splitStr.join(' ');
-
- return { username: user.username, first_name: firstName, last_name: lastName,
+ return { username: user.username, first_name: user.first_name, last_name: user.last_name, nickname: user.nickname,
email: user.email, picture: null };
},
render: function() {
@@ -900,7 +905,7 @@ var GeneralTab = React.createClass({
nameSection = (
<SettingItemMax
- title="Name"
+ title="Full Name"
inputs={inputs}
submit={this.submitName}
server_error={server_error}
@@ -909,15 +914,58 @@ var GeneralTab = React.createClass({
/>
);
} else {
+ var full_name = "";
+
+ if (user.first_name && user.last_name) {
+ full_name = user.first_name + " " + user.last_name;
+ } else if (user.first_name) {
+ full_name = user.first_name;
+ } else if (user.last_name) {
+ full_name = user.last_name;
+ }
+
nameSection = (
<SettingItemMin
- title="Name"
- describe={UserStore.getCurrentUser().full_name}
+ title="Full Name"
+ describe={full_name}
updateSection={function(){self.updateSection("name");}}
/>
);
}
+ var nicknameSection;
+ if (this.props.activeSection === 'nickname') {
+ var inputs = [];
+
+ inputs.push(
+ <div className="form-group">
+ <label className="col-sm-5 control-label">{utils.isMobile() ? "": "Nickname"}</label>
+ <div className="col-sm-7">
+ <input className="form-control" type="text" onChange={this.updateNickname} value={this.state.nickname}/>
+ </div>
+ </div>
+ );
+
+ nicknameSection = (
+ <SettingItemMax
+ title="Nickname"
+ inputs={inputs}
+ submit={this.submitNickname}
+ server_error={server_error}
+ client_error={client_error}
+ updateSection={function(e){self.updateSection("");e.preventDefault();}}
+ />
+ );
+ } else {
+ nicknameSection = (
+ <SettingItemMin
+ title="Nickname"
+ describe={UserStore.getCurrentUser().nickname}
+ updateSection={function(){self.updateSection("nickname");}}
+ />
+ );
+ }
+
var usernameSection;
if (this.props.activeSection === 'username') {
var inputs = [];
@@ -989,7 +1037,7 @@ var GeneralTab = React.createClass({
<SettingPicture
title="Profile Picture"
submit={this.submitPicture}
- src={"/api/v1/users/" + user.id + "/image"}
+ src={"/api/v1/users/" + user.id + "/image?time=" + user.last_picture_update}
server_error={server_error}
client_error={client_error}
updateSection={function(e){self.updateSection("");e.preventDefault();}}
@@ -1000,10 +1048,14 @@ var GeneralTab = React.createClass({
);
} else {
+ var minMessage = "Click Edit to upload an image.";
+ if (user.last_picture_update) {
+ minMessage = "Image last updated " + utils.displayDate(user.last_picture_update)
+ }
pictureSection = (
<SettingItemMin
title="Profile Picture"
- describe="Picture inside."
+ describe={minMessage}
updateSection={function(){self.updateSection("picture");}}
/>
);
@@ -1021,6 +1073,8 @@ var GeneralTab = React.createClass({
<div className="divider-light"/>
{usernameSection}
<div className="divider-light"/>
+ {nicknameSection}
+ <div className="divider-light"/>
{emailSection}
<div className="divider-light"/>
{pictureSection}
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 38f439946..2274f3f2e 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -25,7 +25,11 @@ module.exports = React.createClass({
this.setState({ imgId: nextProps.startId });
},
loadImage: function(id) {
- if (this.state.loaded[id] || this.state.images[id]) return;
+ var imgHeight = $(window).height()-100;
+ if (this.state.loaded[id] || this.state.images[id]){
+ $('.modal .modal-image .image-wrapper img').css("max-height",imgHeight);
+ return;
+ };
var src = "";
if (this.props.imgCount > 0) {
@@ -48,6 +52,7 @@ module.exports = React.createClass({
var loaded = self.state.loaded;
loaded[imgid] = true;
self.setState({ loaded: loaded });
+ $(self.refs.image.getDOMNode()).css("max-height",imgHeight);
};
}(id);
var images = this.state.images;
@@ -56,10 +61,8 @@ module.exports = React.createClass({
},
componentDidUpdate: function() {
if (this.refs.image) {
- var height = $(window).height()-100;
if (this.state.loaded[this.state.imgId]) {
$(this.refs.imageWrap.getDOMNode()).removeClass("default");
- $(this.refs.image.getDOMNode()).css("max-height",height);
}
}
},
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 3aa985863..f70d60e3a 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -61,17 +61,17 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann
);
React.render(
- <Navbar teamName={team_name} />,
+ <Navbar teamDisplayName={team_name} />,
document.getElementById('navbar')
);
React.render(
- <Sidebar teamName={team_name} teamType={team_type} />,
+ <Sidebar teamDisplayName={team_name} teamType={team_type} />,
document.getElementById('sidebar-left')
);
React.render(
- <RenameTeamModal teamName={team_name} />,
+ <RenameTeamModal teamDisplayName={team_name} />,
document.getElementById('rename_team_modal')
);
@@ -91,7 +91,7 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann
);
React.render(
- <TeamMembersModal teamName={team_name} />,
+ <TeamMembersModal teamDisplayName={team_name} />,
document.getElementById('team_members_modal')
);
@@ -186,7 +186,7 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann
);
React.render(
- <SidebarRightMenu teamName={team_name} teamType={team_type} />,
+ <SidebarRightMenu teamDisplayName={team_name} teamType={team_type} />,
document.getElementById('sidebar-menu')
);
diff --git a/web/react/pages/home.jsx b/web/react/pages/home.jsx
index 08dd32f73..b12fa4949 100644
--- a/web/react/pages/home.jsx
+++ b/web/react/pages/home.jsx
@@ -2,13 +2,14 @@
// See License.txt for license information.
var ChannelStore = require('../stores/channel_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
var Constants = require('../utils/constants.jsx');
-global.window.setup_home_page = function() {
+global.window.setup_home_page = function(teamURL) {
var last = ChannelStore.getLastVisitedName();
if (last == null || last.length === 0) {
- window.location.replace("/channels/" + Constants.DEFAULT_CHANNEL);
+ window.location = teamURL + "/channels/" + Constants.DEFAULT_CHANNEL;
} else {
- window.location.replace("/channels/" + last);
+ window.location = teamURL + "/channels/" + last;
}
}
diff --git a/web/react/pages/login.jsx b/web/react/pages/login.jsx
index a4e6b438e..8348f0b5d 100644
--- a/web/react/pages/login.jsx
+++ b/web/react/pages/login.jsx
@@ -3,9 +3,9 @@
var Login = require('../components/login.jsx');
-global.window.setup_login_page = function() {
+global.window.setup_login_page = function(teamDisplayName, teamName) {
React.render(
- <Login />,
+ <Login teamDisplayName={teamDisplayName} teamName={teamName}/>,
document.getElementById('login')
);
};
diff --git a/web/react/pages/password_reset.jsx b/web/react/pages/password_reset.jsx
index 6d0d88a10..c7a208973 100644
--- a/web/react/pages/password_reset.jsx
+++ b/web/react/pages/password_reset.jsx
@@ -3,13 +3,13 @@
var PasswordReset = require('../components/password_reset.jsx');
-global.window.setup_password_reset_page = function(is_reset, team_name, domain, hash, data) {
+global.window.setup_password_reset_page = function(is_reset, team_display_name, team_name, hash, data) {
React.render(
<PasswordReset
isReset={is_reset}
+ teamDisplayName={team_display_name}
teamName={team_name}
- domain={domain}
hash={hash}
data={data}
/>,
diff --git a/web/react/pages/signup_team_complete.jsx b/web/react/pages/signup_team_complete.jsx
index c17cbdfac..346f2ab5a 100644
--- a/web/react/pages/signup_team_complete.jsx
+++ b/web/react/pages/signup_team_complete.jsx
@@ -5,7 +5,7 @@ var SignupTeamComplete =require('../components/signup_team_complete.jsx');
global.window.setup_signup_team_complete_page = function(email, name, data, hash) {
React.render(
- <SignupTeamComplete name={name} email={email} hash={hash} data={data} />,
+ <SignupTeamComplete name={name} email={email} hash={hash} data={data}/>,
document.getElementById('signup-team-complete')
);
-}; \ No newline at end of file
+};
diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx
index 82cf9a942..4eed754cc 100644
--- a/web/react/stores/browser_store.jsx
+++ b/web/react/stores/browser_store.jsx
@@ -1,85 +1,104 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var UserStore = require('../stores/user_store.jsx');
-// Also change model/utils.go ETAG_ROOT_VERSION
-var BROWSER_STORE_VERSION = '.1';
-
-var _initialized = false;
-
-function _initialize() {
- var currentVersion = localStorage.getItem("local_storage_version");
- if (currentVersion !== BROWSER_STORE_VERSION) {
- localStorage.clear();
- sessionStorage.clear();
- localStorage.setItem("local_storage_version", BROWSER_STORE_VERSION);
- }
- _initialized = true;
+var UserStore;
+function getPrefix() {
+ if (!UserStore) UserStore = require('./user_store.jsx');
+ return UserStore.getCurrentId() + '_';
}
-module.exports.setItem = function(name, value) {
- if (!_initialized) _initialize();
- var user_id = UserStore.getCurrentId();
- localStorage.setItem(user_id + "_" + name, value);
-};
+// Also change model/utils.go ETAG_ROOT_VERSION
+var BROWSER_STORE_VERSION = '.4';
-module.exports.getItem = function(name) {
- if (!_initialized) _initialize();
- var user_id = UserStore.getCurrentId();
- return localStorage.getItem(user_id + "_" + name);
-};
+module.exports = {
+ _initialized: false,
-module.exports.removeItem = function(name) {
- if (!_initialized) _initialize();
- var user_id = UserStore.getCurrentId();
- localStorage.removeItem(user_id + "_" + name);
-};
+ _initialize: function() {
+ var currentVersion = localStorage.getItem("local_storage_version");
+ if (currentVersion !== BROWSER_STORE_VERSION) {
+ this.clear();
+ localStorage.setItem("local_storage_version", BROWSER_STORE_VERSION);
+ }
+ this._initialized = true;
+ },
-module.exports.setGlobalItem = function(name, value) {
- if (!_initialized) _initialize();
- localStorage.setItem(name, value);
-};
+ getItem: function(name, defaultValue) {
+ return this.getGlobalItem(getPrefix() + name, defaultValue);
+ },
-module.exports.getGlobalItem = function(name) {
- if (!_initialized) _initialize();
- return localStorage.getItem(name);
-};
+ setItem: function(name, value) {
+ this.setGlobalItem(getPrefix() + name, value);
+ },
-module.exports.removeGlobalItem = function(name) {
- if (!_initialized) _initialize();
- localStorage.removeItem(name);
-};
+ removeItem: function(name) {
+ if (!this._initialized) this._initialize();
-module.exports.clear = function() {
- localStorage.clear();
- sessionStorage.clear();
-};
+ localStorage.removeItem(getPrefix() + name);
+ },
+
+ setGlobalItem: function(name, value) {
+ if (!this._initialized) this._initialize();
+
+ localStorage.setItem(name, JSON.stringify(value));
+ },
+
+ getGlobalItem: function(name, defaultValue) {
+ if (!this._initialized) this._initialize();
-// Preforms the given action on each item that has the given prefix
-// Signiture for action is action(key, value)
-module.exports.actionOnItemsWithPrefix = function (prefix, action) {
- var user_id = UserStore.getCurrentId();
- var id_len = user_id.length;
- var prefix_len = prefix.length;
- for (var key in localStorage) {
- if (key.substring(id_len, id_len + prefix_len) === prefix) {
- var userkey = key.substring(id_len);
- action(userkey, BrowserStore.getItem(key));
+ var result = null;
+ try {
+ result = JSON.parse(localStorage.getItem(name));
+ } catch (err) {}
+
+ if (result === null && typeof defaultValue !== 'undefined') {
+ result = defaultValue;
}
- }
-};
-module.exports.isLocalStorageSupported = function() {
- try {
- sessionStorage.setItem("testSession", '1');
- sessionStorage.removeItem("testSession");
+ return result;
+ },
- localStorage.setItem("testLocal", '1');
- localStorage.removeItem("testLocal", '1');
+ removeGlobalItem: function(name) {
+ if (!this._initialized) this._initialize();
- return true;
- }
- catch (e) {
- return false;
+ localStorage.removeItem(name);
+ },
+
+ clear: function() {
+ localStorage.clear();
+ sessionStorage.clear();
+ },
+
+ /**
+ * Preforms the given action on each item that has the given prefix
+ * Signiture for action is action(key, value)
+ */
+ actionOnItemsWithPrefix: function (prefix, action) {
+ if (!this._initialized) this._initialize();
+
+ var globalPrefix = getPrefix();
+ var globalPrefixiLen = globalPrefix.length;
+ for (var key in localStorage) {
+ if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
+ var userkey = key.substring(globalPrefixiLen);
+ action(userkey, this.getGlobalItem(key));
+ }
+ }
+ },
+
+ isLocalStorageSupported: function() {
+ try {
+ sessionStorage.setItem("testSession", '1');
+ sessionStorage.removeItem("testSession");
+
+ localStorage.setItem("testLocal", '1');
+ if (localStorage.getItem("testLocal") != '1') {
+ return false;
+ }
+ localStorage.removeItem("testLocal", '1');
+
+ return true;
+ } catch (e) {
+ return false;
+ }
}
};
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
index 4429a5312..a97f13391 100644
--- a/web/react/stores/channel_store.jsx
+++ b/web/react/stores/channel_store.jsx
@@ -16,6 +16,7 @@ var MORE_CHANGE_EVENT = 'change';
var EXTRA_INFO_EVENT = 'extra_info';
var ChannelStore = assign({}, EventEmitter.prototype, {
+ _current_id: null,
emitChange: function() {
this.emit(CHANGE_EVENT);
},
@@ -43,40 +44,24 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
removeExtraInfoChangeListener: function(callback) {
this.removeListener(EXTRA_INFO_EVENT, callback);
},
- get: function(id) {
- var current = null;
- var c = this._getChannels();
-
- c.some(function(channel) {
- if (channel.id == id) {
- current = channel;
- return true;
+ findFirstBy: function(field, value) {
+ var channels = this._getChannels();
+ for (var i = 0; i < channels.length; i++) {
+ if (channels[i][field] == value) {
+ return channels[i];
}
- return false;
- });
+ }
- return current;
+ return null;
+ },
+ get: function(id) {
+ return this.findFirstBy('id', id);
},
getMember: function(id) {
- var current = null;
return this.getAllMembers()[id];
},
getByName: function(name) {
- var current = null;
- var c = this._getChannels();
-
- c.some(function(channel) {
- if (channel.name == name) {
- current = channel;
- return true;
- }
-
- return false;
-
- });
-
- return current;
-
+ return this.findFirstBy('name', name);
},
getAll: function() {
return this._getChannels();
@@ -88,10 +73,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
return this._getMoreChannels();
},
setCurrentId: function(id) {
- if (id == null)
- BrowserStore.removeItem("current_channel_id");
- else
- BrowserStore.setItem("current_channel_id", id);
+ this._current_id = id;
},
setLastVisitedName: function(name) {
if (name == null)
@@ -117,12 +99,12 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
this._storeChannelMembers(cm);
},
getCurrentId: function() {
- return BrowserStore.getItem("current_channel_id");
+ return this._current_id;
},
getCurrent: function() {
- var currentId = ChannelStore.getCurrentId();
+ var currentId = this.getCurrentId();
- if (currentId != null)
+ if (currentId)
return this.get(currentId);
else
return null;
@@ -130,7 +112,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
getCurrentMember: function() {
var currentId = ChannelStore.getCurrentId();
- if (currentId != null)
+ if (currentId)
return this.getAllMembers()[currentId];
else
return null;
@@ -145,7 +127,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
var currentId = ChannelStore.getCurrentId();
var extra = null;
- if (currentId != null)
+ if (currentId)
extra = this._getExtraInfos()[currentId];
if (extra == null)
@@ -156,7 +138,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
getExtraInfo: function(channel_id) {
var extra = null;
- if (channel_id != null)
+ if (channel_id)
extra = this._getExtraInfos()[channel_id];
if (extra == null)
@@ -165,49 +147,22 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
return extra;
},
_storeChannels: function(channels) {
- BrowserStore.setItem("channels", JSON.stringify(channels));
+ BrowserStore.setItem("channels", channels);
},
_getChannels: function() {
- var channels = [];
- try {
- channels = JSON.parse(BrowserStore.getItem("channels"));
- }
- catch (err) {
- }
-
- if (channels == null) {
- channels = [];
- }
-
- return channels;
+ return BrowserStore.getItem("channels", []);
},
_storeChannelMembers: function(channelMembers) {
- BrowserStore.setItem("channel_members", JSON.stringify(channelMembers));
+ BrowserStore.setItem("channel_members", channelMembers);
},
_getChannelMembers: function() {
- var members = {};
- try {
- members = JSON.parse(BrowserStore.getItem("channel_members"));
- }
- catch (err) {
- }
-
- if (members == null) {
- members = {};
- }
-
- return members;
+ return BrowserStore.getItem("channel_members", {});
},
_storeMoreChannels: function(channels) {
- BrowserStore.setItem("more_channels", JSON.stringify(channels));
+ BrowserStore.setItem("more_channels", channels);
},
_getMoreChannels: function() {
- var channels = null;
- try {
- channels = JSON.parse(BrowserStore.getItem("more_channels"));
- }
- catch (err) {
- }
+ var channels = BrowserStore.getItem("more_channels");
if (channels == null) {
channels = {};
@@ -217,22 +172,14 @@ var ChannelStore = assign({}, EventEmitter.prototype, {
return channels;
},
_storeExtraInfos: function(extraInfos) {
- BrowserStore.setItem("extra_infos", JSON.stringify(extraInfos));
+ BrowserStore.setItem("extra_infos", extraInfos);
},
_getExtraInfos: function() {
- var members = {};
- try {
- members = JSON.parse(BrowserStore.getItem("extra_infos"));
- }
- catch (err) {
- }
-
- if (members == null) {
- members = {};
- }
-
- return members;
- }
+ return BrowserStore.getItem("extra_infos", {});
+ },
+ isDefault: function(channel) {
+ return channel.name == Constants.DEFAULT_CHANNEL;
+ }
});
ChannelStore.dispatchToken = AppDispatcher.register(function(payload) {
@@ -271,4 +218,4 @@ ChannelStore.dispatchToken = AppDispatcher.register(function(payload) {
}
});
-module.exports = ChannelStore;
+module.exports = ChannelStore; \ No newline at end of file
diff --git a/web/react/stores/error_store.jsx b/web/react/stores/error_store.jsx
index 3aed6aef2..203b692ec 100644
--- a/web/react/stores/error_store.jsx
+++ b/web/react/stores/error_store.jsx
@@ -29,18 +29,11 @@ var ErrorStore = assign({}, EventEmitter.prototype, {
BrowserStore.removeItem("last_error");
},
getLastError: function() {
- var error = null;
- try {
- error = JSON.parse(BrowserStore.getItem("last_error"));
- }
- catch (err) {
- }
-
- return error;
+ return BrowserStore.getItem('last_error');
},
_storeLastError: function(error) {
- BrowserStore.setItem("last_error", JSON.stringify(error));
+ BrowserStore.setItem("last_error", error);
},
});
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index 8bf3fdcb2..5280bfe08 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -106,55 +106,27 @@ var PostStore = assign({}, EventEmitter.prototype, {
this.emitChange();
},
_storePosts: function(channelId, posts) {
- BrowserStore.setItem("posts_" + channelId, JSON.stringify(posts));
+ BrowserStore.setItem("posts_" + channelId, posts);
},
getPosts: function(channelId) {
- var posts = null;
- try {
- posts = JSON.parse(BrowserStore.getItem("posts_" + channelId));
- }
- catch (err) {
- }
-
- return posts;
+ return BrowserStore.getItem("posts_" + channelId);
},
storeSearchResults: function(results, is_mention_search) {
- BrowserStore.setItem("search_results", JSON.stringify(results));
+ BrowserStore.setItem("search_results", results);
is_mention_search = is_mention_search ? true : false; // force to bool
- BrowserStore.setItem("is_mention_search", JSON.stringify(is_mention_search));
+ BrowserStore.setItem("is_mention_search", is_mention_search);
},
getSearchResults: function() {
- var results = null;
- try {
- results = JSON.parse(BrowserStore.getItem("search_results"));
- }
- catch (err) {
- }
-
- return results;
+ return BrowserStore.getItem("search_results");
},
getIsMentionSearch: function() {
- var result = false;
- try {
- result = JSON.parse(BrowserStore.getItem("is_mention_search"));
- }
- catch (err) {
- }
-
- return result;
+ return BrowserStore.getItem("is_mention_search");
},
storeSelectedPost: function(post_list) {
- BrowserStore.setItem("select_post", JSON.stringify(post_list));
+ BrowserStore.setItem("select_post", post_list);
},
getSelectedPost: function() {
- var post_list = null;
- try {
- post_list = JSON.parse(BrowserStore.getItem("select_post"));
- }
- catch (err) {
- }
-
- return post_list;
+ return BrowserStore.getItem("select_post");
},
storeSearchTerm: function(term) {
BrowserStore.setItem("search_term", term);
@@ -165,27 +137,26 @@ var PostStore = assign({}, EventEmitter.prototype, {
storeCurrentDraft: function(draft) {
var channel_id = ChannelStore.getCurrentId();
var user_id = UserStore.getCurrentId();
- BrowserStore.setItem("draft_" + channel_id + "_" + user_id, JSON.stringify(draft));
+ BrowserStore.setItem("draft_" + channel_id + "_" + user_id, draft);
},
getCurrentDraft: function() {
var channel_id = ChannelStore.getCurrentId();
var user_id = UserStore.getCurrentId();
- return JSON.parse(BrowserStore.getItem("draft_" + channel_id + "_" + user_id));
+ return BrowserStore.getItem("draft_" + channel_id + "_" + user_id);
},
storeDraft: function(channel_id, user_id, draft) {
- BrowserStore.setItem("draft_" + channel_id + "_" + user_id, JSON.stringify(draft));
+ BrowserStore.setItem("draft_" + channel_id + "_" + user_id, draft);
},
getDraft: function(channel_id, user_id) {
- return JSON.parse(BrowserStore.getItem("draft_" + channel_id + "_" + user_id));
+ return BrowserStore.getItem("draft_" + channel_id + "_" + user_id);
},
clearDraftUploads: function() {
- BrowserStore.actionOnItemsWithPrefix("draft_", function (key, value) {
- var d = JSON.parse(value);
- if (d) {
- d['uploadsInProgress'] = 0;
- BrowserStore.setItem(key, JSON.stringify(d));
- }
- });
+ BrowserStore.actionOnItemsWithPrefix("draft_", function (key, value) {
+ if (value) {
+ value.uploadsInProgress = 0;
+ BrowserStore.setItem(key, value);
+ }
+ });
}
});
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index 39800ead5..8ebb854c9 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -10,8 +10,6 @@ var client = require('../utils/client.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-var BrowserStore = require('../stores/browser_store.jsx');
-
var CHANGE_EVENT = 'change';
var conn;
diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx
index c494cb5b5..3f12725f8 100644
--- a/web/react/stores/team_store.jsx
+++ b/web/react/stores/team_store.jsx
@@ -57,28 +57,19 @@ var TeamStore = assign({}, EventEmitter.prototype, {
else
return null;
},
+ getCurrentTeamUrl: function() {
+ return window.location.origin + "/" + this.getCurrent().name;
+ },
storeTeam: function(team) {
- var teams = this._getTeams();
- teams[team.id] = team;
- this._storeTeams(teams);
+ var teams = this._getTeams();
+ teams[team.id] = team;
+ this._storeTeams(teams);
},
_storeTeams: function(teams) {
- BrowserStore.setItem("user_teams", JSON.stringify(teams));
+ BrowserStore.setItem("user_teams", teams);
},
_getTeams: function() {
- var teams = {};
-
- try {
- teams = JSON.parse(BrowserStore.getItem("user_teams"));
- }
- catch (err) {
- }
-
- if (teams == null) {
- teams = {};
- }
-
- return teams;
+ return BrowserStore.getItem("user_teams", {});
}
});
diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx
index e832b34c7..d03016c5d 100644
--- a/web/react/stores/user_store.jsx
+++ b/web/react/stores/user_store.jsx
@@ -8,7 +8,7 @@ var client = require('../utils/client.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
-var BrowserStore = require('../stores/browser_store.jsx');
+var BrowserStore = require('./browser_store.jsx');
var CHANGE_EVENT = 'change';
var CHANGE_EVENT_SESSIONS = 'change_sessions';
@@ -18,6 +18,8 @@ var CHANGE_EVENT_STATUSES = 'change_statuses';
var UserStore = assign({}, EventEmitter.prototype, {
+ _current_id: null,
+
emitChange: function(userId) {
this.emit(CHANGE_EVENT, userId);
},
@@ -64,13 +66,19 @@ var UserStore = assign({}, EventEmitter.prototype, {
this.removeListener(CHANGE_EVENT_STATUSES, callback);
},
setCurrentId: function(id) {
- if (id == null)
- BrowserStore.removeGlobalItem("current_user_id");
- else
- BrowserStore.setGlobalItem("current_user_id", id);
+ this._current_id = id;
+ if (id == null) {
+ BrowserStore.removeGlobalItem("current_user_id");
+ } else {
+ BrowserStore.setGlobalItem("current_user_id", id);
+ }
},
getCurrentId: function(skipFetch) {
- var current_id = BrowserStore.getGlobalItem("current_user_id");
+ var current_id = this._current_id;
+
+ if (current_id == null) {
+ current_id = BrowserStore.getGlobalItem("current_user_id");
+ }
// this is a speical case to force fetch the
// current user if it's missing
@@ -93,25 +101,11 @@ var UserStore = assign({}, EventEmitter.prototype, {
return this._getProfiles()[this.getCurrentId()];
},
setCurrentUser: function(user) {
- this.saveProfile(user);
this.setCurrentId(user.id);
- },
- getLastDomain: function() {
- var last_domain = BrowserStore.getItem("last_domain");
- if (last_domain == null) {
- last_domain = "";
- }
- return last_domain;
- },
- setLastDomain: function(domain) {
- BrowserStore.setItem("last_domain", domain);
+ this.saveProfile(user);
},
getLastEmail: function() {
- var last_email = BrowserStore.getItem("last_email");
- if (last_email == null) {
- last_email = "";
- }
- return last_email;
+ return BrowserStore.getItem("last_email", '');
},
setLastEmail: function(email) {
BrowserStore.setItem("last_email", email);
@@ -153,116 +147,51 @@ var UserStore = assign({}, EventEmitter.prototype, {
this._storeProfiles(ps);
},
_storeProfiles: function(profiles) {
- BrowserStore.setGlobalItem("profiles", JSON.stringify(profiles));
+ BrowserStore.setItem("profiles", profiles);
var profileUsernameMap = {};
for (var id in profiles) {
profileUsernameMap[profiles[id].username] = profiles[id];
}
- BrowserStore.setGlobalItem("profileUsernameMap", JSON.stringify(profileUsernameMap));
+ BrowserStore.setItem("profileUsernameMap", profileUsernameMap);
},
_getProfiles: function() {
- var profiles = {};
- try {
- profiles = JSON.parse(BrowserStore.getGlobalItem("profiles"));
- }
- catch (err) {
- }
-
- if (profiles == null) {
- profiles = {};
- }
-
- return profiles;
+ return BrowserStore.getItem("profiles", {});
},
_getProfilesUsernameMap: function() {
- var profileUsernameMap = {};
- try {
- profileUsernameMap = JSON.parse(BrowserStore.getGlobalItem("profileUsernameMap"));
- }
- catch (err) {
- }
-
- if (profileUsernameMap == null) {
- profileUsernameMap = {};
- }
-
- return profileUsernameMap;
+ return BrowserStore.getItem("profileUsernameMap", {});
},
setSessions: function(sessions) {
- BrowserStore.setItem("sessions", JSON.stringify(sessions));
+ BrowserStore.setItem("sessions", sessions);
},
getSessions: function() {
- var sessions = [];
- try {
- sessions = JSON.parse(BrowserStore.getItem("sessions"));
- }
- catch (err) {
- }
- if (sessions == null) {
- sessions = [];
- }
-
- return sessions;
+ return BrowserStore.getItem("sessions", []);
},
setAudits: function(audits) {
- BrowserStore.setItem("audits", JSON.stringify(audits));
+ BrowserStore.setItem("audits", audits);
},
getAudits: function() {
- var audits = [];
- try {
- audits = JSON.parse(BrowserStore.getItem("audits"));
- }
- catch (err) {
- }
-
- if (audits == null) {
- audits = [];
- }
-
- return audits;
+ return BrowserStore.getItem("audits", []);
},
setTeams: function(teams) {
- BrowserStore.setItem("teams", JSON.stringify(teams));
+ BrowserStore.setItem("teams", teams);
},
getTeams: function() {
- var teams = [];
- try {
- teams = JSON.parse(BrowserStore.getItem("teams"));
-
- }
- catch (err) {
- }
-
- if (teams == null) {
- teams = [];
- }
-
- return teams;
+ return BrowserStore.getItem("teams", []);
},
getCurrentMentionKeys: function() {
var user = this.getCurrentUser();
- if (user.notify_props && user.notify_props.mention_keys) {
- var keys = user.notify_props.mention_keys.split(',');
- if (user.full_name.length > 0 && user.notify_props.first_name === "true") {
- var first = user.full_name.split(' ')[0];
- if (first.length > 0) keys.push(first);
- }
+ var keys = [];
- if (user.notify_props.all === "true") keys.push('@all');
- if (user.notify_props.channel === "true") keys.push('@channel');
+ if (user.notify_props && user.notify_props.mention_keys) keys = keys.concat(user.notify_props.mention_keys.split(','));
+ if (user.first_name && user.notify_props.first_name === "true") keys.push(user.first_name);
+ if (user.notify_props.all === "true") keys.push('@all');
+ if (user.notify_props.channel === "true") keys.push('@channel');
- return keys;
- } else {
- return [];
- }
+ return keys;
},
getLastVersion: function() {
- var last_version = BrowserStore.getItem("last_version");
- if (last_version == null) {
- last_version = "";
- }
- return last_version;
+ return BrowserStore.getItem("last_version", '');
},
setLastVersion: function(version) {
BrowserStore.setItem("last_version", version);
@@ -272,7 +201,7 @@ var UserStore = assign({}, EventEmitter.prototype, {
this.emitStatusesChange();
},
_setStatuses: function(statuses) {
- BrowserStore.setItem("statuses", JSON.stringify(statuses));
+ BrowserStore.setItem("statuses", statuses);
},
setStatus: function(user_id, status) {
var statuses = this.getStatuses();
@@ -281,18 +210,7 @@ var UserStore = assign({}, EventEmitter.prototype, {
this.emitStatusesChange();
},
getStatuses: function() {
- var statuses = {};
- try {
- statuses = JSON.parse(BrowserStore.getItem("statuses"));
- }
- catch (err) {
- }
-
- if (statuses == null) {
- statuses = {};
- }
-
- return statuses;
+ return BrowserStore.getItem("statuses", {});
},
getStatus: function(id) {
return this.getStatuses()[id];
@@ -341,4 +259,3 @@ UserStore.setMaxListeners(0);
global.window.UserStore = UserStore;
module.exports = UserStore;
-
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 11d4c2601..1c31dc5ed 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
var BrowserStore = require('../stores/browser_store.jsx');
+var TeamStore = require('../stores/team_store.jsx');
module.exports.track = function(category, action, label, prop, val) {
global.window.snowplow('trackStructEvent', category, action, label, prop, val);
@@ -44,7 +45,12 @@ function handleError(method_name, xhr, status, err) {
module.exports.track('api', 'api_weberror', method_name, 'message', msg);
if (xhr.status == 401) {
- window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search);
+ if (window.location.href.indexOf("/channels") === 0) {
+ window.location.pathname = '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search);
+ } else {
+ var teamURL = window.location.href.split('/channels')[0];
+ window.location.href = teamURL + '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search);
+ }
}
return e;
@@ -205,17 +211,18 @@ module.exports.resetPassword = function(data, success, error) {
module.exports.logout = function() {
module.exports.track('api', 'api_users_logout');
- BrowserStore.clear();
- window.location.href = "/logout";
+ var currentTeamUrl = TeamStore.getCurrentTeamUrl();
+ BrowserStore.clear();
+ window.location.href = currentTeamUrl + "/logout";
};
-module.exports.loginByEmail = function(domain, email, password, success, error) {
+module.exports.loginByEmail = function(name, email, password, success, error) {
$.ajax({
url: "/api/v1/users/login",
dataType: 'json',
contentType: 'application/json',
type: 'POST',
- data: JSON.stringify({domain: domain, email: email, password: password}),
+ data: JSON.stringify({name: name, email: email, password: password}),
success: function(data, textStatus, xhr) {
module.exports.track('api', 'api_users_login_success', data.team_id, 'email', data.email);
success(data, textStatus, xhr);
@@ -317,7 +324,7 @@ module.exports.inviteMembers = function(data, success, error) {
module.exports.track('api', 'api_teams_invite_members');
};
-module.exports.updateTeamName = function(data, success, error) {
+module.exports.updateTeamDisplayName = function(data, success, error) {
$.ajax({
url: "/api/v1/teams/update_name",
dataType: 'json',
@@ -326,7 +333,7 @@ module.exports.updateTeamName = function(data, success, error) {
data: JSON.stringify(data),
success: success,
error: function(xhr, status, err) {
- e = handleError("updateTeamName", xhr, status, err);
+ e = handleError("updateTeamDisplayName", xhr, status, err);
error(e);
}
});
@@ -334,13 +341,13 @@ module.exports.updateTeamName = function(data, success, error) {
module.exports.track('api', 'api_teams_update_name');
};
-module.exports.signupTeam = function(email, name, success, error) {
+module.exports.signupTeam = function(email, display_name, success, error) {
$.ajax({
url: "/api/v1/teams/signup",
dataType: 'json',
contentType: 'application/json',
type: 'POST',
- data: JSON.stringify({email: email, name: name}),
+ data: JSON.stringify({email: email, display_name: display_name}),
success: success,
error: function(xhr, status, err) {
e = handleError("singupTeam", xhr, status, err);
@@ -366,16 +373,16 @@ module.exports.createTeam = function(team, success, error) {
});
};
-module.exports.findTeamByDomain = function(domain, success, error) {
+module.exports.findTeamByName = function(teamName, success, error) {
$.ajax({
- url: "/api/v1/teams/find_team_by_domain",
+ url: "/api/v1/teams/find_team_by_name",
dataType: 'json',
contentType: 'application/json',
type: 'POST',
- data: JSON.stringify({domain: domain}),
+ data: JSON.stringify({name: teamName}),
success: success,
error: function(xhr, status, err) {
- e = handleError("findTeamByDomain", xhr, status, err);
+ e = handleError("findTeamByName", xhr, status, err);
error(e);
}
});
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 3aadfb4b0..187e3c4a3 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -54,7 +54,7 @@ module.exports = {
DEFAULT_CHANNEL: 'town-square',
OFFTOPIC_CHANNEL: 'off-topic',
POST_CHUNK_SIZE: 60,
- RESERVED_DOMAINS: [
+ RESERVED_TEAM_NAMES: [
"www",
"web",
"admin",
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 5ded0e76f..00580af6e 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -16,10 +16,10 @@ module.exports.isEmail = function(email) {
};
module.exports.cleanUpUrlable = function(input) {
- var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-');
- cleaned = cleaned.replace(/^\-+/, '');
- cleaned = cleaned.replace(/\-+$/, '');
- return cleaned;
+ var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-');
+ cleaned = cleaned.replace(/^\-+/, '');
+ cleaned = cleaned.replace(/\-+$/, '');
+ return cleaned;
};
@@ -114,7 +114,7 @@ module.exports.notifyMe = function(title, body, channel) {
if (channel) {
module.exports.switchChannel(channel);
} else {
- window.location.href = "/channels/town-square";
+ window.location.href = "/";
}
};
setTimeout(function(){
@@ -198,7 +198,13 @@ module.exports.getTimestamp = function() {
}
var testUrlMatch = function(text) {
- var urlMatcher = new Autolinker.matchParser.MatchParser;
+ var urlMatcher = new Autolinker.matchParser.MatchParser({
+ urls: true,
+ emails: false,
+ twitter: false,
+ phone: false,
+ hashtag: false,
+ });
var result = [];
var replaceFn = function(match) {
var linkData = {};
@@ -225,7 +231,7 @@ module.exports.extractLinks = function(text) {
}
return { "links": links, "text": text };
-}
+}
module.exports.escapeRegExp = function(string) {
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
@@ -303,18 +309,25 @@ var getYoutubeEmbed = function(link) {
};
var success = function(data) {
- $('.video-uploader.'+youtubeId).html(data.data.uploader);
- $('.video-title.'+youtubeId).find('a').html(data.data.title);
+ if(!data.items.length || !data.items[0].snippet) {
+ return;
+ }
+ var metadata = data.items[0].snippet;
+ $('.video-uploader.'+youtubeId).html(metadata.channelTitle);
+ $('.video-title.'+youtubeId).find('a').html(metadata.title);
$(".post-list-holder-by-time").scrollTop($(".post-list-holder-by-time")[0].scrollHeight);
$(".post-list-holder-by-time").perfectScrollbar('update');
};
- $.ajax({
- async: true,
- url: 'https://gdata.youtube.com/feeds/api/videos/'+youtubeId+'?v=2&alt=jsonc',
- type: 'GET',
- success: success
- });
+ if(config.GoogleDeveloperKey) {
+ $.ajax({
+ async: true,
+ url: "https://www.googleapis.com/youtube/v3/videos",
+ type: 'GET',
+ data: {part:"snippet", id:youtubeId, key:config.GoogleDeveloperKey},
+ success: success
+ });
+ }
return (
<div className="post-comment">
@@ -671,13 +684,13 @@ module.exports.isValidUsername = function (name) {
error = "First character must be a letter.";
}
- else
+ else
{
var lowerName = name.toLowerCase().trim();
- for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++)
+ for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++)
{
- if (lowerName === Constants.RESERVED_USERNAMES[i])
+ if (lowerName === Constants.RESERVED_USERNAMES[i])
{
error = "Cannot use a reserved word as a username.";
break;
@@ -695,8 +708,8 @@ module.exports.switchChannel = function(channel, teammate_name) {
id: channel.id
});
- var domain = window.location.href.split('/channels')[0];
- history.replaceState('data', '', domain + '/channels/' + channel.name);
+ var teamURL = window.location.href.split('/channels')[0];
+ history.replaceState('data', '', teamURL + '/channels/' + channel.name);
if (channel.type === 'D' && teammate_name) {
document.title = teammate_name + " " + document.title.substring(document.title.lastIndexOf("-"));
@@ -771,14 +784,57 @@ Image.prototype.load = function(url, progressCallback) {
Image.prototype.completedPercentage = 0;
-module.exports.getHomeLink = function() {
- if (config.HomeLink != "") {
- return config.HomeLink;
- }
- var parts = window.location.host.split(".");
- if (parts.length <= 1) {
- return window.location.protocol + "//" + window.location.host;
- }
- parts[0] = "www";
- return window.location.protocol + "//" + parts.join(".");
-}
+module.exports.changeColor =function(col, amt) {
+
+ var usePound = false;
+
+ if (col[0] == "#") {
+ col = col.slice(1);
+ usePound = true;
+ }
+
+ var num = parseInt(col,16);
+
+ var r = (num >> 16) + amt;
+
+ if (r > 255) r = 255;
+ else if (r < 0) r = 0;
+
+ var b = ((num >> 8) & 0x00FF) + amt;
+
+ if (b > 255) b = 255;
+ else if (b < 0) b = 0;
+
+ var g = (num & 0x0000FF) + amt;
+
+ if (g > 255) g = 255;
+ else if (g < 0) g = 0;
+
+ return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6);
+};
+
+module.exports.getFullName = function(user) {
+ if (user.first_name && user.last_name) {
+ return user.first_name + " " + user.last_name;
+ } else if (user.first_name) {
+ return user.first_name;
+ } else if (user.last_name) {
+ return user.last_name;
+ } else {
+ return "";
+ }
+};
+
+module.exports.getDisplayName = function(user) {
+ if (user.nickname && user.nickname.trim().length > 0) {
+ return user.nickname;
+ } else {
+ var fullName = module.exports.getFullName(user);
+
+ if (fullName) {
+ return fullName;
+ } else {
+ return user.username;
+ }
+ }
+};
diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss
index fd6225bdd..1fb970075 100644
--- a/web/sass-files/sass/partials/_base.scss
+++ b/web/sass-files/sass/partials/_base.scss
@@ -10,6 +10,9 @@ body {
height: 100%;
&.white {
background: #fff;
+ > .container-fluid {
+ overflow: auto;
+ }
.inner__wrap {
> .row.content {
min-height: 100%;
@@ -53,6 +56,9 @@ div.theme {
.form-control {
@include border-radius(2px);
+ &.no-resize {
+ resize: none;
+ }
}
.form-group {
@@ -126,6 +132,10 @@ div.theme {
to { transform: scale(1) rotate(360deg);}
}
+.glyphicon-refresh-animate {
+ @include animation(spin .7s infinite linear);
+}
+
.black-bg {
background-color: black !important;
}
diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss
index 79142176e..56d03e171 100644
--- a/web/sass-files/sass/partials/_files.scss
+++ b/web/sass-files/sass/partials/_files.scss
@@ -32,6 +32,7 @@
}
}
.preview-img {
+ display: block;
height: auto;
max-width: 100%;
}
@@ -137,7 +138,7 @@
border: 1px solid #E2E2E2;
background-color: #FFF;
background-repeat: no-repeat;
- background-position: left center;
+ background-position: top left;
}
a {
text-decoration: none;
diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss
index 1ec1109a5..adeaa70d7 100644
--- a/web/sass-files/sass/partials/_headers.scss
+++ b/web/sass-files/sass/partials/_headers.scss
@@ -75,14 +75,16 @@
// Team Header in Sidebar
.sidebar--left, .sidebar--menu {
.team__header {
- padding: 0 15px 0 15px;
+ padding: 10px;
@include legacy-pie-clearfix;
a {
color: #fff;
}
.navbar-right {
font-size: 0.85em;
- margin: 16px -5px 0;
+ position: absolute;
+ top: 20px;
+ right: 22px;
.dropdown-toggle {
padding: 0 10px;
}
@@ -93,24 +95,38 @@
}
}
.dropdown__icon {
- background: url("../images/dropdown-icon.png");
- width: 4px;
- height: 16px;
- @include background-size(100% 100%);
- display: inline-block;
+ fill: #fff;
}
}
- .team__name {
+ .settings__link a:hover, a:visited, a:link, a:active {
+ text-decoration: none;
+ }
+ .user__picture {
+ width: 36px;
+ height: 36px;
float: left;
- line-height: 50px;
+ @include border-radius(36px);
+ }
+ .header__info {
+ padding-left: 42px;
+ color: #fff;
+ }
+ .team__name, .user__name {
+ display: block;
+ line-height: 18px;
font-weight: 600;
- font-size: 1.2em;
+ font-size: 16px;
max-width: 80%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
text-decoration: none;
}
+ .user__name {
+ font-size: 14px;
+ font-weight: 400;
+ color: #eee;
+ }
> .nav {
> li {
> a {
@@ -144,9 +160,10 @@
font-size: 14px;
line-height: 50px;
#member_popover {
+ margin-right: 5px;
width: 45px;
color: #999;
-
+ cursor: pointer;
}
&.alt {
margin: 0;
@@ -213,3 +230,25 @@
top: 1px;
}
}
+
+.channel-header__links {
+ height: 32px;
+ vertical-align: top;
+ display: inline-block;
+ width: 15px;
+ margin: 9px 4px 3px 0;
+ &:hover {
+ svg {
+ fill: #888;
+ }
+ }
+ a {
+ height: 100%;
+ display: block;
+ }
+ svg {
+ vertical-align: top;
+ margin-top: 8px;
+ fill: #AAA;
+ }
+}
diff --git a/web/sass-files/sass/partials/_loading.scss b/web/sass-files/sass/partials/_loading.scss
index bc819e8f5..d71055722 100644
--- a/web/sass-files/sass/partials/_loading.scss
+++ b/web/sass-files/sass/partials/_loading.scss
@@ -2,13 +2,8 @@
display: table;
width: 100%;
height: 100%;
- position: absolute;
- @include box-sizing(border-box);
+ padding: 60px;
text-align: center;
- &.loading-screen--channel {
- position: relative;
- padding: 4em 0 3.5em;
- }
.loading__content {
display: table-cell;
vertical-align: middle;
@@ -19,11 +14,7 @@
margin: 0 0.2em 0;
display: inline-block;
}
- }
-}
-.loading-screen {
- .loading__content {
.round {
background-color: #444;
width: 4px;
@@ -32,43 +23,18 @@
margin: 0 2px;
opacity: 0.1;
@include border-radius(10px);
- -moz-animation: move 0.75s infinite linear;
- -webkit-animation: move 0.75s infinite linear;
- }
-
- #round_1 {
- -moz-animation-delay: .2s;
- -webkit-animation-delay: .2s;
+ @include animation(move 0.75s infinite linear);
}
- #round_2 {
- -moz-animation-delay: .4s;
- -webkit-animation-delay: .4s;
- }
-
- #round_3 {
- -moz-animation-delay: .6s;
- -webkit-animation-delay: .6s;
- }
-
- @-moz-keyframes move {
- 0% {
- opacity: 1;
+ @for $i from 1 through 3 {
+ .round-#{$i} {
+ @include animation-delay(.2s*$i);
}
-
- 100% {
- opacity: 0.1;
- };
}
- @-webkit-keyframes move {
- 0% {
- opacity: 1;
- }
-
- 100% {
- opacity: 0.1;
- };
+ @include keyframes(move) {
+ from { opacity: 1; }
+ to { opacity: 0.1; }
}
}
}
diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss
index da46866c8..1396f21a1 100644
--- a/web/sass-files/sass/partials/_mentions.scss
+++ b/web/sass-files/sass/partials/_mentions.scss
@@ -11,13 +11,14 @@
position: absolute;
z-index: 1060;
.mentions-box {
- max-height: 303px;
- position:absolute;
- background-color:#fff;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ background-color: #fff;
border: $border-gray;
overflow-x: hidden;
overflow-y: scroll;
- bottom:0;
+ bottom: 0;
}
}
@@ -36,6 +37,10 @@
}
}
+.mentions-focus {
+ background-color: #E6F2FA;
+}
+
.mentions-text {
font-color:black;
}
diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss
index 707e71cf0..f359037c5 100644
--- a/web/sass-files/sass/partials/_modal.scss
+++ b/web/sass-files/sass/partials/_modal.scss
@@ -154,7 +154,8 @@
background: #FFF;
position: relative;
max-width: 90%;
- min-width: 280px;
+ min-height: 50px;
+ min-width: 320px;
@include border-radius(3px);
display: table;
margin: 0 auto;
diff --git a/web/sass-files/sass/partials/_navbar.scss b/web/sass-files/sass/partials/_navbar.scss
index 62864afb7..6d8f11ce3 100644
--- a/web/sass-files/sass/partials/_navbar.scss
+++ b/web/sass-files/sass/partials/_navbar.scss
@@ -43,6 +43,7 @@
font-size: 16px;
.heading {
margin-right: 3px;
+ font-weight: 600;
color: #fff;
}
.header-dropdown__icon {
diff --git a/web/sass-files/sass/partials/_popover.scss b/web/sass-files/sass/partials/_popover.scss
new file mode 100644
index 000000000..fa1b44841
--- /dev/null
+++ b/web/sass-files/sass/partials/_popover.scss
@@ -0,0 +1,9 @@
+.user-popover {
+ cursor: pointer;
+ display: inline-block;
+}
+
+.user-popover__image {
+ margin: 0 0 10px;
+ @include border-radius(128px);
+} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index d0c536363..481ed63a5 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -119,6 +119,7 @@ body.ios {
table-layout: fixed;
width: 100%;
min-height: 100%;
+ height: 100%;
.post-list__content {
display: table-cell;
vertical-align: bottom;
@@ -186,6 +187,12 @@ body.ios {
.post-create-footer {
@include clearfix;
padding: 0;
+ .has-error {
+ .control-label {
+ font-weight: normal;
+ margin-bottom: 0;
+ }
+ }
.msg-typing {
min-height: 20px;
line-height: 18px;
@@ -207,6 +214,12 @@ body.ios {
.dropdown, .comment-icon__container {
@include opacity(1);
}
+ .dropdown-toggle:after {
+ content: '...';
+ }
+ .dropdown-toggle:hover:after {
+ content: '[...]';
+ }
}
background: #f5f5f5;
}
@@ -424,4 +437,4 @@ body.ios {
width: 40px;
}
}
-}
+} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss
index 8816393c8..4cf3e32a1 100644
--- a/web/sass-files/sass/partials/_post_right.scss
+++ b/web/sass-files/sass/partials/_post_right.scss
@@ -11,7 +11,7 @@
.post {
&.post--root {
- padding: 1em 1em 0;
+ padding: 0 1em 0;
margin: 1em 0;
hr {
border-color: #DDD;
@@ -62,6 +62,11 @@
}
}
+.post-right-channel__name {
+ font-weight: 600;
+ margin: 0 0 10px 0;
+}
+
.post-right-root-container li {
display: inline;
list-style-type: none;
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index 9c0c09ee3..a33d69378 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -413,7 +413,7 @@
}
}
.footer, .footer-pane, .footer-push {
- height: 185px;
+ height: auto;
}
.footer-pane {
.footer-link {
@@ -432,14 +432,16 @@
color: #fff;
.search__form {
border: none;
- padding: 0 10px 0 30px;
+ padding: 0 60px 0 25px;
.form-control {
+ line-height: 31px;
background: none;
color: #fff;
- border-bottom: 1px solid #fff;
- border-bottom: 1px solid rgba(#fff, 0.7);
border-radius: 0;
- padding: 0 0 0 23px;
+ padding: 0 10px 0;
+ @include input-placeholder {
+ color: rgba(#fff, 0.6);
+ }
}
::-webkit-input-placeholder {
color: #fff;
@@ -534,6 +536,11 @@
.sidebar--right__close {
display: none;
}
+ .search__form {
+ .glyphicon {
+ color: #fff;
+ }
+ }
}
.inner__wrap {
&.move--right {
@@ -570,6 +577,8 @@
.modal {
.modal-image {
.image-wrapper {
+ font-size: 12px;
+ max-width: 280px;
.modal-close {
@include opacity(1);
}
diff --git a/web/sass-files/sass/partials/_search.scss b/web/sass-files/sass/partials/_search.scss
index 8d51d00c0..d4a4da243 100644
--- a/web/sass-files/sass/partials/_search.scss
+++ b/web/sass-files/sass/partials/_search.scss
@@ -2,21 +2,20 @@
padding: 8px 8px 8px 0;
}
.sidebar__collapse {
- width: 20px;
- height: 30px;
+ width: auto;
+ height: auto;
position: absolute;
- top: 10px;
- left: 6px;
+ top: 17px;
+ right: 15px;
cursor: pointer;
- background: url("../images/arrow-left.png") center no-repeat;
- @include background-size(10px 15px);
z-index: 5;
display: none;
}
.sidebar__search-icon {
position: absolute;
- left: 40px;
+ left: 15px;
top: 18px;
+ font-size: 16px;
@include opacity(0.8);
display: none;
}
diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss
index af759c650..e60bc290e 100644
--- a/web/sass-files/sass/partials/_settings.scss
+++ b/web/sass-files/sass/partials/_settings.scss
@@ -1,6 +1,10 @@
.user-settings {
background: #fff;
min-height:300px;
+ .table-responsive {
+ max-width: 560px;
+ max-height: 300px;
+ }
}
.settings-modal {
diff --git a/web/sass-files/sass/partials/_sidebar--right.scss b/web/sass-files/sass/partials/_sidebar--right.scss
index a0e82fd2f..d02a92448 100644
--- a/web/sass-files/sass/partials/_sidebar--right.scss
+++ b/web/sass-files/sass/partials/_sidebar--right.scss
@@ -10,6 +10,14 @@
&.move--left {
right: 0;
}
+ .sidebar--right__back {
+ color: #666;
+ width: 20px;
+ text-align: center;
+ margin: 0 0 0 -6px;
+ font-size: 12px;
+ display: inline-block;
+ }
.sidebar-right__body {
border-left: $border-gray;
border-top: $border-gray;
diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss
index a714aa44f..98931279b 100644
--- a/web/sass-files/sass/partials/_signup.scss
+++ b/web/sass-files/sass/partials/_signup.scss
@@ -6,7 +6,7 @@
}
.signup-team__container {
padding: 100px 0px 50px 0px;
- max-width: 340px;
+ max-width: 600px;
margin: 0 auto;
font-size: 1.1em;
position: relative;
@@ -118,4 +118,4 @@
.signup-team-login {
padding-bottom: 10px;
font-weight: 700;
-} \ No newline at end of file
+}
diff --git a/web/sass-files/sass/partials/_variables.scss b/web/sass-files/sass/partials/_variables.scss
index eb1f3eef3..5d883ab44 100644
--- a/web/sass-files/sass/partials/_variables.scss
+++ b/web/sass-files/sass/partials/_variables.scss
@@ -7,10 +7,4 @@ $primary-color: #2389D7;
$primary-color--hover: darken(#2389D7, 5%);
$body-bg: #e9e9e9;
$header-bg: #f9f9f9;
-$border-gray: 1px solid #ddd;
-
-// Animation
-.glyphicon-refresh-animate {
- -animation: spin .7s infinite linear;
- -webkit-animation: spin2 .7s infinite linear;
-} \ No newline at end of file
+$border-gray: 1px solid #ddd; \ No newline at end of file
diff --git a/web/sass-files/sass/styles.scss b/web/sass-files/sass/styles.scss
index 9cc26320c..294f6122a 100644
--- a/web/sass-files/sass/styles.scss
+++ b/web/sass-files/sass/styles.scss
@@ -15,6 +15,7 @@
@import "partials/headers";
@import "partials/footer";
@import "partials/content";
+@import "partials/popover";
@import "partials/post";
@import "partials/post_right";
@import "partials/navbar";
@@ -29,7 +30,7 @@
@import "partials/modal";
@import "partials/mentions";
@import "partials/error";
-@import "partials/loading";
+@import "partials/loading";
// Responsive Css
@import "partials/responsive";
diff --git a/web/static/config/config.js b/web/static/config/config.js
index 45c713da2..0d564b77e 100644
--- a/web/static/config/config.js
+++ b/web/static/config/config.js
@@ -16,6 +16,10 @@ var config = {
RequireInviteNames: false,
AllowSignupDomainsWizard: false,
+ // Google Developer Key (for Youtube API links)
+ // Leave blank to disable
+ GoogleDeveloperKey: "",
+
// Privacy switches
ShowEmail: true,
diff --git a/web/static/images/dropdown-icon.png b/web/static/images/dropdown-icon.png
deleted file mode 100644
index 5c271cfc7..000000000
--- a/web/static/images/dropdown-icon.png
+++ /dev/null
Binary files differ
diff --git a/web/templates/channel.html b/web/templates/channel.html
index d96aee3d4..eaf0f2563 100644
--- a/web/templates/channel.html
+++ b/web/templates/channel.html
@@ -46,7 +46,7 @@
<div id="direct_channel_modal"></div>
<div id="channel_info_modal"></div>
<script>
-window.setup_channel_page('{{ .Props.TeamName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
+window.setup_channel_page('{{ .Props.TeamDisplayName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
</script>
</body>
</html>
diff --git a/web/templates/head.html b/web/templates/head.html
index ead648571..d14340998 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -31,7 +31,6 @@
<link rel="stylesheet" href="/static/css/styles.css">
<script src="/static/js/perfect-scrollbar-0.6.3.jquery.js"></script>
- <script src="/static/js/bundle.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1','packages':['annotationchart']}]}"></script>
@@ -43,6 +42,9 @@
}
config.SiteName = '{{ .SiteName }}';
</script>
+
+ <script src="/static/js/bundle.js"></script>
+
<script>
if (config.LogglyWriteKey != null && config.LogglyWriteKey !== "") {
var _LTracker = _LTracker || [];
@@ -60,7 +62,7 @@
var user = window.UserStore.getCurrentUser(true);
if (user) {
analytics.identify(user.id, {
- name: user.full_name,
+ name: user.nickname,
email: user.email,
createdAt: user.create_at,
username: user.username,
diff --git a/web/templates/home.html b/web/templates/home.html
index abf8062f2..9ec8b7000 100644
--- a/web/templates/home.html
+++ b/web/templates/home.html
@@ -17,7 +17,7 @@
</div>
</div>
<script>
- window.setup_home_page();
+ window.setup_home_page({{.Props.TeamURL}});
</script>
</body>
</html>
diff --git a/web/templates/login.html b/web/templates/login.html
index c107e1ad5..24cebec8f 100644
--- a/web/templates/login.html
+++ b/web/templates/login.html
@@ -7,7 +7,7 @@
<div class="inner__wrap">
<div class="row content">
<div class="signup-header">
- {{.Props.TeamName}}
+ {{.Props.TeamDisplayName}}
</div>
<div class="col-sm-12">
<div id="login"></div>
@@ -20,7 +20,7 @@
</div>
</div>
<script>
- window.setup_login_page();
+window.setup_login_page({{.Props.TeamDisplayName}}, {{.Props.TeamName}});
</script>
</body>
</html>
diff --git a/web/templates/password_reset.html b/web/templates/password_reset.html
index 8b63556b1..6244f6418 100644
--- a/web/templates/password_reset.html
+++ b/web/templates/password_reset.html
@@ -9,7 +9,7 @@
</div>
</div>
<script>
- window.setup_password_reset_page('{{ .Props.IsReset }}', '{{ .Props.TeamName }}', '{{ .Props.Domain }}', '{{ .Props.Hash }}', '{{ .Props.Data }}');
+ window.setup_password_reset_page('{{ .Props.IsReset }}', '{{ .Props.TeamDisplayName }}', '{{ .Props.TeamName }}', '{{ .Props.Hash }}', '{{ .Props.Data }}');
</script>
</body>
</html>
diff --git a/web/templates/signup_team.html b/web/templates/signup_team.html
index fad332bee..b86590589 100644
--- a/web/templates/signup_team.html
+++ b/web/templates/signup_team.html
@@ -12,7 +12,6 @@
<h2>All team communication in one place, searchable and accessible anywhere</h2>
<h4 class="text--light">{{ .SiteName }} is free for an unlimited time, for unlimited users </h4 class="text--light">
<div id="signup-team"></div>
- <a class="signup-team-login" href="/login">or Sign In</a>
</div>
</div>
<div class="footer-push"></div>
@@ -23,7 +22,7 @@
</div>
</div>
<script>
- window.setup_signup_team_page();
+window.setup_signup_team_page();
</script>
</body>
</html>
diff --git a/web/templates/signup_team_complete.html b/web/templates/signup_team_complete.html
index 59f49cdbd..674e54ee2 100644
--- a/web/templates/signup_team_complete.html
+++ b/web/templates/signup_team_complete.html
@@ -19,7 +19,7 @@
</div>
</div>
<script>
- window.setup_signup_team_complete_page('{{.Props.Email}}', '{{.Props.Name}}', '{{.Props.Data}}', '{{.Props.Hash}}');
+window.setup_signup_team_complete_page('{{.Props.Email}}', '{{.Props.DisplayName}}', '{{.Props.Data}}', '{{.Props.Hash}}');
</script>
</body>
</html>
diff --git a/web/templates/signup_team_confirm.html b/web/templates/signup_team_confirm.html
index 9e21126da..3a6134af3 100644
--- a/web/templates/signup_team_confirm.html
+++ b/web/templates/signup_team_confirm.html
@@ -8,7 +8,6 @@
<div class="row content">
<div class="col-sm-12">
<div class="signup-team__container">
- <p>Did you mean to sign-in rather than sign up? Sign in <a href="/login">here</a>. </p>
<h3>Sign up Complete</h3>
<p>Please check your email: {{ .Props.Email }}<br>
You email contains a link to set up your team</p>
diff --git a/web/templates/signup_user_complete.html b/web/templates/signup_user_complete.html
index 5fe907ba7..0cc655b63 100644
--- a/web/templates/signup_user_complete.html
+++ b/web/templates/signup_user_complete.html
@@ -19,7 +19,7 @@
</div>
</div>
<script>
- window.setup_signup_user_complete_page('{{.Props.Email}}', '{{.Props.TeamDomain}}', '{{.Props.TeamName}}', '{{.Props.TeamId}}', '{{.Props.Data}}', '{{.Props.Hash}}');
+ window.setup_signup_user_complete_page('{{.Props.Email}}', '{{.Props.TeamName}}', '{{.Props.TeamDisplayName}}', '{{.Props.TeamId}}', '{{.Props.Data}}', '{{.Props.Hash}}');
</script>
</body>
</html>
diff --git a/web/web.go b/web/web.go
index e771157d8..3e4bc2d53 100644
--- a/web/web.go
+++ b/web/web.go
@@ -42,25 +42,28 @@ func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) {
func InitWeb() {
l4g.Debug("Initializing web routes")
+ mainrouter := api.Srv.Router
+
staticDir := utils.FindDir("web/static")
l4g.Debug("Using static directory at %v", staticDir)
- api.Srv.Router.PathPrefix("/static/").Handler(http.StripPrefix("/static/",
- http.FileServer(http.Dir(staticDir))))
-
- api.Srv.Router.Handle("/", api.AppHandler(root)).Methods("GET")
- api.Srv.Router.Handle("/login", api.AppHandler(login)).Methods("GET")
- api.Srv.Router.Handle("/signup_team_confirm/", api.AppHandler(signupTeamConfirm)).Methods("GET")
- api.Srv.Router.Handle("/signup_team_complete/", api.AppHandler(signupTeamComplete)).Methods("GET")
- api.Srv.Router.Handle("/signup_user_complete/", api.AppHandler(signupUserComplete)).Methods("GET")
-
- api.Srv.Router.Handle("/logout", api.AppHandler(logout)).Methods("GET")
-
- api.Srv.Router.Handle("/verify", api.AppHandler(verifyEmail)).Methods("GET")
- api.Srv.Router.Handle("/find_team", api.AppHandler(findTeam)).Methods("GET")
- api.Srv.Router.Handle("/reset_password", api.AppHandler(resetPassword)).Methods("GET")
-
- csr := api.Srv.Router.PathPrefix("/channels").Subrouter()
- csr.Handle("/{name:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}", api.UserRequired(getChannel)).Methods("GET")
+ mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
+
+ mainrouter.Handle("/", api.AppHandlerIndependent(root)).Methods("GET")
+ mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}", api.AppHandler(login)).Methods("GET")
+ mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/", api.AppHandler(login)).Methods("GET")
+ mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
+ mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
+ mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
+ // Bug in gorilla.mux pervents us from using regex here.
+ mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET")
+
+ // Anything added here must have an _ in it so it does not conflict with team names
+ mainrouter.Handle("/signup_team_complete/", api.AppHandlerIndependent(signupTeamComplete)).Methods("GET")
+ mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET")
+ mainrouter.Handle("/signup_team_confirm/", api.AppHandlerIndependent(signupTeamConfirm)).Methods("GET")
+ mainrouter.Handle("/verify_email", api.AppHandlerIndependent(verifyEmail)).Methods("GET")
+ mainrouter.Handle("/find_team", api.AppHandlerIndependent(findTeam)).Methods("GET")
+ mainrouter.Handle("/signup_team", api.AppHandlerIndependent(signup)).Methods("GET")
watchAndParseTemplates()
}
@@ -128,46 +131,53 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
}
if len(c.Session.UserId) == 0 {
- if api.IsTestDomain(r) || strings.Index(r.Host, "www") == 0 || strings.Index(r.Host, "beta") == 0 || strings.Index(r.Host, "ci") == 0 {
- page := NewHtmlTemplatePage("signup_team", "Signup")
- page.Render(c, w)
- } else {
- login(c, w, r)
- }
+ page := NewHtmlTemplatePage("signup_team", "Signup")
+ page.Render(c, w)
} else {
page := NewHtmlTemplatePage("home", "Home")
+ page.Props["TeamURL"] = c.GetTeamURL()
page.Render(c, w)
}
}
-func login(c *api.Context, w http.ResponseWriter, r *http.Request) {
+func signup(c *api.Context, w http.ResponseWriter, r *http.Request) {
+
if !CheckBrowserCompatability(c, r) {
return
}
- teamName := "Beta"
- teamDomain := ""
- siteDomain := "." + utils.Cfg.ServiceSettings.Domain
+ page := NewHtmlTemplatePage("signup_team", "Signup")
+ page.Render(c, w)
+}
+
+func login(c *api.Context, w http.ResponseWriter, r *http.Request) {
+ if !CheckBrowserCompatability(c, r) {
+ return
+ }
+ params := mux.Vars(r)
+ teamName := params["team"]
- if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
- teamDomain = "developer"
- } else if utils.Cfg.ServiceSettings.Mode == utils.MODE_BETA {
- teamDomain = "beta"
+ var team *model.Team
+ if tResult := <-api.Srv.Store.Team().GetByName(teamName); tResult.Err != nil {
+ l4g.Error("Couldn't find team name=%v, teamURL=%v, err=%v", teamName, c.GetTeamURL(), tResult.Err.Message)
+ // This should probably do somthing nicer
+ http.Redirect(w, r, "http://"+r.Host, http.StatusTemporaryRedirect)
+ return
} else {
- teamDomain, siteDomain = model.GetSubDomain(c.TeamUrl)
- siteDomain = "." + siteDomain + ".com"
+ team = tResult.Data.(*model.Team)
+ }
- if tResult := <-api.Srv.Store.Team().GetByDomain(teamDomain); tResult.Err != nil {
- l4g.Error("Couldn't find team teamDomain=%v, siteDomain=%v, teamUrl=%v, err=%v", teamDomain, siteDomain, c.TeamUrl, tResult.Err.Message)
- } else {
- teamName = tResult.Data.(*model.Team).Name
- }
+ // If we are already logged into this team then go to home
+ if len(c.Session.UserId) != 0 && c.Session.TeamId == team.Id {
+ page := NewHtmlTemplatePage("home", "Home")
+ page.Props["TeamURL"] = c.GetTeamURL()
+ page.Render(c, w)
+ return
}
page := NewHtmlTemplatePage("login", "Login")
+ page.Props["TeamDisplayName"] = team.DisplayName
page.Props["TeamName"] = teamName
- page.Props["TeamDomain"] = teamDomain
- page.Props["SiteDomain"] = siteDomain
page.Render(c, w)
}
@@ -198,7 +208,7 @@ func signupTeamComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
page := NewHtmlTemplatePage("signup_team_complete", "Complete Team Sign Up")
page.Props["Email"] = props["email"]
- page.Props["Name"] = props["name"]
+ page.Props["DisplayName"] = props["display_name"]
page.Props["Data"] = data
page.Props["Hash"] = hash
page.Render(c, w)
@@ -225,8 +235,8 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
}
props["email"] = ""
+ props["display_name"] = team.DisplayName
props["name"] = team.Name
- props["domain"] = team.Domain
props["id"] = team.Id
data = model.MapToJson(props)
hash = ""
@@ -249,8 +259,8 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
page := NewHtmlTemplatePage("signup_user_complete", "Complete User Sign Up")
page.Props["Email"] = props["email"]
+ page.Props["TeamDisplayName"] = props["display_name"]
page.Props["TeamName"] = props["name"]
- page.Props["TeamDomain"] = props["domain"]
page.Props["TeamId"] = props["id"]
page.Props["Data"] = data
page.Props["Hash"] = hash
@@ -259,12 +269,12 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
func logout(c *api.Context, w http.ResponseWriter, r *http.Request) {
api.Logout(c, w, r)
- http.Redirect(w, r, "/", http.StatusFound)
+ http.Redirect(w, r, c.GetTeamURL(), http.StatusFound)
}
func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
- name := params["name"]
+ name := params["channelname"]
var channelId string
if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil {
@@ -303,8 +313,8 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
//api.Handle404(w, r)
//Bad channel urls just redirect to the town-square for now
-
- http.Redirect(w,r,"/channels/town-square", http.StatusFound)
+
+ http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
return
}
}
@@ -319,8 +329,8 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
}
page := NewHtmlTemplatePage("channel", "")
- page.Title = name + " - " + team.Name + " " + page.SiteName
- page.Props["TeamName"] = team.Name
+ page.Title = name + " - " + team.DisplayName + " " + page.SiteName
+ page.Props["TeamDisplayName"] = team.DisplayName
page.Props["TeamType"] = team.Type
page.Props["TeamId"] = team.Id
page.Props["ChannelName"] = name
@@ -331,7 +341,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) {
resend := r.URL.Query().Get("resend")
- domain := r.URL.Query().Get("domain")
+ name := r.URL.Query().Get("name")
email := r.URL.Query().Get("email")
hashedId := r.URL.Query().Get("hid")
userId := r.URL.Query().Get("uid")
@@ -339,7 +349,7 @@ func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) {
if resend == "true" {
teamId := ""
- if result := <-api.Srv.Store.Team().GetByDomain(domain); result.Err != nil {
+ if result := <-api.Srv.Store.Team().GetByName(name); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -351,7 +361,7 @@ func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) {
return
} else {
user := result.Data.(*model.User)
- api.FireAndForgetVerifyEmail(user.Id, strings.Split(user.FullName, " ")[0], user.Email, domain, c.TeamUrl)
+ api.FireAndForgetVerifyEmail(user.Id, strings.Split(user.Nickname, " ")[0], user.Email, name, c.GetTeamURL())
http.Redirect(w, r, "/", http.StatusFound)
return
}
@@ -387,6 +397,8 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) {
isResetLink := true
hash := r.URL.Query().Get("h")
data := r.URL.Query().Get("d")
+ params := mux.Vars(r)
+ teamName := params["team"]
if len(hash) == 0 || len(data) == 0 {
isResetLink = false
@@ -405,30 +417,25 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) {
}
}
- teamName := "Developer/Beta"
- domain := ""
- if utils.Cfg.ServiceSettings.Mode != utils.MODE_DEV {
- domain, _ = model.GetSubDomain(c.TeamUrl)
-
- var team *model.Team
- if tResult := <-api.Srv.Store.Team().GetByDomain(domain); tResult.Err != nil {
- c.Err = tResult.Err
- return
- } else {
- team = tResult.Data.(*model.Team)
- }
+ teamDisplayName := "Developer/Beta"
+ var team *model.Team
+ if tResult := <-api.Srv.Store.Team().GetByName(teamName); tResult.Err != nil {
+ c.Err = tResult.Err
+ return
+ } else {
+ team = tResult.Data.(*model.Team)
+ }
- if team != nil {
- teamName = team.Name
- }
+ if team != nil {
+ teamDisplayName = team.DisplayName
}
page := NewHtmlTemplatePage("password_reset", "")
page.Title = "Reset Password - " + page.SiteName
- page.Props["TeamName"] = teamName
+ page.Props["TeamDisplayName"] = teamDisplayName
page.Props["Hash"] = hash
page.Props["Data"] = data
- page.Props["Domain"] = domain
+ page.Props["TeamName"] = teamName
page.Props["IsReset"] = strconv.FormatBool(isResetLink)
page.Render(c, w)
}