Этот коммит содержится в:
=Corey Hulen
2015-07-21 12:26:27 -08:00
родитель e87adce4a0 bee26e3f26
Коммит 848eeeeef4
149 изменённых файлов: 2321 добавлений и 2607 удалений

Просмотреть файл

@@ -41,17 +41,23 @@ build:
install: install:
@go get $(GOFLAGS) github.com/tools/godep @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; \ echo restoring go libs using godep; \
$(GOPATH)/bin/godep restore; \ $(GOPATH)/bin/godep restore; \
echo starting mattermost-mysql; \ echo starting mattermost-mysql; \
docker run --name mattermost-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mostest \ 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; \ -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 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; \ echo starting mattermost-redis; \
docker run --name mattermost-redis -p 6379:6379 -d redis > /dev/null; \ 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 fi
@cd web/react/ && npm install @cd web/react/ && npm install

Просмотреть файл

@@ -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. 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. 12. Environment tags my be left blank. Press next.
13. You will be asked to review your information. Press Launch. 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! 4. Try it out!
26. Return to the dashboard on the sidebar and wait for beanstalk update the environment. 14. Wait for beanstalk to update the environment.
27. Try it out by entering the domain you mapped into your browser. 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 Contributing
------------ ------------
@@ -137,8 +121,5 @@ To contribute to this open source project please review the Mattermost Contribut
License License
------- -------
Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details. 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.
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.

Просмотреть файл

@@ -16,10 +16,10 @@ var ServerTemplates *template.Template
type ServerTemplatePage Page type ServerTemplatePage Page
func NewServerTemplatePage(templateName, teamUrl string) *ServerTemplatePage { func NewServerTemplatePage(templateName, teamURL string) *ServerTemplatePage {
props := make(map[string]string) props := make(map[string]string)
props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl 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 { func (me *ServerTemplatePage) Render() string {

Просмотреть файл

@@ -24,12 +24,12 @@ func Setup() {
func SetupBenchmark() (*model.Team, *model.User, *model.Channel) { func SetupBenchmark() (*model.Team, *model.User, *model.Channel) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := &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) channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)

Просмотреть файл

@@ -12,8 +12,8 @@ const (
USER_PASSWORD = "passwd" USER_PASSWORD = "passwd"
CHANNEL_TYPE = model.CHANNEL_OPEN CHANNEL_TYPE = model.CHANNEL_OPEN
FUZZ_USER_EMAIL_PREFIX_LEN = 10 FUZZ_USER_EMAIL_PREFIX_LEN = 10
BTEST_TEAM_NAME = "TestTeam" BTEST_TEAM_DISPLAY_NAME = "TestTeam"
BTEST_TEAM_DOMAIN_NAME = "z-z-testdomaina" BTEST_TEAM_NAME = "z-z-testdomaina"
BTEST_TEAM_EMAIL = "test@nowhere.com" BTEST_TEAM_EMAIL = "test@nowhere.com"
BTEST_TEAM_TYPE = model.TEAM_OPEN BTEST_TEAM_TYPE = model.TEAM_OPEN
BTEST_USER_NAME = "Mr. Testing Tester" BTEST_USER_NAME = "Mr. Testing Tester"

Просмотреть файл

@@ -39,22 +39,22 @@ func NewAutoTeamCreator(client *model.Client) *AutoTeamCreator {
func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) { func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) {
var teamEmail string var teamEmail string
var teamDisplayName string
var teamName string var teamName string
var teamDomain string
if cfg.Fuzzy { if cfg.Fuzzy {
teamEmail = utils.FuzzEmail() teamEmail = utils.FuzzEmail()
teamDisplayName = utils.FuzzName()
teamName = utils.FuzzName() teamName = utils.FuzzName()
teamDomain = utils.FuzzName()
} else { } else {
teamEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset) teamEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset)
teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) teamDisplayName = utils.RandomName(cfg.NameLength, cfg.NameCharset)
teamDomain = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId() teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId()
} }
team := &model.Team{ team := &model.Team{
Name: teamName, DisplayName: teamDisplayName,
Domain: teamDomain, Name: teamName,
Email: teamEmail, Email: teamEmail,
Type: model.TEAM_OPEN, Type: model.TEAM_OPEN,
} }
result, err := cfg.client.CreateTeam(team) result, err := cfg.client.CreateTeam(team)

Просмотреть файл

@@ -33,15 +33,15 @@ func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator {
// Basic test team and user so you always know one // Basic test team and user so you always know one
func CreateBasicUser(client *model.Client) *model.AppError { 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 { 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) result, err := client.CreateTeam(newteam)
if err != nil { if err != nil {
return err return err
} }
basicteam := result.Data.(*model.Team) 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, "") result, err = client.CreateUser(newuser, "")
if err != nil { if err != nil {
return err return err
@@ -65,7 +65,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
user := &model.User{ user := &model.User{
TeamId: cfg.teamID, TeamId: cfg.teamID,
Email: userEmail, Email: userEmail,
FullName: userName, Nickname: userName,
Password: USER_PASSWORD} Password: USER_PASSWORD}
result, err := cfg.client.CreateUser(user, "") result, err := cfg.client.CreateUser(user, "")

Просмотреть файл

@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"net/http" "net/http"
"strings" "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 := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED)
message.Add("channel_id", id) message.Add("channel_id", id)
store.PublishAndForget(message) PublishAndForget(message)
result := make(map[string]string) result := make(map[string]string)
result["id"] = id 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) message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED)
store.PublishAndForget(message) PublishAndForget(message)
<-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id) <-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id)
w.Write([]byte(cm.ToJson())) w.Write([]byte(cm.ToJson()))

Просмотреть файл

@@ -138,10 +138,10 @@ func BenchmarkJoinChannel(b *testing.B) {
} }
// Secondary test user to join channels created by primary test user // 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd") Client.LoginByEmail(team.Name, user.Email, "pwd")
// Benchmark Start // Benchmark Start
b.ResetTimer() b.ResetTimer()

Просмотреть файл

@@ -14,17 +14,17 @@ import (
func TestCreateChannel(t *testing.T) { func TestCreateChannel(t *testing.T) {
Setup() 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) 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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} channel := model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
rchannel, err := Client.CreateChannel(&channel) rchannel, err := Client.CreateChannel(&channel)
@@ -94,18 +94,18 @@ func TestCreateChannel(t *testing.T) {
func TestCreateDirectChannel(t *testing.T) { func TestCreateDirectChannel(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := make(map[string]string)
data["user_id"] = user2.Id data["user_id"] = user2.Id
@@ -149,23 +149,23 @@ func TestCreateDirectChannel(t *testing.T) {
func TestUpdateChannel(t *testing.T) { func TestUpdateChannel(t *testing.T) {
Setup() 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) 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) userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) 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) userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) 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) userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
userStd.Roles = "" 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 := &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) 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") 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" desc = "b" + model.NewId() + "b"
upChannel1 = &model.Channel{Id: channel1.Id, Description: desc} 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 { if _, err := Client.UpdateChannel(upChannel1); err == nil {
t.Fatal("Standard User should have failed to update") t.Fatal("Standard User should have failed to update")
@@ -220,14 +220,14 @@ func TestUpdateChannel(t *testing.T) {
func TestUpdateChannelDesc(t *testing.T) { func TestUpdateChannelDesc(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := &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) 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") 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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_id"] = channel1.Id
data["channel_description"] = "new desc" data["channel_description"] = "new desc"
@@ -282,14 +282,14 @@ func TestUpdateChannelDesc(t *testing.T) {
func TestGetChannel(t *testing.T) { func TestGetChannel(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -324,14 +324,14 @@ func TestGetChannel(t *testing.T) {
func TestGetMoreChannel(t *testing.T) { func TestGetMoreChannel(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) 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 := &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) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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("")) rget := Client.Must(Client.GetMoreChannels(""))
data := rget.Data.(*model.ChannelList) data := rget.Data.(*model.ChannelList)
@@ -368,14 +368,14 @@ func TestGetMoreChannel(t *testing.T) {
func TestJoinChannel(t *testing.T) { func TestJoinChannel(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) 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 := &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) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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)) Client.Must(Client.JoinChannel(channel1.Id))
@@ -399,10 +399,10 @@ func TestJoinChannel(t *testing.T) {
data["user_id"] = user1.Id data["user_id"] = user1.Id
rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel) 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) 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 { if _, err := Client.JoinChannel(rchannel.Id); err == nil {
t.Fatal("shoudn't be able to join direct channel") t.Fatal("shoudn't be able to join direct channel")
@@ -412,14 +412,14 @@ func TestJoinChannel(t *testing.T) {
func TestLeaveChannel(t *testing.T) { func TestLeaveChannel(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) 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 := &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) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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)) Client.Must(Client.JoinChannel(channel1.Id))
@@ -461,25 +461,25 @@ func TestLeaveChannel(t *testing.T) {
func TestDeleteChannel(t *testing.T) { func TestDeleteChannel(t *testing.T) {
Setup() 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) 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) userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) 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) userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) 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 := &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) channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id) 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 := &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) 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") 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) userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) 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 { if _, err := Client.JoinChannel(channel1.Id); err == nil {
t.Fatal("should have failed to join deleted channel") t.Fatal("should have failed to join deleted channel")
@@ -531,14 +531,14 @@ func TestDeleteChannel(t *testing.T) {
func TestGetChannelExtraInfo(t *testing.T) { func TestGetChannelExtraInfo(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -552,19 +552,19 @@ func TestGetChannelExtraInfo(t *testing.T) {
func TestAddChannelMember(t *testing.T) { func TestAddChannelMember(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := &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) 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 { if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil {
t.Fatal("Should have errored, user not in channel") 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)) Client.Must(Client.DeleteChannel(channel2.Id))
@@ -610,30 +610,30 @@ func TestAddChannelMember(t *testing.T) {
func TestRemoveChannelMember(t *testing.T) { func TestRemoveChannelMember(t *testing.T) {
Setup() 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) 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) userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) 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) userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) 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 := &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) channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)) 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 := &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) 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) userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) 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 := &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) 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 { if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil {
t.Fatal("Should have errored, user not channel admin") 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.AddChannelMember(channel2.Id, userStd.Id))
Client.Must(Client.DeleteChannel(channel2.Id)) Client.Must(Client.DeleteChannel(channel2.Id))
@@ -680,14 +680,14 @@ func TestRemoveChannelMember(t *testing.T) {
func TestUpdateNotifyLevel(t *testing.T) { func TestUpdateNotifyLevel(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := &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) 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") 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) 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["channel_id"] = channel1.Id
data["user_id"] = user2.Id data["user_id"] = user2.Id
@@ -762,14 +762,14 @@ func TestUpdateNotifyLevel(t *testing.T) {
func TestFuzzyChannel(t *testing.T) { func TestFuzzyChannel(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 // Strings that should pass as acceptable channel names
var fuzzyStringsPass = []string{ var fuzzyStringsPass = []string{

Просмотреть файл

@@ -27,8 +27,6 @@ var commands = []commandHandler{
func InitCommand(r *mux.Router) { func InitCommand(r *mux.Router) {
l4g.Debug("Initializing command api routes") l4g.Debug("Initializing command api routes")
r.Handle("/command", ApiUserRequired(command)).Methods("POST") r.Handle("/command", ApiUserRequired(command)).Methods("POST")
hub.Start()
} }
func command(c *Context, w http.ResponseWriter, r *http.Request) { 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]) numPosts, _ = strconv.Atoi(tokens[numArgs+2])
} }
} }
client := model.NewClient(c.TeamUrl + "/api/v1") client := model.NewClient(c.GetSiteURL() + "/api/v1")
if doTeams { if doTeams {
if err := CreateBasicUser(client); err != nil { if err := CreateBasicUser(client); err != nil {
l4g.Error("Failed to create testing enviroment") l4g.Error("Failed to create testing enviroment")
return true 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( enviroment, err := CreateTestEnviromentWithTeams(
client, client,
utils.Range{numTeams, numTeams}, utils.Range{numTeams, numTeams},
@@ -344,7 +342,7 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
} else { } else {
l4g.Info("Testing enviroment created") l4g.Info("Testing enviroment created")
for i := 0; i < len(enviroment.Teams); i++ { 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) 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 { if err == false {
usersr = utils.Range{10, 15} 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 := NewAutoUserCreator(client, c.Session.TeamId)
userCreator.Fuzzy = doFuzz userCreator.Fuzzy = doFuzz
userCreator.CreateTestUsers(usersr) userCreator.CreateTestUsers(usersr)
@@ -413,7 +411,7 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool {
if err == false { if err == false {
channelsr = utils.Range{20, 30} channelsr = utils.Range{20, 30}
} }
client := model.NewClient(c.TeamUrl + "/api/v1") client := model.NewClient(c.GetSiteURL() + "/api/v1")
client.MockSession(c.Session.Id) client.MockSession(c.Session.Id)
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId) channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
channelCreator.Fuzzy = doFuzz 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) client.MockSession(c.Session.Id)
testPoster := NewAutoPostCreator(client, command.ChannelId) testPoster := NewAutoPostCreator(client, command.ChannelId)
testPoster.Fuzzy = doFuzz testPoster.Fuzzy = doFuzz

Просмотреть файл

@@ -12,14 +12,14 @@ import (
func TestSuggestRootCommands(t *testing.T) { func TestSuggestRootCommands(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 { if _, err := Client.Command("", "", true); err == nil {
t.Fatal("Should fail") t.Fatal("Should fail")
@@ -55,14 +55,14 @@ func TestSuggestRootCommands(t *testing.T) {
func TestLogoutCommands(t *testing.T) { func TestLogoutCommands(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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) rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command)
if rs1.GotoLocation != "/logout" { if rs1.GotoLocation != "/logout" {
@@ -73,14 +73,14 @@ func TestLogoutCommands(t *testing.T) {
func TestJoinCommands(t *testing.T) { func TestJoinCommands(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) 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) channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
Client.Must(Client.LeaveChannel(channel2.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) store.Must(Srv.Store.User().VerifyEmail(user1.Id))

Просмотреть файл

@@ -17,12 +17,14 @@ import (
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE) var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
type Context struct { type Context struct {
Session model.Session Session model.Session
RequestId string RequestId string
IpAddress string IpAddress string
TeamUrl string Path string
Path string Err *model.AppError
Err *model.AppError teamURLValid bool
teamURL string
siteURL string
} }
type Page struct { type Page struct {
@@ -30,32 +32,36 @@ type Page struct {
Title string Title string
SiteName string SiteName string
FeedbackEmail string FeedbackEmail string
TeamUrl string TeamURL string
Props map[string]string Props map[string]string
} }
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { 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 { 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 { 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 { 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 { 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 { 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 { type handler struct {
@@ -64,6 +70,7 @@ type handler struct {
requireSystemAdmin bool requireSystemAdmin bool
isApi bool isApi bool
isUserActivity bool isUserActivity bool
isTeamIndependent bool
} }
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 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 := &Context{}
c.RequestId = model.NewId() c.RequestId = model.NewId()
c.IpAddress = GetIpAddress(r) c.IpAddress = GetIpAddress(r)
c.Path = r.URL.Path
protocol := "http" 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_REQUEST_ID, c.RequestId)
w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version) 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 { if c.Err == nil && h.requireUser {
c.UserRequired() c.UserRequired()
} }
@@ -165,7 +180,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(c.Err.ToJson())) w.Write([]byte(c.Err.ToJson()))
} else { } else {
if c.Err.StatusCode == http.StatusUnauthorized { 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 { } else {
RenderWebError(c.Err, w, r) RenderWebError(c.Err, w, r)
} }
@@ -265,6 +280,16 @@ func (c *Context) IsSystemAdmin() bool {
return false 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) { func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
sessionCache.Remove(c.Session.Id) 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) 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 { func GetIpAddress(r *http.Request) string {
address := r.Header.Get(model.HEADER_FORWARDED) address := r.Header.Get(model.HEADER_FORWARDED)
if len(address) == 0 { if len(address) == 0 {

Просмотреть файл

@@ -13,9 +13,9 @@ import (
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
"github.com/nfnt/resize" "github.com/nfnt/resize"
_ "golang.org/x/image/bmp"
"image" "image"
_ "image/gif" _ "image/gif"
_ "golang.org/x/image/bmp"
"image/jpeg" "image/jpeg"
"io" "io"
"net/http" "net/http"
@@ -115,7 +115,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
return 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) resStruct.Filenames = append(resStruct.Filenames, fileUrl)
} }
@@ -363,7 +363,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapToJson(newProps) data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) 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") { if !c.HasPermissionsToChannel(cchan, "getPublicLink") {
return return

Просмотреть файл

@@ -24,14 +24,14 @@ import (
func TestUploadFile(t *testing.T) { func TestUploadFile(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -111,14 +111,14 @@ func TestUploadFile(t *testing.T) {
func TestGetFile(t *testing.T) { func TestGetFile(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -173,10 +173,10 @@ func TestGetFile(t *testing.T) {
t.Fatal("file get failed") 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) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) store.Must(Srv.Store.User().VerifyEmail(user2.Id))
@@ -187,7 +187,7 @@ func TestGetFile(t *testing.T) {
data := model.MapToJson(newProps) data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) 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 { if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr != nil {
t.Fatal(downErr) t.Fatal(downErr)
@@ -258,18 +258,18 @@ func TestGetFile(t *testing.T) {
func TestGetPublicLink(t *testing.T) { func TestGetPublicLink(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := &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) 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") 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] data["filename"] = filenames[0]
if _, err := Client.GetPublicLink(data); err == nil { if _, err := Client.GetPublicLink(data); err == nil {
t.Fatal("should have errored, user not member of channel") t.Fatal("should have errored, user not member of channel")

Просмотреть файл

@@ -11,6 +11,7 @@ import (
"github.com/mattermost/platform/store" "github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
"net/http" "net/http"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -121,7 +122,7 @@ func CreateValetPost(c *Context, post *model.Post) (*model.Post, *model.AppError
rpost = result.Data.(*model.Post) rpost = result.Data.(*model.Post)
} }
fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl) fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL())
return rpost, nil return rpost, nil
} }
@@ -200,14 +201,14 @@ func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.P
} else { } else {
rpost = result.Data.(*model.Post) rpost = result.Data.(*model.Post)
fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl) fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL())
} }
return rpost, nil return rpost, nil
} }
func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) {
go func() { go func() {
// Get a list of user names (to be used as keywords) and ids for the given team // 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 { for _, k := range splitKeys {
keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
} }
}
// If turned on, add the user's case sensitive first name // If turned on, add the user's case sensitive first name
if profile.NotifyProps["first_name"] == "true" { if profile.NotifyProps["first_name"] == "true" {
splitName := strings.Split(profile.FullName, " ") keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
if len(splitName) > 0 && splitName[0] != "" {
keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id)
}
}
} }
// Add @all to keywords if user has them turned on // 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) mentionedUsers = append(mentionedUsers, k)
} }
var teamName string var teamDisplayName string
var teamURL string
if result := <-tchan; result.Err != nil { if result := <-tchan; result.Err != nil {
l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err)
return return
} else { } 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 // Build and send the emails
location, _ := time.LoadLocation("UTC") location, _ := time.LoadLocation("UTC")
tm := time.Unix(post.CreateAt/1000, 0).In(location) tm := time.Unix(post.CreateAt/1000, 0).In(location)
subjectPage := NewServerTemplatePage("post_subject", teamUrl) subjectPage := NewServerTemplatePage("post_subject", teamURL)
subjectPage.Props["TeamName"] = teamName subjectPage.Props["TeamDisplayName"] = teamDisplayName
subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["SubjectText"] = subjectText
subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Month"] = tm.Month().String()[:3]
subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
@@ -394,11 +394,9 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
continue continue
} }
firstName := strings.Split(profileMap[id].FullName, " ")[0] bodyPage := NewServerTemplatePage("post_body", teamURL)
bodyPage.Props["Nickname"] = profileMap[id].FirstName
bodyPage := NewServerTemplatePage("post_body", teamUrl) bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["FullName"] = firstName
bodyPage.Props["TeamName"] = teamName
bodyPage.Props["ChannelName"] = channelName bodyPage.Props["ChannelName"] = channelName
bodyPage.Props["BodyText"] = bodyText bodyPage.Props["BodyText"] = bodyText
bodyPage.Props["SenderName"] = senderName 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["Month"] = tm.Month().String()[:3]
bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) 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 { 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) 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 := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
message.Add("post", post.ToJson()) 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 { if len(mentionedUsers) != 0 {
message.Add("mentions", model.ArrayToJson(mentionedUsers)) 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("channel_id", rpost.ChannelId)
message.Add("message", rpost.Message) message.Add("message", rpost.Message)
store.PublishAndForget(message) PublishAndForget(message)
w.Write([]byte(rpost.ToJson())) 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) cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
pchan := Srv.Store.Post().Get(postId) pchan := Srv.Store.Post().Get(postId)
if !c.HasPermissionsToChannel(cchan, "deletePost") {
return
}
if result := <-pchan; result.Err != nil { if result := <-pchan; result.Err != nil {
c.Err = result.Err c.Err = result.Err
return return
} else { } else {
post := result.Data.(*model.PostList).Posts[postId] post := result.Data.(*model.PostList).Posts[postId]
if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(post.UserId) {
return
}
if post == nil { if post == nil {
c.SetInvalidParam("deletePost", "postId") c.SetInvalidParam("deletePost", "postId")
return return
@@ -641,7 +653,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
return 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 = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
c.Err.StatusCode = http.StatusForbidden c.Err.StatusCode = http.StatusForbidden
return return
@@ -656,7 +668,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
message.Add("post_id", post.Id) message.Add("post_id", post.Id)
message.Add("channel_id", post.ChannelId) message.Add("channel_id", post.ChannelId)
store.PublishAndForget(message) PublishAndForget(message)
result := make(map[string]string) result := make(map[string]string)
result["id"] = postId result["id"] = postId

Просмотреть файл

@@ -15,21 +15,21 @@ import (
func TestCreatePost(t *testing.T) { func TestCreatePost(t *testing.T) {
Setup() 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) 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -96,18 +96,18 @@ func TestCreatePost(t *testing.T) {
t.Fatal("Should have been forbidden") 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"} post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
_, err = Client.CreatePost(post7) _, err = Client.CreatePost(post7)
if err.StatusCode != http.StatusForbidden { if err.StatusCode != http.StatusForbidden {
t.Fatal("Should have been forbidden") 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) user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) 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 := &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) channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
@@ -126,21 +126,21 @@ func TestCreatePost(t *testing.T) {
func TestCreateValetPost(t *testing.T) { func TestCreateValetPost(t *testing.T) {
Setup() 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) 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -181,18 +181,18 @@ func TestCreateValetPost(t *testing.T) {
t.Fatal("Should have been forbidden") 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"} post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
_, err = Client.CreateValetPost(post5) _, err = Client.CreateValetPost(post5)
if err != nil { if err != nil {
t.Fatal(err) 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) user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) 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 := &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) channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
@@ -218,21 +218,21 @@ func TestCreateValetPost(t *testing.T) {
func TestUpdatePost(t *testing.T) { func TestUpdatePost(t *testing.T) {
Setup() 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) 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -289,14 +289,14 @@ func TestUpdatePost(t *testing.T) {
func TestGetPosts(t *testing.T) { func TestGetPosts(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -354,14 +354,14 @@ func TestGetPosts(t *testing.T) {
func TestSearchPosts(t *testing.T) { func TestSearchPosts(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -400,14 +400,14 @@ func TestSearchPosts(t *testing.T) {
func TestSearchHashtagPosts(t *testing.T) { func TestSearchHashtagPosts(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -431,14 +431,14 @@ func TestSearchHashtagPosts(t *testing.T) {
func TestGetPostsCache(t *testing.T) { func TestGetPostsCache(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -480,14 +480,18 @@ func TestGetPostsCache(t *testing.T) {
func TestDeletePosts(t *testing.T) { func TestDeletePosts(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) 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) r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList)
if len(r2.Posts) != 4 { 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) { func TestEmailMention(t *testing.T) {
Setup() 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -551,14 +563,14 @@ func TestEmailMention(t *testing.T) {
func TestFuzzyPosts(t *testing.T) { func TestFuzzyPosts(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)

Просмотреть файл

@@ -28,7 +28,6 @@ func NewServer() {
Srv = &Server{} Srv = &Server{}
Srv.Server = manners.NewServer() Srv.Server = manners.NewServer()
Srv.Store = store.NewSqlStore() Srv.Store = store.NewSqlStore()
store.RedisClient()
Srv.Router = mux.NewRouter() Srv.Router = mux.NewRouter()
Srv.Router.NotFoundHandler = http.HandlerFunc(Handle404) Srv.Router.NotFoundHandler = http.HandlerFunc(Handle404)
@@ -54,7 +53,7 @@ func StopServer() {
Srv.Server.Shutdown <- true Srv.Server.Shutdown <- true
Srv.Store.Close() Srv.Store.Close()
store.RedisClose() hub.Stop()
l4g.Info("Server stopped") l4g.Info("Server stopped")
} }

79
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
}
*/

Просмотреть файл

@@ -6,8 +6,6 @@ package api
import ( import (
l4g "code.google.com/p/log4go" l4g "code.google.com/p/log4go"
"fmt" "fmt"
"github.com/awslabs/aws-sdk-go/aws"
"github.com/awslabs/aws-sdk-go/service/route53"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils" "github.com/mattermost/platform/utils"
@@ -24,11 +22,11 @@ func InitTeam(r *mux.Router) {
sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST") sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST")
sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST") sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST")
sr.Handle("/signup", ApiAppHandler(signupTeam)).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("/find_teams", ApiAppHandler(findTeams)).Methods("POST")
sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST") sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST")
sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).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("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST")
sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET") 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) m := model.MapFromJson(r.Body)
email := strings.ToLower(strings.TrimSpace(m["email"])) email := strings.ToLower(strings.TrimSpace(m["email"]))
name := strings.TrimSpace(m["name"]) displayName := strings.TrimSpace(m["display_name"])
if len(email) == 0 { if len(email) == 0 {
c.SetInvalidParam("signupTeam", "email") c.SetInvalidParam("signupTeam", "email")
return return
} }
if len(name) == 0 { if len(displayName) == 0 {
c.SetInvalidParam("signupTeam", "name") c.SetInvalidParam("signupTeam", "display_name")
return return
} }
subjectPage := NewServerTemplatePage("signup_team_subject", c.TeamUrl) subjectPage := NewServerTemplatePage("signup_team_subject", c.GetSiteURL())
bodyPage := NewServerTemplatePage("signup_team_body", c.TeamUrl) bodyPage := NewServerTemplatePage("signup_team_body", c.GetSiteURL())
bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink
props := make(map[string]string) props := make(map[string]string)
props["email"] = email props["email"] = email
props["name"] = name props["display_name"] = displayName
props["time"] = fmt.Sprintf("%v", model.GetMillis()) props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props) data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) 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 { if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
c.Err = err c.Err = err
return 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"] m["follow_link"] = bodyPage.Props["Link"]
} }
@@ -119,25 +117,16 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
found := FindTeamByDomain(c, teamSignup.Team.Domain, "true") found := FindTeamByName(c, teamSignup.Team.Name, "true")
if c.Err != nil { if c.Err != nil {
return return
} }
if found { 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 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 teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault
if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil { 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.Team = *rteam
teamSignup.User = *ruser teamSignup.User = *ruser
@@ -211,87 +200,14 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
} }
} }
func doesSubDomainExist(subDomain string) bool { func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) {
// 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) {
m := model.MapFromJson(r.Body) 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"])) all := strings.ToLower(strings.TrimSpace(m["all"]))
found := FindTeamByDomain(c, domain, all) found := FindTeamByName(c, name, all)
if c.Err != nil { if c.Err != nil {
return 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 { if name == "" || len(name) > 64 {
c.SetInvalidParam("findTeamByDomain", "domain") c.SetInvalidParam("findTeamByName", "domain")
return false return false
} }
if model.IsReservedDomain(domain) { if model.IsReservedTeamName(name) {
c.Err = model.NewAppError("findTeamByDomain", "This URL is unavailable. Please try another.", "d="+domain) c.Err = model.NewAppError("findTeamByName", "This URL is unavailable. Please try another.", "name="+name)
return false return false
} }
if all == "false" { if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil { return false
return false
} else {
return true
}
} else { } else {
if doesSubDomainExist(domain) { return true
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
}
}
}
return false
} }
return false
} }
func findTeams(c *Context, w http.ResponseWriter, r *http.Request) { 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)) s := make([]string, 0, len(teams))
for _, v := range teams { for _, v := range teams {
s = append(s, v.Domain) s = append(s, v.Name)
} }
w.Write([]byte(model.ArrayToJson(s))) w.Write([]byte(model.ArrayToJson(s)))
@@ -394,33 +279,8 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
protocol := "http" subjectPage := NewServerTemplatePage("find_teams_subject", c.GetSiteURL())
bodyPage := NewServerTemplatePage("find_teams_body", c.GetSiteURL())
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)] = ""
}
}
}
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { 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) 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"]) ia = append(ia, invite["email"])
} }
InviteMembers(team, user, ia) InviteMembers(c, team, user, ia)
w.Write([]byte(invites.ToJson())) 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 { for _, invite := range invites {
if len(invite) > 0 { if len(invite) > 0 {
teamUrl := "" teamURL := ""
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV { teamURL = c.GetTeamURLFromTeam(team)
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)
}
sender := "" sender := user.GetDisplayName()
if len(strings.TrimSpace(user.FullName)) == 0 {
sender = user.Username
} else {
sender = user.FullName
}
senderRole := "" senderRole := ""
if strings.Contains(user.Roles, model.ROLE_ADMIN) || strings.Contains(user.Roles, model.ROLE_SYSTEM_ADMIN) { 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" senderRole = "member"
} }
subjectPage := NewServerTemplatePage("invite_subject", teamUrl) subjectPage := NewServerTemplatePage("invite_subject", teamURL)
subjectPage.Props["SenderName"] = sender subjectPage.Props["SenderName"] = sender
subjectPage.Props["TeamName"] = team.Name subjectPage.Props["TeamDisplayName"] = team.DisplayName
bodyPage := NewServerTemplatePage("invite_body", teamUrl) bodyPage := NewServerTemplatePage("invite_body", teamURL)
bodyPage.Props["TeamName"] = team.Name bodyPage.Props["TeamDisplayName"] = team.DisplayName
bodyPage.Props["SenderName"] = sender bodyPage.Props["SenderName"] = sender
bodyPage.Props["SenderStatus"] = senderRole bodyPage.Props["SenderStatus"] = senderRole
@@ -514,12 +363,12 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
props := make(map[string]string) props := make(map[string]string)
props["email"] = invite props["email"] = invite
props["id"] = team.Id props["id"] = team.Id
props["display_name"] = team.DisplayName
props["name"] = team.Name props["name"] = team.Name
props["domain"] = team.Domain
props["time"] = fmt.Sprintf("%v", model.GetMillis()) props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props) data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) 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 { if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"]) 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) props := model.MapFromJson(r.Body)
new_name := props["new_name"] new_name := props["new_name"]
if len(new_name) == 0 { if len(new_name) == 0 {
c.SetInvalidParam("updateTeamName", "new_name") c.SetInvalidParam("updateTeamDisplayName", "new_name")
return return
} }
teamId := props["team_id"] teamId := props["team_id"]
if len(teamId) > 0 && len(teamId) != 26 { if len(teamId) > 0 && len(teamId) != 26 {
c.SetInvalidParam("updateTeamName", "team_id") c.SetInvalidParam("updateTeamDisplayName", "team_id")
return return
} else if len(teamId) == 0 { } else if len(teamId) == 0 {
teamId = c.Session.TeamId teamId = c.Session.TeamId
} }
if !c.HasPermissionsToTeam(teamId, "updateTeamName") { if !c.HasPermissionsToTeam(teamId, "updateTeamDisplayName") {
return return
} }
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) { 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 c.Err.StatusCode = http.StatusForbidden
return 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 c.Err = result.Err
return return
} }

Просмотреть файл

@@ -32,8 +32,8 @@ func TestCreateFromSignupTeam(t *testing.T) {
data := model.MapToJson(props) data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) 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} 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"], FullName: "Corey Hulen", Password: "hello"} 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} 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) 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") t.Fatal("full name didn't match")
} }
@@ -71,24 +71,24 @@ func TestCreateFromSignupTeam(t *testing.T) {
func TestCreateTeam(t *testing.T) { func TestCreateTeam(t *testing.T) {
Setup() 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) rteam, err := Client.CreateTeam(&team)
if err != nil { if err != nil {
t.Fatal(err) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
if len(c1.Channels) != 2 { if len(c1.Channels) != 2 {
t.Fatal("default channels not created") 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") t.Fatal("full name didn't match")
} }
@@ -111,10 +111,10 @@ func TestCreateTeam(t *testing.T) {
func TestFindTeamByEmail(t *testing.T) { func TestFindTeamByEmail(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -122,7 +122,7 @@ func TestFindTeamByEmail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} else { } else {
domains := r1.Data.([]string) domains := r1.Data.([]string)
if domains[0] != team.Domain { if domains[0] != team.Name {
t.Fatal(domains) t.Fatal(domains)
} }
} }
@@ -139,14 +139,14 @@ XXXXXX investigate and fix failing test
func TestFindTeamByDomain(t *testing.T) { func TestFindTeamByDomain(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) t.Fatal(err)
} else { } else {
val := r1.Data.(bool) 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) t.Fatal(err)
} else { } else {
val := r1.Data.(bool) val := r1.Data.(bool)
@@ -179,10 +179,10 @@ func TestFindTeamByDomain(t *testing.T) {
func TestFindTeamByEmailSend(t *testing.T) { func TestFindTeamByEmailSend(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -203,14 +203,14 @@ func TestFindTeamByEmailSend(t *testing.T) {
func TestInviteMembers(t *testing.T) { func TestInviteMembers(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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 := make(map[string]string)
invite["email"] = model.NewId() + "corey@test.com" 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() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := make(map[string]string)
data["new_name"] = "NewName" 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") t.Fatal("Should have errored, not admin")
} }
Client.LoginByEmail(team.Domain, user.Email, "pwd") Client.LoginByEmail(team.Name, user.Email, "pwd")
data["new_name"] = "" data["new_name"] = ""
if _, err := Client.UpdateTeamName(data); err == nil { if _, err := Client.UpdateTeamDisplayName(data); err == nil {
t.Fatal("Should have errored, empty name") t.Fatal("Should have errored, empty name")
} }
data["new_name"] = "NewName" data["new_name"] = "NewName"
if _, err := Client.UpdateTeamName(data); err != nil { if _, err := Client.UpdateTeamDisplayName(data); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// No GET team web service, so hard to confirm here that team name updated // No GET team web service, so hard to confirm here that team name updated
data["team_id"] = "junk" 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") t.Fatal("Should have errored, junk team id")
} }
data["team_id"] = "12345678901234567890123456" 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") t.Fatal("Should have errored, bad team id")
} }
data["team_id"] = team.Id data["team_id"] = team.Id
data["new_name"] = "NewNameAgain" data["new_name"] = "NewNameAgain"
if _, err := Client.UpdateTeamName(data); err != nil { if _, err := Client.UpdateTeamDisplayName(data); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// No GET team web service, so hard to confirm here that team name updated // 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) { func TestFuzzyTeamCreate(t *testing.T) {
for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ { for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ {
testName := "Name" testDisplayName := "Name"
testEmail := "test@nowhere.com" testEmail := "test@nowhere.com"
if i < len(utils.FUZZY_STRINGS_NAMES) { 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) { if i < len(utils.FUZZY_STRINGS_EMAILS) {
testEmail = utils.FUZZY_STRINGS_EMAILS[i] 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) _, err := Client.CreateTeam(&team)
if err != nil { if err != nil {
@@ -307,22 +307,22 @@ func TestFuzzyTeamCreate(t *testing.T) {
func TestGetMyTeam(t *testing.T) { func TestGetMyTeam(t *testing.T) {
Setup() 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) 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, "") ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) 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 { if result, err := Client.GetMyTeam(""); err != nil {
t.Fatal("Failed to get user") t.Fatal("Failed to get user")
} else { } else {
if result.Data.(*model.Team).Name != team.Name { if result.Data.(*model.Team).DisplayName != team.DisplayName {
t.Fatal("team names did not match") 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") t.Fatal("team domains did not match")
} }
if result.Data.(*model.Team).Type != team.Type { if result.Data.(*model.Team).Type != team.Type {
@@ -334,25 +334,25 @@ func TestGetMyTeam(t *testing.T) {
func TestUpdateValetFeature(t *testing.T) { func TestUpdateValetFeature(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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) 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) user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) 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 := make(map[string]string)
data["allow_valet"] = "true" data["allow_valet"] = "true"
@@ -360,7 +360,7 @@ func TestUpdateValetFeature(t *testing.T) {
t.Fatal("Should have errored, not admin") t.Fatal("Should have errored, not admin")
} }
Client.LoginByEmail(team.Domain, user.Email, "pwd") Client.LoginByEmail(team.Name, user.Email, "pwd")
data["allow_valet"] = "" data["allow_valet"] = ""
if _, err := Client.UpdateValetFeature(data); err == nil { 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") 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["team_id"] = team.Id
data["allow_valet"] = "true" data["allow_valet"] = "true"

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -18,7 +18,7 @@
<tr> <tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;"> <td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your email</h2> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -34,7 +34,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -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}}

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -42,7 +42,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -18,7 +18,7 @@
<tr> <tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;"> <td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2> <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"> <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> <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> </p>
@@ -37,7 +37,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -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}}

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -18,7 +18,7 @@
<tr> <tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;"> <td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your password</h2> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -34,7 +34,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -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}}

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -37,7 +37,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -1 +1 @@
{{define "post_subject"}}[{{.Props.TeamName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}} {{define "post_subject"}}[{{.Props.TeamDisplayName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}}

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -18,7 +18,7 @@
<tr> <tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;"> <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> <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"> <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> <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> </p>
@@ -37,7 +37,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -40,7 +40,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -37,7 +37,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -1 +1 @@
{{define "verify_subject"}}[{{ .Props.TeamName }} {{ .SiteName }}] Email Verification{{end}} {{define "verify_subject"}}[{{ .Props.TeamDisplayName }} {{ .SiteName }}] Email Verification{{end}}

Просмотреть файл

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 30px 10px; text-align:left;"> <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> </td>
</tr> </tr>
<tr> <tr>
@@ -17,8 +17,8 @@
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto"> <table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
<tr> <tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;"> <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> <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> <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamURL}}">{{.SiteName}}</a>.</p>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -34,7 +34,7 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <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>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

Просмотреть файл

@@ -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) 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 user.EmailVerified {
if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
l4g.Error("Failed to set email verified err=%v", cresult.Err) l4g.Error("Failed to set email verified err=%v", cresult.Err)
} }
} else { } 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{}) 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 // This message goes to every channel, so the channelId is irrelevant
message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER)
store.PublishAndForget(message) PublishAndForget(message)
return ruser return ruser
} }
} }
func fireAndForgetWelcomeEmail(name, email, teamName, link string) { func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link string) {
go func() { go func() {
subjectPage := NewServerTemplatePage("welcome_subject", link) subjectPage := NewServerTemplatePage("welcome_subject", link)
bodyPage := NewServerTemplatePage("welcome_body", link) bodyPage := NewServerTemplatePage("welcome_body", link)
bodyPage.Props["FullName"] = name bodyPage.Props["Nickname"] = name
bodyPage.Props["TeamName"] = teamName bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { 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() { 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 := NewServerTemplatePage("verify_subject", teamURL)
subjectPage.Props["TeamName"] = teamName subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("verify_body", teamUrl) bodyPage := NewServerTemplatePage("verify_body", teamURL)
bodyPage.Props["FullName"] = name bodyPage.Props["Nickname"] = name
bodyPage.Props["TeamName"] = teamName bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["VerifyUrl"] = link bodyPage.Props["VerifyUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { 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 var team *model.Team
if result.Data == nil && len(props["email"]) != 0 && len(props["domain"]) != 0 { if result.Data == nil && len(props["email"]) != 0 && len(props["name"]) != 0 {
extraInfo = props["email"] + " in " + props["domain"] 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 c.Err = nr.Err
return return
} else { } else {
@@ -293,7 +292,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
return 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 = model.NewAppError("login", "Login failed because email address has not been verified", extraInfo)
c.Err.StatusCode = http.StatusForbidden c.Err.StatusCode = http.StatusForbidden
return 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(model.HEADER_ETAG_SERVER, etag)
w.Header().Set("Cache-Control", "max-age=120, public") // 2 mins
w.Write([]byte(model.UserMapToJson(profiles))) w.Write([]byte(model.UserMapToJson(profiles)))
return return
} }
@@ -729,6 +727,8 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
Srv.Store.User().UpdateLastPictureUpdate(c.Session.UserId)
c.LogAudit("") 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 { if tresult := <-Srv.Store.Team().Get(rusers[1].TeamId); tresult.Err != nil {
l4g.Error(tresult.Err.Message) l4g.Error(tresult.Err.Message)
} else { } 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 { if tresult := <-tchan; tresult.Err != nil {
l4g.Error(tresult.Err.Message) l4g.Error(tresult.Err.Message)
} else { } 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) data := make(map[string]string)
@@ -986,14 +988,14 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
domain := props["domain"] name := props["name"]
if len(domain) == 0 { if len(name) == 0 {
c.SetInvalidParam("sendPasswordReset", "domain") c.SetInvalidParam("sendPasswordReset", "name")
return return
} }
var team *model.Team 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 c.Err = result.Err
return return
} else { } else {
@@ -1015,10 +1017,10 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapToJson(newProps) data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.ResetSalt)) 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) subjectPage := NewServerTemplatePage("reset_subject", c.GetTeamURLFromTeam(team))
bodyPage := NewServerTemplatePage("reset_body", c.TeamUrl) bodyPage := NewServerTemplatePage("reset_body", c.GetTeamURLFromTeam(team))
bodyPage.Props["ResetUrl"] = link bodyPage.Props["ResetUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { 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 return
} }
domain := props["domain"] name := props["name"]
if len(domain) == 0 { if len(name) == 0 {
c.SetInvalidParam("resetPassword", "domain") c.SetInvalidParam("resetPassword", "name")
return return
} }
c.LogAuditWithUserId(userId, "attempt") c.LogAuditWithUserId(userId, "attempt")
var team *model.Team 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 c.Err = result.Err
return return
} else { } else {
@@ -1108,19 +1110,19 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(userId, "success") 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"] = "" props["new_password"] = ""
w.Write([]byte(model.MapToJson(props))) w.Write([]byte(model.MapToJson(props)))
} }
func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) { func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, method string) {
go func() { go func() {
subjectPage := NewServerTemplatePage("password_change_subject", teamUrl) subjectPage := NewServerTemplatePage("password_change_subject", teamURL)
subjectPage.Props["TeamName"] = teamName subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("password_change_body", teamUrl) bodyPage := NewServerTemplatePage("password_change_body", teamURL)
bodyPage.Props["TeamName"] = teamName bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["Method"] = method bodyPage.Props["Method"] = method
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { 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() { go func() {
subjectPage := NewServerTemplatePage("email_change_subject", teamUrl) subjectPage := NewServerTemplatePage("email_change_subject", teamURL)
subjectPage.Props["TeamName"] = teamName subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("email_change_body", teamUrl) bodyPage := NewServerTemplatePage("email_change_body", teamURL)
bodyPage.Props["TeamName"] = teamName bodyPage.Props["TeamDisplayName"] = teamDisplayName
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send update password email successfully err=%v", err) l4g.Error("Failed to send update password email successfully err=%v", err)

Просмотреть файл

@@ -25,18 +25,18 @@ import (
func TestCreateUser(t *testing.T) { func TestCreateUser(t *testing.T) {
Setup() 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) 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, "") ruser, err := Client.CreateUser(&user, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if ruser.Data.(*model.User).FullName != user.FullName { if ruser.Data.(*model.User).Nickname != user.Nickname {
t.Fatal("full name didn't match") t.Fatal("nickname didn't match")
} }
if ruser.Data.(*model.User).Password != "" { 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 { if _, err := Client.CreateUser(&user2, ""); err == nil {
t.Fatal("Should have failed using reserved bot name") t.Fatal("Should have failed using reserved bot name")
@@ -82,10 +82,10 @@ func TestCreateUser(t *testing.T) {
func TestCreateUserAllowedDomains(t *testing.T) { func TestCreateUserAllowedDomains(t *testing.T) {
Setup() 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) 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, "") _, err := Client.CreateUser(&user, "")
if err == nil { if err == nil {
@@ -102,10 +102,10 @@ func TestCreateUserAllowedDomains(t *testing.T) {
func TestLogin(t *testing.T) { func TestLogin(t *testing.T) {
Setup() 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) 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, "") ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) 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) t.Fatal(err)
} else { } else {
if result.Data.(*model.User).Email != user.Email { 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") 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") t.Fatal("should have failed")
} }
@@ -142,10 +142,10 @@ func TestLogin(t *testing.T) {
Client.AuthToken = "" 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)) 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 { if _, err := Client.CreateUserFromSignup(&user2, "junk", "1231312"); err == nil {
t.Fatal("Should have errored, signed up without hashed email") 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 := make(map[string]string)
props["email"] = user2.Email props["email"] = user2.Email
props["id"] = rteam2.Data.(*model.Team).Id 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()) props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props) data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash) 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") t.Fatal("From verfied hash")
} }
@@ -171,16 +171,16 @@ func TestLogin(t *testing.T) {
func TestSessions(t *testing.T) { func TestSessions(t *testing.T) {
Setup() 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) 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) ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
deviceId := model.NewId() deviceId := model.NewId()
Client.LoginByEmailWithDevice(team.Domain, user.Email, user.Password, deviceId) Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId)
Client.LoginByEmail(team.Domain, user.Email, user.Password) Client.LoginByEmail(team.Name, user.Email, user.Password)
r1, err := Client.GetSessions(ruser.Id) r1, err := Client.GetSessions(ruser.Id)
if err != nil { if err != nil {
@@ -228,18 +228,18 @@ func TestSessions(t *testing.T) {
func TestGetUser(t *testing.T) { func TestGetUser(t *testing.T) {
Setup() 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) 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, "") ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) 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, "") ruser2, _ := Client.CreateUser(&user2, "")
store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id)) 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 rId := ruser.Data.(*model.User).Id
if result, err := Client.GetUser(rId, ""); err != nil { if result, err := Client.GetUser(rId, ""); err != nil {
@@ -299,16 +299,16 @@ func TestGetUser(t *testing.T) {
func TestGetAudits(t *testing.T) { func TestGetAudits(t *testing.T) {
Setup() 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) 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, "") ruser, _ := Client.CreateUser(&user, "")
store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id))
time.Sleep(100 * time.Millisecond) 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) time.Sleep(100 * time.Millisecond)
@@ -352,14 +352,14 @@ func TestUserCreateImage(t *testing.T) {
t.Fatal("Failed to create correct color") 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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", "", "") Client.DoGet("/users/"+user.Id+"/image", "", "")
@@ -368,10 +368,10 @@ func TestUserCreateImage(t *testing.T) {
func TestUserUploadProfileImage(t *testing.T) { func TestUserUploadProfileImage(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -384,7 +384,7 @@ func TestUserUploadProfileImage(t *testing.T) {
t.Fatal("Should have errored") 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 { if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil {
t.Fatal("Should have errored") t.Fatal("Should have errored")
@@ -465,12 +465,12 @@ func TestUserUploadProfileImage(t *testing.T) {
func TestUserUpdate(t *testing.T) { func TestUserUpdate(t *testing.T) {
Setup() 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) team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
time1 := model.GetMillis() 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -478,7 +478,7 @@ func TestUserUpdate(t *testing.T) {
t.Fatal("Should have errored") t.Fatal("Should have errored")
} }
Client.LoginByEmail(team.Domain, user.Email, "pwd") Client.LoginByEmail(team.Name, user.Email, "pwd")
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@@ -486,7 +486,7 @@ func TestUserUpdate(t *testing.T) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
user.FullName = "Jim Jimmy" user.Nickname = "Jim Jimmy"
user.TeamId = "12345678901234567890123456" user.TeamId = "12345678901234567890123456"
user.LastActivityAt = time2 user.LastActivityAt = time2
user.LastPingAt = time2 user.LastPingAt = time2
@@ -496,8 +496,8 @@ func TestUserUpdate(t *testing.T) {
if result, err := Client.UpdateUser(user); err != nil { if result, err := Client.UpdateUser(user); err != nil {
t.Fatal(err) t.Fatal(err)
} else { } else {
if result.Data.(*model.User).FullName != "Jim Jimmy" { if result.Data.(*model.User).Nickname != "Jim Jimmy" {
t.Fatal("FullName did not update properly") t.Fatal("Nickname did not update properly")
} }
if result.Data.(*model.User).TeamId != team.Id { if result.Data.(*model.User).TeamId != team.Id {
t.Fatal("TeamId should not have updated") t.Fatal("TeamId should not have updated")
@@ -526,13 +526,13 @@ func TestUserUpdate(t *testing.T) {
t.Fatal("Should have errored") 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 { if _, err := Client.UpdateUser(user); err == nil {
t.Fatal("Should have errored") t.Fatal("Should have errored")
@@ -542,10 +542,10 @@ func TestUserUpdate(t *testing.T) {
func TestUserUpdatePassword(t *testing.T) { func TestUserUpdatePassword(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -553,7 +553,7 @@ func TestUserUpdatePassword(t *testing.T) {
t.Fatal("Should have errored") 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 { if _, err := Client.UpdateUserPassword("123", "pwd", "newpwd"); err == nil {
t.Fatal("Should have errored") t.Fatal("Should have errored")
@@ -584,14 +584,14 @@ func TestUserUpdatePassword(t *testing.T) {
t.Fatal("LastPasswordUpdate should have changed") 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) 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) 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 { if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil {
t.Fatal("Should have errored") t.Fatal("Should have errored")
@@ -601,14 +601,14 @@ func TestUserUpdatePassword(t *testing.T) {
func TestUserUpdateRoles(t *testing.T) { func TestUserUpdateRoles(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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") 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 { if _, err := Client.UpdateUserRoles(data); err == nil {
t.Fatal("Should have errored, not admin") 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) 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) user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) 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 data["user_id"] = user2.Id
@@ -641,7 +641,7 @@ func TestUserUpdateRoles(t *testing.T) {
t.Fatal("Should have errored, wrong team") 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["user_id"] = "junk"
data["new_roles"] = "admin" data["new_roles"] = "admin"
@@ -670,14 +670,14 @@ func TestUserUpdateRoles(t *testing.T) {
func TestUserUpdateActive(t *testing.T) { func TestUserUpdateActive(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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") 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 { if _, err := Client.UpdateActive(user.Id, false); err == nil {
t.Fatal("Should have errored, not admin") 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) 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) user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id)) 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 { if _, err := Client.UpdateActive(user.Id, false); err == nil {
t.Fatal("Should have errored, wrong team") 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 { if _, err := Client.UpdateActive("junk", false); err == nil {
t.Fatal("Should have errored, bad id") t.Fatal("Should have errored, bad id")
@@ -734,16 +734,16 @@ func TestUserUpdateActive(t *testing.T) {
func TestSendPasswordReset(t *testing.T) { func TestSendPasswordReset(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
data := make(map[string]string) data := make(map[string]string)
data["email"] = user.Email data["email"] = user.Email
data["domain"] = team.Domain data["name"] = team.Name
if _, err := Client.SendPasswordReset(data); err != nil { if _, err := Client.SendPasswordReset(data); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -760,24 +760,24 @@ func TestSendPasswordReset(t *testing.T) {
} }
data["email"] = user.Email data["email"] = user.Email
data["domain"] = "" data["name"] = ""
if _, err := Client.SendPasswordReset(data); err == nil { 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 { 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) { func TestResetPassword(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) store.Must(Srv.Store.User().VerifyEmail(user.Id))
@@ -788,7 +788,7 @@ func TestResetPassword(t *testing.T) {
props["time"] = fmt.Sprintf("%v", model.GetMillis()) props["time"] = fmt.Sprintf("%v", model.GetMillis())
data["data"] = model.MapToJson(props) data["data"] = model.MapToJson(props)
data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.ServiceSettings.ResetSalt)) 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 { if _, err := Client.ResetPassword(data); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -846,10 +846,10 @@ func TestResetPassword(t *testing.T) {
t.Fatal("Should have errored - bad domain") 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) team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
data["domain"] = team2.Domain data["domain"] = team2.Name
if _, err := Client.ResetPassword(data); err == nil { if _, err := Client.ResetPassword(data); err == nil {
t.Fatal("Should have errored - domain team doesn't match user team") 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) { func TestUserUpdateNotify(t *testing.T) {
Setup() 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) 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) user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id)) 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") 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 { if result, err := Client.UpdateUserNotify(data); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -926,7 +926,7 @@ func TestUserUpdateNotify(t *testing.T) {
func TestFuzzyUserCreate(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) rteam, _ := Client.CreateTeam(&team)
for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ { 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] 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, "") _, err := Client.CreateUser(&user, "")
if err != nil { if err != nil {
@@ -952,14 +952,14 @@ func TestFuzzyUserCreate(t *testing.T) {
func TestStatuses(t *testing.T) { func TestStatuses(t *testing.T) {
Setup() 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) 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) ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) 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() r1, err := Client.GetStatuses()
if err != nil { if err != nil {

Просмотреть файл

@@ -70,7 +70,7 @@ func (c *WebConn) readPump() {
} else { } else {
msg.TeamId = c.TeamId msg.TeamId = c.TeamId
msg.UserId = c.UserId msg.UserId = c.UserId
store.PublishAndForget(&msg) PublishAndForget(&msg)
} }
} }
} }

Просмотреть файл

@@ -5,12 +5,14 @@ package api
import ( import (
l4g "code.google.com/p/log4go" l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/model"
) )
type Hub struct { type Hub struct {
teamHubs map[string]*TeamHub teamHubs map[string]*TeamHub
register chan *WebConn register chan *WebConn
unregister chan *WebConn unregister chan *WebConn
broadcast chan *model.Message
stop chan string stop chan string
} }
@@ -18,9 +20,16 @@ var hub = &Hub{
register: make(chan *WebConn), register: make(chan *WebConn),
unregister: make(chan *WebConn), unregister: make(chan *WebConn),
teamHubs: make(map[string]*TeamHub), teamHubs: make(map[string]*TeamHub),
broadcast: make(chan *model.Message),
stop: make(chan string), stop: make(chan string),
} }
func PublishAndForget(message *model.Message) {
go func() {
hub.Broadcast(message)
}()
}
func (h *Hub) Register(webConn *WebConn) { func (h *Hub) Register(webConn *WebConn) {
h.register <- webConn h.register <- webConn
} }
@@ -29,8 +38,14 @@ func (h *Hub) Unregister(webConn *WebConn) {
h.unregister <- webConn h.unregister <- webConn
} }
func (h *Hub) Stop(teamId string) { func (h *Hub) Broadcast(message *model.Message) {
h.stop <- teamId if message != nil {
h.broadcast <- message
}
}
func (h *Hub) Stop() {
h.stop <- "all"
} }
func (h *Hub) Start() { func (h *Hub) Start() {
@@ -53,18 +68,17 @@ func (h *Hub) Start() {
if nh, ok := h.teamHubs[c.TeamId]; ok { if nh, ok := h.teamHubs[c.TeamId]; ok {
nh.Unregister(c) nh.Unregister(c)
} }
case msg := <-h.broadcast:
case s := <-h.stop: nh := h.teamHubs[msg.TeamId]
if len(s) == 0 { if nh != nil {
l4g.Debug("stopping all connections") nh.broadcast <- msg
for _, v := range h.teamHubs {
v.Stop()
}
return
} else if nh, ok := h.teamHubs[s]; ok {
delete(h.teamHubs, s)
nh.Stop()
} }
case s := <-h.stop:
l4g.Debug("stopping %v connections", s)
for _, v := range h.teamHubs {
v.Stop()
}
return
} }
} }
}() }()

Просмотреть файл

@@ -17,13 +17,13 @@ func TestSocket(t *testing.T) {
Setup() Setup()
url := "ws://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1/websocket" 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) 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) user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id)) 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 := &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) channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -39,10 +39,10 @@ func TestSocket(t *testing.T) {
t.Fatal(err) 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) user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id)) 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 := http.Header{}
header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)
@@ -115,9 +115,6 @@ func TestSocket(t *testing.T) {
}() }()
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
hub.Stop(team.Id)
} }
func TestZZWebSocketTearDown(t *testing.T) { func TestZZWebSocketTearDown(t *testing.T) {

Просмотреть файл

@@ -6,8 +6,6 @@ package api
import ( import (
l4g "code.google.com/p/log4go" l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"strings"
) )
type TeamHub struct { type TeamHub struct {
@@ -43,43 +41,6 @@ func (h *TeamHub) Stop() {
} }
func (h *TeamHub) Start() { 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() { go func() {
for { for {
select { select {
@@ -110,7 +71,6 @@ func (h *TeamHub) Start() {
webCon.WebSocket.Close() webCon.WebSocket.Close()
} }
pubsub.Close()
return return
} }
} }

Просмотреть файл

@@ -9,7 +9,6 @@
}, },
"ServiceSettings": { "ServiceSettings": {
"SiteName": "Mattermost", "SiteName": "Mattermost",
"Domain": "xxxxxxmustbefilledin.com",
"Mode" : "dev", "Mode" : "dev",
"AllowTesting" : false, "AllowTesting" : false,
"UseSSL": false, "UseSSL": false,
@@ -31,19 +30,11 @@
"Trace": false, "Trace": false,
"AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS"
}, },
"RedisSettings": {
"DataSource": "dockerhost:6379",
"MaxOpenConns": 1000
},
"AWSSettings": { "AWSSettings": {
"S3AccessKeyId": "", "S3AccessKeyId": "",
"S3SecretAccessKey": "", "S3SecretAccessKey": "",
"S3Bucket": "", "S3Bucket": "",
"S3Region": "", "S3Region": ""
"Route53AccessKeyId": "",
"Route53SecretAccessKey": "",
"Route53ZoneId": "",
"Route53Region": ""
}, },
"ImageSettings": { "ImageSettings": {
"ThumbnailWidth": 200, "ThumbnailWidth": 200,
@@ -54,11 +45,12 @@
"ProfileHeight": 128 "ProfileHeight": 128
}, },
"EmailSettings": { "EmailSettings": {
"ByPassEmail" : true,
"SMTPUsername": "", "SMTPUsername": "",
"SMTPPassword": "", "SMTPPassword": "",
"SMTPServer": "", "SMTPServer": "",
"UseTLS": false, "UseTLS": false,
"FeedbackEmail": "feedback@xxxxxxmustbefilledin.com", "FeedbackEmail": "",
"FeedbackName": "", "FeedbackName": "",
"ApplePushServer": "", "ApplePushServer": "",
"ApplePushCertPublic": "", "ApplePushCertPublic": "",

Просмотреть файл

@@ -9,7 +9,6 @@
}, },
"ServiceSettings": { "ServiceSettings": {
"SiteName": "Mattermost", "SiteName": "Mattermost",
"Domain": "",
"Mode" : "dev", "Mode" : "dev",
"AllowTesting" : false, "AllowTesting" : false,
"UseSSL": false, "UseSSL": false,
@@ -31,19 +30,11 @@
"Trace": false, "Trace": false,
"AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS"
}, },
"RedisSettings": {
"DataSource": "localhost:6379",
"MaxOpenConns": 1000
},
"AWSSettings": { "AWSSettings": {
"S3AccessKeyId": "", "S3AccessKeyId": "",
"S3SecretAccessKey": "", "S3SecretAccessKey": "",
"S3Bucket": "", "S3Bucket": "",
"S3Region": "", "S3Region": ""
"Route53AccessKeyId": "",
"Route53SecretAccessKey": "",
"Route53ZoneId": "",
"Route53Region": ""
}, },
"ImageSettings": { "ImageSettings": {
"ThumbnailWidth": 200, "ThumbnailWidth": 200,
@@ -54,9 +45,10 @@
"ProfileHeight": 128 "ProfileHeight": 128
}, },
"EmailSettings": { "EmailSettings": {
"ByPassEmail" : true,
"SMTPUsername": "", "SMTPUsername": "",
"SMTPPassword": "", "SMTPPassword": "",
"SMTPServer": "localhost:25", "SMTPServer": "",
"UseTLS": false, "UseTLS": false,
"FeedbackEmail": "", "FeedbackEmail": "",
"FeedbackName": "", "FeedbackName": "",

Просмотреть файл

@@ -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 // Check for username parameter and create a user if present
username, ok1 := params["username"] username, ok1 := params["username"]
teamname, ok2 := params["teamname"] teamDisplayName, ok2 := params["teamname"]
var teamID string var teamID string
var userID string var userID string
if ok1 && ok2 { if ok1 && ok2 {
l4g.Info("Creating user and team") l4g.Info("Creating user and team")
// Create team for testing // Create team for testing
team := &model.Team{ team := &model.Team{
Name: teamname[0], DisplayName: teamDisplayName[0],
Domain: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE), Name: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE), Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
Type: model.TEAM_OPEN, Type: model.TEAM_OPEN,
} }
if result := <-api.Srv.Store.Team().Save(team); result.Err != nil { 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{ user := &model.User{
TeamId: teamID, TeamId: teamID,
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE), Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
FullName: username[0], Nickname: username[0],
Password: api.USER_PASSWORD} Password: api.USER_PASSWORD}
result, err := client.CreateUser(user, "") result, err := client.CreateUser(user, "")

Просмотреть файл

@@ -10,7 +10,7 @@ import (
type ExtraMember struct { type ExtraMember struct {
Id string `json:"id"` Id string `json:"id"`
FullName string `json:"full_name"` Nickname string `json:"nickname"`
Email string `json:"email"` Email string `json:"email"`
Roles string `json:"roles"` Roles string `json:"roles"`
Username string `json:"username"` Username string `json:"username"`
@@ -20,9 +20,6 @@ func (o *ExtraMember) Sanitize(options map[string]bool) {
if len(options) == 0 || !options["email"] { if len(options) == 0 || !options["email"] {
o.Email = "" o.Email = ""
} }
if len(options) == 0 || !options["fullname"] {
o.FullName = ""
}
} }
type ChannelExtra struct { type ChannelExtra struct {

Просмотреть файл

@@ -100,10 +100,10 @@ func (c *Client) Must(result *Result, err *AppError) *Result {
return 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 := make(map[string]string)
m["email"] = email m["email"] = email
m["name"] = name m["display_name"] = displayName
if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil { if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil {
return nil, err return nil, err
} else { } 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 := make(map[string]string)
m["domain"] = domain m["name"] = name
m["all"] = fmt.Sprintf("%v", allServers) 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 return nil, err
} else { } else {
val := false 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 { if r, err := c.DoPost("/teams/update_name", MapToJson(data)); err != nil {
return nil, err return nil, err
} else { } else {
@@ -249,17 +249,17 @@ func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
return c.login(m) 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 := make(map[string]string)
m["domain"] = domain m["name"] = name
m["email"] = email m["email"] = email
m["password"] = password m["password"] = password
return c.login(m) 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 := make(map[string]string)
m["domain"] = domain m["name"] = name
m["email"] = email m["email"] = email
m["password"] = password m["password"] = password
m["device_id"] = deviceId m["device_id"] = deviceId

Просмотреть файл

@@ -18,8 +18,8 @@ type Team struct {
CreateAt int64 `json:"create_at"` CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"` UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
DisplayName string `json:"display_name"`
Name string `json:"name"` Name string `json:"name"`
Domain string `json:"domain"`
Email string `json:"email"` Email string `json:"email"`
Type string `json:"type"` Type string `json:"type"`
CompanyName string `json:"company_name"` CompanyName string `json:"company_name"`
@@ -97,20 +97,20 @@ func (o *Team) IsValid() *AppError {
return NewAppError("Team.IsValid", "Invalid email", "id="+o.Id) 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) return NewAppError("Team.IsValid", "Invalid name", "id="+o.Id)
} }
if len(o.Domain) > 64 { if len(o.Name) > 64 {
return NewAppError("Team.IsValid", "Invalid domain", "id="+o.Id) 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) return NewAppError("Team.IsValid", "This URL is unavailable. Please try another.", "id="+o.Id)
} }
if !IsValidDomain(o.Domain) { if !IsValidTeamName(o.Name) {
return NewAppError("Team.IsValid", "Domain must be 4 or more lowercase alphanumeric characters", "id="+o.Id) 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) { if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {

Просмотреть файл

@@ -9,7 +9,7 @@ import (
) )
func TestTeamSignupJson(t *testing.T) { func TestTeamSignupJson(t *testing.T) {
team := Team{Id: NewId(), Name: NewId()} team := Team{Id: NewId(), DisplayName: NewId()}
o := TeamSignup{Team: team, Data: "data"} o := TeamSignup{Team: team, Data: "data"}
json := o.ToJson() json := o.ToJson()
ro := TeamSignupFromJson(strings.NewReader(json)) ro := TeamSignupFromJson(strings.NewReader(json))

Просмотреть файл

@@ -9,7 +9,7 @@ import (
) )
func TestTeamJson(t *testing.T) { func TestTeamJson(t *testing.T) {
o := Team{Id: NewId(), Name: NewId()} o := Team{Id: NewId(), DisplayName: NewId()}
json := o.ToJson() json := o.ToJson()
ro := TeamFromJson(strings.NewReader(json)) ro := TeamFromJson(strings.NewReader(json))
@@ -46,18 +46,18 @@ func TestTeamIsValid(t *testing.T) {
} }
o.Email = "corey@hulen.com" o.Email = "corey@hulen.com"
o.Name = strings.Repeat("01234567890", 20) o.DisplayName = strings.Repeat("01234567890", 20)
if err := o.IsValid(); err == nil { if err := o.IsValid(); err == nil {
t.Fatal("should be invalid") t.Fatal("should be invalid")
} }
o.Name = "1234" o.DisplayName = "1234"
o.Domain = "ZZZZZZZ" o.Name = "ZZZZZZZ"
if err := o.IsValid(); err == nil { if err := o.IsValid(); err == nil {
t.Fatal("should be invalid") t.Fatal("should be invalid")
} }
o.Domain = "zzzzz" o.Name = "zzzzz"
o.Type = TEAM_OPEN o.Type = TEAM_OPEN
if err := o.IsValid(); err != nil { if err := o.IsValid(); err != nil {
t.Fatal(err) t.Fatal(err)
@@ -65,12 +65,12 @@ func TestTeamIsValid(t *testing.T) {
} }
func TestTeamPreSave(t *testing.T) { func TestTeamPreSave(t *testing.T) {
o := Team{Name: "test"} o := Team{DisplayName: "test"}
o.PreSave() o.PreSave()
o.Etag() o.Etag()
} }
func TestTeamPreUpdate(t *testing.T) { func TestTeamPreUpdate(t *testing.T) {
o := Team{Name: "test"} o := Team{DisplayName: "test"}
o.PreUpdate() o.PreUpdate()
} }

Просмотреть файл

@@ -37,7 +37,9 @@ type User struct {
AuthData string `json:"auth_data"` AuthData string `json:"auth_data"`
Email string `json:"email"` Email string `json:"email"`
EmailVerified bool `json:"email_verified"` 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"` Roles string `json:"roles"`
LastActivityAt int64 `json:"last_activity_at"` LastActivityAt int64 `json:"last_activity_at"`
LastPingAt int64 `json:"last_ping_at"` LastPingAt int64 `json:"last_ping_at"`
@@ -45,6 +47,7 @@ type User struct {
Props StringMap `json:"props"` Props StringMap `json:"props"`
NotifyProps StringMap `json:"notify_props"` NotifyProps StringMap `json:"notify_props"`
LastPasswordUpdate int64 `json:"last_password_update"` 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 // 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) return NewAppError("User.IsValid", "Invalid email", "user_id="+u.Id)
} }
if len(u.FullName) > 64 { if len(u.Nickname) > 64 {
return NewAppError("User.IsValid", "Invalid full name", "user_id="+u.Id) 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 return nil
@@ -151,7 +162,7 @@ func (u *User) SetDefaultNotifications() {
u.NotifyProps["first_name"] = "false" u.NotifyProps["first_name"] = "false"
u.NotifyProps["all"] = "true" u.NotifyProps["all"] = "true"
u.NotifyProps["channel"] = "true" u.NotifyProps["channel"] = "true"
splitName := strings.Split(u.FullName, " ") splitName := strings.Split(u.Nickname, " ")
if len(splitName) > 0 && splitName[0] != "" { if len(splitName) > 0 && splitName[0] != "" {
u.NotifyProps["first_name"] = "true" u.NotifyProps["first_name"] = "true"
u.NotifyProps["mention_keys"] += "," + splitName[0] u.NotifyProps["mention_keys"] += "," + splitName[0]
@@ -190,7 +201,8 @@ func (u *User) Sanitize(options map[string]bool) {
u.Email = "" u.Email = ""
} }
if len(options) != 0 && !options["fullname"] { if len(options) != 0 && !options["fullname"] {
u.FullName = "" u.FirstName = ""
u.LastName = ""
} }
if len(options) != 0 && !options["skypeid"] { if len(options) != 0 && !options["skypeid"] {
// TODO - fill in when SkypeId is added to user model // 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 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 // UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User { func UserFromJson(data io.Reader) *User {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)

Просмотреть файл

@@ -80,13 +80,73 @@ func TestUserIsValid(t *testing.T) {
} }
user.Email = "test@nowhere.com" user.Email = "test@nowhere.com"
user.FullName = strings.Repeat("01234567890", 20) user.Nickname = strings.Repeat("01234567890", 20)
if err := user.IsValid(); err == nil { if err := user.IsValid(); err == nil {
t.Fatal() t.Fatal()
} }
user.FullName = "" user.Nickname = ""
if err := user.IsValid(); err != nil { if err := user.IsValid(); err != nil {
t.Fatal(err) 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")
}
} }

Просмотреть файл

@@ -148,7 +148,7 @@ func IsValidEmail(email string) bool {
return false return false
} }
var reservedDomains = []string{ var reservedName = []string{
"www", "www",
"web", "web",
"admin", "admin",
@@ -168,10 +168,10 @@ var reservedDomains = []string{
"api", "api",
} }
func IsReservedDomain(s string) bool { func IsReservedTeamName(s string) bool {
s = strings.ToLower(s) s = strings.ToLower(s)
for _, value := range reservedDomains { for _, value := range reservedName {
if strings.Index(s, value) == 0 { if strings.Index(s, value) == 0 {
return true return true
} }
@@ -180,7 +180,7 @@ func IsReservedDomain(s string) bool {
return false return false
} }
func IsValidDomain(s string) bool { func IsValidTeamName(s string) bool {
if !IsValidAlphaNum(s) { if !IsValidAlphaNum(s) {
return false return false

Просмотреть файл

@@ -83,9 +83,9 @@ var domains = []struct {
{"test", true}, {"test", true},
} }
func TestValidDomain(t *testing.T) { func TestValidTeamName(t *testing.T) {
for _, v := range domains { 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) t.Errorf("expect %v as %v", v.value, v.expected)
} }
} }
@@ -102,9 +102,9 @@ var tReservedDomains = []struct {
{"spin-punch-admin", false}, {"spin-punch-admin", false},
} }
func TestReservedDomain(t *testing.T) { func TestReservedTeamName(t *testing.T) {
for _, v := range tReservedDomains { 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) t.Errorf("expect %v as %v", v.value, v.expected)
} }
} }

Просмотреть файл

@@ -10,7 +10,7 @@ DOCKER SETUP
3. Add a line to your /etc/hosts that goes `<Docker IP> dockerhost` 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 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 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. 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 7. Use `make run` to run your code
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

Просмотреть файл

@@ -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
}
}

Просмотреть файл

@@ -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()
}

Просмотреть файл

@@ -360,7 +360,7 @@ func (s SqlChannelStore) GetExtraMembers(channelId string, limit int) StoreChann
result := StoreResult{} result := StoreResult{}
var members []model.ExtraMember 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 { if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetExtraMembers", "We couldn't get the extra info for channel members", "channel_id="+channelId+", "+err.Error()) result.Err = model.NewAppError("SqlChannelStore.GetExtraMembers", "We couldn't get the extra info for channel members", "channel_id="+channelId+", "+err.Error())
} else { } else {

Просмотреть файл

@@ -200,13 +200,13 @@ func TestChannelMemberStore(t *testing.T) {
u1 := model.User{} u1 := model.User{}
u1.TeamId = model.NewId() u1.TeamId = model.NewId()
u1.Email = model.NewId() u1.Email = model.NewId()
u1.FullName = model.NewId() u1.Nickname = model.NewId()
Must(store.User().Save(&u1)) Must(store.User().Save(&u1))
u2 := model.User{} u2 := model.User{}
u2.TeamId = model.NewId() u2.TeamId = model.NewId()
u2.Email = model.NewId() u2.Email = model.NewId()
u2.FullName = model.NewId() u2.Nickname = model.NewId()
Must(store.User().Save(&u2)) Must(store.User().Save(&u2))
o1 := model.ChannelMember{} o1 := model.ChannelMember{}

Просмотреть файл

@@ -36,6 +36,11 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore {
} }
func (s SqlPostStore) UpgradeSchemaIfNeeded() { 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() { func (s SqlPostStore) CreateIndexesIfNotExists() {

Просмотреть файл

@@ -83,7 +83,14 @@ func NewSqlStore() Store {
func setupConnection(con_type string, driver string, dataSource string, maxIdle int, maxOpen int, trace bool) *gorp.DbMap { 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 { if err != nil {
l4g.Critical("Failed to open sql connection to '%v' err:%v", dataSource, err) l4g.Critical("Failed to open sql connection to '%v' err:%v", dataSource, err)
time.Sleep(time.Second) time.Sleep(time.Second)
@@ -106,7 +113,7 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
if driver == "sqlite3" { if driver == "sqlite3" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}} dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}}
} else if driver == "mysql" { } 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" { } else if driver == "postgres" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}} dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}}
} else { } else {
@@ -122,62 +129,9 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
return dbmap return dbmap
} }
func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, colType string, defaultValue string) bool { func (ss SqlStore) DoesColumnExist(tableName string, columnName 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
COUNT(0) AS column_exists
FROM
information_schema.COLUMNS
WHERE
TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ?
AND COLUMN_NAME = ?`,
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 false
}
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + colType + " DEFAULT '" + defaultValue + "'")
if err != nil {
l4g.Critical("Failed to create column %v", err)
time.Sleep(time.Second)
panic("Failed to create column " + err.Error())
}
return true
}
func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
count, err := ss.GetMaster().SelectInt( count, err := ss.GetMaster().SelectInt(
`SELECT `SELECT
COUNT(0) AS column_exists COUNT(0) AS column_exists
FROM FROM
information_schema.COLUMNS information_schema.COLUMNS
@@ -194,11 +148,30 @@ func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) boo
panic("Failed to check if column exists " + err.Error()) 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 return false
} }
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName) _, 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)
panic("Failed to create column " + err.Error())
}
return true
}
func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool {
if !ss.DoesColumnExist(tableName, columnName) {
return false
}
_, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
if err != nil { if err != nil {
l4g.Critical("Failed to drop column %v", err) l4g.Critical("Failed to drop column %v", err)
time.Sleep(time.Second) time.Sleep(time.Second)
@@ -208,6 +181,25 @@ func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) boo
return true 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 + " 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 rename column %v", err)
time.Sleep(time.Second)
panic("Failed to drop column " + err.Error())
}
return true
}
func (ss SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) { func (ss SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) {
ss.createIndexIfNotExists(indexName, tableName, columnName, false) ss.createIndexIfNotExists(indexName, tableName, columnName, false)
} }
@@ -217,55 +209,27 @@ func (ss SqlStore) CreateFullTextIndexIfNotExists(indexName string, tableName st
} }
func (ss SqlStore) createIndexIfNotExists(indexName string, tableName string, columnName string, fullText bool) { 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 utils.Cfg.SqlSettings.DriverName == "postgres" { if err != nil {
_, err := ss.GetMaster().SelectStr("SELECT to_regclass($1)", indexName) l4g.Critical("Failed to check index", err)
// 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
}
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")
time.Sleep(time.Second) time.Sleep(time.Second)
panic("Failed to create index because of missing driver") panic("Failed to check index" + err.Error())
}
if count > 0 {
return
}
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", err)
time.Sleep(time.Second)
panic("Failed to create index " + err.Error())
} }
} }

Просмотреть файл

@@ -17,8 +17,8 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
for _, db := range sqlStore.GetAllConns() { for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id") table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id")
table.ColMap("Id").SetMaxSize(26) table.ColMap("Id").SetMaxSize(26)
table.ColMap("Name").SetMaxSize(64) table.ColMap("DisplayName").SetMaxSize(64)
table.ColMap("Domain").SetMaxSize(64).SetUnique(true) table.ColMap("Name").SetMaxSize(64).SetUnique(true)
table.ColMap("Email").SetMaxSize(128) table.ColMap("Email").SetMaxSize(128)
table.ColMap("CompanyName").SetMaxSize(64) table.ColMap("CompanyName").SetMaxSize(64)
table.ColMap("AllowedDomains").SetMaxSize(500) table.ColMap("AllowedDomains").SetMaxSize(500)
@@ -28,6 +28,15 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
} }
func (s SqlTeamStore) UpgradeSchemaIfNeeded() { 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() { 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 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()) result.Err = model.NewAppError("SqlTeamStore.Save", "A team with that domain already exists", "id="+team.Id+", "+err.Error())
} else { } else {
result.Err = model.NewAppError("SqlTeamStore.Save", "We couldn't save the team", "id="+team.Id+", "+err.Error()) 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 { } else {
oldTeam := oldResult.(*model.Team) oldTeam := oldResult.(*model.Team)
team.CreateAt = oldTeam.CreateAt team.CreateAt = oldTeam.CreateAt
team.Domain = oldTeam.Domain team.Name = oldTeam.Name
if count, err := s.GetMaster().Update(team); err != nil { 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()) 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 return storeChannel
} }
func (s SqlTeamStore) UpdateName(name string, teamId string) StoreChannel { func (s SqlTeamStore) UpdateDisplayName(name string, teamId string) StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)
go func() { go func() {
result := StoreResult{} 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) result.Err = model.NewAppError("SqlTeamStore.UpdateName", "We couldn't update the team name", "team_id="+teamId)
} else { } else {
result.Data = teamId result.Data = teamId
@@ -153,7 +162,7 @@ func (s SqlTeamStore) Get(id string) StoreChannel {
return storeChannel return storeChannel
} }
func (s SqlTeamStore) GetByDomain(domain string) StoreChannel { func (s SqlTeamStore) GetByName(name string) StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)
go func() { go func() {
@@ -161,8 +170,8 @@ func (s SqlTeamStore) GetByDomain(domain string) StoreChannel {
team := model.Team{} team := model.Team{}
if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Domain = :Domain", map[string]interface{}{"Domain": domain}); err != nil { if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetByDomain", "We couldn't find the existing team", "domain="+domain+", "+err.Error()) result.Err = model.NewAppError("SqlTeamStore.GetByName", "We couldn't find the existing team", "name="+name+", "+err.Error())
} }
result.Data = &team result.Data = &team

Просмотреть файл

@@ -13,8 +13,8 @@ func TestTeamStoreSave(t *testing.T) {
Setup() Setup()
o1 := model.Team{} o1 := model.Team{}
o1.Name = "Name" o1.DisplayName = "DisplayName"
o1.Domain = "a" + model.NewId() + "b" o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com" o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN o1.Type = model.TEAM_OPEN
@@ -36,8 +36,8 @@ func TestTeamStoreUpdate(t *testing.T) {
Setup() Setup()
o1 := model.Team{} o1 := model.Team{}
o1.Name = "Name" o1.DisplayName = "DisplayName"
o1.Domain = "a" + model.NewId() + "b" o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com" o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN o1.Type = model.TEAM_OPEN
if err := (<-store.Team().Save(&o1)).Err; err != nil { 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() Setup()
o1 := &model.Team{} o1 := &model.Team{}
o1.Name = "Name" o1.DisplayName = "Display Name"
o1.Domain = "a" + model.NewId() + "b" o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com" o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN o1.Type = model.TEAM_OPEN
o1 = (<-store.Team().Save(o1)).Data.(*model.Team) 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) t.Fatal(err)
} }
ro1 := (<-store.Team().Get(o1.Id)).Data.(*model.Team) ro1 := (<-store.Team().Get(o1.Id)).Data.(*model.Team)
if ro1.Name != newName { if ro1.DisplayName != newDisplayName {
t.Fatal("Name not updated") t.Fatal("DisplayName not updated")
} }
} }
@@ -87,8 +87,8 @@ func TestTeamStoreGet(t *testing.T) {
Setup() Setup()
o1 := model.Team{} o1 := model.Team{}
o1.Name = "Name" o1.DisplayName = "DisplayName"
o1.Domain = "a" + model.NewId() + "b" o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com" o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN o1.Type = model.TEAM_OPEN
Must(store.Team().Save(&o1)) 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() Setup()
o1 := model.Team{} o1 := model.Team{}
o1.Name = "Name" o1.DisplayName = "DisplayName"
o1.Domain = "a" + model.NewId() + "b" o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com" o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN o1.Type = model.TEAM_OPEN
@@ -119,7 +119,7 @@ func TestTeamStoreGetByDomain(t *testing.T) {
t.Fatal(err) 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) t.Fatal(r1.Err)
} else { } else {
if r1.Data.(*model.Team).ToJson() != o1.ToJson() { 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") t.Fatal("Missing id should have failed")
} }
} }
@@ -136,8 +136,8 @@ func TestTeamStoreGetForEmail(t *testing.T) {
Setup() Setup()
o1 := model.Team{} o1 := model.Team{}
o1.Name = "Name" o1.DisplayName = "DisplayName"
o1.Domain = "a" + model.NewId() + "b" o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com" o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN o1.Type = model.TEAM_OPEN
Must(store.Team().Save(&o1)) Must(store.Team().Save(&o1))

Просмотреть файл

@@ -24,7 +24,9 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
table.ColMap("Password").SetMaxSize(128) table.ColMap("Password").SetMaxSize(128)
table.ColMap("AuthData").SetMaxSize(128) table.ColMap("AuthData").SetMaxSize(128)
table.ColMap("Email").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("Roles").SetMaxSize(64)
table.ColMap("Props").SetMaxSize(4000) table.ColMap("Props").SetMaxSize(4000)
table.ColMap("NotifyProps").SetMaxSize(2000) table.ColMap("NotifyProps").SetMaxSize(2000)
@@ -35,9 +37,29 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
return us 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() { func (us SqlUserStore) CreateIndexesIfNotExists() {
us.CreateIndexIfNotExists("idx_users_team_id", "Users", "TeamId") us.CreateIndexIfNotExists("idx_users_team_id", "Users", "TeamId")
us.CreateIndexIfNotExists("idx_users_email", "Users", "Email") 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.AuthData = oldUser.AuthData
user.Password = oldUser.Password user.Password = oldUser.Password
user.LastPasswordUpdate = oldUser.LastPasswordUpdate user.LastPasswordUpdate = oldUser.LastPasswordUpdate
user.LastPictureUpdate = oldUser.LastPictureUpdate
user.TeamId = oldUser.TeamId user.TeamId = oldUser.TeamId
user.LastActivityAt = oldUser.LastActivityAt user.LastActivityAt = oldUser.LastActivityAt
user.LastPingAt = oldUser.LastPingAt user.LastPingAt = oldUser.LastPingAt
@@ -150,6 +173,27 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha
return storeChannel 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 { func (us SqlUserStore) UpdateLastPingAt(userId string, time int64) StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)

Просмотреть файл

@@ -36,9 +36,9 @@ type Store interface {
type TeamStore interface { type TeamStore interface {
Save(team *model.Team) StoreChannel Save(team *model.Team) StoreChannel
Update(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 Get(id string) StoreChannel
GetByDomain(domain string) StoreChannel GetByName(name string) StoreChannel
GetTeamsForEmail(domain string) StoreChannel GetTeamsForEmail(domain string) StoreChannel
} }
@@ -77,6 +77,7 @@ type PostStore interface {
type UserStore interface { type UserStore interface {
Save(user *model.User) StoreChannel Save(user *model.User) StoreChannel
Update(user *model.User, allowRoleUpdate bool) StoreChannel Update(user *model.User, allowRoleUpdate bool) StoreChannel
UpdateLastPictureUpdate(userId string) StoreChannel
UpdateLastPingAt(userId string, time int64) StoreChannel UpdateLastPingAt(userId string, time int64) StoreChannel
UpdateLastActivityAt(userId string, time int64) StoreChannel UpdateLastActivityAt(userId string, time int64) StoreChannel
UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel

Просмотреть файл

@@ -19,13 +19,11 @@ const (
type ServiceSettings struct { type ServiceSettings struct {
SiteName string SiteName string
Domain string
Mode string Mode string
AllowTesting bool AllowTesting bool
UseSSL bool UseSSL bool
Port string Port string
Version string Version string
Shards map[string]string
InviteSalt string InviteSalt string
PublicLinkSalt string PublicLinkSalt string
ResetSalt string ResetSalt string
@@ -42,11 +40,6 @@ type SqlSettings struct {
AtRestEncryptKey string AtRestEncryptKey string
} }
type RedisSettings struct {
DataSource string
MaxOpenConns int
}
type LogSettings struct { type LogSettings struct {
ConsoleEnable bool ConsoleEnable bool
ConsoleLevel string ConsoleLevel string
@@ -57,14 +50,10 @@ type LogSettings struct {
} }
type AWSSettings struct { type AWSSettings struct {
S3AccessKeyId string S3AccessKeyId string
S3SecretAccessKey string S3SecretAccessKey string
S3Bucket string S3Bucket string
S3Region string S3Region string
Route53AccessKeyId string
Route53SecretAccessKey string
Route53ZoneId string
Route53Region string
} }
type ImageSettings struct { type ImageSettings struct {
@@ -77,6 +66,7 @@ type ImageSettings struct {
} }
type EmailSettings struct { type EmailSettings struct {
ByPassEmail bool
SMTPUsername string SMTPUsername string
SMTPPassword string SMTPPassword string
SMTPServer string SMTPServer string
@@ -112,7 +102,6 @@ type Config struct {
LogSettings LogSettings LogSettings LogSettings
ServiceSettings ServiceSettings ServiceSettings ServiceSettings
SqlSettings SqlSettings SqlSettings SqlSettings
RedisSettings RedisSettings
AWSSettings AWSSettings AWSSettings AWSSettings
ImageSettings ImageSettings ImageSettings ImageSettings
EmailSettings EmailSettings EmailSettings EmailSettings
@@ -218,18 +207,10 @@ func LoadConfig(fileName string) {
panic("Error decoding configuration " + err.Error()) 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 // Check for a valid email for feedback, if not then do feedback@domain
if _, err := mail.ParseAddress(config.EmailSettings.FeedbackEmail); err != nil { 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) configureLog(config.LogSettings)

Просмотреть файл

@@ -4,24 +4,9 @@
package utils package utils
import ( import (
"os"
"testing" "testing"
) )
func TestConfig(t *testing.T) { func TestConfig(t *testing.T) {
LoadConfig("config.json") 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()
}
}

Просмотреть файл

@@ -8,14 +8,14 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
"html"
"net" "net"
"net/mail" "net/mail"
"net/smtp" "net/smtp"
"html"
) )
func CheckMailSettings() *model.AppError { 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", "") return model.NewAppError("CheckMailSettings", "No email settings present, mail will not be sent", "")
} }
conn, err := connectToSMTPServer() conn, err := connectToSMTPServer()
@@ -79,6 +79,10 @@ func newSMTPClient(conn net.Conn) (*smtp.Client, *model.AppError) {
func SendMail(to, subject, body string) *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} fromMail := mail.Address{"", Cfg.EmailSettings.FeedbackEmail}
toMail := mail.Address{"", to} toMail := mail.Address{"", to}
@@ -95,11 +99,6 @@ func SendMail(to, subject, body string) *model.AppError {
} }
message += "\r\n<html><body>" + body + "</body></html>" 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() conn, err1 := connectToSMTPServer()
if err1 != nil { if err1 != nil {
return err1 return err1

Просмотреть файл

@@ -1,6 +1,7 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_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 Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
var ExtraMembers = React.createClass({ var PopoverListMembers = React.createClass({
componentDidMount: function() { componentDidMount: function() {
var originalLeave = $.fn.popover.Constructor.prototype.leave; var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj) { $.fn.popover.Constructor.prototype.leave = function(obj) {
@@ -35,30 +36,29 @@ var ExtraMembers = React.createClass({
$("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true}); $("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true});
$('body').on('click', function (e) { $('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'); $("#member_popover").popover('hide');
} }
}); });
}, },
render: function() { render: function() {
var count = this.props.members.length == 0 ? "-" : this.props.members.length; var popoverHtml = '';
count = this.props.members.length > 19 ? "20+" : count; var members = this.props.members;
var data_content = ""; var count = (members.length > 20) ? "20+" : (members.length || '-');
var sortedMembers = this.props.members;
if(sortedMembers) { if (members) {
sortedMembers.sort(function(a,b) { members.sort(function(a,b) {
return a.username.localeCompare(b.username); return a.username.localeCompare(b.username);
}) });
sortedMembers.forEach(function(m) { members.forEach(function(m) {
data_content += "<div style='white-space: nowrap'>" + m.username + "</div>"; popoverHtml += "<div class='text--nowrap'>" + m.username + "</div>";
}); });
} }
return ( 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"> <div id="member_tooltip" data-toggle="tooltip" title="View Channel Members">
{count} <span className="glyphicon glyphicon-user" aria-hidden="true"></span> {count} <span className="glyphicon glyphicon-user" aria-hidden="true"></span>
</div> </div>
@@ -78,6 +78,7 @@ function getStateFromStores() {
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'ChannelHeader',
componentDidMount: function() { componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(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}}); $(".channel-header__info .description").popover({placement : 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
}, },
_onSocketChange: function(msg) { _onSocketChange: function(msg) {
if(msg.action === "new_user") { if (msg.action === "new_user") {
AsyncClient.getChannelExtraInfo(true); AsyncClient.getChannelExtraInfo(true);
} }
}, },
@@ -107,15 +108,14 @@ module.exports = React.createClass({
return getStateFromStores(); return getStateFromStores();
}, },
handleLeave: function(e) { handleLeave: function(e) {
var self = this;
Client.leaveChannel(this.state.channel.id, Client.leaveChannel(this.state.channel.id,
function(data) { function(data) {
var townsquare = ChannelStore.getByName('town-square'); var townsquare = ChannelStore.getByName('town-square');
utils.switchChannel(townsquare); utils.switchChannel(townsquare);
}.bind(this), },
function(err) { function(err) {
AsyncClient.dispatchError(err, "handleLeave"); AsyncClient.dispatchError(err, "handleLeave");
}.bind(this) }
); );
}, },
searchMentions: function(e) { searchMentions: function(e) {
@@ -131,52 +131,29 @@ module.exports = React.createClass({
AppDispatcher.handleServerAction({ AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH_TERM, type: ActionTypes.RECIEVED_SEARCH_TERM,
term: terms, 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() { render: function() {
if (this.state.channel == null) { if (this.state.channel == null) {
return ( return null;
<div></div>
);
} }
var description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true}); var channel = this.state.channel;
var popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>); var description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
var channelTitle = ""; var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
var channelName = this.state.channel.name; var channelTitle = channel.display_name;
var currentId = UserStore.getCurrentId(); var currentId = UserStore.getCurrentId();
var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1; 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 = (this.state.channel.type === 'D');
var isDirect = false;
if (this.state.channel.type === 'O') { if (isDirect) {
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;
if (this.state.users.length > 1) { if (this.state.users.length > 1) {
if (this.state.users[0].id === UserStore.getCurrentId()) { var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)];
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} />; channelTitle = <UserProfile userId={contact.id} overwriteName={contact.nickname || contact.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} />;
}
} }
} }
@@ -192,25 +169,28 @@ module.exports = React.createClass({
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span> <span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a> </a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown"> <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_info" data-channelid={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> { !ChannelStore.isDefault(channel) ?
{ isAdmin ? <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> <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="#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={this.state.channel.display_name} data-channelid={this.state.channel.id}>Notification Preferences</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 && channelName != Constants.DEFAULT_CHANNEL ? { isAdmin && !ChannelStore.isDefault(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="#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 ? { isAdmin && !ChannelStore.isDefault(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> <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> <li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
: "" : null
} }
</ul> </ul>
</div> </div>
@@ -220,14 +200,13 @@ module.exports = React.createClass({
<a href="#"><strong className="heading">{channelTitle}</strong></a> <a href="#"><strong className="heading">{channelTitle}</strong></a>
} }
</th> </th>
<th><ExtraMembers members={this.state.users} channelId={this.state.channel.id} /></th> <th><PopoverListMembers members={this.state.users} channelId={channel.id} /></th>
{ searchForm } <th className="search-bar__container"><NavbarSearchBox /></th>
<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"> <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> <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>
</a> <ul className="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="channel_header_right_dropdown">
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_right_dropdown" style={{"left": "-150px"}}>
<li role="presentation"><a role="menuitem" href="#" onClick={this.searchMentions}>Recent Mentions</a></li> <li role="presentation"><a role="menuitem" href="#" onClick={this.searchMentions}>Recent Mentions</a></li>
</ul> </ul>
</div> </div>
@@ -237,5 +216,3 @@ module.exports = React.createClass({
); );
} }
}); });

Просмотреть файл

@@ -43,7 +43,7 @@ module.exports = React.createClass({
client.createPost(post, ChannelStore.getCurrent(), client.createPost(post, ChannelStore.getCurrent(),
function(data) { function(data) {
this.setState({ messageText: '', submitting: false, post_error: null }); this.setState({ messageText: '', submitting: false, post_error: null, server_error: null });
this.clearPreviews(); this.clearPreviews();
AsyncClient.getPosts(true, this.props.channelId); AsyncClient.getPosts(true, this.props.channelId);
@@ -57,6 +57,7 @@ module.exports = React.createClass({
function(err) { function(err) {
var state = {}; var state = {};
state.server_error = err.message; state.server_error = err.message;
state.submitting = false;
if (err.message === "Invalid RootId parameter") { if (err.message === "Invalid RootId parameter") {
if ($('#post_deleted').length > 0) $('#post_deleted').modal('show'); if ($('#post_deleted').length > 0) $('#post_deleted').modal('show');

Просмотреть файл

@@ -234,7 +234,7 @@ module.exports = React.createClass({
}, },
render: function() { 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 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; var limit_error = this.state.limit_error ? <div className='has-error'><label className='control-label'>{this.state.limit_error}</label></div> : null;

Просмотреть файл

@@ -12,7 +12,7 @@ module.exports = React.createClass({
Client.deleteChannel(this.state.channel_id, Client.deleteChannel(this.state.channel_id,
function(data) { function(data) {
AsyncClient.getChannels(true); AsyncClient.getChannels(true);
window.location.href = '/channels/town-square'; window.location.href = '/';
}.bind(this), }.bind(this),
function(err) { function(err) {
AsyncClient.dispatchError(err, "handleDelete"); AsyncClient.dispatchError(err, "handleDelete");

Просмотреть файл

@@ -56,13 +56,13 @@ module.exports = React.createClass({
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
var newState = {}; var newState = {};
if(BrowserStore.getItem('edit_state_transfer')) { 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'); BrowserStore.removeItem('edit_state_transfer');
} else { } else {
var button = e.relatedTarget; 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') }; 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); PostStore.addSelectedPostChangeListener(this._onChange);
}, },

Просмотреть файл

@@ -43,7 +43,7 @@ module.exports = React.createClass({
<h4 className="modal-title" ref="title">Edit {this.state.title} Description</h4> <h4 className="modal-title" ref="title">Edit {this.state.title} Description</h4>
</div> </div>
<div className="modal-body"> <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>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button> <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>

Просмотреть файл

@@ -4,6 +4,7 @@
var Client = require('../utils/client.jsx'); var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var Textbox = require('./textbox.jsx'); var Textbox = require('./textbox.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
module.exports = React.createClass({ module.exports = React.createClass({
handleEdit: function(e) { handleEdit: function(e) {
@@ -13,14 +14,14 @@ module.exports = React.createClass({
if (updatedPost.message.length === 0) { if (updatedPost.message.length === 0) {
var tempState = this.state; var tempState = this.state;
delete tempState.editText; delete tempState.editText;
BrowserStore.setItem('edit_state_transfer', JSON.stringify(tempState)); BrowserStore.setItem('edit_state_transfer', tempState);
$("#edit_post").modal('hide'); $("#edit_post").modal('hide');
$("#delete_post").modal('show'); $("#delete_post").modal('show');
return; return;
} }
updatedPost.id = this.state.post_id updatedPost.id = this.state.post_id;
updatedPost.channel_id = this.state.channel_id updatedPost.channel_id = this.state.channel_id;
Client.updatePost(updatedPost, Client.updatePost(updatedPost,
function(data) { function(data) {

Просмотреть файл

@@ -38,7 +38,7 @@ module.exports = React.createClass({
<div className="modal-body"> <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>{"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> </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>
<div className="modal-footer"> <div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button> <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>

24
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>
);
}
});

Просмотреть файл

@@ -1,102 +1,21 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx'); var client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx');
var Constants = require('../utils/constants.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>
);
}
});
module.exports = React.createClass({ module.exports = React.createClass({
handleSubmit: function(e) { handleSubmit: function(e) {
e.preventDefault(); e.preventDefault();
var state = { } var state = { }
var domain = this.refs.domain.getDOMNode().value.trim(); var name = this.props.teamName
if (!domain) { if (!name) {
state.server_error = "A domain is required" state.server_error = "Bad team name"
this.setState(state); this.setState(state);
return; return;
} }
@@ -124,23 +43,22 @@ module.exports = React.createClass({
state.server_error = ""; state.server_error = "";
this.setState(state); this.setState(state);
client.loginByEmail(domain, email, password, client.loginByEmail(name, email, password,
function(data) { function(data) {
UserStore.setLastDomain(domain);
UserStore.setLastEmail(email);
UserStore.setCurrentUser(data); UserStore.setCurrentUser(data);
UserStore.setLastEmail(email);
var redirect = utils.getUrlParameter("redirect"); var redirect = utils.getUrlParameter("redirect");
if (redirect) { if (redirect) {
window.location.href = decodeURI(redirect); window.location.pathname = decodeURI(redirect);
} else { } else {
window.location.href = '/channels/town-square'; window.location.pathname = '/' + name + '/channels/town-square';
} }
}.bind(this), }.bind(this),
function(err) { function(err) {
if (err.message == "Login failed because email address has not been verified") { 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; return;
} }
state.server_error = err.message; state.server_error = err.message;
@@ -161,35 +79,35 @@ module.exports = React.createClass({
priorEmail = decodeURIComponent(emailParam); priorEmail = decodeURIComponent(emailParam);
} }
var subDomainClass = "form-control hidden"; var teamDisplayName = this.props.teamDisplayName;
var subDomain = utils.getSubDomain(); var teamName = this.props.teamName;
if (utils.isTestDomain()) { var focusEmail = false;
subDomainClass = "form-control"; var focusPassword = false;
subDomain = UserStore.getLastDomain(); if (priorEmail != "") {
} else if (subDomain == "") { focusPassword = true;
return (<FindTeamDomain />); } else {
focusEmail = true;
} }
return ( return (
<div className="signup-team__container"> <div className="signup-team__container">
<div> <div>
<span className="signup-team__name">{ subDomain }</span> <span className="signup-team__name">{ teamDisplayName }</span>
<br/> <br/>
<span className="signup-team__subdomain">{ utils.getDomainWithOutSub() }</span> <span className="signup-team__subdomain">/{ teamName }/</span>
<br/> <br/>
<br/> <br/>
</div> </div>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<div className={server_error ? 'form-group has-error' : 'form-group'}> <div className={server_error ? 'form-group has-error' : 'form-group'}>
{ server_error } { server_error }
<input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
</div> </div>
<div className={server_error ? 'form-group has-error' : 'form-group'}> <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>
<div className={server_error ? 'form-group has-error' : 'form-group'}> <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>
<div className="form-group"> <div className="form-group">
<button type="submit" className="btn btn-primary">Sign in</button> <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> <span><a href="/find_team">{"Find other " + strings.TeamPlural}</a></span>
</div> </div>
<div className="form-group"> <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>
<div className="external-link"> <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> </div>
</form> </form>
</div> </div>

Просмотреть файл

@@ -23,6 +23,7 @@ module.exports = React.createClass({
var member = this.props.member; var member = this.props.member;
var isAdmin = this.props.isAdmin; var isAdmin = this.props.isAdmin;
var isMemberAdmin = member.roles.indexOf("admin") > -1; var isMemberAdmin = member.roles.indexOf("admin") > -1;
var timestamp = UserStore.getCurrentUser().update_at;
var invite; var invite;
if (member.invited && this.props.handleInvite) { if (member.invited && this.props.handleInvite) {
@@ -48,12 +49,12 @@ module.exports = React.createClass({
</div> </div>
); );
} else { } 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 ( return (
<div className="row member-div"> <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-name">{member.username}</span>
<span className="member-email">{member.email}</span> <span className="member-email">{member.email}</span>
{ invite } { invite }

Просмотреть файл

@@ -5,6 +5,7 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var Client = require('../utils/client.jsx'); var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var utils = require('../utils/utils.jsx');
var MemberListTeamItem = React.createClass({ var MemberListTeamItem = React.createClass({
handleMakeMember: function() { handleMakeMember: function() {
@@ -59,9 +60,10 @@ var MemberListTeamItem = React.createClass({
return {}; return {};
}, },
render: function() { 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 user = this.props.user;
var currentRoles = "Member" var currentRoles = "Member";
var timestamp = UserStore.getCurrentUser().update_at;
if (user.roles.length > 0) { if (user.roles.length > 0) {
currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1); currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1);
@@ -83,9 +85,9 @@ var MemberListTeamItem = React.createClass({
return ( return (
<div className="row member-div"> <div className="row member-div">
<img className="post-profile-img pull-left" src={"/api/v1/users/" + user.id + "/image"} height="36" width="36" /> <img className="post-profile-img pull-left" src={"/api/v1/users/" + user.id + "/image?time=" + timestamp} height="36" width="36" />
<span className="member-name">{user.full_name.trim() ? user.full_name : user.username}</span> <span className="member-name">{utils.getDisplayName(user)}</span>
<span className="member-email">{user.full_name.trim() ? user.username : email}</span> <span className="member-email">{email}</span>
<div className="dropdown member-drop"> <div className="dropdown member-drop">
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true"> <a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
<span>{currentRoles} </span> <span>{currentRoles} </span>

Просмотреть файл

@@ -1,19 +1,27 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var UserStore = require("../stores/user_store.jsx");
module.exports = React.createClass({ module.exports = React.createClass({
handleClick: function() { handleClick: function() {
this.props.handleClick(this.props.username); this.props.handleClick(this.props.username);
}, },
getInitialState: function() {
return null;
},
render: function() { render: function() {
var self = this;
var icon; var icon;
if (this.props.id != null) { var timestamp = UserStore.getCurrentUser().update_at;
icon = <span><img className="mention-img" src={"/api/v1/users/" + this.props.id + "/image"}/></span>; 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 { } else {
icon = <span><i className="mention-img fa fa-users fa-2x"></i></span>; icon = <span><i className="mention-img fa fa-users fa-2x"></i></span>;
} }
return ( 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">{icon}</div>
<div className="pull-left mention-align"><span>@{this.props.username}</span><span className="mention-fullname">{this.props.secondary_text}</span></div> <div className="pull-left mention-align"><span>@{this.props.username}</span><span className="mention-fullname">{this.props.secondary_text}</span></div>
</div> </div>

Просмотреть файл

@@ -7,19 +7,48 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Mention = require('./mention.jsx'); var Mention = require('./mention.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var Utils = require('../utils/utils.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
var MAX_HEIGHT_LIST = 292;
var MAX_ITEMS_IN_LIST = 25;
var ITEM_HEIGHT = 36;
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "MentionList",
componentDidMount: function() { componentDidMount: function() {
PostStore.addMentionDataChangeListener(this._onChange); PostStore.addMentionDataChangeListener(this._onChange);
var self = this; var self = this;
$('body').on('keypress.mentionlist', '#'+this.props.id,
$('body').on('keydown.mentionlist', '#'+this.props.id,
function(e) { 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.stopPropagation();
e.preventDefault(); 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() { componentWillUnmount: function() {
PostStore.removeMentionDataChangeListener(this._onChange); 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) { _onChange: function(id, mentionText, excludeList) {
if (id !== this.props.id) return; if (id !== this.props.id) return;
@@ -40,6 +90,7 @@ module.exports = React.createClass({
var newState = this.state; var newState = this.state;
if (mentionText != null) newState.mentionText = mentionText; if (mentionText != null) newState.mentionText = mentionText;
if (excludeList != null) newState.excludeUsers = excludeList; if (excludeList != null) newState.excludeUsers = excludeList;
this.setState(newState); this.setState(newState);
}, },
handleClick: function(name) { handleClick: function(name) {
@@ -51,6 +102,21 @@ module.exports = React.createClass({
this.setState({ mentionText: '-1' }); 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() { addFirstMention: function() {
if (!this.refs.mention0) return; if (!this.refs.mention0) return;
this.refs.mention0.handleClick(); this.refs.mention0.handleClick();
@@ -58,6 +124,23 @@ module.exports = React.createClass({
isEmpty: function() { isEmpty: function() {
return (!this.refs.mention0); 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) { alreadyMentioned: function(username) {
var excludeUsers = this.state.excludeUsers; var excludeUsers = this.state.excludeUsers;
for (var i = 0; i < excludeUsers.length; i++) { for (var i = 0; i < excludeUsers.length; i++) {
@@ -68,11 +151,12 @@ module.exports = React.createClass({
return false; return false;
}, },
getInitialState: function() { getInitialState: function() {
return { excludeUsers: [], mentionText: "-1" }; return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" };
}, },
render: function() { render: function() {
var self = this;
var mentionText = this.state.mentionText; var mentionText = this.state.mentionText;
if (mentionText === '-1') return (<div/>); if (mentionText === '-1') return null;
var profiles = UserStore.getActiveOnlyProfiles(); var profiles = UserStore.getActiveOnlyProfiles();
var users = []; var users = [];
@@ -82,14 +166,16 @@ module.exports = React.createClass({
var all = {}; var all = {};
all.username = "all"; all.username = "all";
all.full_name = ""; all.nickname = "";
all.secondary_text = "Notifies everyone in the team"; all.secondary_text = "Notifies everyone in the team";
all.id = "allmention";
users.push(all); users.push(all);
var channel = {}; var channel = {};
channel.username = "channel"; channel.username = "channel";
channel.full_name = ""; channel.nickname = "";
channel.secondary_text = "Notifies everyone in the channel"; channel.secondary_text = "Notifies everyone in the channel";
channel.id = "channelmention";
users.push(channel); users.push(channel);
users.sort(function(a,b) { users.sort(function(a,b) {
@@ -100,44 +186,42 @@ module.exports = React.createClass({
var mentions = {}; var mentions = {};
var index = 0; var index = 0;
for (var i = 0; i < users.length; i++) { for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) {
if (Object.keys(mentions).length >= 25) break;
if (this.alreadyMentioned(users[i].username)) continue; if (this.alreadyMentioned(users[i].username)) continue;
var firstName = "", lastName = ""; if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText,0) === 0)
if (users[i].full_name.length > 0) { || (users[i].last_name && users[i].last_name.lastIndexOf(mentionText,0) === 0) || users[i].username.lastIndexOf(mentionText,0) === 0) {
var splitName = users[i].full_name.split(' '); mentions[index] = (
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] = (
<Mention <Mention
ref={'mention' + index} ref={'mention' + index}
username={users[i].username} username={users[i].username}
secondary_text={users[i].secondary_text} secondary_text={Utils.getFullName(users[i])}
id={users[i].id} 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} /> handleClick={this.handleClick} />
); );
index++; index++;
} }
} }
var numMentions = Object.keys(mentions).length; var numMentions = Object.keys(mentions).length;
if (numMentions < 1) return (<div/>); if (numMentions < 1) return null;
var height = (numMentions*36) + 4; var $mention_tab = $('#'+this.props.id);
var width = $('#'+this.props.id).parent().width(); var maxHeight = Math.min(MAX_HEIGHT_LIST, $mention_tab.offset().top - 10);
var bottom = $(window).height() - $('#'+this.props.id).offset().top; var style = {
var left = $('#'+this.props.id).offset().left; height: Math.min(maxHeight, (numMentions*ITEM_HEIGHT) + 4),
var max_height = $('#'+this.props.id).offset().top - 10; width: $mention_tab.parent().width(),
bottom: $(window).height() - $mention_tab.offset().top,
left: $mention_tab.offset().left
};
return ( return (
<div className="mentions--top" style={{height: height, width: width, bottom: bottom, left: left}}> <div className="mentions--top" style={style}>
<div ref="mentionlist" className="mentions-box" style={{height: height, width: width}}> <div ref="mentionlist" className="mentions-box" id="mentionsbox">
{ mentions } { mentions }
</div> </div>
</div> </div>

Просмотреть файл

@@ -7,6 +7,7 @@ var client = require('../utils/client.jsx');
var asyncClient = require('../utils/async_client.jsx'); var asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var LoadingScreen = require('./loading_screen.jsx');
function getStateFromStores() { function getStateFromStores() {
return { return {
@@ -16,6 +17,8 @@ function getStateFromStores() {
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "MoreChannelsModal",
componentDidMount: function() { componentDidMount: function() {
ChannelStore.addMoreChangeListener(this._onChange); ChannelStore.addMoreChangeListener(this._onChange);
$(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) { $(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-name">{channel.display_name}</p>
<p className="more-channel-description">{channel.description}</p> <p className="more-channel-description">{channel.description}</p>
</td> </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> </tr>
) )
})} })}
@@ -100,15 +103,7 @@ module.exports = React.createClass({
<p className="primary-message">No more channels to join</p> <p className="primary-message">No more channels to join</p>
<p className="secondary-message">Click 'Create New Channel' to make a new one</p> <p className="secondary-message">Click 'Create New Channel' to make a new one</p>
</div>) </div>)
: <div ref="loadingscreen" className="loading-screen loading-screen--channel"> : <LoadingScreen /> }
<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>
}
{ server_error } { server_error }
</div> </div>
<div className="modal-footer"> <div className="modal-footer">

Просмотреть файл

@@ -51,7 +51,7 @@ module.exports = React.createClass({
}, },
render: function() { render: function() {
return ( return (
<span className="msg-typing">{ this.state.text }</span> <span className="msg-typing">{ this.state.text }</span>
); );
} }
}); });

Просмотреть файл

@@ -2,21 +2,21 @@
// See License.txt for license information. // See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var utils = require('../utils/utils.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 AsyncClient = require('../utils/async_client.jsx');
var Sidebar = require('./sidebar.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var ChannelStore = require('../stores/channel_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 UserProfile = require('./user_profile.jsx');
var MessageWrapper = require('./message_wrapper.jsx'); var MessageWrapper = require('./message_wrapper.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
function getCountsStateFromStores() { function getCountsStateFromStores() {
var count = 0; var count = 0;
var channels = ChannelStore.getAll(); var channels = ChannelStore.getAll();
var members = ChannelStore.getAllMembers(); var members = ChannelStore.getAllMembers();
@@ -34,7 +34,7 @@ function getCountsStateFromStores() {
} }
}); });
return { count: count } return { count: count };
} }
var NotifyCounts = React.createClass({ var NotifyCounts = React.createClass({
@@ -54,99 +54,11 @@ var NotifyCounts = React.createClass({
return getCountsStateFromStores(); return getCountsStateFromStores();
}, },
render: function() { render: function() {
if (this.state.count == 0) { if (this.state.count) {
return (<span></span>); return <span className="badge badge-notify">{ this.state.count }</span>;
} else {
return null;
} }
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";
}
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({ module.exports = React.createClass({
displayName: 'Navbar',
componentDidMount: function() { componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(this._onChange); ChannelStore.addExtraInfoChangeListener(this._onChange);
var self = this; $('.inner__wrap').click(this.hideSidebars);
$('.inner__wrap').click(self.hideSidebars);
$('body').on('click.infopopover', function(e){ $('body').on('click.infopopover', function(e) {
if ($(e.target).attr('data-toggle') !== 'popover' if ($(e.target).attr('data-toggle') !== 'popover'
&& $(e.target).parents('.popover.in').length === 0) { && $(e.target).parents('.popover.in').length === 0) {
$('.info-popover').popover('hide'); $('.info-popover').popover('hide');
@@ -181,13 +94,13 @@ module.exports = React.createClass({
}, },
handleLeave: function(e) { handleLeave: function(e) {
client.leaveChannel(this.state.channel.id, client.leaveChannel(this.state.channel.id,
function(data) { function(data, text, req) {
AsyncClient.getChannels(true); AsyncClient.getChannels(true);
window.location.href = '/channels/town-square'; window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square';
}.bind(this), }.bind(this),
function(err) { function(err) {
AsyncClient.dispatchError(err, "handleLeave"); AsyncClient.dispatchError(err, "handleLeave");
}.bind(this) }
); );
}, },
hideSidebars: function(e) { hideSidebars: function(e) {
@@ -204,7 +117,7 @@ module.exports = React.createClass({
}); });
if (e.target.className != 'navbar-toggle' && e.target.className != 'icon-bar') { 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--left').removeClass('move--right');
$('.sidebar--right').removeClass('move--left'); $('.sidebar--right').removeClass('move--left');
$('.sidebar--menu').removeClass('move--left'); $('.sidebar--menu').removeClass('move--left');
@@ -229,24 +142,22 @@ module.exports = React.createClass({
render: function() { render: function() {
var currentId = UserStore.getCurrentId(); var currentId = UserStore.getCurrentId();
var channelName = "";
var popoverContent = ""; var popoverContent = "";
var channelTitle = this.props.teamName; var channelTitle = this.props.teamDisplayName;
var isAdmin = false; var isAdmin = false;
var isDirect = false; var isDirect = false;
var description = "" var description = ""
var channel = this.state.channel;
if (this.state.channel) { if (channel) {
var channel = this.state.channel; description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true}); popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>);
channelName = this.state.channel.name;
isAdmin = this.state.member.roles.indexOf("admin") > -1; isAdmin = this.state.member.roles.indexOf("admin") > -1;
if (channel.type === 'O') { if (channel.type === 'O') {
channelTitle = this.state.channel.display_name; channelTitle = channel.display_name;
} else if (channel.type === 'P') { } else if (channel.type === 'P') {
channelTitle = this.state.channel.display_name; channelTitle = channel.display_name;
} else if (channel.type === 'D') { } else if (channel.type === 'D') {
isDirect = true; isDirect = true;
if (this.state.users.length > 1) { if (this.state.users.length > 1) {
@@ -258,12 +169,11 @@ module.exports = React.createClass({
} }
} }
if(this.state.channel.description.length == 0){ if (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>); 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 : var navbar_collapse_button = currentId != null ? null :
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1"> <button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
<span className="sr-only">Toggle sidebar</span> <span className="sr-only">Toggle sidebar</span>
@@ -292,60 +202,55 @@ module.exports = React.createClass({
{ navbar_collapse_button } { navbar_collapse_button }
{ sidebar_collapse_button } { sidebar_collapse_button }
{ right_sidebar_collapse_button } { right_sidebar_collapse_button }
{ !isDirect && this.state.channel ? { !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">
<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>
</div>
</div>
: "" }
{ isDirect && this.state.channel ?
<div className="navbar-brand"> <div className="navbar-brand">
<strong> <div className="dropdown">
<a href="#"><strong className="heading">{channelTitle}</strong></a> <div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
</strong> <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>
: "" } : null
{ !this.state.channel ? }
{ isDirect && channel ?
<div className="navbar-brand"> <div className="navbar-brand">
<strong> <a href="#" className="heading">{ channelTitle }</a>
<a href="/"><strong className="heading">{ channelTitle }</strong></a> </div>
</strong> : null }
{ !channel ?
<div className="navbar-brand">
<a href="/" className="heading">{ channelTitle }</a>
</div> </div>
: "" } : "" }
</div> </div>
<div className="collapse navbar-collapse" id="navbar-collapse-1">
{ loginForm }
</div>
</div> </div>
</nav> </nav>
); );
} }
}); });

Просмотреть файл

@@ -6,6 +6,8 @@ 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 asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.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({ module.exports = React.createClass({
handleSubmit: function(e) { handleSubmit: function(e) {
@@ -60,13 +62,13 @@ module.exports = React.createClass({
var self = this; var self = this;
client.createChannel(channel, client.createChannel(channel,
function(data) { function() {
this.refs.display_name.getDOMNode().value = ""; this.refs.display_name.getDOMNode().value = "";
this.refs.channel_name.getDOMNode().value = ""; this.refs.channel_name.getDOMNode().value = "";
this.refs.channel_desc.getDOMNode().value = ""; this.refs.channel_desc.getDOMNode().value = "";
$(self.refs.modal.getDOMNode()).modal('hide'); $(self.refs.modal.getDOMNode()).modal('hide');
window.location.href = "/channels/" + channel.name; window.location = TeamStore.getCurrentTeamUrl() + "/channels/" + channel.name;
asyncClient.getChannels(true); asyncClient.getChannels(true);
}.bind(this), }.bind(this),
function(err) { function(err) {
@@ -122,7 +124,7 @@ module.exports = React.createClass({
</div> </div>
<div className="form-group"> <div className="form-group">
<label className='control-label'>Description</label> <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> </div>
{ server_error } { server_error }
</form> </form>

Просмотреть файл

@@ -10,13 +10,6 @@ SendResetPasswordLink = React.createClass({
e.preventDefault(); e.preventDefault();
var state = {}; 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(); var email = this.refs.email.getDOMNode().value.trim();
if (!email) { if (!email) {
state.error = "Please enter a valid email address." state.error = "Please enter a valid email address."
@@ -29,17 +22,17 @@ SendResetPasswordLink = React.createClass({
data = {}; data = {};
data['email'] = email; data['email'] = email;
data['domain'] = domain; data['name'] = this.props.teamName;
client.sendPasswordReset(data, client.sendPasswordReset(data,
function(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.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(); $(this.refs.reset_form.getDOMNode()).hide();
}.bind(this), }.bind(this),
function(err) { function(err) {
this.setState({ error: err.message, update_text: null, more_update_text: null }); this.setState({ error: err.message, update_text: null, more_update_text: null });
}.bind(this) }.bind(this)
); );
}, },
getInitialState: function() { getInitialState: function() {
return {}; 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 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 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 ( return (
<div className="col-sm-12"> <div className="col-sm-12">
<div className="signup-team__container"> <div className="signup-team__container">
<h3>Password Reset</h3> <h3>Password Reset</h3>
{ update_text } { update_text }
<form onSubmit={this.handleSendLink} ref="reset_form"> <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> <p>{"To reset your password, enter the email address you used to sign up for " + this.props.teamDisplayName + "."}</p>
<div className="form-group">
<input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
</div>
<div className={error ? 'form-group has-error' : 'form-group'}> <div className={error ? 'form-group has-error' : 'form-group'}>
<input type="text" className="form-control" name="email" ref="email" placeholder="Email" /> <input type="text" className="form-control" name="email" ref="email" placeholder="Email" />
</div> </div>
@@ -83,13 +65,6 @@ ResetPassword = React.createClass({
e.preventDefault(); e.preventDefault();
var state = {}; 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(); var password = this.refs.password.getDOMNode().value.trim();
if (!password || password.length < 5) { if (!password || password.length < 5) {
state.error = "Please enter at least 5 characters." state.error = "Please enter at least 5 characters."
@@ -104,41 +79,30 @@ ResetPassword = React.createClass({
data['new_password'] = password; data['new_password'] = password;
data['hash'] = this.props.hash; data['hash'] = this.props.hash;
data['data'] = this.props.data; data['data'] = this.props.data;
data['domain'] = domain; data['name'] = this.props.teamName;
client.resetPassword(data, client.resetPassword(data,
function(data) { function(data) {
this.setState({ error: null, update_text: "Your password has been updated successfully." }); this.setState({ error: null, update_text: "Your password has been updated successfully." });
}.bind(this), }.bind(this),
function(err) { function(err) {
this.setState({ error: err.message, update_text: null }); this.setState({ error: err.message, update_text: null });
}.bind(this) }.bind(this)
); );
}, },
getInitialState: function() { getInitialState: function() {
return {}; return {};
}, },
render: function() { 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 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 ( return (
<div className="col-sm-12"> <div className="col-sm-12">
<div className="signup-team__container"> <div className="signup-team__container">
<h3>Password Reset</h3> <h3>Password Reset</h3>
<form onSubmit={this.handlePasswordReset}> <form onSubmit={this.handlePasswordReset}>
<p>{"Enter a new password for your " + this.props.teamName + " " + config.SiteName + " account."}</p> <p>{"Enter a new password for your " + this.props.teamDisplayName + " " + config.SiteName + " account."}</p>
<div className="form-group">
<input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
</div>
<div className={error ? 'form-group has-error' : 'form-group'}> <div className={error ? 'form-group has-error' : 'form-group'}>
<input type="password" className="form-control" name="password" ref="password" placeholder="Password" /> <input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
</div> </div>
@@ -161,14 +125,15 @@ module.exports = React.createClass({
if (this.props.isReset === "false") { if (this.props.isReset === "false") {
return ( return (
<SendResetPasswordLink <SendResetPasswordLink
teamDisplayName={this.props.teamDisplayName}
teamName={this.props.teamName} teamName={this.props.teamName}
/> />
); );
} else { } else {
return ( return (
<ResetPassword <ResetPassword
teamDisplayName={this.props.teamDisplayName}
teamName={this.props.teamName} teamName={this.props.teamName}
domain={this.props.domain}
hash={this.props.hash} hash={this.props.hash}
data={this.props.data} data={this.props.data}
/> />

Просмотреть файл

@@ -10,6 +10,7 @@ var UserStore = require('../stores/user_store.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "Post",
componentDidMount: function() { componentDidMount: function() {
$('.modal').on('show.bs.modal', function () { $('.modal').on('show.bs.modal', function () {
$('.modal-body').css('overflow-y', 'auto'); $('.modal-body').css('overflow-y', 'auto');
@@ -19,7 +20,7 @@ module.exports = React.createClass({
handleCommentClick: function(e) { handleCommentClick: function(e) {
e.preventDefault(); e.preventDefault();
data = {}; var data = {};
data.order = [this.props.post.id]; data.order = [this.props.post.id];
data.posts = this.props.posts; data.posts = this.props.posts;
@@ -33,6 +34,10 @@ module.exports = React.createClass({
results: null results: null
}); });
}, },
forceUpdateInfo: function() {
this.refs.info.forceUpdate();
this.refs.header.forceUpdate();
},
getInitialState: function() { getInitialState: function() {
return { }; return { };
}, },
@@ -48,7 +53,6 @@ module.exports = React.createClass({
var commentCount = 0; var commentCount = 0;
var commentRootId = parentPost ? post.root_id : post.id; var commentRootId = parentPost ? post.root_id : post.id;
var rootUser = "";
for (var postId in posts) { for (var postId in posts) {
if (posts[postId].root_id == commentRootId) { if (posts[postId].root_id == commentRootId) {
commentCount += 1; 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; 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){ var rootUser = this.props.sameRoot ? "same--root" : "other--root";
rootUser = "same--root";
}
else {
rootUser = "other--root";
}
var postType = ""; var postType = "";
if (type != "Post"){ if (type != "Post"){
@@ -74,18 +73,20 @@ module.exports = React.createClass({
currentUserCss = "current--user"; currentUserCss = "current--user";
} }
var timestamp = UserStore.getCurrentUser().update_at;
return ( return (
<div> <div>
<div id={post.id} className={"post " + this.props.sameUser + " " + rootUser + " " + postType + " " + currentUserCss}> <div id={post.id} className={"post " + this.props.sameUser + " " + rootUser + " " + postType + " " + currentUserCss}>
{ !this.props.hideProfilePic ? { !this.props.hideProfilePic ?
<div className="post-profile-img__container"> <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> </div>
: "" } : null }
<div className="post__content"> <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} /> <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> </div>
</div> </div>

Просмотреть файл

@@ -71,11 +71,22 @@ module.exports = React.createClass({
name = <a className="theme" onClick={function(){ utils.searchForTerm(profile.username); }}>{profile.username}</a>; 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 = ( comment = (
<p className="post-link"> <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> </p>
); );
@@ -120,7 +131,7 @@ module.exports = React.createClass({
return ( return (
<div className="post-body"> <div className="post-body">
{ comment } { 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 ? { filenames && filenames.length > 0 ?
<div className="post-image__columns"> <div className="post-image__columns">
{ postFiles } { postFiles }

Просмотреть файл

@@ -11,6 +11,7 @@ module.exports = React.createClass({
render: function() { render: function() {
var post = this.props.post; var post = this.props.post;
var isOwner = UserStore.getCurrentId() == post.user_id; var isOwner = UserStore.getCurrentId() == post.user_id;
var isAdmin = UserStore.getCurrentUser().roles.indexOf("admin") > -1
var type = "Post" var type = "Post"
if (post.root_id.length > 0) { if (post.root_id.length > 0) {
@@ -30,13 +31,11 @@ module.exports = React.createClass({
<div className="dropdown"> <div className="dropdown">
{ isOwner || (this.props.allowReply === "true" && type != "Comment") ? { isOwner || (this.props.allowReply === "true" && type != "Comment") ?
<div> <div>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false"> <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
[...]
</a>
<ul className="dropdown-menu" role="menu"> <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="#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> { this.props.allowReply === "true" ? <li role="presentation"><a className="reply-link theme" href="#" onClick={this.props.handleCommentClick}>Reply</a></li>
: "" } : "" }

Просмотреть файл

@@ -6,8 +6,8 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' ); var UserProfile = require( './user_profile.jsx' );
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var CreatePost = require('./create_post.jsx');
var Post = require('./post.jsx'); var Post = require('./post.jsx');
var LoadingScreen = require('./loading_screen.jsx');
var SocketStore = require('../stores/socket_store.jsx'); var SocketStore = require('../stores/socket_store.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.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({ module.exports = React.createClass({
displayName: "PostList",
scrollPosition: 0, scrollPosition: 0,
preventScrollTrigger: false, preventScrollTrigger: false,
gotMorePosts: 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('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;');
utils.changeCss('div.theme', 'background-color:'+user.props.theme+';'); utils.changeCss('div.theme', 'background-color:'+user.props.theme+';');
utils.changeCss('.btn.btn-primary', 'background: ' + 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('.modal .modal-header', 'background: ' + user.props.theme+';');
utils.changeCss('.mention', 'background: ' + user.props.theme+';'); utils.changeCss('.mention', 'background: ' + user.props.theme+';');
utils.changeCss('.mention-link', 'color: ' + user.props.theme+';'); utils.changeCss('.mention-link', 'color: ' + user.props.theme+';');
@@ -78,6 +49,7 @@ module.exports = React.createClass({
PostStore.addChangeListener(this._onChange); PostStore.addChangeListener(this._onChange);
ChannelStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange);
UserStore.addStatusesChangeListener(this._onTimeChange);
SocketStore.addChangeListener(this._onSocketChange); SocketStore.addChangeListener(this._onSocketChange);
$(".post-list-holder-by-time").perfectScrollbar(); $(".post-list-holder-by-time").perfectScrollbar();
@@ -157,28 +129,25 @@ module.exports = React.createClass({
componentWillUnmount: function() { componentWillUnmount: function() {
PostStore.removeChangeListener(this._onChange); PostStore.removeChangeListener(this._onChange);
ChannelStore.removeChangeListener(this._onChange); ChannelStore.removeChangeListener(this._onChange);
UserStore.removeStatusesChangeListener(this._onTimeChange);
SocketStore.removeChangeListener(this._onSocketChange); SocketStore.removeChangeListener(this._onSocketChange);
$('body').off('click.userpopover'); $('body').off('click.userpopover');
}, },
resize: function() { resize: function() {
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
if (this.gotMorePosts) { if (this.gotMorePosts) {
this.gotMorePosts = false; 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).scrollTop($(post_holder).scrollTop() + (post_holder.scrollHeight-this.oldScrollHeight) );
$(post_holder).perfectScrollbar('update');
} else { } else {
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
if ($("#new_message")[0] && !this.scrolledToNew) { if ($("#new_message")[0] && !this.scrolledToNew) {
$(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63); $(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63);
$(post_holder).perfectScrollbar('update');
this.scrolledToNew = true; this.scrolledToNew = true;
} else { } else {
$(post_holder).scrollTop(post_holder.scrollHeight); $(post_holder).scrollTop(post_holder.scrollHeight);
$(post_holder).perfectScrollbar('update');
} }
} }
$(post_holder).perfectScrollbar('update');
}, },
_onChange: function() { _onChange: function() {
var newState = getStateFromStores(); var newState = getStateFromStores();
@@ -239,12 +208,8 @@ module.exports = React.createClass({
var index = post_list.order.indexOf(msg.props.post_id); var index = post_list.order.indexOf(msg.props.post_id);
if (index > -1) post_list.order.splice(index, 1); if (index > -1) post_list.order.splice(index, 1);
var scrollSave = $(".post-list-holder-by-time").scrollTop();
this.setState({ post_list: post_list }); this.setState({ post_list: post_list });
$(".post-list-holder-by-time").scrollTop(scrollSave)
PostStore.storePosts(msg.channel_id, post_list); PostStore.storePosts(msg.channel_id, post_list);
} else { } else {
AsyncClient.getPosts(true, msg.channel_id); 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) { if (activeRootPostId === msg.props.post_id && UserStore.getCurrentId() != msg.user_id) {
$('#post_deleted').modal('show'); $('#post_deleted').modal('show');
} }
} else if(msg.action == "new_user") { } else if (msg.action == "new_user") {
AsyncClient.getProfiles(); 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) { getMorePosts: function(e) {
e.preventDefault(); e.preventDefault();
@@ -297,7 +268,7 @@ module.exports = React.createClass({
}, },
function(err) { function(err) {
$(self.refs.loadmore.getDOMNode()).text("Load more messages"); $(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) var teammate = utils.getDirectTeammate(channel.id)
if (teammate) { 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 = ( more_messages = (
<div className="channel-intro"> <div className="channel-intro">
<div className="post-profile-img__container channel-intro-img"> <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>
<div className="channel-intro-profile"> <div className="channel-intro-profile">
<strong><UserProfile userId={teammate.id} /></strong> <strong><UserProfile userId={teammate.id} /></strong>
</div> </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> </div>
); );
} else { } else {
@@ -370,7 +344,7 @@ module.exports = React.createClass({
} }
} }
if (channel.name === Constants.DEFAULT_CHANNEL) { if (ChannelStore.isDefault(channel)) {
more_messages = ( more_messages = (
<div className="channel-intro"> <div className="channel-intro">
<h4 className="channel-intro-title">Welcome</h4> <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." } { channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
<br/> <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" 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> </p>
</div> </div>
); );
@@ -420,37 +394,35 @@ module.exports = React.createClass({
var postCtls = []; 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 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--) { for (var i = order.length-1; i >= 0; i--) {
var post = posts[order[i]]; var post = posts[order[i]];
var parentPost; var parentPost = post.parent_id ? posts[post.parent_id] : null;
if (post.parent_id) { var sameUser = '';
parentPost = posts[post.parent_id]; var sameRoot = false;
} else { var hideProfilePic = false;
parentPost = null; var prevPost = (i < order.length - 1) ? posts[order[i + 1]] : null;
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
hideProfilePic = (prevPost.user_id === post.user_id) && !utils.isComment(prevPost) && !utils.isComment(post);
} }
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;
// 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 === '';
// check if it's the last comment in a consecutive string of comments on the same post // check if it's the last comment in a consecutive string of comments on the same post
var isLastComment = false; // it is the last comment if it is last post in the channel or the next post has a different root post
if (utils.isComment(post)) { var isLastComment = utils.isComment(post) && (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
isLastComment = (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); 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( postCtls.push(
<div className="date-separator"> <div className="date-separator">
<hr className="separator__hr" /> <hr className="separator__hr" />
@@ -469,20 +441,10 @@ module.exports = React.createClass({
); );
} }
postCtls.push(postCtl); postCtls.push(postCtl);
previousPostDay = utils.getDateForUnixTicks(post.create_at); previousPostDay = currentPostDay;
} }
} } else {
else { postCtls.push(<LoadingScreen position="absolute" />);
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>
);
} }
return ( return (
@@ -497,5 +459,3 @@ module.exports = React.createClass({
); );
} }
}); });

Просмотреть файл

@@ -43,7 +43,7 @@ RhsHeaderPost = React.createClass({
}); });
}, },
render: function() { 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 ( return (
<div className="sidebar--right__header"> <div className="sidebar--right__header">
@@ -67,6 +67,8 @@ RootPost = React.createClass({
var message = utils.textToJsx(this.props.post.message); var message = utils.textToJsx(this.props.post.message);
var filenames = this.props.post.filenames; var filenames = this.props.post.filenames;
var isOwner = UserStore.getCurrentId() == this.props.post.user_id; 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"; var type = "Post";
if (this.props.post.root_id.length > 0) { if (this.props.post.root_id.length > 0) {
@@ -78,6 +80,10 @@ RootPost = React.createClass({
currentUserCss = "current--user"; currentUserCss = "current--user";
} }
if (channel) {
channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
}
if (filenames) { if (filenames) {
var postFiles = []; var postFiles = [];
var images = []; var images = [];
@@ -117,8 +123,9 @@ RootPost = React.createClass({
return ( return (
<div className={"post post--root " + currentUserCss}> <div className={"post post--root " + currentUserCss}>
<div className="post-right-channel__name">{ channelName }</div>
<div className="post-profile-img__container"> <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>
<div className="post__content"> <div className="post__content">
<ul className="post-header"> <ul className="post-header">
@@ -128,9 +135,7 @@ RootPost = React.createClass({
<div className="dropdown"> <div className="dropdown">
{ isOwner ? { isOwner ?
<div> <div>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false"> <a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
[...]
</a>
<ul className="dropdown-menu" role="menu"> <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="#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> <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 message = utils.textToJsx(this.props.post.message);
var timestamp = UserStore.getCurrentUser().update_at;
return ( return (
<div className={commentClass + " " + currentUserCss}> <div className={commentClass + " " + currentUserCss}>
<div className="post-profile-img__container"> <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>
<div className="post__content"> <div className="post__content">
<ul className="post-header"> <ul className="post-header">
@@ -240,9 +246,7 @@ CommentPost = React.createClass({
<li className="post-header-col post-header__reply"> <li className="post-header-col post-header__reply">
{ isOwner ? { isOwner ?
<div className="dropdown" onClick={function(e){$('.post-list-holder-by-time').scrollTop($(".post-list-holder-by-time").scrollTop() + 50);}}> <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 href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
[...]
</a>
<ul className="dropdown-menu" role="menu"> <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="#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> <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() { componentDidMount: function() {
PostStore.addSelectedPostChangeListener(this._onChange); PostStore.addSelectedPostChangeListener(this._onChange);
PostStore.addChangeListener(this._onChangeAll); PostStore.addChangeListener(this._onChangeAll);
UserStore.addStatusesChangeListener(this._onTimeChange);
this.resize(); this.resize();
var self = this; var self = this;
$(window).resize(function(){ $(window).resize(function(){
@@ -294,6 +299,7 @@ module.exports = React.createClass({
componentWillUnmount: function() { componentWillUnmount: function() {
PostStore.removeSelectedPostChangeListener(this._onChange); PostStore.removeSelectedPostChangeListener(this._onChange);
PostStore.removeChangeListener(this._onChangeAll); PostStore.removeChangeListener(this._onChangeAll);
UserStore.removeStatusesChangeListener(this._onTimeChange);
}, },
_onChange: function() { _onChange: function() {
if (this.isMounted()) { if (this.isMounted()) {
@@ -304,7 +310,6 @@ module.exports = React.createClass({
} }
}, },
_onChangeAll: function() { _onChangeAll: function() {
if (this.isMounted()) { if (this.isMounted()) {
// if something was changed in the channel like adding a // if something was changed in the channel like adding a
@@ -333,6 +338,12 @@ module.exports = React.createClass({
this.setState(getStateFromStores()); 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() { getInitialState: function() {
return getStateFromStores(); return getStateFromStores();
}, },
@@ -392,7 +403,7 @@ module.exports = React.createClass({
<RootPost post={root_post} commentCount={posts_array.length}/> <RootPost post={root_post} commentCount={posts_array.length}/>
<div className="post-right-comments-container"> <div className="post-right-comments-container">
{ posts_array.map(function(cpost) { { 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>
<div className="post-create__container"> <div className="post-create__container">

Просмотреть файл

@@ -6,6 +6,8 @@ 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 AsyncClient = require('../utils/async_client.jsx');
var ChannelStore = require('../stores/channel_store.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({ module.exports = React.createClass({
handleSubmit: function(e) { handleSubmit: function(e) {
@@ -60,12 +62,12 @@ module.exports = React.createClass({
return; return;
Client.updateChannel(channel, Client.updateChannel(channel,
function(data) { function(data, text, req) {
this.refs.display_name.getDOMNode().value = ""; this.refs.display_name.getDOMNode().value = "";
this.refs.channel_name.getDOMNode().value = ""; this.refs.channel_name.getDOMNode().value = "";
$('#' + this.props.modalId).modal('hide'); $('#' + 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); AsyncClient.getChannels(true);
}.bind(this), }.bind(this),
function(err) { function(err) {

Просмотреть файл

@@ -24,13 +24,13 @@ module.exports = React.createClass({
if (!valid) if (!valid)
return; return;
if (this.props.teamName === name) if (this.props.teamDisplayName === name)
return; return;
var data = {}; var data = {};
data["new_name"] = name; data["new_name"] = name;
Client.updateTeamName(data, Client.updateTeamDisplayName(data,
function(data) { function(data) {
$('#rename_team_link').modal('hide'); $('#rename_team_link').modal('hide');
window.location.reload(); window.location.reload();
@@ -47,11 +47,11 @@ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
var self = this; var self = this;
$(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) { $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) {
self.setState({ name: self.props.teamName }); self.setState({ name: self.props.teamDisplayName });
}); });
}, },
getInitialState: function() { getInitialState: function() {
return { name: this.props.teamName }; return { name: this.props.teamDisplayName };
}, },
render: function() { render: function() {

Просмотреть файл

@@ -3,6 +3,7 @@
var client = require('../utils/client.jsx'); var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var PostStore = require('../stores/post_store.jsx'); var PostStore = require('../stores/post_store.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
@@ -10,14 +11,14 @@ var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
function getSearchTermStateFromStores() { function getSearchTermStateFromStores() {
term = PostStore.getSearchTerm(); var term = PostStore.getSearchTerm() || '';
if (!term) term = "";
return { return {
search_term: term search_term: term
}; };
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'SearchBar',
componentDidMount: function() { componentDidMount: function() {
PostStore.addSearchTermChangeListener(this._onChange); PostStore.addSearchTermChangeListener(this._onChange);
}, },
@@ -58,14 +59,14 @@ module.exports = React.createClass({
e.target.select(); e.target.select();
}, },
performSearch: function(terms, isMentionSearch) { performSearch: function(terms, isMentionSearch) {
if (terms.length > 0) { if (terms.length) {
$("#search-spinner").removeClass("hidden"); this.setState({isSearching: true});
client.search( client.search(
terms, terms,
function(data) { function(data) {
$("#search-spinner").addClass("hidden"); this.setState({isSearching: false});
if(utils.isMobile()) { if (utils.isMobile()) {
$('#search')[0].value = ""; React.findDOMNode(this.refs.search).value = '';
} }
AppDispatcher.handleServerAction({ AppDispatcher.handleServerAction({
@@ -73,18 +74,17 @@ module.exports = React.createClass({
results: data, results: data,
is_mention_search: isMentionSearch is_mention_search: isMentionSearch
}); });
}, }.bind(this),
function(err) { function(err) {
$("#search-spinner").addClass("hidden"); this.setState({isSearching: false});
dispatchError(err, "search"); AsyncClient.dispatchError(err, "search");
} }.bind(this)
); );
} }
}, },
handleSubmit: function(e) { handleSubmit: function(e) {
e.preventDefault(); e.preventDefault();
terms = this.state.search_term.trim(); this.performSearch(this.state.search_term.trim());
this.performSearch(terms);
}, },
getInitialState: function() { getInitialState: function() {
return getSearchTermStateFromStores(); return getSearchTermStateFromStores();
@@ -92,11 +92,18 @@ module.exports = React.createClass({
render: function() { render: function() {
return ( return (
<div> <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> <span className="glyphicon glyphicon-search sidebar__search-icon"></span>
<form role="form" className="search__form relative-div" onSubmit={this.handleSubmit}> <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} /> <input
<span id="search-spinner" className="glyphicon glyphicon-refresh glyphicon-refresh-animate hidden"></span> 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> </form>
</div> </div>
); );

Просмотреть файл

@@ -8,12 +8,13 @@ var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' ); var UserProfile = require( './user_profile.jsx' );
var SearchBox =require('./search_bar.jsx'); var SearchBox =require('./search_bar.jsx');
var utils = require('../utils/utils.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 AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
RhsHeaderSearch = React.createClass({ var RhsHeaderSearch = React.createClass({
handleClose: function(e) { handleClose: function(e) {
e.preventDefault(); e.preventDefault();
@@ -32,13 +33,13 @@ RhsHeaderSearch = React.createClass({
return ( return (
<div className="sidebar--right__header"> <div className="sidebar--right__header">
<span className="sidebar--right__title">{title}</span> <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> </div>
); );
} }
}); });
SearchItem = React.createClass({ var SearchItem = React.createClass({
handleClick: function(e) { handleClick: function(e) {
e.preventDefault(); e.preventDefault();
@@ -62,39 +63,41 @@ SearchItem = React.createClass({
}); });
}, },
function(err) { function(err) {
dispatchError(err, "getPost"); AsyncClient.dispatchError(err, "getPost");
} }
); );
var postChannel = ChannelStore.get(this.props.post.channel_id); var postChannel = ChannelStore.get(this.props.post.channel_id);
var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : ""; var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : "";
utils.switchChannel(postChannel,teammate); utils.switchChannel(postChannel, teammate);
}, },
render: function() { render: function() {
var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch}); var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch});
var channelName = ""; 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) {
if (channel.type === 'D') { channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
channelName = "Private Message";
} else {
channelName = channel.display_name;
}
} }
return ( return (
<div className="search-item-container post" onClick={this.handleClick}> <div className="search-item-container post" onClick={this.handleClick}>
<div className="search-channel__name">{ channelName }</div> <div className="search-channel__name">{ channelName }</div>
<div className="post-profile-img__container"> <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>
<div className="post__content"> <div className="post__content">
<ul className="post-header"> <ul className="post-header">
<li className="post-header-col"><strong><UserProfile userId={this.props.post.user_id} /></strong></li> <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> </ul>
<div className="search-item-snippet"><span>{message}</span></div> <div className="search-item-snippet"><span>{message}</span></div>
</div> </div>
@@ -103,11 +106,13 @@ SearchItem = React.createClass({
} }
}); });
function getStateFromStores() { function getStateFromStores() {
return { results: PostStore.getSearchResults() }; return { results: PostStore.getSearchResults() };
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: 'SearchResults',
componentDidMount: function() { componentDidMount: function() {
PostStore.addSearchChangeListener(this._onChange); PostStore.addSearchChangeListener(this._onChange);
this.resize(); this.resize();
@@ -143,41 +148,24 @@ module.exports = React.createClass({
var results = this.state.results; var results = this.state.results;
var currentId = UserStore.getCurrentId(); var currentId = UserStore.getCurrentId();
var searchForm = currentId == null ? null : <SearchBox />; var searchForm = currentId ? <SearchBox /> : null;
var noResults = (!results || !results.order || !results.order.length);
var searchTerm = PostStore.getSearchTerm();
if (results == null) {
return (
<div className="sidebar--right__header">
<div className="sidebar__heading">Search Results</div>
</div>
);
}
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 ( return (
<div className="sidebar--right__content"> <div className="sidebar--right__content">
<div className="search-bar__container sidebar--right__search-header">{searchForm}</div> <div className="search-bar__container sidebar--right__search-header">{searchForm}</div>
<div className="sidebar-right__body"> <div className="sidebar-right__body">
<RhsHeaderSearch isMentionSearch={this.props.isMentionSearch} /> <RhsHeaderSearch isMentionSearch={this.props.isMentionSearch} />
<div id="search-items-container" className="search-items-container"> <div id="search-items-container" className="search-items-container">
{results.order.map(function(id) {
var post = results.posts[id]; { noResults ? <div className="sidebar--right__subheader">No results</div>
return <SearchItem key={post.id} post={post} term={PostStore.getSearchTerm()} isMentionSearch={self.props.isMentionSearch} /> : 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> </div>
</div> </div>

Просмотреть файл

@@ -6,6 +6,7 @@ var ChannelStore = require('../stores/channel_store.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var SocketStore = require('../stores/socket_store.jsx'); var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx'); var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx'); var SearchBox = require('./search_bar.jsx');
@@ -13,93 +14,6 @@ var SearchBox = require('./search_bar.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; 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() { function getStateFromStores() {
var members = ChannelStore.getAllMembers(); var members = ChannelStore.getAllMembers();
var team_member_map = UserStore.getActiveOnlyProfiles(); var team_member_map = UserStore.getActiveOnlyProfiles();
@@ -131,7 +45,7 @@ function getStateFromStores() {
var channel = ChannelStore.getByName(channelName); var channel = ChannelStore.getByName(channelName);
if (channel != null) { 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.teammate_username = teammate.username;
channel.status = UserStore.getStatus(teammate.id); channel.status = UserStore.getStatus(teammate.id);
@@ -150,7 +64,7 @@ function getStateFromStores() {
var tempChannel = {}; var tempChannel = {};
tempChannel.fake = true; tempChannel.fake = true;
tempChannel.name = channelName; 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.status = UserStore.getStatus(teammate.id);
tempChannel.last_post_at = 0; tempChannel.last_post_at = 0;
readDirectChannels.push(tempChannel); readDirectChannels.push(tempChannel);
@@ -192,7 +106,7 @@ function getStateFromStores() {
}; };
} }
var SidebarLoggedIn = React.createClass({ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange);
UserStore.addChangeListener(this._onChange); UserStore.addChangeListener(this._onChange);
@@ -249,11 +163,27 @@ var SidebarLoggedIn = React.createClass({
var repRegex = new RegExp("<br>", "g"); var repRegex = new RegExp("<br>", "g");
var post = JSON.parse(msg.props.post); 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) { if (msg.length > 50) {
msg = msg.substring(0,49) + "..."; 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") { if (!user.notify_props || user.notify_props.desktop_sound === "true") {
utils.ding(); utils.ding();
} }
@@ -367,7 +297,7 @@ var SidebarLoggedIn = React.createClass({
); );
} else { } else {
return ( 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 ( return (
<div> <div>
<SidebarHeader teamName={this.props.teamName} teamType={this.props.teamType} /> <SidebarHeader teamDisplayName={this.props.teamDisplayName} teamType={this.props.teamType} />
<SearchBox /> <SearchBox />
<div className="nav-pills__container"> <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} />;
}
}
});

Просмотреть файл

@@ -37,17 +37,13 @@ var NavbarDropdown = React.createClass({
var invite_link = ""; var invite_link = "";
var manage_link = ""; var manage_link = "";
var rename_link = ""; var rename_link = "";
var currentUser = UserStore.getCurrentUser() var currentUser = UserStore.getCurrentUser();
var isAdmin = false; var isAdmin = false;
if (currentUser != null) { if (currentUser != null) {
isAdmin = currentUser.roles.indexOf("admin") > -1; isAdmin = currentUser.roles.indexOf("admin") > -1;
invite_link = ( invite_link = ( <li> <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a> </li>);
<li>
<a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a>
</li>
);
if (this.props.teamType == "O") { if (this.props.teamType == "O") {
team_link = ( team_link = (
@@ -59,43 +55,31 @@ var NavbarDropdown = React.createClass({
} }
if (isAdmin) { if (isAdmin) {
manage_link = ( manage_link = ( <li> <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a> </li>);
<li> rename_link = ( <li> <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a> </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 = []; var teams = [];
teams.push(<li className="divider" key="div"></li>);
if (this.state.teams.length > 1) { if (this.state.teams.length > 1) {
for (var i = 0; i < this.state.teams.length; i++) { for (var i = 0; i < this.state.teams.length; i++) {
var domain = this.state.teams[i]; var teamName = this.state.teams[i];
if (domain == utils.getSubDomain()) teams.push(<li key={ teamName }><a href={window.location.origin + "/" + teamName }>Switch to { teamName }</a></li>);
continue;
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><a href={window.location.origin + "/signup_team" }>Create a New Team</a></li>);
return ( return (
<ul className="nav navbar-nav navbar-right"> <ul className="nav navbar-nav navbar-right">
<li className="dropdown"> <li className="dropdown">
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> <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> </a>
<ul className="dropdown-menu" role="menu"> <ul className="dropdown-menu" role="menu">
<li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li> <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 } { invite_link }
{ team_link } { team_link }
{ manage_link } { manage_link }
@@ -113,23 +97,32 @@ var NavbarDropdown = React.createClass({
}); });
module.exports = React.createClass({ module.exports = React.createClass({
handleSubmit: function(e) { displayName: 'SidebarHeader',
e.preventDefault();
}, getDefaultProps: function() {
getInitialState: function() { return {
return { }; teamName: config.SiteName
};
}, },
render: function() { 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 ( return (
<div className="team__header theme"> <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} /> <NavbarDropdown teamType={this.props.teamType} />
</div> </div>
); );
} }
}); });

Просмотреть файл

@@ -49,12 +49,12 @@ module.exports = React.createClass({
} }
var siteName = config.SiteName != null ? config.SiteName : ""; 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 ( return (
<div> <div>
<div className="team__header theme"> <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>
<div className="nav-pills__container"> <div className="nav-pills__container">

Просмотреть файл

@@ -20,8 +20,8 @@ module.exports = React.createClass({
state.email_error = ""; state.email_error = "";
} }
team.name = this.refs.name.getDOMNode().value.trim(); team.display_name = this.refs.name.getDOMNode().value.trim();
if (!team.name) { if (!team.display_name) {
state.name_error = "This field is required"; state.name_error = "This field is required";
state.inValid = true; state.inValid = true;
} }
@@ -34,7 +34,7 @@ module.exports = React.createClass({
return; return;
} }
client.signupTeam(team.email, team.name, client.signupTeam(team.email, team.display_name,
function(data) { function(data) {
if (data["follow_link"]) { if (data["follow_link"]) {
window.location.href = data["follow_link"]; window.location.href = data["follow_link"];
@@ -61,7 +61,7 @@ module.exports = React.createClass({
return ( return (
<form role="form" onSubmit={this.handleSubmit}> <form role="form" onSubmit={this.handleSubmit}>
<div className={ email_error ? "form-group has-error" : "form-group" }> <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 } { email_error }
</div> </div>
<div className={ name_error ? "form-group has-error" : "form-group" }> <div className={ name_error ? "form-group has-error" : "form-group" }>
@@ -70,6 +70,9 @@ module.exports = React.createClass({
</div> </div>
{ server_error } { server_error }
<button className="btn btn-md btn-primary" type="submit">Sign up for Free</button> <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> </form>
); );
} }

Просмотреть файл

@@ -15,7 +15,7 @@ WelcomePage = React.createClass({
return; return;
} }
e.preventDefault(); e.preventDefault();
this.props.state.wizard = "team_name"; this.props.state.wizard = "team_display_name";
this.props.updateParent(this.props.state); this.props.updateParent(this.props.state);
}, },
handleDiffEmail: function (e) { handleDiffEmail: function (e) {
@@ -57,6 +57,17 @@ WelcomePage = React.createClass({
getInitialState: function() { getInitialState: function() {
return { use_diff: false }; 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() { render: function() {
client.track('signup', 'signup_team_01_welcome'); client.track('signup', 'signup_team_01_welcome');
@@ -77,7 +88,7 @@ WelcomePage = React.createClass({
<span className="black">{ this.props.state.team.email }</span><br /> <span className="black">{ this.props.state.team.email }</span><br />
</p> </p>
<div className="form-group"> <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 } { storage_error }
</div> </div>
<hr /> <hr />
@@ -92,15 +103,15 @@ WelcomePage = React.createClass({
{ email_error } { email_error }
</div> </div>
{ server_error } { 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> </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> </div>
); );
} }
}); });
TeamNamePage = React.createClass({ TeamDisplayNamePage = React.createClass({
submitBack: function (e) { submitBack: function (e) {
e.preventDefault(); e.preventDefault();
this.props.state.wizard = "welcome"; this.props.state.wizard = "welcome";
@@ -109,19 +120,24 @@ TeamNamePage = React.createClass({
submitNext: function (e) { submitNext: function (e) {
e.preventDefault(); e.preventDefault();
var name = this.refs.name.getDOMNode().value.trim(); var display_name = this.refs.name.getDOMNode().value.trim();
if (!name) { if (!display_name) {
this.setState({name_error: "This field is required"}); this.setState({name_error: "This field is required"});
return; return;
} }
this.props.state.wizard = "team_url"; 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); this.props.updateParent(this.props.state);
}, },
getInitialState: function() { getInitialState: function() {
return { }; return { };
}, },
handleFocus: function(e) {
e.preventDefault();
e.currentTarget.select();
},
render: function() { render: function() {
client.track('signup', 'signup_team_02_name'); client.track('signup', 'signup_team_02_name');
@@ -130,29 +146,31 @@ TeamNamePage = React.createClass({
return ( return (
<div> <div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>{utils.toTitleCase(strings.Team) + " Name"}</h2> <h2>{utils.toTitleCase(strings.Team) + " Name"}</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }> <div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row"> <div className="row">
<div className="col-sm-9"> <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>
</div> </div>
{ name_error } { name_error }
</div> </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> <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 type="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="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div> </div>
); );
} }
}); });
TeamUrlPage = React.createClass({ TeamURLPage = React.createClass({
submitBack: function (e) { submitBack: function (e) {
e.preventDefault(); e.preventDefault();
this.props.state.wizard = "team_name"; this.props.state.wizard = "team_display_name";
this.props.updateParent(this.props.state); this.props.updateParent(this.props.state);
}, },
submitNext: function (e) { submitNext: function (e) {
@@ -172,18 +190,18 @@ TeamUrlPage = React.createClass({
return; return;
} }
else if (cleaned_name.length <= 3 || cleaned_name.length > 15) { 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; return;
} }
for (var index = 0; index < constants.RESERVED_DOMAINS.length; index++) { for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) {
if (cleaned_name.indexOf(constants.RESERVED_DOMAINS[index]) == 0) { if (cleaned_name.indexOf(constants.RESERVED_TEAM_NAMES[index]) == 0) {
this.setState({name_error: "This Team URL name is unavailable"}) this.setState({name_error: "This team name is unavailable"})
return; return;
} }
} }
client.findTeamByDomain(name, client.findTeamByName(name,
function(data) { function(data) {
if (!data) { if (!data) {
if (config.AllowSignupDomainsWizard) { if (config.AllowSignupDomainsWizard) {
@@ -193,7 +211,7 @@ TeamUrlPage = React.createClass({
this.props.state.team.type = 'O'; this.props.state.team.type = 'O';
} }
this.props.state.team.domain = name; this.props.state.team.name = name;
this.props.updateParent(this.props.state); this.props.updateParent(this.props.state);
} }
else { else {
@@ -210,6 +228,11 @@ TeamUrlPage = React.createClass({
getInitialState: function() { getInitialState: function() {
return { }; return { };
}, },
handleFocus: function(e) {
e.preventDefault();
e.currentTarget.select();
},
render: function() { render: function() {
client.track('signup', 'signup_team_03_url'); client.track('signup', 'signup_team_03_url');
@@ -218,14 +241,15 @@ TeamUrlPage = React.createClass({
return ( return (
<div> <div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>{utils.toTitleCase(strings.Team) + " URL"}</h2> <h2>{utils.toTitleCase(strings.Team) + " URL"}</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }> <div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row"> <div className="row">
<div className="col-sm-9"> <div className="col-sm-9">
<div className="input-group"> <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">{ window.location.origin + "/" }</span>
<span className="input-group-addon">.{ utils.getDomainWithOutSub() }</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> </div>
</div> </div>
@@ -233,8 +257,9 @@ TeamUrlPage = React.createClass({
</div> </div>
<p className="black">{"Pick something short and memorable for your " + strings.Team + "'s web address."}</p> <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> <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 type="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="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div> </div>
); );
} }
@@ -291,6 +316,7 @@ AllowedDomainsPage = React.createClass({
return ( return (
<div> <div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Email Domain</h2> <h2>Email Domain</h2>
<p> <p>
@@ -303,7 +329,7 @@ AllowedDomainsPage = React.createClass({
<div className="col-sm-9"> <div className="col-sm-9">
<div className="input-group"> <div className="input-group">
<span className="input-group-addon">@</span> <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> </div>
</div> </div>
@@ -313,8 +339,9 @@ AllowedDomainsPage = React.createClass({
<p> <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> <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> </p>
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp; <button type="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="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div> </div>
); );
} }
@@ -356,7 +383,7 @@ EmailItem = React.createClass({
return ( return (
<div className={ email_error ? "form-group has-error" : "form-group" }> <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 } { email_error }
</div> </div>
); );
@@ -424,16 +451,22 @@ SendInivtesPage = React.createClass({
var emails = []; var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) { 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 ( return (
<div> <div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Send Invitations</h2> <h2>Send Invitations</h2>
{ emails } { emails }
<div className="form-group"><button className="btn-default btn" onClick={this.submitAddInvite}>Add Invitation</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 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 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> <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><a href="#" onClick={this.submitSkip}>Skip this step</a></div>
</div> </div>
@@ -477,20 +510,22 @@ UsernamePage = React.createClass({
return ( return (
<div> <div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a username</h2> <h2>Choose a username</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }> <div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row"> <div className="row">
<div className="col-sm-9"> <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>
</div> </div>
{ name_error } { name_error }
</div> </div>
<p>{"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others."}</p> <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> <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 type="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="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div> </div>
); );
} }
@@ -531,18 +566,11 @@ PasswordPage = React.createClass({
props.state.wizard = "finished"; props.state.wizard = "finished";
props.updateParent(props.state, true); props.updateParent(props.state, true);
if (utils.isTestDomain()) { window.location.href = window.location.origin + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email);
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);
}
// client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password,
// function(data) { // function(data) {
// UserStore.setLastDomain(teamSignup.team.domain); // TeamStore.setLastName(teamSignup.team.domain);
// UserStore.setLastEmail(teamSignup.team.email); // UserStore.setLastEmail(teamSignup.team.email);
// UserStore.setCurrentUser(data); // UserStore.setCurrentUser(data);
// window.location.href = '/channels/town-square'; // window.location.href = '/channels/town-square';
@@ -570,13 +598,14 @@ PasswordPage = React.createClass({
return ( return (
<div> <div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" /> <img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a password</h2> <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> <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={ name_error ? "form-group has-error" : "form-group" }>
<div className="row"> <div className="row">
<div className="col-sm-9"> <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>
</div> </div>
{ name_error } { 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> <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>
<div className="form-group"> <div className="form-group">
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp; <button type="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="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> </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> <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> </div>
); );
} }
@@ -596,28 +626,23 @@ PasswordPage = React.createClass({
module.exports = React.createClass({ module.exports = React.createClass({
updateParent: function(state, skipSet) { updateParent: function(state, skipSet) {
BrowserStore.setGlobalItem(this.props.hash, JSON.stringify(state)); BrowserStore.setGlobalItem(this.props.hash, state);
if (!skipSet) { if (!skipSet) {
this.setState(state); this.setState(state);
} }
}, },
getInitialState: function() { getInitialState: function() {
var props = null; var props = BrowserStore.getGlobalItem(this.props.hash);
try {
props = JSON.parse(BrowserStore.getGlobalItem(this.props.hash));
}
catch(parse_error) {
}
if (!props) { if (!props) {
props = {}; props = {};
props.wizard = "welcome"; props.wizard = "welcome";
props.team = {}; props.team = {};
props.team.email = this.props.email; 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.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.team.allowed_domains = "";
props.invites = []; props.invites = [];
props.invites.push(""); props.invites.push("");
@@ -628,19 +653,19 @@ module.exports = React.createClass({
props.data = this.props.data; props.data = this.props.data;
} }
return props ; return props;
}, },
render: function() { render: function() {
if (this.state.wizard == "welcome") { if (this.state.wizard == "welcome") {
return <WelcomePage state={this.state} updateParent={this.updateParent} /> return <WelcomePage state={this.state} updateParent={this.updateParent} />
} }
if (this.state.wizard == "team_name") { if (this.state.wizard == "team_display_name") {
return <TeamNamePage state={this.state} updateParent={this.updateParent} /> return <TeamDisplayNamePage state={this.state} updateParent={this.updateParent} />
} }
if (this.state.wizard == "team_url") { 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") { if (this.state.wizard == "allowed_domains") {

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше