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

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

@@ -41,17 +41,23 @@ build:
install:
@go get $(GOFLAGS) github.com/tools/godep
@if [ $(shell docker ps | grep -ci mattermost-mysql) -eq 0 ]; then \
@if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 0 ]; then \
echo restoring go libs using godep; \
$(GOPATH)/bin/godep restore; \
echo starting mattermost-mysql; \
docker run --name mattermost-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mostest \
-e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql > /dev/null; \
elif [ $(shell docker ps | grep -ci mattermost-mysql) -eq 0 ]; then \
echo restarting mattermost-mysql; \
docker start mattermost-mysql > /dev/null; \
fi
@if [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \
@if [ $(shell docker ps -a | grep -ci mattermost-redis) -eq 0 ]; then \
echo starting mattermost-redis; \
docker run --name mattermost-redis -p 6379:6379 -d redis > /dev/null; \
elif [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \
echo restarting mattermost-redis; \
docker start mattermost-redis > /dev/null; \
fi
@cd web/react/ && npm install

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

@@ -108,26 +108,10 @@ AWS Elastic Beanstalk Setup (Docker)
11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next.
12. Environment tags my be left blank. Press next.
13. You will be asked to review your information. Press Launch.
14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later.
2. Map a wildcard domain to the new elastic beanstalk application
15. From the AWS console select route 53
16. From the sidebar select Hosted Zones
17. Select the domain you want to use or create a new one.
18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 1.13.
19. Save the record set
3. Set the environment variable "MATTERMOST\_DOMAIN" to the domain you mapped above (example.com not www.example.com)
20. Return the Elastic Beanstalk from the AWS console.
21. Select the environment you created.
22. Select configuration from the sidebar.
23. Click the gear beside software configuration.
24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com.
25. Select apply.
4. Try it out!
26. Return to the dashboard on the sidebar and wait for beanstalk update the environment.
27. Try it out by entering the domain you mapped into your browser.
14. Wait for beanstalk to update the environment.
15. Try it out by entering the domain of the form \*.elasticbeanstalk.com found at the top of the dashboard into your browser. You can also map your own domain if you wish.
Contributing
------------
@@ -137,8 +121,5 @@ To contribute to this open source project please review the Mattermost Contribut
License
-------
Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details.
As an exception, Admin Tools and Configuration Files are are made available under the terms of the Apache License, version 2.0. See LICENSE.txt for more information.
Mattermost is licensed under an "Apache-wrapped AGPL" model, which means you can run and link to the system using Configuration Files and Admin Tools licensed under Apache, version 2.0, as described in the LICENSE file, as an explicit exception to the terms of the GNU Affero General Public License (AGPL) that applies to most of the remaining source files. See individual files for details.

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

@@ -16,10 +16,10 @@ var ServerTemplates *template.Template
type ServerTemplatePage Page
func NewServerTemplatePage(templateName, teamUrl string) *ServerTemplatePage {
func NewServerTemplatePage(templateName, teamURL string) *ServerTemplatePage {
props := make(map[string]string)
props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl
return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamUrl: teamUrl, Props: props}
return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamURL: teamURL, Props: props}
}
func (me *ServerTemplatePage) Render() string {

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

@@ -24,12 +24,12 @@ func Setup() {
func SetupBenchmark() (*model.Team, *model.User, *model.Channel) {
Setup()
team := &model.Team{Name: "Benchmark Team", Domain: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Benchmark Team", Name: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", FullName: "Mr. Benchmarker", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", Nickname: "Mr. Benchmarker", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel := &model.Channel{DisplayName: "Benchmark Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)

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

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

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

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

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

@@ -33,15 +33,15 @@ func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator {
// Basic test team and user so you always know one
func CreateBasicUser(client *model.Client) *model.AppError {
result, _ := client.FindTeamByDomain(BTEST_TEAM_DOMAIN_NAME, true)
result, _ := client.FindTeamByName(BTEST_TEAM_NAME, true)
if result.Data.(bool) == false {
newteam := &model.Team{Name: BTEST_TEAM_NAME, Domain: BTEST_TEAM_DOMAIN_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE}
newteam := &model.Team{DisplayName: BTEST_TEAM_DISPLAY_NAME, Name: BTEST_TEAM_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE}
result, err := client.CreateTeam(newteam)
if err != nil {
return err
}
basicteam := result.Data.(*model.Team)
newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, FullName: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD}
newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD}
result, err = client.CreateUser(newuser, "")
if err != nil {
return err
@@ -65,7 +65,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) {
user := &model.User{
TeamId: cfg.teamID,
Email: userEmail,
FullName: userName,
Nickname: userName,
Password: USER_PASSWORD}
result, err := cfg.client.CreateUser(user, "")

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

@@ -8,7 +8,6 @@ import (
"fmt"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"net/http"
"strings"
)
@@ -542,7 +541,7 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED)
message.Add("channel_id", id)
store.PublishAndForget(message)
PublishAndForget(message)
result := make(map[string]string)
result["id"] = id
@@ -657,7 +656,7 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED)
store.PublishAndForget(message)
PublishAndForget(message)
<-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id)
w.Write([]byte(cm.ToJson()))

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

@@ -138,10 +138,10 @@ func BenchmarkJoinChannel(b *testing.B) {
}
// Secondary test user to join channels created by primary test user
user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", FullName: "That Guy", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", Nickname: "That Guy", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
// Benchmark Start
b.ResetTimer()

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

@@ -14,17 +14,17 @@ import (
func TestCreateChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel := model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
rchannel, err := Client.CreateChannel(&channel)
@@ -94,18 +94,18 @@ func TestCreateChannel(t *testing.T) {
func TestCreateDirectChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
data := make(map[string]string)
data["user_id"] = user2.Id
@@ -149,23 +149,23 @@ func TestCreateDirectChannel(t *testing.T) {
func TestUpdateChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id))
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id))
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
userStd.Roles = ""
Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -184,7 +184,7 @@ func TestUpdateChannel(t *testing.T) {
t.Fatal("Channel admin failed to skip displayName")
}
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
desc = "b" + model.NewId() + "b"
upChannel1 = &model.Channel{Id: channel1.Id, Description: desc}
@@ -210,7 +210,7 @@ func TestUpdateChannel(t *testing.T) {
}
}
Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
Client.LoginByEmail(team.Name, userStd.Email, "pwd")
if _, err := Client.UpdateChannel(upChannel1); err == nil {
t.Fatal("Standard User should have failed to update")
@@ -220,14 +220,14 @@ func TestUpdateChannel(t *testing.T) {
func TestUpdateChannelDesc(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -266,11 +266,11 @@ func TestUpdateChannelDesc(t *testing.T) {
t.Fatal("should have errored on bad channel desc")
}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
data["channel_id"] = channel1.Id
data["channel_description"] = "new desc"
@@ -282,14 +282,14 @@ func TestUpdateChannelDesc(t *testing.T) {
func TestGetChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -324,14 +324,14 @@ func TestGetChannel(t *testing.T) {
func TestGetMoreChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -339,11 +339,11 @@ func TestGetMoreChannel(t *testing.T) {
channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
rget := Client.Must(Client.GetMoreChannels(""))
data := rget.Data.(*model.ChannelList)
@@ -368,14 +368,14 @@ func TestGetMoreChannel(t *testing.T) {
func TestJoinChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -383,11 +383,11 @@ func TestJoinChannel(t *testing.T) {
channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
Client.Must(Client.JoinChannel(channel1.Id))
@@ -399,10 +399,10 @@ func TestJoinChannel(t *testing.T) {
data["user_id"] = user1.Id
rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
Client.LoginByEmail(team.Domain, user3.Email, "pwd")
Client.LoginByEmail(team.Name, user3.Email, "pwd")
if _, err := Client.JoinChannel(rchannel.Id); err == nil {
t.Fatal("shoudn't be able to join direct channel")
@@ -412,14 +412,14 @@ func TestJoinChannel(t *testing.T) {
func TestLeaveChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -427,11 +427,11 @@ func TestLeaveChannel(t *testing.T) {
channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id}
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
Client.Must(Client.JoinChannel(channel1.Id))
@@ -461,25 +461,25 @@ func TestLeaveChannel(t *testing.T) {
func TestDeleteChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id))
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id))
Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd")
channelMadeByCA := &model.Channel{DisplayName: "C Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -500,11 +500,11 @@ func TestDeleteChannel(t *testing.T) {
t.Fatal("should have failed to post to deleted channel")
}
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
Client.LoginByEmail(team.Name, userStd.Email, "pwd")
if _, err := Client.JoinChannel(channel1.Id); err == nil {
t.Fatal("should have failed to join deleted channel")
@@ -531,14 +531,14 @@ func TestDeleteChannel(t *testing.T) {
func TestGetChannelExtraInfo(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -552,19 +552,19 @@ func TestGetChannelExtraInfo(t *testing.T) {
func TestAddChannelMember(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
@@ -591,13 +591,13 @@ func TestAddChannelMember(t *testing.T) {
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil {
t.Fatal("Should have errored, user not in channel")
}
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
Client.Must(Client.DeleteChannel(channel2.Id))
@@ -610,30 +610,30 @@ func TestAddChannelMember(t *testing.T) {
func TestRemoveChannelMember(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"}
userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id))
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id))
Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd")
channelMadeByCA := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel)
Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id))
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userStd.Id))
@@ -660,13 +660,13 @@ func TestRemoveChannelMember(t *testing.T) {
channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
Client.LoginByEmail(team.Domain, userStd.Email, "pwd")
Client.LoginByEmail(team.Name, userStd.Email, "pwd")
if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil {
t.Fatal("Should have errored, user not channel admin")
}
Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd")
Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd")
Client.Must(Client.AddChannelMember(channel2.Id, userStd.Id))
Client.Must(Client.DeleteChannel(channel2.Id))
@@ -680,14 +680,14 @@ func TestRemoveChannelMember(t *testing.T) {
func TestUpdateNotifyLevel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -746,10 +746,10 @@ func TestUpdateNotifyLevel(t *testing.T) {
t.Fatal("Should have errored - bad notify level")
}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
data["channel_id"] = channel1.Id
data["user_id"] = user2.Id
@@ -762,14 +762,14 @@ func TestUpdateNotifyLevel(t *testing.T) {
func TestFuzzyChannel(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
// Strings that should pass as acceptable channel names
var fuzzyStringsPass = []string{

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

@@ -27,8 +27,6 @@ var commands = []commandHandler{
func InitCommand(r *mux.Router) {
l4g.Debug("Initializing command api routes")
r.Handle("/command", ApiUserRequired(command)).Methods("POST")
hub.Start()
}
func command(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -323,14 +321,14 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
}
}
client := model.NewClient(c.TeamUrl + "/api/v1")
client := model.NewClient(c.GetSiteURL() + "/api/v1")
if doTeams {
if err := CreateBasicUser(client); err != nil {
l4g.Error("Failed to create testing enviroment")
return true
}
client.LoginByEmail(BTEST_TEAM_DOMAIN_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
enviroment, err := CreateTestEnviromentWithTeams(
client,
utils.Range{numTeams, numTeams},
@@ -344,7 +342,7 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
} else {
l4g.Info("Testing enviroment created")
for i := 0; i < len(enviroment.Teams); i++ {
l4g.Info("Team Created: " + enviroment.Teams[i].Domain)
l4g.Info("Team Created: " + enviroment.Teams[i].Name)
l4g.Info("\t User to login: " + enviroment.Enviroments[i].Users[0].Email + ", " + USER_PASSWORD)
}
}
@@ -383,7 +381,7 @@ func loadTestUsersCommand(c *Context, command *model.Command) bool {
if err == false {
usersr = utils.Range{10, 15}
}
client := model.NewClient(c.TeamUrl + "/api/v1")
client := model.NewClient(c.GetSiteURL() + "/api/v1")
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
userCreator.Fuzzy = doFuzz
userCreator.CreateTestUsers(usersr)
@@ -413,7 +411,7 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool {
if err == false {
channelsr = utils.Range{20, 30}
}
client := model.NewClient(c.TeamUrl + "/api/v1")
client := model.NewClient(c.GetSiteURL() + "/api/v1")
client.MockSession(c.Session.Id)
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
channelCreator.Fuzzy = doFuzz
@@ -465,7 +463,7 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool {
}
}
client := model.NewClient(c.TeamUrl + "/api/v1")
client := model.NewClient(c.GetSiteURL() + "/api/v1")
client.MockSession(c.Session.Id)
testPoster := NewAutoPostCreator(client, command.ChannelId)
testPoster.Fuzzy = doFuzz

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

@@ -12,14 +12,14 @@ import (
func TestSuggestRootCommands(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
if _, err := Client.Command("", "", true); err == nil {
t.Fatal("Should fail")
@@ -55,14 +55,14 @@ func TestSuggestRootCommands(t *testing.T) {
func TestLogoutCommands(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command)
if rs1.GotoLocation != "/logout" {
@@ -73,14 +73,14 @@ func TestLogoutCommands(t *testing.T) {
func TestJoinCommands(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -90,7 +90,7 @@ func TestJoinCommands(t *testing.T) {
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
Client.Must(Client.LeaveChannel(channel2.Id))
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))

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

@@ -17,12 +17,14 @@ import (
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
type Context struct {
Session model.Session
RequestId string
IpAddress string
TeamUrl string
Path string
Err *model.AppError
Session model.Session
RequestId string
IpAddress string
Path string
Err *model.AppError
teamURLValid bool
teamURL string
siteURL string
}
type Page struct {
@@ -30,32 +32,36 @@ type Page struct {
Title string
SiteName string
FeedbackEmail string
TeamUrl string
TeamURL string
Props map[string]string
}
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
return &handler{h, false, false, true, false}
return &handler{h, false, false, true, false, false}
}
func AppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
return &handler{h, false, false, false, false}
return &handler{h, false, false, false, false, false}
}
func AppHandlerIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
return &handler{h, false, false, false, false, true}
}
func ApiUserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
return &handler{h, true, false, true, true}
return &handler{h, true, false, true, true, false}
}
func ApiUserRequiredActivity(h func(*Context, http.ResponseWriter, *http.Request), isUserActivity bool) http.Handler {
return &handler{h, true, false, true, isUserActivity}
return &handler{h, true, false, true, isUserActivity, false}
}
func UserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
return &handler{h, true, false, false, false}
return &handler{h, true, false, false, false, false}
}
func ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
return &handler{h, true, true, true, false}
return &handler{h, true, true, true, false, false}
}
type handler struct {
@@ -64,6 +70,7 @@ type handler struct {
requireSystemAdmin bool
isApi bool
isUserActivity bool
isTeamIndependent bool
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -73,7 +80,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := &Context{}
c.RequestId = model.NewId()
c.IpAddress = GetIpAddress(r)
c.Path = r.URL.Path
protocol := "http"
@@ -90,7 +96,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
c.TeamUrl = protocol + "://" + r.Host
c.setSiteURL(protocol + "://" + r.Host)
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version)
@@ -135,6 +141,15 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
if h.isApi || h.isTeamIndependent {
c.setTeamURL(c.GetSiteURL(), false)
c.Path = r.URL.Path
} else {
splitURL := strings.Split(r.URL.Path, "/")
c.setTeamURL(protocol+"://"+r.Host+"/"+splitURL[1], true)
c.Path = "/" + strings.Join(splitURL[2:], "/")
}
if c.Err == nil && h.requireUser {
c.UserRequired()
}
@@ -165,7 +180,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(c.Err.ToJson()))
} else {
if c.Err.StatusCode == http.StatusUnauthorized {
http.Redirect(w, r, "/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
} else {
RenderWebError(c.Err, w, r)
}
@@ -265,6 +280,16 @@ func (c *Context) IsSystemAdmin() bool {
return false
}
func (c *Context) IsTeamAdmin(userId string) bool {
if uresult := <-Srv.Store.User().Get(userId); uresult.Err != nil {
c.Err = uresult.Err
return false
} else {
user := uresult.Data.(*model.User)
return strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId
}
}
func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
sessionCache.Remove(c.Session.Id)
@@ -289,6 +314,39 @@ func (c *Context) SetUnknownError(where string, details string) {
c.Err = model.NewAppError(where, "An unknown error has occured. Please contact support.", details)
}
func (c *Context) setTeamURL(url string, valid bool) {
c.teamURL = url
c.teamURLValid = valid
}
func (c *Context) setTeamURLFromSession() {
if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil {
c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true)
}
}
func (c *Context) setSiteURL(url string) {
c.siteURL = url
}
func (c *Context) GetTeamURLFromTeam(team *model.Team) string {
return c.GetSiteURL() + "/" + team.Name
}
func (c *Context) GetTeamURL() string {
if !c.teamURLValid {
c.setTeamURLFromSession()
if !c.teamURLValid {
l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent")
}
}
return c.teamURL
}
func (c *Context) GetSiteURL() string {
return c.siteURL
}
func GetIpAddress(r *http.Request) string {
address := r.Header.Get(model.HEADER_FORWARDED)
if len(address) == 0 {

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

@@ -13,9 +13,9 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/nfnt/resize"
_ "golang.org/x/image/bmp"
"image"
_ "image/gif"
_ "golang.org/x/image/bmp"
"image/jpeg"
"io"
"net/http"
@@ -115,7 +115,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
fileUrl := c.TeamUrl + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename)
fileUrl := c.GetSiteURL() + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename)
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
}
@@ -363,7 +363,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.TeamUrl, getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)
url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)
if !c.HasPermissionsToChannel(cchan, "getPublicLink") {
return

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

@@ -24,14 +24,14 @@ import (
func TestUploadFile(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -111,14 +111,14 @@ func TestUploadFile(t *testing.T) {
func TestGetFile(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -173,10 +173,10 @@ func TestGetFile(t *testing.T) {
t.Fatal("file get failed")
}
team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
@@ -187,7 +187,7 @@ func TestGetFile(t *testing.T) {
data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))
Client.LoginByEmail(team2.Domain, user2.Email, "pwd")
Client.LoginByEmail(team2.Name, user2.Email, "pwd")
if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr != nil {
t.Fatal(downErr)
@@ -258,18 +258,18 @@ func TestGetFile(t *testing.T) {
func TestGetPublicLink(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -344,7 +344,7 @@ func TestGetPublicLink(t *testing.T) {
t.Fatal("Should have errored - bad file path")
}
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
data["filename"] = filenames[0]
if _, err := Client.GetPublicLink(data); err == nil {
t.Fatal("should have errored, user not member of channel")

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

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

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

@@ -15,21 +15,21 @@ import (
func TestCreatePost(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -96,18 +96,18 @@ func TestCreatePost(t *testing.T) {
t.Fatal("Should have been forbidden")
}
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
_, err = Client.CreatePost(post7)
if err.StatusCode != http.StatusForbidden {
t.Fatal("Should have been forbidden")
}
user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id))
Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
Client.LoginByEmail(team2.Name, user3.Email, "pwd")
channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
@@ -126,21 +126,21 @@ func TestCreatePost(t *testing.T) {
func TestCreateValetPost(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -181,18 +181,18 @@ func TestCreateValetPost(t *testing.T) {
t.Fatal("Should have been forbidden")
}
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
_, err = Client.CreateValetPost(post5)
if err != nil {
t.Fatal(err)
}
user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user3.Id))
Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
Client.LoginByEmail(team2.Name, user3.Email, "pwd")
channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
@@ -218,21 +218,21 @@ func TestCreateValetPost(t *testing.T) {
func TestUpdatePost(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -289,14 +289,14 @@ func TestUpdatePost(t *testing.T) {
func TestGetPosts(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -354,14 +354,14 @@ func TestGetPosts(t *testing.T) {
func TestSearchPosts(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -400,14 +400,14 @@ func TestSearchPosts(t *testing.T) {
func TestSearchHashtagPosts(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -431,14 +431,14 @@ func TestSearchHashtagPosts(t *testing.T) {
func TestGetPostsCache(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -480,14 +480,18 @@ func TestGetPostsCache(t *testing.T) {
func TestDeletePosts(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
userAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"}
userAdmin = Client.Must(Client.CreateUser(userAdmin, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(userAdmin.Id))
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -522,21 +526,29 @@ func TestDeletePosts(t *testing.T) {
r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList)
if len(r2.Posts) != 4 {
t.Fatal("should have returned 5 items")
t.Fatal("should have returned 4 items")
}
time.Sleep(10 * time.Millisecond)
post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
Client.LoginByEmail(team.Name, userAdmin.Email, "pwd")
Client.Must(Client.DeletePost(channel1.Id, post4.Id))
}
func TestEmailMention(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", FullName: "Bob Bobby", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", Nickname: "Bob Bobby", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -551,14 +563,14 @@ func TestEmailMention(t *testing.T) {
func TestFuzzyPosts(t *testing.T) {
Setup()
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
Client.LoginByEmail(team.Domain, user.Email, "pwd")
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)

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

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

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 (
l4g "code.google.com/p/log4go"
"fmt"
"github.com/awslabs/aws-sdk-go/aws"
"github.com/awslabs/aws-sdk-go/service/route53"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
@@ -24,11 +22,11 @@ func InitTeam(r *mux.Router) {
sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST")
sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST")
sr.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST")
sr.Handle("/find_team_by_domain", ApiAppHandler(findTeamByDomain)).Methods("POST")
sr.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST")
sr.Handle("/find_teams", ApiAppHandler(findTeams)).Methods("POST")
sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST")
sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST")
sr.Handle("/update_name", ApiUserRequired(updateTeamName)).Methods("POST")
sr.Handle("/update_name", ApiUserRequired(updateTeamDisplayName)).Methods("POST")
sr.Handle("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST")
sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET")
}
@@ -37,38 +35,38 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
m := model.MapFromJson(r.Body)
email := strings.ToLower(strings.TrimSpace(m["email"]))
name := strings.TrimSpace(m["name"])
displayName := strings.TrimSpace(m["display_name"])
if len(email) == 0 {
c.SetInvalidParam("signupTeam", "email")
return
}
if len(name) == 0 {
c.SetInvalidParam("signupTeam", "name")
if len(displayName) == 0 {
c.SetInvalidParam("signupTeam", "display_name")
return
}
subjectPage := NewServerTemplatePage("signup_team_subject", c.TeamUrl)
bodyPage := NewServerTemplatePage("signup_team_body", c.TeamUrl)
subjectPage := NewServerTemplatePage("signup_team_subject", c.GetSiteURL())
bodyPage := NewServerTemplatePage("signup_team_body", c.GetSiteURL())
bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink
props := make(map[string]string)
props["email"] = email
props["name"] = name
props["display_name"] = displayName
props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash))
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash))
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
c.Err = err
return
}
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV || utils.Cfg.EmailSettings.ByPassEmail {
m["follow_link"] = bodyPage.Props["Link"]
}
@@ -119,25 +117,16 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
found := FindTeamByDomain(c, teamSignup.Team.Domain, "true")
found := FindTeamByName(c, teamSignup.Team.Name, "true")
if c.Err != nil {
return
}
if found {
c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Domain)
c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Name)
return
}
if IsBetaDomain(r) {
for key, value := range utils.Cfg.ServiceSettings.Shards {
if strings.Index(r.Host, key) == 0 {
createSubDomain(teamSignup.Team.Domain, value)
break
}
}
}
teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault
if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil {
@@ -166,7 +155,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
InviteMembers(rteam, ruser, teamSignup.Invites)
InviteMembers(c, rteam, ruser, teamSignup.Invites)
teamSignup.Team = *rteam
teamSignup.User = *ruser
@@ -211,87 +200,14 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
func doesSubDomainExist(subDomain string) bool {
// if it's configured for testing then skip this step
if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
return false
}
creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
r53req := &route53.ListResourceRecordSetsInput{
HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
MaxItems: aws.String("1"),
StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)),
}
if result, err := r53.ListResourceRecordSets(r53req); err != nil {
l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err)
return true
} else {
for _, v := range result.ResourceRecordSets {
if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) {
return true
}
}
}
return false
}
func createSubDomain(subDomain string, target string) {
if utils.Cfg.AWSSettings.Route53AccessKeyId == "" {
return
}
creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "")
r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region}))
rr := route53.ResourceRecord{
Value: aws.String(target),
}
rrs := make([]*route53.ResourceRecord, 1)
rrs[0] = &rr
change := route53.Change{
Action: aws.String("CREATE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)),
TTL: aws.Long(300),
Type: aws.String("CNAME"),
ResourceRecords: rrs,
},
}
changes := make([]*route53.Change, 1)
changes[0] = &change
r53req := &route53.ChangeResourceRecordSetsInput{
HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId),
ChangeBatch: &route53.ChangeBatch{
Changes: changes,
},
}
if _, err := r53.ChangeResourceRecordSets(r53req); err != nil {
l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err)
return
}
}
func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) {
func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) {
m := model.MapFromJson(r.Body)
domain := strings.ToLower(strings.TrimSpace(m["domain"]))
name := strings.ToLower(strings.TrimSpace(m["name"]))
all := strings.ToLower(strings.TrimSpace(m["all"]))
found := FindTeamByDomain(c, domain, all)
found := FindTeamByName(c, name, all)
if c.Err != nil {
return
@@ -304,56 +220,25 @@ func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
func FindTeamByDomain(c *Context, domain string, all string) bool {
func FindTeamByName(c *Context, name string, all string) bool {
if domain == "" || len(domain) > 64 {
c.SetInvalidParam("findTeamByDomain", "domain")
if name == "" || len(name) > 64 {
c.SetInvalidParam("findTeamByName", "domain")
return false
}
if model.IsReservedDomain(domain) {
c.Err = model.NewAppError("findTeamByDomain", "This URL is unavailable. Please try another.", "d="+domain)
if model.IsReservedTeamName(name) {
c.Err = model.NewAppError("findTeamByName", "This URL is unavailable. Please try another.", "name="+name)
return false
}
if all == "false" {
if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
return false
} else {
return true
}
if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
return false
} else {
if doesSubDomainExist(domain) {
return true
}
protocol := "http"
if utils.Cfg.ServiceSettings.UseSSL {
protocol = "https"
}
for key, _ := range utils.Cfg.ServiceSettings.Shards {
url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain)
if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 {
url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain)
}
client := model.NewClient(url)
if result, err := client.FindTeamByDomain(domain, false); err != nil {
c.Err = err
return false
} else {
if result.Data.(bool) {
return true
}
}
}
return false
return true
}
return false
}
func findTeams(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -376,7 +261,7 @@ func findTeams(c *Context, w http.ResponseWriter, r *http.Request) {
s := make([]string, 0, len(teams))
for _, v := range teams {
s = append(s, v.Domain)
s = append(s, v.Name)
}
w.Write([]byte(model.ArrayToJson(s)))
@@ -394,33 +279,8 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
protocol := "http"
if utils.Cfg.ServiceSettings.UseSSL {
protocol = "https"
}
subjectPage := NewServerTemplatePage("find_teams_subject", c.TeamUrl)
bodyPage := NewServerTemplatePage("find_teams_body", c.TeamUrl)
for key, _ := range utils.Cfg.ServiceSettings.Shards {
url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain)
if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 {
url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain)
}
client := model.NewClient(url)
if result, err := client.FindTeams(email); err != nil {
l4g.Error("An error occured while finding teams at %v err=%v", key, err)
} else {
data := result.Data.([]string)
for _, domain := range data {
bodyPage.Props[fmt.Sprintf("%v://%v.%v", protocol, domain, utils.Cfg.ServiceSettings.Domain)] = ""
}
}
}
subjectPage := NewServerTemplatePage("find_teams_subject", c.GetSiteURL())
bodyPage := NewServerTemplatePage("find_teams_body", c.GetSiteURL())
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("An error occured while sending an email in emailTeams err=%v", err)
@@ -470,29 +330,18 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
ia = append(ia, invite["email"])
}
InviteMembers(team, user, ia)
InviteMembers(c, team, user, ia)
w.Write([]byte(invites.ToJson()))
}
func InviteMembers(team *model.Team, user *model.User, invites []string) {
func InviteMembers(c *Context, team *model.Team, user *model.User, invites []string) {
for _, invite := range invites {
if len(invite) > 0 {
teamUrl := ""
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
teamUrl = "http://localhost:8065"
} else if utils.Cfg.ServiceSettings.UseSSL {
teamUrl = fmt.Sprintf("https://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain)
} else {
teamUrl = fmt.Sprintf("http://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain)
}
teamURL := ""
teamURL = c.GetTeamURLFromTeam(team)
sender := ""
if len(strings.TrimSpace(user.FullName)) == 0 {
sender = user.Username
} else {
sender = user.FullName
}
sender := user.GetDisplayName()
senderRole := ""
if strings.Contains(user.Roles, model.ROLE_ADMIN) || strings.Contains(user.Roles, model.ROLE_SYSTEM_ADMIN) {
@@ -501,11 +350,11 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
senderRole = "member"
}
subjectPage := NewServerTemplatePage("invite_subject", teamUrl)
subjectPage := NewServerTemplatePage("invite_subject", teamURL)
subjectPage.Props["SenderName"] = sender
subjectPage.Props["TeamName"] = team.Name
bodyPage := NewServerTemplatePage("invite_body", teamUrl)
bodyPage.Props["TeamName"] = team.Name
subjectPage.Props["TeamDisplayName"] = team.DisplayName
bodyPage := NewServerTemplatePage("invite_body", teamURL)
bodyPage.Props["TeamDisplayName"] = team.DisplayName
bodyPage.Props["SenderName"] = sender
bodyPage.Props["SenderStatus"] = senderRole
@@ -514,12 +363,12 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
props := make(map[string]string)
props["email"] = invite
props["id"] = team.Id
props["display_name"] = team.DisplayName
props["name"] = team.Name
props["domain"] = team.Domain
props["time"] = fmt.Sprintf("%v", model.GetMillis())
data := model.MapToJson(props)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt))
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", teamUrl, url.QueryEscape(data), url.QueryEscape(hash))
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash))
if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV {
l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"])
@@ -532,35 +381,35 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) {
}
}
func updateTeamName(c *Context, w http.ResponseWriter, r *http.Request) {
func updateTeamDisplayName(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
new_name := props["new_name"]
if len(new_name) == 0 {
c.SetInvalidParam("updateTeamName", "new_name")
c.SetInvalidParam("updateTeamDisplayName", "new_name")
return
}
teamId := props["team_id"]
if len(teamId) > 0 && len(teamId) != 26 {
c.SetInvalidParam("updateTeamName", "team_id")
c.SetInvalidParam("updateTeamDisplayName", "team_id")
return
} else if len(teamId) == 0 {
teamId = c.Session.TeamId
}
if !c.HasPermissionsToTeam(teamId, "updateTeamName") {
if !c.HasPermissionsToTeam(teamId, "updateTeamDisplayName") {
return
}
if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) {
c.Err = model.NewAppError("updateTeamName", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err = model.NewAppError("updateTeamDisplayName", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
if result := <-Srv.Store.Team().UpdateName(new_name, c.Session.TeamId); result.Err != nil {
if result := <-Srv.Store.Team().UpdateDisplayName(new_name, c.Session.TeamId); result.Err != nil {
c.Err = result.Err
return
}

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

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

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

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your email</h2>
<p>You updated your email for {{.Props.TeamName}} on {{ .TeamUrl }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
<p>You updated your email for {{.Props.TeamDisplayName}} on {{ .TeamURL }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -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;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -42,7 +42,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2>
<p>{{.Props.TeamName}} started using {{.SiteName}}.<br> The team {{.Props.SenderStatus}} <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamName}}</strong>.</p>
<p>{{.Props.TeamDisplayName}} started using {{.SiteName}}.<br> The team {{.Props.SenderStatus}} <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamDisplayName}}</strong>.</p>
<p style="margin: 20px 0 15px">
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Join Team</a>
</p>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -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;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your password</h2>
<p>You updated your password for {{.Props.TeamName}} on {{ .TeamUrl }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
<p>You updated your password for {{.Props.TeamDisplayName}} on {{ .TeamURL }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -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;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -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;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You requested a password reset</h2>
<p>To change your password, click below.<br>If you did not mean to reset your password, please ignore this email and your password will remain the same.</p>
<p>To change your password, click "Reset Password" below.<br>If you did not mean to reset your password, please ignore this email and your password will remain the same.</p>
<p style="margin: 20px 0 15px">
<a href="{{.Props.ResetUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Reset Password</a>
</p>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -40,7 +40,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -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;">
<tr>
<td style="padding: 20px 30px 10px; text-align:left;">
<img src="{{.TeamUrl}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -17,8 +17,8 @@
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamName}} team at {{.SiteName}}!</h2>
<p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamUrl}}">{{.SiteName}}</a>.</p>
<h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamDisplayName}} team at {{.SiteName}}!</h2>
<p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamURL}}">{{.SiteName}}</a>.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
<img width="65" src="{{.TeamUrl}}/static/images/circles.png" alt="">
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>

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

@@ -181,14 +181,13 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
}
//fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square")
//fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square")
if user.EmailVerified {
if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
l4g.Error("Failed to set email verified err=%v", cresult.Err)
}
} else {
FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl)
FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.FirstName, ruser.Email, team.DisplayName, c.GetTeamURLFromTeam(team))
}
ruser.Sanitize(map[string]bool{})
@@ -196,19 +195,19 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
// This message goes to every channel, so the channelId is irrelevant
message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER)
store.PublishAndForget(message)
PublishAndForget(message)
return ruser
}
}
func fireAndForgetWelcomeEmail(name, email, teamName, link string) {
func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link string) {
go func() {
subjectPage := NewServerTemplatePage("welcome_subject", link)
bodyPage := NewServerTemplatePage("welcome_body", link)
bodyPage.Props["FullName"] = name
bodyPage.Props["TeamName"] = teamName
bodyPage.Props["Nickname"] = name
bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -218,16 +217,16 @@ func fireAndForgetWelcomeEmail(name, email, teamName, link string) {
}()
}
func FireAndForgetVerifyEmail(userId, name, email, teamName, teamUrl string) {
func FireAndForgetVerifyEmail(userId, name, email, teamDisplayName, teamURL string) {
go func() {
link := fmt.Sprintf("%s/verify?uid=%s&hid=%s", teamUrl, userId, model.HashPassword(userId))
link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s", teamURL, userId, model.HashPassword(userId))
subjectPage := NewServerTemplatePage("verify_subject", teamUrl)
subjectPage.Props["TeamName"] = teamName
bodyPage := NewServerTemplatePage("verify_body", teamUrl)
bodyPage.Props["FullName"] = name
bodyPage.Props["TeamName"] = teamName
subjectPage := NewServerTemplatePage("verify_subject", teamURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("verify_body", teamURL)
bodyPage.Props["Nickname"] = name
bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["VerifyUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -251,10 +250,10 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
}
var team *model.Team
if result.Data == nil && len(props["email"]) != 0 && len(props["domain"]) != 0 {
extraInfo = props["email"] + " in " + props["domain"]
if result.Data == nil && len(props["email"]) != 0 && len(props["name"]) != 0 {
extraInfo = props["email"] + " in " + props["name"]
if nr := <-Srv.Store.Team().GetByDomain(props["domain"]); nr.Err != nil {
if nr := <-Srv.Store.Team().GetByName(props["name"]); nr.Err != nil {
c.Err = nr.Err
return
} else {
@@ -293,7 +292,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
if !user.EmailVerified {
if !user.EmailVerified && !utils.Cfg.EmailSettings.ByPassEmail {
c.Err = model.NewAppError("login", "Login failed because email address has not been verified", extraInfo)
c.Err.StatusCode = http.StatusForbidden
return
@@ -517,7 +516,6 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) {
}
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
w.Header().Set("Cache-Control", "max-age=120, public") // 2 mins
w.Write([]byte(model.UserMapToJson(profiles)))
return
}
@@ -729,6 +727,8 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
Srv.Store.User().UpdateLastPictureUpdate(c.Session.UserId)
c.LogAudit("")
}
@@ -756,7 +756,8 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
if tresult := <-Srv.Store.Team().Get(rusers[1].TeamId); tresult.Err != nil {
l4g.Error(tresult.Err.Message)
} else {
fireAndForgetEmailChangeEmail(rusers[1].Email, tresult.Data.(*model.Team).Name, c.TeamUrl)
team := tresult.Data.(*model.Team)
fireAndForgetEmailChangeEmail(rusers[1].Email, team.DisplayName, c.GetTeamURLFromTeam(team))
}
}
@@ -827,7 +828,8 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
if tresult := <-tchan; tresult.Err != nil {
l4g.Error(tresult.Err.Message)
} else {
fireAndForgetPasswordChangeEmail(user.Email, tresult.Data.(*model.Team).Name, c.TeamUrl, "using the settings menu")
team := tresult.Data.(*model.Team)
fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using the settings menu")
}
data := make(map[string]string)
@@ -986,14 +988,14 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
domain := props["domain"]
if len(domain) == 0 {
c.SetInvalidParam("sendPasswordReset", "domain")
name := props["name"]
if len(name) == 0 {
c.SetInvalidParam("sendPasswordReset", "name")
return
}
var team *model.Team
if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -1015,10 +1017,10 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapToJson(newProps)
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.ResetSalt))
link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash))
link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash))
subjectPage := NewServerTemplatePage("reset_subject", c.TeamUrl)
bodyPage := NewServerTemplatePage("reset_body", c.TeamUrl)
subjectPage := NewServerTemplatePage("reset_subject", c.GetTeamURLFromTeam(team))
bodyPage := NewServerTemplatePage("reset_body", c.GetTeamURLFromTeam(team))
bodyPage.Props["ResetUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -1060,16 +1062,16 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
domain := props["domain"]
if len(domain) == 0 {
c.SetInvalidParam("resetPassword", "domain")
name := props["name"]
if len(name) == 0 {
c.SetInvalidParam("resetPassword", "name")
return
}
c.LogAuditWithUserId(userId, "attempt")
var team *model.Team
if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil {
if result := <-Srv.Store.Team().GetByName(name); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -1108,19 +1110,19 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(userId, "success")
}
fireAndForgetPasswordChangeEmail(user.Email, team.Name, c.TeamUrl, "using a reset password link")
fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using a reset password link")
props["new_password"] = ""
w.Write([]byte(model.MapToJson(props)))
}
func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) {
func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, method string) {
go func() {
subjectPage := NewServerTemplatePage("password_change_subject", teamUrl)
subjectPage.Props["TeamName"] = teamName
bodyPage := NewServerTemplatePage("password_change_body", teamUrl)
bodyPage.Props["TeamName"] = teamName
subjectPage := NewServerTemplatePage("password_change_subject", teamURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("password_change_body", teamURL)
bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["Method"] = method
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -1130,13 +1132,13 @@ func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) {
}()
}
func fireAndForgetEmailChangeEmail(email, teamName, teamUrl string) {
func fireAndForgetEmailChangeEmail(email, teamDisplayName, teamURL string) {
go func() {
subjectPage := NewServerTemplatePage("email_change_subject", teamUrl)
subjectPage.Props["TeamName"] = teamName
bodyPage := NewServerTemplatePage("email_change_body", teamUrl)
bodyPage.Props["TeamName"] = teamName
subjectPage := NewServerTemplatePage("email_change_subject", teamURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("email_change_body", teamURL)
bodyPage.Props["TeamDisplayName"] = teamDisplayName
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send update password email successfully err=%v", err)

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

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

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

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

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

@@ -5,12 +5,14 @@ package api
import (
l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/model"
)
type Hub struct {
teamHubs map[string]*TeamHub
register chan *WebConn
unregister chan *WebConn
broadcast chan *model.Message
stop chan string
}
@@ -18,9 +20,16 @@ var hub = &Hub{
register: make(chan *WebConn),
unregister: make(chan *WebConn),
teamHubs: make(map[string]*TeamHub),
broadcast: make(chan *model.Message),
stop: make(chan string),
}
func PublishAndForget(message *model.Message) {
go func() {
hub.Broadcast(message)
}()
}
func (h *Hub) Register(webConn *WebConn) {
h.register <- webConn
}
@@ -29,8 +38,14 @@ func (h *Hub) Unregister(webConn *WebConn) {
h.unregister <- webConn
}
func (h *Hub) Stop(teamId string) {
h.stop <- teamId
func (h *Hub) Broadcast(message *model.Message) {
if message != nil {
h.broadcast <- message
}
}
func (h *Hub) Stop() {
h.stop <- "all"
}
func (h *Hub) Start() {
@@ -53,18 +68,17 @@ func (h *Hub) Start() {
if nh, ok := h.teamHubs[c.TeamId]; ok {
nh.Unregister(c)
}
case s := <-h.stop:
if len(s) == 0 {
l4g.Debug("stopping all connections")
for _, v := range h.teamHubs {
v.Stop()
}
return
} else if nh, ok := h.teamHubs[s]; ok {
delete(h.teamHubs, s)
nh.Stop()
case msg := <-h.broadcast:
nh := h.teamHubs[msg.TeamId]
if nh != nil {
nh.broadcast <- msg
}
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()
url := "ws://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1/websocket"
team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Domain, user1.Email, "pwd")
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
@@ -39,10 +39,10 @@ func TestSocket(t *testing.T) {
t.Fatal(err)
}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
Client.LoginByEmail(team.Domain, user2.Email, "pwd")
Client.LoginByEmail(team.Name, user2.Email, "pwd")
header2 := http.Header{}
header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken)
@@ -115,9 +115,6 @@ func TestSocket(t *testing.T) {
}()
time.Sleep(2 * time.Second)
hub.Stop(team.Id)
}
func TestZZWebSocketTearDown(t *testing.T) {

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

@@ -6,8 +6,6 @@ package api
import (
l4g "code.google.com/p/log4go"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"strings"
)
type TeamHub struct {
@@ -43,43 +41,6 @@ func (h *TeamHub) Stop() {
}
func (h *TeamHub) Start() {
pubsub := store.RedisClient().PubSub()
go func() {
defer func() {
l4g.Debug("redis reader finished for teamId=%v", h.teamId)
hub.Stop(h.teamId)
}()
l4g.Debug("redis reader starting for teamId=%v", h.teamId)
err := pubsub.Subscribe(h.teamId)
if err != nil {
l4g.Error("Error while subscribing to redis %v %v", h.teamId, err)
return
}
for {
if payload, err := pubsub.ReceiveTimeout(REDIS_WAIT); err != nil {
if strings.Contains(err.Error(), "i/o timeout") {
if len(h.connections) == 0 {
l4g.Debug("No active connections so sending stop %v", h.teamId)
return
}
} else {
return
}
} else {
msg := store.GetMessageFromPayload(payload)
if msg != nil {
h.broadcast <- msg
}
}
}
}()
go func() {
for {
select {
@@ -110,7 +71,6 @@ func (h *TeamHub) Start() {
webCon.WebSocket.Close()
}
pubsub.Close()
return
}
}

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

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

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

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

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

@@ -57,17 +57,17 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
// Check for username parameter and create a user if present
username, ok1 := params["username"]
teamname, ok2 := params["teamname"]
teamDisplayName, ok2 := params["teamname"]
var teamID string
var userID string
if ok1 && ok2 {
l4g.Info("Creating user and team")
// Create team for testing
team := &model.Team{
Name: teamname[0],
Domain: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
Type: model.TEAM_OPEN,
DisplayName: teamDisplayName[0],
Name: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE),
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
Type: model.TEAM_OPEN,
}
if result := <-api.Srv.Store.Team().Save(team); result.Err != nil {
@@ -90,7 +90,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
user := &model.User{
TeamId: teamID,
Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE),
FullName: username[0],
Nickname: username[0],
Password: api.USER_PASSWORD}
result, err := client.CreateUser(user, "")

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

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

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

@@ -100,10 +100,10 @@ func (c *Client) Must(result *Result, err *AppError) *Result {
return result
}
func (c *Client) SignupTeam(email string, name string) (*Result, *AppError) {
func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppError) {
m := make(map[string]string)
m["email"] = email
m["name"] = name
m["display_name"] = displayName
if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil {
return nil, err
} else {
@@ -130,11 +130,11 @@ func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
}
}
func (c *Client) FindTeamByDomain(domain string, allServers bool) (*Result, *AppError) {
func (c *Client) FindTeamByName(name string, allServers bool) (*Result, *AppError) {
m := make(map[string]string)
m["domain"] = domain
m["name"] = name
m["all"] = fmt.Sprintf("%v", allServers)
if r, err := c.DoPost("/teams/find_team_by_domain", MapToJson(m)); err != nil {
if r, err := c.DoPost("/teams/find_team_by_name", MapToJson(m)); err != nil {
return nil, err
} else {
val := false
@@ -179,7 +179,7 @@ func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
}
}
func (c *Client) UpdateTeamName(data map[string]string) (*Result, *AppError) {
func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppError) {
if r, err := c.DoPost("/teams/update_name", MapToJson(data)); err != nil {
return nil, err
} else {
@@ -249,17 +249,17 @@ func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
return c.login(m)
}
func (c *Client) LoginByEmail(domain string, email string, password string) (*Result, *AppError) {
func (c *Client) LoginByEmail(name string, email string, password string) (*Result, *AppError) {
m := make(map[string]string)
m["domain"] = domain
m["name"] = name
m["email"] = email
m["password"] = password
return c.login(m)
}
func (c *Client) LoginByEmailWithDevice(domain string, email string, password string, deviceId string) (*Result, *AppError) {
func (c *Client) LoginByEmailWithDevice(name string, email string, password string, deviceId string) (*Result, *AppError) {
m := make(map[string]string)
m["domain"] = domain
m["name"] = name
m["email"] = email
m["password"] = password
m["device_id"] = deviceId

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

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

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

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

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

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

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

@@ -37,7 +37,9 @@ type User struct {
AuthData string `json:"auth_data"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
FullName string `json:"full_name"`
Nickname string `json:"nickname"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Roles string `json:"roles"`
LastActivityAt int64 `json:"last_activity_at"`
LastPingAt int64 `json:"last_ping_at"`
@@ -45,6 +47,7 @@ type User struct {
Props StringMap `json:"props"`
NotifyProps StringMap `json:"notify_props"`
LastPasswordUpdate int64 `json:"last_password_update"`
LastPictureUpdate int64 `json:"last_picture_update"`
}
// IsValid validates the user and returns an error if it isn't configured
@@ -81,8 +84,16 @@ func (u *User) IsValid() *AppError {
return NewAppError("User.IsValid", "Invalid email", "user_id="+u.Id)
}
if len(u.FullName) > 64 {
return NewAppError("User.IsValid", "Invalid full name", "user_id="+u.Id)
if len(u.Nickname) > 64 {
return NewAppError("User.IsValid", "Invalid nickname", "user_id="+u.Id)
}
if len(u.FirstName) > 64 {
return NewAppError("User.IsValid", "Invalid first name", "user_id="+u.Id)
}
if len(u.LastName) > 64 {
return NewAppError("User.IsValid", "Invalid last name", "user_id="+u.Id)
}
return nil
@@ -151,7 +162,7 @@ func (u *User) SetDefaultNotifications() {
u.NotifyProps["first_name"] = "false"
u.NotifyProps["all"] = "true"
u.NotifyProps["channel"] = "true"
splitName := strings.Split(u.FullName, " ")
splitName := strings.Split(u.Nickname, " ")
if len(splitName) > 0 && splitName[0] != "" {
u.NotifyProps["first_name"] = "true"
u.NotifyProps["mention_keys"] += "," + splitName[0]
@@ -190,7 +201,8 @@ func (u *User) Sanitize(options map[string]bool) {
u.Email = ""
}
if len(options) != 0 && !options["fullname"] {
u.FullName = ""
u.FirstName = ""
u.LastName = ""
}
if len(options) != 0 && !options["skypeid"] {
// TODO - fill in when SkypeId is added to user model
@@ -225,6 +237,28 @@ func (u *User) AddNotifyProp(key string, value string) {
u.NotifyProps[key] = value
}
func (u *User) GetFullName() string {
if u.FirstName != "" && u.LastName != "" {
return u.FirstName + " " + u.LastName
} else if u.FirstName != "" {
return u.FirstName
} else if u.LastName != "" {
return u.LastName
} else {
return ""
}
}
func (u *User) GetDisplayName() string {
if u.Nickname != "" {
return u.Nickname
} else if fullName := u.GetFullName(); fullName != "" {
return fullName
} else {
return u.Username
}
}
// UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User {
decoder := json.NewDecoder(data)

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

@@ -80,13 +80,73 @@ func TestUserIsValid(t *testing.T) {
}
user.Email = "test@nowhere.com"
user.FullName = strings.Repeat("01234567890", 20)
user.Nickname = strings.Repeat("01234567890", 20)
if err := user.IsValid(); err == nil {
t.Fatal()
}
user.FullName = ""
user.Nickname = ""
if err := user.IsValid(); err != nil {
t.Fatal(err)
}
user.FirstName = ""
user.LastName = ""
if err := user.IsValid(); err != nil {
t.Fatal(err)
}
user.FirstName = strings.Repeat("01234567890", 20)
if err := user.IsValid(); err == nil {
t.Fatal(err)
}
user.FirstName = ""
user.LastName = strings.Repeat("01234567890", 20)
if err := user.IsValid(); err == nil {
t.Fatal(err)
}
}
func TestUserGetFullName(t *testing.T) {
user := User{}
if fullName := user.GetFullName(); fullName != "" {
t.Fatal("Full name should be blank")
}
user.FirstName = "first"
if fullName := user.GetFullName(); fullName != "first" {
t.Fatal("Full name should be first name")
}
user.FirstName = ""
user.LastName = "last"
if fullName := user.GetFullName(); fullName != "last" {
t.Fatal("Full name should be last name")
}
user.FirstName = "first"
if fullName := user.GetFullName(); fullName != "first last" {
t.Fatal("Full name should be first name and last name")
}
}
func TestUserGetDisplayName(t *testing.T) {
user := User{Username: "user"}
if displayName := user.GetDisplayName(); displayName != "user" {
t.Fatal("Display name should be username")
}
user.FirstName = "first"
user.LastName = "last"
if displayName := user.GetDisplayName(); displayName != "first last" {
t.Fatal("Display name should be full name")
}
user.Nickname = "nickname"
if displayName := user.GetDisplayName(); displayName != "nickname" {
t.Fatal("Display name should be nickname")
}
}

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

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

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

@@ -83,9 +83,9 @@ var domains = []struct {
{"test", true},
}
func TestValidDomain(t *testing.T) {
func TestValidTeamName(t *testing.T) {
for _, v := range domains {
if IsValidDomain(v.value) != v.expected {
if IsValidTeamName(v.value) != v.expected {
t.Errorf("expect %v as %v", v.value, v.expected)
}
}
@@ -102,9 +102,9 @@ var tReservedDomains = []struct {
{"spin-punch-admin", false},
}
func TestReservedDomain(t *testing.T) {
func TestReservedTeamName(t *testing.T) {
for _, v := range tReservedDomains {
if IsReservedDomain(v.value) != v.expected {
if IsReservedTeamName(v.value) != v.expected {
t.Errorf("expect %v as %v", v.value, v.expected)
}
}

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

@@ -10,7 +10,7 @@ DOCKER SETUP
3. Add a line to your /etc/hosts that goes `<Docker IP> dockerhost`
4. Run `boot2docker shellinit` and copy the export statements to your ~/.bash_profile
Any issues? Please let us know on our forums at: http://bit.ly/1MY1kul
Any issues? Please let us know on our forums at: http://forum.mattermost.org
GO SETUP
@@ -39,4 +39,4 @@ MATTERMOST SETUP
6. Then do `cd platform` and `make test`. Provided the test runs fine, you now have a complete build environment.
7. Use `make run` to run your code
Any issues? Please let us know on our forums at: http://bit.ly/1MY1kul
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{}
var members []model.ExtraMember
_, err := s.GetReplica().Select(&members, "SELECT Id, FullName, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = :ChannelId LIMIT :Limit", map[string]interface{}{"ChannelId": channelId, "Limit": limit})
_, err := s.GetReplica().Select(&members, "SELECT Id, Nickname, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = :ChannelId LIMIT :Limit", map[string]interface{}{"ChannelId": channelId, "Limit": limit})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetExtraMembers", "We couldn't get the extra info for channel members", "channel_id="+channelId+", "+err.Error())
} else {

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

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

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

@@ -36,6 +36,11 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore {
}
func (s SqlPostStore) UpgradeSchemaIfNeeded() {
// These execs are for upgrading currently created databases to full utf8mb4 compliance
// Will be removed as seen fit for upgrading
s.GetMaster().Exec("ALTER TABLE Posts charset=utf8mb4")
s.GetMaster().Exec("ALTER TABLE Posts MODIFY COLUMN Message varchar(4000) CHARACTER SET utf8mb4")
}
func (s SqlPostStore) CreateIndexesIfNotExists() {

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

@@ -83,7 +83,14 @@ func NewSqlStore() Store {
func setupConnection(con_type string, driver string, dataSource string, maxIdle int, maxOpen int, trace bool) *gorp.DbMap {
db, err := dbsql.Open(driver, dataSource)
charset := ""
if strings.Index(dataSource, "?") > -1 {
charset = "&charset=utf8mb4,utf8"
} else {
charset = "?charset=utf8mb4,utf8"
}
db, err := dbsql.Open(driver, dataSource+charset)
if err != nil {
l4g.Critical("Failed to open sql connection to '%v' err:%v", dataSource, err)
time.Sleep(time.Second)
@@ -106,7 +113,7 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
if driver == "sqlite3" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}}
} else if driver == "mysql" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}}
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}}
} else if driver == "postgres" {
dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}}
} else {
@@ -122,62 +129,9 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
return dbmap
}
func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, colType string, defaultValue string) bool {
var count int64
var err error
if utils.Cfg.SqlSettings.DriverName == "postgres" {
count, err = ss.GetMaster().SelectInt(
`SELECT
COUNT(0) AS column_exists
FROM
information_schema.COLUMNS
WHERE
TABLE_NAME = $1
AND COLUMN_NAME = $2`,
tableName,
columnName,
)
} else if utils.Cfg.SqlSettings.DriverName == "mysql" {
count, err = ss.GetMaster().SelectInt(
`SELECT
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 {
func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool {
count, err := ss.GetMaster().SelectInt(
`SELECT
`SELECT
COUNT(0) AS column_exists
FROM
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())
}
if count == 0 {
return count > 0
}
func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool {
if ss.DoesColumnExist(tableName, columnName) {
return false
}
_, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " 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 {
l4g.Critical("Failed to drop column %v", err)
time.Sleep(time.Second)
@@ -208,6 +181,25 @@ func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) boo
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) {
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) {
if utils.Cfg.SqlSettings.DriverName == "postgres" {
_, err := ss.GetMaster().SelectStr("SELECT to_regclass($1)", indexName)
// It should fail if the index does not exist
if err == nil {
return
}
query := ""
if fullText {
query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + columnName + "))"
} else {
query = "CREATE INDEX " + indexName + " ON " + tableName + " (" + columnName + ")"
}
_, err = ss.GetMaster().Exec(query)
if err != nil {
l4g.Critical("Failed to create index %v", err)
time.Sleep(time.Second)
panic("Failed to create index " + err.Error())
}
} else if utils.Cfg.SqlSettings.DriverName == "mysql" {
count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
if err != nil {
l4g.Critical("Failed to check index %v", err)
time.Sleep(time.Second)
panic("Failed to check index " + err.Error())
}
if count > 0 {
return
}
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")
count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
if err != nil {
l4g.Critical("Failed to check index", err)
time.Sleep(time.Second)
panic("Failed to 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() {
table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id")
table.ColMap("Id").SetMaxSize(26)
table.ColMap("Name").SetMaxSize(64)
table.ColMap("Domain").SetMaxSize(64).SetUnique(true)
table.ColMap("DisplayName").SetMaxSize(64)
table.ColMap("Name").SetMaxSize(64).SetUnique(true)
table.ColMap("Email").SetMaxSize(128)
table.ColMap("CompanyName").SetMaxSize(64)
table.ColMap("AllowedDomains").SetMaxSize(500)
@@ -28,6 +28,15 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
}
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
defaultValue := "0"
if utils.Cfg.TeamSettings.AllowValetDefault {
defaultValue = "1"
}
s.CreateColumnIfNotExists("Teams", "AllowValet", "AllowedDomains", "tinyint(1)", defaultValue)
if !s.DoesColumnExist("Teams", "DisplayName") {
s.RenameColumnIfExists("Teams", "Name", "DisplayName", "varchar(64)")
s.RenameColumnIfExists("Teams", "Domain", "Name", "varchar(64)")
}
}
func (s SqlTeamStore) CreateIndexesIfNotExists() {
@@ -56,7 +65,7 @@ func (s SqlTeamStore) Save(team *model.Team) StoreChannel {
}
if err := s.GetMaster().Insert(team); err != nil {
if IsUniqueConstraintError(err.Error(), "Domain", "teams_domain_key") {
if IsUniqueConstraintError(err.Error(), "Name", "teams_domain_key") {
result.Err = model.NewAppError("SqlTeamStore.Save", "A team with that domain already exists", "id="+team.Id+", "+err.Error())
} else {
result.Err = model.NewAppError("SqlTeamStore.Save", "We couldn't save the team", "id="+team.Id+", "+err.Error())
@@ -94,7 +103,7 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel {
} else {
oldTeam := oldResult.(*model.Team)
team.CreateAt = oldTeam.CreateAt
team.Domain = oldTeam.Domain
team.Name = oldTeam.Name
if count, err := s.GetMaster().Update(team); err != nil {
result.Err = model.NewAppError("SqlTeamStore.Update", "We encounted an error updating the team", "id="+team.Id+", "+err.Error())
@@ -112,14 +121,14 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel {
return storeChannel
}
func (s SqlTeamStore) UpdateName(name string, teamId string) StoreChannel {
func (s SqlTeamStore) UpdateDisplayName(name string, teamId string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
if _, err := s.GetMaster().Exec("UPDATE Teams SET Name = :Name WHERE Id = :Id", map[string]interface{}{"Name": name, "Id": teamId}); err != nil {
if _, err := s.GetMaster().Exec("UPDATE Teams SET DisplayName = :Name WHERE Id = :Id", map[string]interface{}{"Name": name, "Id": teamId}); err != nil {
result.Err = model.NewAppError("SqlTeamStore.UpdateName", "We couldn't update the team name", "team_id="+teamId)
} else {
result.Data = teamId
@@ -153,7 +162,7 @@ func (s SqlTeamStore) Get(id string) StoreChannel {
return storeChannel
}
func (s SqlTeamStore) GetByDomain(domain string) StoreChannel {
func (s SqlTeamStore) GetByName(name string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
@@ -161,8 +170,8 @@ func (s SqlTeamStore) GetByDomain(domain string) StoreChannel {
team := model.Team{}
if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Domain = :Domain", map[string]interface{}{"Domain": domain}); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetByDomain", "We couldn't find the existing team", "domain="+domain+", "+err.Error())
if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Name = :Name", map[string]interface{}{"Name": name}); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetByName", "We couldn't find the existing team", "name="+name+", "+err.Error())
}
result.Data = &team

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

@@ -13,8 +13,8 @@ func TestTeamStoreSave(t *testing.T) {
Setup()
o1 := model.Team{}
o1.Name = "Name"
o1.Domain = "a" + model.NewId() + "b"
o1.DisplayName = "DisplayName"
o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
@@ -36,8 +36,8 @@ func TestTeamStoreUpdate(t *testing.T) {
Setup()
o1 := model.Team{}
o1.Name = "Name"
o1.Domain = "a" + model.NewId() + "b"
o1.DisplayName = "DisplayName"
o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
if err := (<-store.Team().Save(&o1)).Err; err != nil {
@@ -61,25 +61,25 @@ func TestTeamStoreUpdate(t *testing.T) {
}
}
func TestTeamStoreUpdateName(t *testing.T) {
func TestTeamStoreUpdateDisplayName(t *testing.T) {
Setup()
o1 := &model.Team{}
o1.Name = "Name"
o1.Domain = "a" + model.NewId() + "b"
o1.DisplayName = "Display Name"
o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
o1 = (<-store.Team().Save(o1)).Data.(*model.Team)
newName := "NewName"
newDisplayName := "NewDisplayName"
if err := (<-store.Team().UpdateName(newName, o1.Id)).Err; err != nil {
if err := (<-store.Team().UpdateDisplayName(newDisplayName, o1.Id)).Err; err != nil {
t.Fatal(err)
}
ro1 := (<-store.Team().Get(o1.Id)).Data.(*model.Team)
if ro1.Name != newName {
t.Fatal("Name not updated")
if ro1.DisplayName != newDisplayName {
t.Fatal("DisplayName not updated")
}
}
@@ -87,8 +87,8 @@ func TestTeamStoreGet(t *testing.T) {
Setup()
o1 := model.Team{}
o1.Name = "Name"
o1.Domain = "a" + model.NewId() + "b"
o1.DisplayName = "DisplayName"
o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
Must(store.Team().Save(&o1))
@@ -106,12 +106,12 @@ func TestTeamStoreGet(t *testing.T) {
}
}
func TestTeamStoreGetByDomain(t *testing.T) {
func TestTeamStoreGetByName(t *testing.T) {
Setup()
o1 := model.Team{}
o1.Name = "Name"
o1.Domain = "a" + model.NewId() + "b"
o1.DisplayName = "DisplayName"
o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
@@ -119,7 +119,7 @@ func TestTeamStoreGetByDomain(t *testing.T) {
t.Fatal(err)
}
if r1 := <-store.Team().GetByDomain(o1.Domain); r1.Err != nil {
if r1 := <-store.Team().GetByName(o1.Name); r1.Err != nil {
t.Fatal(r1.Err)
} else {
if r1.Data.(*model.Team).ToJson() != o1.ToJson() {
@@ -127,7 +127,7 @@ func TestTeamStoreGetByDomain(t *testing.T) {
}
}
if err := (<-store.Team().GetByDomain("")).Err; err == nil {
if err := (<-store.Team().GetByName("")).Err; err == nil {
t.Fatal("Missing id should have failed")
}
}
@@ -136,8 +136,8 @@ func TestTeamStoreGetForEmail(t *testing.T) {
Setup()
o1 := model.Team{}
o1.Name = "Name"
o1.Domain = "a" + model.NewId() + "b"
o1.DisplayName = "DisplayName"
o1.Name = "a" + model.NewId() + "b"
o1.Email = model.NewId() + "@nowhere.com"
o1.Type = model.TEAM_OPEN
Must(store.Team().Save(&o1))

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

@@ -24,7 +24,9 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
table.ColMap("Password").SetMaxSize(128)
table.ColMap("AuthData").SetMaxSize(128)
table.ColMap("Email").SetMaxSize(128)
table.ColMap("FullName").SetMaxSize(64)
table.ColMap("Nickname").SetMaxSize(64)
table.ColMap("FirstName").SetMaxSize(64)
table.ColMap("LastName").SetMaxSize(64)
table.ColMap("Roles").SetMaxSize(64)
table.ColMap("Props").SetMaxSize(4000)
table.ColMap("NotifyProps").SetMaxSize(2000)
@@ -35,9 +37,29 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
return us
}
func (s SqlUserStore) UpgradeSchemaIfNeeded() {
func (us SqlUserStore) UpgradeSchemaIfNeeded() {
us.CreateColumnIfNotExists("Users", "LastPictureUpdate", "LastPasswordUpdate", "bigint(20)", "0")
// migrating the FullName column to Nickname and adding the FirstName and LastName columns for MM-825
if us.RenameColumnIfExists("Users", "FullName", "Nickname", "varchar(64)") {
us.CreateColumnIfNotExists("Users", "FirstName", "Nickname", "varchar(64)", "")
us.CreateColumnIfNotExists("Users", "LastName", "FirstName", "varchar(64)", "")
// infer values of first and last name by splitting the previous full name
if _, err := us.GetMaster().Exec("UPDATE Users SET FirstName = SUBSTRING_INDEX(SUBSTRING_INDEX(Nickname, ' ', 1), ' ', -1)"); err != nil {
panic("Failed to set first name from nickname " + err.Error())
}
// only set the last name from full names that are comprised of multiple words (ie that have at least one space in them)
if _, err := us.GetMaster().Exec("Update Users SET LastName = SUBSTRING(Nickname, INSTR(Nickname, ' ') + 1) " +
"WHERE CHAR_LENGTH(REPLACE(Nickname, ' ', '')) < CHAR_LENGTH(Nickname)"); err != nil {
panic("Failed to set last name from nickname " + err.Error())
}
}
}
//func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool {
func (us SqlUserStore) CreateIndexesIfNotExists() {
us.CreateIndexIfNotExists("idx_users_team_id", "Users", "TeamId")
us.CreateIndexIfNotExists("idx_users_email", "Users", "Email")
@@ -120,6 +142,7 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha
user.AuthData = oldUser.AuthData
user.Password = oldUser.Password
user.LastPasswordUpdate = oldUser.LastPasswordUpdate
user.LastPictureUpdate = oldUser.LastPictureUpdate
user.TeamId = oldUser.TeamId
user.LastActivityAt = oldUser.LastActivityAt
user.LastPingAt = oldUser.LastPingAt
@@ -150,6 +173,27 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha
return storeChannel
}
func (us SqlUserStore) UpdateLastPictureUpdate(userId string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
curTime := model.GetMillis()
if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = ?, UpdateAt = ? WHERE Id = ?", curTime, curTime, userId); err != nil {
result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "We couldn't update the update_at", "user_id="+userId)
} else {
result.Data = userId
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (us SqlUserStore) UpdateLastPingAt(userId string, time int64) StoreChannel {
storeChannel := make(StoreChannel)

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

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

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

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

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

@@ -4,24 +4,9 @@
package utils
import (
"os"
"testing"
)
func TestConfig(t *testing.T) {
LoadConfig("config.json")
}
func TestEnvOverride(t *testing.T) {
os.Setenv("MATTERMOST_DOMAIN", "testdomain.com")
LoadConfig("config_docker.json")
if Cfg.ServiceSettings.Domain != "testdomain.com" {
t.Fail()
}
LoadConfig("config.json")
if Cfg.ServiceSettings.Domain == "testdomain.com" {
t.Fail()
}
}

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

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

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

@@ -1,6 +1,7 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var PostStore = require('../stores/post_store.jsx');
@@ -16,7 +17,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
var ExtraMembers = React.createClass({
var PopoverListMembers = React.createClass({
componentDidMount: function() {
var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj) {
@@ -35,30 +36,29 @@ var ExtraMembers = React.createClass({
$("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true});
$('body').on('click', function (e) {
if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) {
if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) {
$("#member_popover").popover('hide');
}
});
},
render: function() {
var count = this.props.members.length == 0 ? "-" : this.props.members.length;
count = this.props.members.length > 19 ? "20+" : count;
var data_content = "";
var sortedMembers = this.props.members;
var popoverHtml = '';
var members = this.props.members;
var count = (members.length > 20) ? "20+" : (members.length || '-');
if(sortedMembers) {
sortedMembers.sort(function(a,b) {
if (members) {
members.sort(function(a,b) {
return a.username.localeCompare(b.username);
})
});
sortedMembers.forEach(function(m) {
data_content += "<div style='white-space: nowrap'>" + m.username + "</div>";
members.forEach(function(m) {
popoverHtml += "<div class='text--nowrap'>" + m.username + "</div>";
});
}
return (
<div style={{"cursor" : "pointer"}} id="member_popover" data-toggle="popover" data-content={data_content} data-original-title="Members" >
<div id="member_popover" data-toggle="popover" data-content={popoverHtml} data-original-title="Members" >
<div id="member_tooltip" data-toggle="tooltip" title="View Channel Members">
{count} <span className="glyphicon glyphicon-user" aria-hidden="true"></span>
</div>
@@ -78,6 +78,7 @@ function getStateFromStores() {
}
module.exports = React.createClass({
displayName: 'ChannelHeader',
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(this._onChange);
@@ -99,7 +100,7 @@ module.exports = React.createClass({
$(".channel-header__info .description").popover({placement : 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
},
_onSocketChange: function(msg) {
if(msg.action === "new_user") {
if (msg.action === "new_user") {
AsyncClient.getChannelExtraInfo(true);
}
},
@@ -107,15 +108,14 @@ module.exports = React.createClass({
return getStateFromStores();
},
handleLeave: function(e) {
var self = this;
Client.leaveChannel(this.state.channel.id,
function(data) {
var townsquare = ChannelStore.getByName('town-square');
utils.switchChannel(townsquare);
}.bind(this),
},
function(err) {
AsyncClient.dispatchError(err, "handleLeave");
}.bind(this)
}
);
},
searchMentions: function(e) {
@@ -131,52 +131,29 @@ module.exports = React.createClass({
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH_TERM,
term: terms,
do_search: false
do_search: true,
is_mention_search: true
});
Client.search(
terms,
function(data) {
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_SEARCH,
results: data,
is_mention_search: true
});
},
function(err) {
dispatchError(err, "search");
}
);
},
render: function() {
if (this.state.channel == null) {
return (
<div></div>
);
return null;
}
var description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true});
var popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>);
var channelTitle = "";
var channelName = this.state.channel.name;
var channel = this.state.channel;
var description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
var channelTitle = channel.display_name;
var currentId = UserStore.getCurrentId();
var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1;
var searchForm = <th className="search-bar__container"><NavbarSearchBox /></th>;
var isDirect = false;
var isDirect = (this.state.channel.type === 'D');
if (this.state.channel.type === 'O') {
channelTitle = this.state.channel.display_name;
} else if (this.state.channel.type === 'P') {
channelTitle = this.state.channel.display_name;
} else if (this.state.channel.type === 'D') {
isDirect = true;
if (isDirect) {
if (this.state.users.length > 1) {
if (this.state.users[0].id === UserStore.getCurrentId()) {
channelTitle = <UserProfile userId={this.state.users[1].id} overwriteName={this.state.users[1].full_name ? this.state.users[1].full_name : this.state.users[1].username} />;
} else {
channelTitle = <UserProfile userId={this.state.users[0].id} overwriteName={this.state.users[0].full_name ? this.state.users[0].full_name : this.state.users[0].username} />;
}
var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)];
channelTitle = <UserProfile userId={contact.id} overwriteName={contact.nickname || contact.username} />;
}
}
@@ -192,25 +169,28 @@ module.exports = React.createClass({
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={this.state.channel.id} href="#">View Info</a></li>
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
{ isAdmin ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_info" data-channelid={channel.id} href="#">View Info</a></li>
{ !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
: null
}
{ isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: ""
: null
}
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Set Channel Description...</a></li>
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Notification Preferences</a></li>
{ isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={this.state.channel.display_name} data-name={this.state.channel.name} data-channelid={this.state.channel.id}>Rename Channel...</a></li>
: ""
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
{ isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename Channel...</a></li>
: null
}
{ isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Delete Channel...</a></li>
: ""
{ isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
: null
}
{ channelName != Constants.DEFAULT_CHANNEL ?
{ !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
: ""
: null
}
</ul>
</div>
@@ -220,14 +200,13 @@ module.exports = React.createClass({
<a href="#"><strong className="heading">{channelTitle}</strong></a>
}
</th>
<th><ExtraMembers members={this.state.users} channelId={this.state.channel.id} /></th>
{ searchForm }
<th><PopoverListMembers members={this.state.users} channelId={channel.id} /></th>
<th className="search-bar__container"><NavbarSearchBox /></th>
<th>
<div className="dropdown" style={{"marginLeft":"5px", "marginRight":"10px"}}>
<div className="dropdown channel-header__links">
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_right_dropdown" data-toggle="dropdown" aria-expanded="true">
<i className="fa fa-caret-down"></i>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_right_dropdown" style={{"left": "-150px"}}>
<span dangerouslySetInnerHTML={{__html: " <svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>"}} /> </a>
<ul className="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="channel_header_right_dropdown">
<li role="presentation"><a role="menuitem" href="#" onClick={this.searchMentions}>Recent Mentions</a></li>
</ul>
</div>
@@ -237,5 +216,3 @@ module.exports = React.createClass({
);
}
});

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

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

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

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

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

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

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

@@ -56,13 +56,13 @@ module.exports = React.createClass({
$(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) {
var newState = {};
if(BrowserStore.getItem('edit_state_transfer')) {
newState = JSON.parse(BrowserStore.getItem('edit_state_transfer'));
newState = BrowserStore.getItem('edit_state_transfer');
BrowserStore.removeItem('edit_state_transfer');
} else {
var button = e.relatedTarget;
newState = { title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments') };
}
self.setState(newState)
self.setState(newState);
});
PostStore.addSelectedPostChangeListener(this._onChange);
},

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

@@ -43,7 +43,7 @@ module.exports = React.createClass({
<h4 className="modal-title" ref="title">Edit {this.state.title} Description</h4>
</div>
<div className="modal-body">
<textarea className="form-control" style={{resize: "none"}} rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
<textarea className="form-control no-resize" rows="6" ref="channelDesc" maxLength="1024" value={this.state.description} onChange={this.handleUserInput}></textarea>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>

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

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

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

@@ -38,7 +38,7 @@ module.exports = React.createClass({
<div className="modal-body">
<p>{"The link below is used for open " + strings.TeamPlural + " or if you allowed your " + strings.Team + " members to sign up using their " + strings.Company + " email addresses."}
</p>
<textarea className="form-control" style={{resize: "none"}} readOnly="true" value={this.state.value}></textarea>
<textarea className="form-control no-resize" readOnly="true" value={this.state.value}></textarea>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>

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.
// See License.txt for license information.
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
var FindTeamDomain = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var state = { }
var domain = this.refs.domain.getDOMNode().value.trim();
if (!domain) {
state.server_error = "A domain is required"
this.setState(state);
return;
}
if (!BrowserStore.isLocalStorageSupported()) {
state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing.";
this.setState(state);
return;
}
state.server_error = "";
this.setState(state);
client.findTeamByDomain(domain,
function(data) {
console.log(data);
if (data) {
window.location.href = window.location.protocol + "//" + domain + "." + utils.getDomainWithOutSub();
}
else {
this.state.server_error = "We couldn't find your " + strings.Team + ".";
this.setState(this.state);
}
}.bind(this),
function(err) {
this.state.server_error = err.message;
this.setState(this.state);
}.bind(this)
);
},
getInitialState: function() {
return { };
},
render: function() {
var server_error = this.state.server_error ? <label className="control-label">{this.state.server_error}</label> : null;
return (
<div className="signup-team__container">
<div>
<span className="signup-team__name">{ config.SiteName }</span>
<br/>
<span className="signup-team__subdomain">Enter your {strings.Team}'s domain.</span>
<br/>
<br/>
</div>
<form onSubmit={this.handleSubmit}>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
{ server_error }
<input type="text" className="form-control" name="domain" ref="domain" placeholder="team domain" />
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">Continue</button>
</div>
<div>
<span>Don't remember your {strings.Team}'s domain? <a href="/find_team">Find it here</a></span>
</div>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div>
<span>{"Want to create your own " + strings.Team + "?"} <a href={utils.getHomeLink()} className="signup-team-login">Sign up now</a></span>
</div>
</form>
</div>
);
}
});
var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var state = { }
var domain = this.refs.domain.getDOMNode().value.trim();
if (!domain) {
state.server_error = "A domain is required"
var name = this.props.teamName
if (!name) {
state.server_error = "Bad team name"
this.setState(state);
return;
}
@@ -124,23 +43,22 @@ module.exports = React.createClass({
state.server_error = "";
this.setState(state);
client.loginByEmail(domain, email, password,
client.loginByEmail(name, email, password,
function(data) {
UserStore.setLastDomain(domain);
UserStore.setLastEmail(email);
UserStore.setCurrentUser(data);
UserStore.setLastEmail(email);
var redirect = utils.getUrlParameter("redirect");
if (redirect) {
window.location.href = decodeURI(redirect);
window.location.pathname = decodeURI(redirect);
} else {
window.location.href = '/channels/town-square';
window.location.pathname = '/' + name + '/channels/town-square';
}
}.bind(this),
function(err) {
if (err.message == "Login failed because email address has not been verified") {
window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email);
window.location.href = '/verify_email?name=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email);
return;
}
state.server_error = err.message;
@@ -161,35 +79,35 @@ module.exports = React.createClass({
priorEmail = decodeURIComponent(emailParam);
}
var subDomainClass = "form-control hidden";
var subDomain = utils.getSubDomain();
var teamDisplayName = this.props.teamDisplayName;
var teamName = this.props.teamName;
if (utils.isTestDomain()) {
subDomainClass = "form-control";
subDomain = UserStore.getLastDomain();
} else if (subDomain == "") {
return (<FindTeamDomain />);
var focusEmail = false;
var focusPassword = false;
if (priorEmail != "") {
focusPassword = true;
} else {
focusEmail = true;
}
return (
<div className="signup-team__container">
<div>
<span className="signup-team__name">{ subDomain }</span>
<span className="signup-team__name">{ teamDisplayName }</span>
<br/>
<span className="signup-team__subdomain">{ utils.getDomainWithOutSub() }</span>
<span className="signup-team__subdomain">/{ teamName }/</span>
<br/>
<br/>
</div>
<form onSubmit={this.handleSubmit}>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
{ server_error }
<input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
</div>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
<input type="email" className="form-control" name="email" defaultValue={priorEmail} ref="email" placeholder="Email" />
<input autoFocus={focusEmail} type="email" className="form-control" name="email" defaultValue={priorEmail} ref="email" placeholder="Email" />
</div>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
<input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
<input autoFocus={focusPassword} type="password" className="form-control" name="password" ref="password" placeholder="Password" />
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">Sign in</button>
@@ -198,10 +116,10 @@ module.exports = React.createClass({
<span><a href="/find_team">{"Find other " + strings.TeamPlural}</a></span>
</div>
<div className="form-group">
<a href="/reset_password">I forgot my password</a>
<a href={"/" + teamName + "/reset_password"}>I forgot my password</a>
</div>
<div className="external-link">
<span>{"Want to create your own " + strings.Team + "?"} <a href={utils.getHomeLink()} className="signup-team-login">Sign up now</a></span>
<span>{"Want to create your own " + strings.Team + "?"} <a href="/" className="signup-team-login">Sign up now</a></span>
</div>
</form>
</div>

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

@@ -23,6 +23,7 @@ module.exports = React.createClass({
var member = this.props.member;
var isAdmin = this.props.isAdmin;
var isMemberAdmin = member.roles.indexOf("admin") > -1;
var timestamp = UserStore.getCurrentUser().update_at;
var invite;
if (member.invited && this.props.handleInvite) {
@@ -48,12 +49,12 @@ module.exports = React.createClass({
</div>
);
} else {
invite = <div className="member-role text-capitalize" style={{marginRight: 15}}>{member.roles || 'Member'}</div>;
invite = <div className="member-role text-capitalize">{member.roles || 'Member'}<span className="caret hidden"></span></div>;
}
return (
<div className="row member-div">
<img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image"} height="36" width="36" />
<img className="post-profile-img pull-left" src={"/api/v1/users/" + member.id + "/image?time=" + timestamp} height="36" width="36" />
<span className="member-name">{member.username}</span>
<span className="member-email">{member.email}</span>
{ invite }

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

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

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

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

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

@@ -7,19 +7,48 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Mention = require('./mention.jsx');
var Constants = require('../utils/constants.jsx');
var Utils = require('../utils/utils.jsx');
var ActionTypes = Constants.ActionTypes;
var MAX_HEIGHT_LIST = 292;
var MAX_ITEMS_IN_LIST = 25;
var ITEM_HEIGHT = 36;
module.exports = React.createClass({
displayName: "MentionList",
componentDidMount: function() {
PostStore.addMentionDataChangeListener(this._onChange);
var self = this;
$('body').on('keypress.mentionlist', '#'+this.props.id,
$('body').on('keydown.mentionlist', '#'+this.props.id,
function(e) {
if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 13) {
if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) {
e.stopPropagation();
e.preventDefault();
self.addFirstMention();
self.addCurrentMention();
}
else if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 38 || e.which === 40)) {
e.stopPropagation();
e.preventDefault();
var tempSelectedMention = -1;
if (e.which === 38) {
if (self.getSelection(self.state.selectedMention - 1))
self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username });
else {
while (self.getSelection(++tempSelectedMention))
; //Need to find the top of the list
self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username });
}
}
else if (e.which === 40) {
if (self.getSelection(self.state.selectedMention + 1))
self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username });
else
self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username });
}
self.scrollToMention(e.which, tempSelectedMention);
}
}
);
@@ -32,7 +61,28 @@ module.exports = React.createClass({
},
componentWillUnmount: function() {
PostStore.removeMentionDataChangeListener(this._onChange);
$('body').off('keypress.mentionlist', '#'+this.props.id);
$('body').off('keydown.mentionlist', '#'+this.props.id);
},
componentDidUpdate: function() {
if (this.state.mentionText != "-1") {
if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) {
var tempSelectedMention = -1;
var foundMatch = false;
while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) {
if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) {
this.setState({ selectedMention: tempSelectedMention });
foundMatch = true;
break;
}
}
if (this.getSelection(0) && !foundMatch) {
this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username });
}
}
}
else if (this.state.selectedMention !== 0) {
this.setState({ selectedMention: 0, selectedUsername: "" });
}
},
_onChange: function(id, mentionText, excludeList) {
if (id !== this.props.id) return;
@@ -40,6 +90,7 @@ module.exports = React.createClass({
var newState = this.state;
if (mentionText != null) newState.mentionText = mentionText;
if (excludeList != null) newState.excludeUsers = excludeList;
this.setState(newState);
},
handleClick: function(name) {
@@ -51,6 +102,21 @@ module.exports = React.createClass({
this.setState({ mentionText: '-1' });
},
handleMouseEnter: function(listId) {
this.setState({ selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username });
},
getSelection: function(listId) {
if (!this.refs['mention' + listId])
return false;
else
return true;
},
addCurrentMention: function() {
if (!this.getSelection(this.state.selectedMention))
this.addFirstMention();
else
this.refs['mention' + this.state.selectedMention].handleClick();
},
addFirstMention: function() {
if (!this.refs.mention0) return;
this.refs.mention0.handleClick();
@@ -58,6 +124,23 @@ module.exports = React.createClass({
isEmpty: function() {
return (!this.refs.mention0);
},
scrollToMention: function(keyPressed, ifLoopUp) {
var direction = keyPressed === 38 ? "up" : "down";
var scrollAmount = 0;
if (direction === "up" && ifLoopUp !== -1)
scrollAmount = $("#mentionsbox").height() * 100; //Makes sure that it scrolls all the way to the bottom
else if (direction === "down" && this.state.selectedMention === 0)
scrollAmount = 0;
else if (direction === "up")
scrollAmount = "-=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
else if (direction === "down")
scrollAmount = "+=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5);
$("#mentionsbox").animate({
scrollTop: scrollAmount
}, 75);
},
alreadyMentioned: function(username) {
var excludeUsers = this.state.excludeUsers;
for (var i = 0; i < excludeUsers.length; i++) {
@@ -68,11 +151,12 @@ module.exports = React.createClass({
return false;
},
getInitialState: function() {
return { excludeUsers: [], mentionText: "-1" };
return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" };
},
render: function() {
var self = this;
var mentionText = this.state.mentionText;
if (mentionText === '-1') return (<div/>);
if (mentionText === '-1') return null;
var profiles = UserStore.getActiveOnlyProfiles();
var users = [];
@@ -82,14 +166,16 @@ module.exports = React.createClass({
var all = {};
all.username = "all";
all.full_name = "";
all.nickname = "";
all.secondary_text = "Notifies everyone in the team";
all.id = "allmention";
users.push(all);
var channel = {};
channel.username = "channel";
channel.full_name = "";
channel.nickname = "";
channel.secondary_text = "Notifies everyone in the channel";
channel.id = "channelmention";
users.push(channel);
users.sort(function(a,b) {
@@ -100,44 +186,42 @@ module.exports = React.createClass({
var mentions = {};
var index = 0;
for (var i = 0; i < users.length; i++) {
if (Object.keys(mentions).length >= 25) break;
for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) {
if (this.alreadyMentioned(users[i].username)) continue;
var firstName = "", lastName = "";
if (users[i].full_name.length > 0) {
var splitName = users[i].full_name.split(' ');
firstName = splitName[0].toLowerCase();
lastName = splitName.length > 1 ? splitName[splitName.length-1].toLowerCase() : "";
users[i].secondary_text = users[i].full_name;
}
if (firstName.lastIndexOf(mentionText,0) === 0
|| lastName.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) {
mentions[i+1] = (
if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText,0) === 0)
|| (users[i].last_name && users[i].last_name.lastIndexOf(mentionText,0) === 0) || users[i].username.lastIndexOf(mentionText,0) === 0) {
mentions[index] = (
<Mention
ref={'mention' + index}
username={users[i].username}
secondary_text={users[i].secondary_text}
secondary_text={Utils.getFullName(users[i])}
id={users[i].id}
listId={index}
isFocused={this.state.selectedMention === index ? "mentions-focus" : ""}
handleMouseEnter={function(value) { return function() { self.handleMouseEnter(value); } }(index)}
handleClick={this.handleClick} />
);
index++;
}
}
var numMentions = Object.keys(mentions).length;
if (numMentions < 1) return (<div/>);
if (numMentions < 1) return null;
var height = (numMentions*36) + 4;
var width = $('#'+this.props.id).parent().width();
var bottom = $(window).height() - $('#'+this.props.id).offset().top;
var left = $('#'+this.props.id).offset().left;
var max_height = $('#'+this.props.id).offset().top - 10;
var $mention_tab = $('#'+this.props.id);
var maxHeight = Math.min(MAX_HEIGHT_LIST, $mention_tab.offset().top - 10);
var style = {
height: Math.min(maxHeight, (numMentions*ITEM_HEIGHT) + 4),
width: $mention_tab.parent().width(),
bottom: $(window).height() - $mention_tab.offset().top,
left: $mention_tab.offset().left
};
return (
<div className="mentions--top" style={{height: height, width: width, bottom: bottom, left: left}}>
<div ref="mentionlist" className="mentions-box" style={{height: height, width: width}}>
<div className="mentions--top" style={style}>
<div ref="mentionlist" className="mentions-box" id="mentionsbox">
{ mentions }
</div>
</div>

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

@@ -7,6 +7,7 @@ var client = require('../utils/client.jsx');
var asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var LoadingScreen = require('./loading_screen.jsx');
function getStateFromStores() {
return {
@@ -16,6 +17,8 @@ function getStateFromStores() {
}
module.exports = React.createClass({
displayName: "MoreChannelsModal",
componentDidMount: function() {
ChannelStore.addMoreChangeListener(this._onChange);
$(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) {
@@ -90,7 +93,7 @@ module.exports = React.createClass({
<p className="more-channel-name">{channel.display_name}</p>
<p className="more-channel-description">{channel.description}</p>
</td>
<td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="pull-right btn btn-primary">Join</button></td>
<td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="btn btn-primary">Join</button></td>
</tr>
)
})}
@@ -100,15 +103,7 @@ module.exports = React.createClass({
<p className="primary-message">No more channels to join</p>
<p className="secondary-message">Click 'Create New Channel' to make a new one</p>
</div>)
: <div ref="loadingscreen" className="loading-screen loading-screen--channel">
<div className="loading__content">
<h3>Loading</h3>
<div id="round_1" className="round"></div>
<div id="round_2" className="round"></div>
<div id="round_3" className="round"></div>
</div>
</div>
}
: <LoadingScreen /> }
{ server_error }
</div>
<div className="modal-footer">

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

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

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

@@ -2,21 +2,21 @@
// See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var utils = require('../utils/utils.jsx');
var client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var Sidebar = require('./sidebar.jsx');
var UserStore = require('../stores/user_store.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var Constants = require('../utils/constants.jsx');
var TeamStore = require('../stores/team_store.jsx');
var UserProfile = require('./user_profile.jsx');
var MessageWrapper = require('./message_wrapper.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
function getCountsStateFromStores() {
var count = 0;
var channels = ChannelStore.getAll();
var members = ChannelStore.getAllMembers();
@@ -34,7 +34,7 @@ function getCountsStateFromStores() {
}
});
return { count: count }
return { count: count };
}
var NotifyCounts = React.createClass({
@@ -54,99 +54,11 @@ var NotifyCounts = React.createClass({
return getCountsStateFromStores();
},
render: function() {
if (this.state.count == 0) {
return (<span></span>);
if (this.state.count) {
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({
displayName: 'Navbar',
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
ChannelStore.addExtraInfoChangeListener(this._onChange);
var self = this;
$('.inner__wrap').click(self.hideSidebars);
$('.inner__wrap').click(this.hideSidebars);
$('body').on('click.infopopover', function(e){
$('body').on('click.infopopover', function(e) {
if ($(e.target).attr('data-toggle') !== 'popover'
&& $(e.target).parents('.popover.in').length === 0) {
$('.info-popover').popover('hide');
@@ -181,13 +94,13 @@ module.exports = React.createClass({
},
handleLeave: function(e) {
client.leaveChannel(this.state.channel.id,
function(data) {
function(data, text, req) {
AsyncClient.getChannels(true);
window.location.href = '/channels/town-square';
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square';
}.bind(this),
function(err) {
AsyncClient.dispatchError(err, "handleLeave");
}.bind(this)
}
);
},
hideSidebars: function(e) {
@@ -204,7 +117,7 @@ module.exports = React.createClass({
});
if (e.target.className != 'navbar-toggle' && e.target.className != 'icon-bar') {
$('.inner__wrap').removeClass('move--right').removeClass('move--left').removeClass('move--left-small');
$('.inner__wrap').removeClass('move--right move--left move--left-small');
$('.sidebar--left').removeClass('move--right');
$('.sidebar--right').removeClass('move--left');
$('.sidebar--menu').removeClass('move--left');
@@ -229,24 +142,22 @@ module.exports = React.createClass({
render: function() {
var currentId = UserStore.getCurrentId();
var channelName = "";
var popoverContent = "";
var channelTitle = this.props.teamName;
var channelTitle = this.props.teamDisplayName;
var isAdmin = false;
var isDirect = false;
var description = ""
var channel = this.state.channel;
if (this.state.channel) {
var channel = this.state.channel;
description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true});
popoverContent = React.renderToString(<MessageWrapper message={this.state.channel.description}/>);
channelName = this.state.channel.name;
if (channel) {
description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true});
popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
isAdmin = this.state.member.roles.indexOf("admin") > -1;
if (channel.type === 'O') {
channelTitle = this.state.channel.display_name;
channelTitle = channel.display_name;
} else if (channel.type === 'P') {
channelTitle = this.state.channel.display_name;
channelTitle = channel.display_name;
} else if (channel.type === 'D') {
isDirect = true;
if (this.state.users.length > 1) {
@@ -258,12 +169,11 @@ module.exports = React.createClass({
}
}
if(this.state.channel.description.length == 0){
popoverContent = React.renderToString(<div>No channel description yet. <br /><a href='#' data-toggle='modal' data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id} data-target='#edit_channel'>Click here</a> to add one.</div>);
if (channel.description.length == 0) {
popoverContent = React.renderToString(<div>No channel description yet. <br /><a href='#' data-toggle='modal' data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id} data-target='#edit_channel'>Click here</a> to add one.</div>);
}
}
var loginForm = currentId == null ? <NavbarLoginForm /> : null;
var navbar_collapse_button = currentId != null ? null :
<button type="button" className="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
<span className="sr-only">Toggle sidebar</span>
@@ -292,60 +202,55 @@ module.exports = React.createClass({
{ navbar_collapse_button }
{ sidebar_collapse_button }
{ right_sidebar_collapse_button }
{ !isDirect && this.state.channel ?
<div className="navbar-brand">
<div className="dropdown">
<div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
<strong className="heading">{channelTitle} </strong>
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
{ isAdmin ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: ""
}
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={this.state.channel.description} data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Set Channel Description...</a></li>
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Notification Preferences</a></li>
{ isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={this.state.channel.display_name} data-name={this.state.channel.name} data-channelid={this.state.channel.id}>Rename Channel...</a></li>
: ""
}
{ isAdmin && channelName != Constants.DEFAULT_CHANNEL ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={this.state.channel.display_name} data-channelid={this.state.channel.id}>Delete Channel...</a></li>
: ""
}
{ channelName != Constants.DEFAULT_CHANNEL ?
<li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
: ""
}
</ul>
</div>
</div>
: "" }
{ isDirect && this.state.channel ?
{ !isDirect && channel ?
<div className="navbar-brand">
<strong>
<a href="#"><strong className="heading">{channelTitle}</strong></a>
</strong>
<div className="dropdown">
<div data-toggle="popover" data-content={popoverContent} className="description info-popover"></div>
<a href="#" className="dropdown-toggle theme" type="button" id="channel_header_dropdown" data-toggle="dropdown" aria-expanded="true">
<span className="heading">{channelTitle} </span>
<span className="glyphicon glyphicon-chevron-down header-dropdown__icon"></span>
</a>
<ul className="dropdown-menu" role="menu" aria-labelledby="channel_header_dropdown">
{ !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_invite" href="#">Add Members</a></li>
: null
}
{ isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" data-toggle="modal" data-target="#channel_members" href="#">Manage Members</a></li>
: null
}
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}>Set Channel Description...</a></li>
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#channel_notifications" data-title={channel.display_name} data-channelid={channel.id}>Notification Preferences</a></li>
{ isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#rename_channel" data-display={channel.display_name} data-name={channel.name} data-channelid={channel.id}>Rename Channel...</a></li>
: null
}
{ isAdmin && !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" data-toggle="modal" data-target="#delete_channel" data-title={channel.display_name} data-channelid={channel.id}>Delete Channel...</a></li>
: null
}
{ !ChannelStore.isDefault(channel) ?
<li role="presentation"><a role="menuitem" href="#" onClick={this.handleLeave}>Leave Channel</a></li>
: null
}
</ul>
</div>
</div>
: "" }
{ !this.state.channel ?
: null
}
{ isDirect && channel ?
<div className="navbar-brand">
<strong>
<a href="/"><strong className="heading">{ channelTitle }</strong></a>
</strong>
<a href="#" className="heading">{ channelTitle }</a>
</div>
: null }
{ !channel ?
<div className="navbar-brand">
<a href="/" className="heading">{ channelTitle }</a>
</div>
: "" }
</div>
<div className="collapse navbar-collapse" id="navbar-collapse-1">
{ loginForm }
</div>
</div>
</nav>
);
}
}
});

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

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

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

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

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

@@ -10,6 +10,7 @@ var UserStore = require('../stores/user_store.jsx');
var ActionTypes = Constants.ActionTypes;
module.exports = React.createClass({
displayName: "Post",
componentDidMount: function() {
$('.modal').on('show.bs.modal', function () {
$('.modal-body').css('overflow-y', 'auto');
@@ -19,7 +20,7 @@ module.exports = React.createClass({
handleCommentClick: function(e) {
e.preventDefault();
data = {};
var data = {};
data.order = [this.props.post.id];
data.posts = this.props.posts;
@@ -33,6 +34,10 @@ module.exports = React.createClass({
results: null
});
},
forceUpdateInfo: function() {
this.refs.info.forceUpdate();
this.refs.header.forceUpdate();
},
getInitialState: function() {
return { };
},
@@ -48,7 +53,6 @@ module.exports = React.createClass({
var commentCount = 0;
var commentRootId = parentPost ? post.root_id : post.id;
var rootUser = "";
for (var postId in posts) {
if (posts[postId].root_id == commentRootId) {
commentCount += 1;
@@ -57,12 +61,7 @@ module.exports = React.createClass({
var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null;
if (this.props.sameRoot){
rootUser = "same--root";
}
else {
rootUser = "other--root";
}
var rootUser = this.props.sameRoot ? "same--root" : "other--root";
var postType = "";
if (type != "Post"){
@@ -74,18 +73,20 @@ module.exports = React.createClass({
currentUserCss = "current--user";
}
var timestamp = UserStore.getCurrentUser().update_at;
return (
<div>
<div id={post.id} className={"post " + this.props.sameUser + " " + rootUser + " " + postType + " " + currentUserCss}>
{ !this.props.hideProfilePic ?
<div className="post-profile-img__container">
<img className="post-profile-img" src={"/api/v1/users/" + post.user_id + "/image"} height="36" width="36" />
<img className="post-profile-img" src={"/api/v1/users/" + post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
: "" }
: null }
<div className="post__content">
<PostHeader post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
<PostHeader ref="header" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
<PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} />
<PostInfo post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply="true" />
<PostInfo ref="info" post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} allowReply="true" />
</div>
</div>
</div>

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

@@ -71,11 +71,22 @@ module.exports = React.createClass({
name = <a className="theme" onClick={function(){ utils.searchForTerm(profile.username); }}>{profile.username}</a>;
}
var message = parentPost.message;
var message = ""
if(parentPost.message) {
message = utils.replaceHtmlEntities(parentPost.message)
} else if (parentPost.filenames.length) {
message = parentPost.filenames[0].split('/').pop();
if (parentPost.filenames.length === 2) {
message += " plus 1 other file";
} else if (parentPost.filenames.length > 2) {
message += " plus " + (parentPost.filenames.length - 1) + " other files";
}
}
comment = (
<p className="post-link">
<span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{utils.replaceHtmlEntities(message)}</a></span>
<span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{message}</a></span>
</p>
);
@@ -120,7 +131,7 @@ module.exports = React.createClass({
return (
<div className="post-body">
{ comment }
<p key={post.Id+"_message"} className={postClass}><span>{inner}</span></p>
<p key={post.id+"_message"} className={postClass}><span>{inner}</span></p>
{ filenames && filenames.length > 0 ?
<div className="post-image__columns">
{ postFiles }

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

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

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

@@ -6,8 +6,8 @@ var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var UserProfile = require( './user_profile.jsx' );
var AsyncClient = require('../utils/async_client.jsx');
var CreatePost = require('./create_post.jsx');
var Post = require('./post.jsx');
var LoadingScreen = require('./loading_screen.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
@@ -26,37 +26,8 @@ function getStateFromStores() {
};
}
function changeColor(col, amt) {
var usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
var num = parseInt(col,16);
var r = (num >> 16) + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
var b = ((num >> 8) & 0x00FF) + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
var g = (num & 0x0000FF) + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6);
}
module.exports = React.createClass({
displayName: "PostList",
scrollPosition: 0,
preventScrollTrigger: false,
gotMorePosts: false,
@@ -69,7 +40,7 @@ module.exports = React.createClass({
utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;');
utils.changeCss('div.theme', 'background-color:'+user.props.theme+';');
utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';');
utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + changeColor(user.props.theme, -10) +';');
utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';');
utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';');
utils.changeCss('.mention', 'background: ' + user.props.theme+';');
utils.changeCss('.mention-link', 'color: ' + user.props.theme+';');
@@ -78,6 +49,7 @@ module.exports = React.createClass({
PostStore.addChangeListener(this._onChange);
ChannelStore.addChangeListener(this._onChange);
UserStore.addStatusesChangeListener(this._onTimeChange);
SocketStore.addChangeListener(this._onSocketChange);
$(".post-list-holder-by-time").perfectScrollbar();
@@ -157,28 +129,25 @@ module.exports = React.createClass({
componentWillUnmount: function() {
PostStore.removeChangeListener(this._onChange);
ChannelStore.removeChangeListener(this._onChange);
UserStore.removeStatusesChangeListener(this._onTimeChange);
SocketStore.removeChangeListener(this._onSocketChange);
$('body').off('click.userpopover');
},
resize: function() {
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
if (this.gotMorePosts) {
this.gotMorePosts = false;
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
$(post_holder).scrollTop($(post_holder).scrollTop() + (post_holder.scrollHeight-this.oldScrollHeight) );
$(post_holder).perfectScrollbar('update');
} else {
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
if ($("#new_message")[0] && !this.scrolledToNew) {
$(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63);
$(post_holder).perfectScrollbar('update');
this.scrolledToNew = true;
} else {
$(post_holder).scrollTop(post_holder.scrollHeight);
$(post_holder).perfectScrollbar('update');
}
}
$(post_holder).perfectScrollbar('update');
},
_onChange: function() {
var newState = getStateFromStores();
@@ -239,12 +208,8 @@ module.exports = React.createClass({
var index = post_list.order.indexOf(msg.props.post_id);
if (index > -1) post_list.order.splice(index, 1);
var scrollSave = $(".post-list-holder-by-time").scrollTop();
this.setState({ post_list: post_list });
$(".post-list-holder-by-time").scrollTop(scrollSave)
PostStore.storePosts(msg.channel_id, post_list);
} else {
AsyncClient.getPosts(true, msg.channel_id);
@@ -253,10 +218,16 @@ module.exports = React.createClass({
if (activeRootPostId === msg.props.post_id && UserStore.getCurrentId() != msg.user_id) {
$('#post_deleted').modal('show');
}
} else if(msg.action == "new_user") {
} else if (msg.action == "new_user") {
AsyncClient.getProfiles();
}
},
_onTimeChange: function() {
for (var id in this.state.post_list.posts) {
if (!this.refs[id]) continue;
this.refs[id].forceUpdateInfo();
}
},
getMorePosts: function(e) {
e.preventDefault();
@@ -297,7 +268,7 @@ module.exports = React.createClass({
},
function(err) {
$(self.refs.loadmore.getDOMNode()).text("Load more messages");
dispatchError(err, "getPosts");
AsyncClient.dispatchError(err, "getPosts");
}
);
},
@@ -338,16 +309,19 @@ module.exports = React.createClass({
var teammate = utils.getDirectTeammate(channel.id)
if (teammate) {
var teammate_name = teammate.full_name.length > 0 ? teammate.full_name : teammate.username;
var teammate_name = teammate.nickname.length > 0 ? teammate.nickname : teammate.username;
more_messages = (
<div className="channel-intro">
<div className="post-profile-img__container channel-intro-img">
<img className="post-profile-img" src={"/api/v1/users/" + teammate.id + "/image"} height="50" width="50" />
<img className="post-profile-img" src={"/api/v1/users/" + teammate.id + "/image?time=" + teammate.update_at} height="50" width="50" />
</div>
<div className="channel-intro-profile">
<strong><UserProfile userId={teammate.id} /></strong>
</div>
<p className="channel-intro-text">{"This is the start of your private message history with " + teammate_name + "." }<br/>{"Private messages and files shared here are not shown to people outside this area."}</p>
<p className="channel-intro-text">
{"This is the start of your private message history with " + teammate_name + "." }<br/>
{"Private messages and files shared here are not shown to people outside this area."}
</p>
</div>
);
} else {
@@ -370,7 +344,7 @@ module.exports = React.createClass({
}
}
if (channel.name === Constants.DEFAULT_CHANNEL) {
if (ChannelStore.isDefault(channel)) {
more_messages = (
<div className="channel-intro">
<h4 className="channel-intro-title">Welcome</h4>
@@ -410,7 +384,7 @@ module.exports = React.createClass({
{ channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
<br/>
<a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}><i className="fa fa-pencil"></i>Set a description</a>
<a className="intro-links" style={userStyle} data-toggle="modal" data-target="#channel_invite"><i className="fa fa-user-plus"></i>Invite others to this {ui_type}</a>
<a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#channel_invite"><i className="fa fa-user-plus"></i>Invite others to this {ui_type}</a>
</p>
</div>
);
@@ -420,37 +394,35 @@ module.exports = React.createClass({
var postCtls = [];
if (posts != undefined) {
if (posts) {
var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date();
var currentPostDay = new Date();
var currentPostDay;
for (var i = order.length-1; i >= 0; i--) {
var post = posts[order[i]];
var parentPost;
var parentPost = post.parent_id ? posts[post.parent_id] : null;
if (post.parent_id) {
parentPost = posts[post.parent_id];
} else {
parentPost = null;
var sameUser = '';
var sameRoot = false;
var hideProfilePic = false;
var prevPost = (i < order.length - 1) ? posts[order[i + 1]] : null;
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
var isLastComment = false;
if (utils.isComment(post)) {
// it is the last comment if it is last post in the channel or the next post has a different root post
isLastComment = (i === 0 || posts[order[i-1]].root_id != post.root_id);
}
// it is the last comment if it is last post in the channel or the next post has a different root post
var isLastComment = utils.isComment(post) && (i === 0 || posts[order[i-1]].root_id != post.root_id);
var postCtl = <Post sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id} posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} />;
var postCtl = <Post ref={post.id} sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id} posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} />;
currentPostDay = utils.getDateForUnixTicks(post.create_at);
if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) {
if (currentPostDay.toDateString() != previousPostDay.toDateString()) {
postCtls.push(
<div className="date-separator">
<hr className="separator__hr" />
@@ -469,20 +441,10 @@ module.exports = React.createClass({
);
}
postCtls.push(postCtl);
previousPostDay = utils.getDateForUnixTicks(post.create_at);
previousPostDay = currentPostDay;
}
}
else {
postCtls.push(
<div ref="loadingscreen" className="loading-screen">
<div className="loading__content">
<h3>Loading</h3>
<div id="round_1" className="round"></div>
<div id="round_2" className="round"></div>
<div id="round_3" className="round"></div>
</div>
</div>
);
} else {
postCtls.push(<LoadingScreen position="absolute" />);
}
return (
@@ -497,5 +459,3 @@ module.exports = React.createClass({
);
}
});

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

@@ -43,7 +43,7 @@ RhsHeaderPost = React.createClass({
});
},
render: function() {
var back = this.props.fromSearch ? <a href="#" onClick={this.handleBack} style={{color:"black"}}>{"< "}</a> : "";
var back = this.props.fromSearch ? <a href="#" onClick={this.handleBack} className="sidebar--right__back"><i className="fa fa-chevron-left"></i></a> : "";
return (
<div className="sidebar--right__header">
@@ -67,6 +67,8 @@ RootPost = React.createClass({
var message = utils.textToJsx(this.props.post.message);
var filenames = this.props.post.filenames;
var isOwner = UserStore.getCurrentId() == this.props.post.user_id;
var timestamp = UserStore.getProfile(this.props.post.user_id).update_at;
var channel = ChannelStore.get(this.props.post.channel_id);
var type = "Post";
if (this.props.post.root_id.length > 0) {
@@ -78,6 +80,10 @@ RootPost = React.createClass({
currentUserCss = "current--user";
}
if (channel) {
channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
}
if (filenames) {
var postFiles = [];
var images = [];
@@ -117,8 +123,9 @@ RootPost = React.createClass({
return (
<div className={"post post--root " + currentUserCss}>
<div className="post-right-channel__name">{ channelName }</div>
<div className="post-profile-img__container">
<img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image"} height="36" width="36" />
<img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
<div className="post__content">
<ul className="post-header">
@@ -128,9 +135,7 @@ RootPost = React.createClass({
<div className="dropdown">
{ isOwner ?
<div>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
[...]
</a>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={this.props.post.message} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id}>Edit</a></li>
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id} data-comments={this.props.commentCount}>Delete</a></li>
@@ -227,11 +232,12 @@ CommentPost = React.createClass({
}
var message = utils.textToJsx(this.props.post.message);
var timestamp = UserStore.getCurrentUser().update_at;
return (
<div className={commentClass + " " + currentUserCss}>
<div className="post-profile-img__container">
<img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image"} height="36" width="36" />
<img className="post-profile-img" src={"/api/v1/users/" + this.props.post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div>
<div className="post__content">
<ul className="post-header">
@@ -240,9 +246,7 @@ CommentPost = React.createClass({
<li className="post-header-col post-header__reply">
{ isOwner ?
<div className="dropdown" onClick={function(e){$('.post-list-holder-by-time').scrollTop($(".post-list-holder-by-time").scrollTop() + 50);}}>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false">
[...]
</a>
<a href="#" className="dropdown-toggle theme" type="button" data-toggle="dropdown" aria-expanded="false" />
<ul className="dropdown-menu" role="menu">
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#edit_post" data-title={type} data-message={this.props.post.message} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id}>Edit</a></li>
<li role="presentation"><a href="#" role="menuitem" data-toggle="modal" data-target="#delete_post" data-title={type} data-postid={this.props.post.id} data-channelid={this.props.post.channel_id} data-comments={0}>Delete</a></li>
@@ -282,6 +286,7 @@ module.exports = React.createClass({
componentDidMount: function() {
PostStore.addSelectedPostChangeListener(this._onChange);
PostStore.addChangeListener(this._onChangeAll);
UserStore.addStatusesChangeListener(this._onTimeChange);
this.resize();
var self = this;
$(window).resize(function(){
@@ -294,6 +299,7 @@ module.exports = React.createClass({
componentWillUnmount: function() {
PostStore.removeSelectedPostChangeListener(this._onChange);
PostStore.removeChangeListener(this._onChangeAll);
UserStore.removeStatusesChangeListener(this._onTimeChange);
},
_onChange: function() {
if (this.isMounted()) {
@@ -304,7 +310,6 @@ module.exports = React.createClass({
}
},
_onChangeAll: function() {
if (this.isMounted()) {
// if something was changed in the channel like adding a
@@ -333,6 +338,12 @@ module.exports = React.createClass({
this.setState(getStateFromStores());
}
},
_onTimeChange: function() {
for (var id in this.state.post_list.posts) {
if (!this.refs[id]) continue;
this.refs[id].forceUpdate();
}
},
getInitialState: function() {
return getStateFromStores();
},
@@ -392,7 +403,7 @@ module.exports = React.createClass({
<RootPost post={root_post} commentCount={posts_array.length}/>
<div className="post-right-comments-container">
{ posts_array.map(function(cpost) {
return <CommentPost key={cpost.id} post={cpost} selected={ (cpost.id == selected_post.id) } />
return <CommentPost ref={cpost.id} key={cpost.id} post={cpost} selected={ (cpost.id == selected_post.id) } />
})}
</div>
<div className="post-create__container">

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

@@ -6,6 +6,8 @@ var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var Constants = require('../utils/constants.jsx');
module.exports = React.createClass({
handleSubmit: function(e) {
@@ -60,12 +62,12 @@ module.exports = React.createClass({
return;
Client.updateChannel(channel,
function(data) {
function(data, text, req) {
this.refs.display_name.getDOMNode().value = "";
this.refs.channel_name.getDOMNode().value = "";
$('#' + this.props.modalId).modal('hide');
window.location.href = '/channels/' + this.state.channel_name;
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + this.state.channel_name;
AsyncClient.getChannels(true);
}.bind(this),
function(err) {

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

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

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

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

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

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

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

@@ -6,6 +6,7 @@ var ChannelStore = require('../stores/channel_store.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx');
@@ -13,93 +14,6 @@ var SearchBox = require('./search_bar.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
var SidebarLoginForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var state = { }
var domain = this.refs.domain.getDOMNode().value.trim();
if (!domain) {
state.server_error = "A domain is required"
this.setState(state);
return;
}
var email = this.refs.email.getDOMNode().value.trim();
if (!email) {
state.server_error = "An email is required"
this.setState(state);
return;
}
var password = this.refs.password.getDOMNode().value.trim();
if (!password) {
state.server_error = "A password is required"
this.setState(state);
return;
}
state.server_error = "";
this.setState(state);
client.loginByEmail(domain, email, password,
function(data) {
UserStore.setLastDomain(domain);
UserStore.setLastEmail(email);
UserStore.setCurrentUser(data);
var redirect = utils.getUrlParameter("redirect");
if (redirect) {
window.location.href = decodeURI(redirect);
} else {
window.location.href = '/channels/town-square';
}
}.bind(this),
function(err) {
if (err.message == "Login failed because email address has not been verified") {
window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email);
return;
}
state.server_error = err.message;
this.valid = false;
this.setState(state);
}.bind(this)
);
},
getInitialState: function() {
return { };
},
render: function() {
var server_error = this.state.server_error ? <label className="control-label">{this.state.server_error}</label> : null;
var subDomain = utils.getSubDomain();
var subDomainClass = "form-control hidden";
if (subDomain == "") {
subDomain = UserStore.getLastDomain();
subDomainClass = "form-control";
}
return (
<form className="" onSubmit={this.handleSubmit}>
<a href="/find_team">{"Find your " + strings.Team}</a>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
{ server_error }
<input type="text" className={subDomainClass} name="domain" defaultValue={subDomain} ref="domain" placeholder="Domain" />
</div>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
<input type="text" className="form-control" name="email" defaultValue={UserStore.getLastEmail()} ref="email" placeholder="Email" />
</div>
<div className={server_error ? 'form-group has-error' : 'form-group'}>
<input type="password" className="form-control" name="password" ref="password" placeholder="Password" />
</div>
<button type="submit" className="btn btn-default">Login</button>
</form>
);
}
});
function getStateFromStores() {
var members = ChannelStore.getAllMembers();
var team_member_map = UserStore.getActiveOnlyProfiles();
@@ -131,7 +45,7 @@ function getStateFromStores() {
var channel = ChannelStore.getByName(channelName);
if (channel != null) {
channel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username;
channel.display_name = utils.getDisplayName(teammate);
channel.teammate_username = teammate.username;
channel.status = UserStore.getStatus(teammate.id);
@@ -150,7 +64,7 @@ function getStateFromStores() {
var tempChannel = {};
tempChannel.fake = true;
tempChannel.name = channelName;
tempChannel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username;
tempChannel.display_name = utils.getDisplayName(teammate);
tempChannel.status = UserStore.getStatus(teammate.id);
tempChannel.last_post_at = 0;
readDirectChannels.push(tempChannel);
@@ -192,7 +106,7 @@ function getStateFromStores() {
};
}
var SidebarLoggedIn = React.createClass({
module.exports = React.createClass({
componentDidMount: function() {
ChannelStore.addChangeListener(this._onChange);
UserStore.addChangeListener(this._onChange);
@@ -249,11 +163,27 @@ var SidebarLoggedIn = React.createClass({
var repRegex = new RegExp("<br>", "g");
var post = JSON.parse(msg.props.post);
var msg = post.message.replace(repRegex, "\n").split("\n")[0].replace("<mention>", "").replace("</mention>", "");
var msgProps = msg.props;
var msg = post.message.replace(repRegex, "\n").replace(/\n+/g, " ").replace("<mention>", "").replace("</mention>", "");
if (msg.length > 50) {
msg = msg.substring(0,49) + "...";
}
utils.notifyMe(title, username + " wrote: " + msg, channel);
if (msg.length === 0) {
if (msgProps.image) {
utils.notifyMe(title, username + " uploaded an image", channel);
}
else if (msgProps.otherFile) {
utils.notifyMe(title, username + " uploaded a file", channel);
}
else {
utils.notifyMe(title, username + " did something new", channel);
}
}
else {
utils.notifyMe(title, username + " wrote: " + msg, channel);
}
if (!user.notify_props || user.notify_props.desktop_sound === "true") {
utils.ding();
}
@@ -367,7 +297,7 @@ var SidebarLoggedIn = React.createClass({
);
} else {
return (
<li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href={"/channels/"+channel.name}><span className="status" dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
<li key={channel.name} className={active}><a className={"sidebar-channel " + titleClass} href={TeamStore.getCurrentTeamUrl() + "/channels/"+channel.name}><span className="status" dangerouslySetInnerHTML={{__html: statusIcon}} /> {badge}{channel.display_name}</a></li>
);
}
@@ -398,7 +328,7 @@ var SidebarLoggedIn = React.createClass({
}
return (
<div>
<SidebarHeader teamName={this.props.teamName} teamType={this.props.teamType} />
<SidebarHeader teamDisplayName={this.props.teamDisplayName} teamType={this.props.teamType} />
<SearchBox />
<div className="nav-pills__container">
@@ -424,25 +354,3 @@ var SidebarLoggedIn = React.createClass({
);
}
});
var SidebarLoggedOut = React.createClass({
render: function() {
return (
<div>
<SidebarHeader teamName={this.props.teamName} />
<SidebarLoginForm />
</div>
);
}
});
module.exports = React.createClass({
render: function() {
var currentId = UserStore.getCurrentId();
if (currentId != null) {
return <SidebarLoggedIn teamName={this.props.teamName} teamType={this.props.teamType} />;
} else {
return <SidebarLoggedOut teamName={this.props.teamName} />;
}
}
});

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

@@ -37,17 +37,13 @@ var NavbarDropdown = React.createClass({
var invite_link = "";
var manage_link = "";
var rename_link = "";
var currentUser = UserStore.getCurrentUser()
var currentUser = UserStore.getCurrentUser();
var isAdmin = false;
if (currentUser != null) {
isAdmin = currentUser.roles.indexOf("admin") > -1;
invite_link = (
<li>
<a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a>
</li>
);
invite_link = ( <li> <a href="#" data-toggle="modal" data-target="#invite_member">Invite New Member</a> </li>);
if (this.props.teamType == "O") {
team_link = (
@@ -59,43 +55,31 @@ var NavbarDropdown = React.createClass({
}
if (isAdmin) {
manage_link = (
<li>
<a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a>
</li>
);
rename_link = (
<li>
<a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a>
</li>
);
manage_link = ( <li> <a href="#" data-toggle="modal" data-target="#team_members">Manage Team</a> </li>);
rename_link = ( <li> <a href="#" data-toggle="modal" data-target="#rename_team_link">Rename</a> </li>);
}
var teams = [];
teams.push(<li className="divider" key="div"></li>);
if (this.state.teams.length > 1) {
for (var i = 0; i < this.state.teams.length; i++) {
var domain = this.state.teams[i];
var teamName = this.state.teams[i];
if (domain == utils.getSubDomain())
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 key={ teamName }><a href={window.location.origin + "/" + teamName }>Switch to { teamName }</a></li>);
}
}
teams.push(<li><a href={window.location.origin + "/signup_team" }>Create a New Team</a></li>);
return (
<ul className="nav navbar-nav navbar-right">
<li className="dropdown">
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
<i className="dropdown__icon"></i>
<span className="dropdown__icon" dangerouslySetInnerHTML={{__html: " <svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>"}} />
</a>
<ul className="dropdown-menu" role="menu">
<li><a href="#" data-toggle="modal" data-target="#user_settings1">Account Settings</a></li>
{ isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : "" }
{ isAdmin ? <li><a href="#" data-toggle="modal" data-target="#team_settings">Team Settings</a></li> : null }
{ invite_link }
{ team_link }
{ manage_link }
@@ -113,23 +97,32 @@ var NavbarDropdown = React.createClass({
});
module.exports = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
},
getInitialState: function() {
return { };
displayName: 'SidebarHeader',
getDefaultProps: function() {
return {
teamName: config.SiteName
};
},
render: function() {
var teamName = this.props.teamName ? this.props.teamName : config.SiteName;
var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : config.SiteName;
var me = UserStore.getCurrentUser()
if (!me) {
return null;
}
return (
<div className="team__header theme">
<a className="team__name" href="/channels/town-square">{ teamName }</a>
<a className="settings_link" href="#" data-toggle="modal" data-target="#user_settings1">
<img className="user__picture" src={"/api/v1/users/" + me.id + "/image?time=" + me.update_at} />
<div className="header__info">
<div className="user__name">{ '@' + me.username}</div>
<div className="team__name">{ teamDisplayName }</div>
</div>
</a>
<NavbarDropdown teamType={this.props.teamType} />
</div>
);
}
});

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

@@ -49,12 +49,12 @@ module.exports = React.createClass({
}
var siteName = config.SiteName != null ? config.SiteName : "";
var teamName = this.props.teamName ? this.props.teamName : siteName;
var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : siteName;
return (
<div>
<div className="team__header theme">
<a className="team__name" href="/channels/town-square">{ teamName }</a>
<a className="team__name" href="/channels/town-square">{ teamDisplayName }</a>
</div>
<div className="nav-pills__container">

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

@@ -20,8 +20,8 @@ module.exports = React.createClass({
state.email_error = "";
}
team.name = this.refs.name.getDOMNode().value.trim();
if (!team.name) {
team.display_name = this.refs.name.getDOMNode().value.trim();
if (!team.display_name) {
state.name_error = "This field is required";
state.inValid = true;
}
@@ -34,7 +34,7 @@ module.exports = React.createClass({
return;
}
client.signupTeam(team.email, team.name,
client.signupTeam(team.email, team.display_name,
function(data) {
if (data["follow_link"]) {
window.location.href = data["follow_link"];
@@ -61,7 +61,7 @@ module.exports = React.createClass({
return (
<form role="form" onSubmit={this.handleSubmit}>
<div className={ email_error ? "form-group has-error" : "form-group" }>
<input type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" />
<input autoFocus={true} type="email" ref="email" className="form-control" placeholder="Email Address" maxLength="128" />
{ email_error }
</div>
<div className={ name_error ? "form-group has-error" : "form-group" }>
@@ -70,6 +70,9 @@ module.exports = React.createClass({
</div>
{ server_error }
<button className="btn btn-md btn-primary" type="submit">Sign up for Free</button>
<div className="form-group form-group--small">
<span><a href="/find_team">{"Find my " + strings.Team}</a></span>
</div>
</form>
);
}

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

@@ -15,7 +15,7 @@ WelcomePage = React.createClass({
return;
}
e.preventDefault();
this.props.state.wizard = "team_name";
this.props.state.wizard = "team_display_name";
this.props.updateParent(this.props.state);
},
handleDiffEmail: function (e) {
@@ -57,6 +57,17 @@ WelcomePage = React.createClass({
getInitialState: function() {
return { use_diff: false };
},
handleKeyPress: function(event) {
if (event.keyCode == 13) {
this.submitNext(event);
}
},
componentWillMount: function() {
document.addEventListener("keyup", this.handleKeyPress, false);
},
componentWillUnmount: function() {
document.removeEventListener("keyup", this.handleKeyPress, false);
},
render: function() {
client.track('signup', 'signup_team_01_welcome');
@@ -77,7 +88,7 @@ WelcomePage = React.createClass({
<span className="black">{ this.props.state.team.email }</span><br />
</p>
<div className="form-group">
<button className="btn-primary btn form-group" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
<button className="btn-primary btn form-group" type="submit" onClick={this.submitNext}><i className="glyphicon glyphicon-ok"></i>Yes, this address is correct</button>
{ storage_error }
</div>
<hr />
@@ -92,15 +103,15 @@ WelcomePage = React.createClass({
{ email_error }
</div>
{ server_error }
<button className="btn btn-md btn-primary" onClick={this.handleDiffSubmit} type="submit">Use this instead</button>
<button className="btn btn-md btn-primary" type="button" onClick={this.handleDiffSubmit} type="submit">Use this instead</button>
</div>
<button onClick={this.handleDiffEmail} className={ this.state.use_diff ? "btn-default btn hidden" : "btn-default btn" }>Use a different address</button>
<button type="button" onClick={this.handleDiffEmail} className={ this.state.use_diff ? "btn-default btn hidden" : "btn-default btn" }>Use a different address</button>
</div>
);
}
});
TeamNamePage = React.createClass({
TeamDisplayNamePage = React.createClass({
submitBack: function (e) {
e.preventDefault();
this.props.state.wizard = "welcome";
@@ -109,19 +120,24 @@ TeamNamePage = React.createClass({
submitNext: function (e) {
e.preventDefault();
var name = this.refs.name.getDOMNode().value.trim();
if (!name) {
var display_name = this.refs.name.getDOMNode().value.trim();
if (!display_name) {
this.setState({name_error: "This field is required"});
return;
}
this.props.state.wizard = "team_url";
this.props.state.team.name = name;
this.props.state.team.display_name = display_name;
this.props.updateParent(this.props.state);
},
getInitialState: function() {
return { };
},
handleFocus: function(e) {
e.preventDefault();
e.currentTarget.select();
},
render: function() {
client.track('signup', 'signup_team_02_name');
@@ -130,29 +146,31 @@ TeamNamePage = React.createClass({
return (
<div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>{utils.toTitleCase(strings.Team) + " Name"}</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.name} />
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.display_name} autoFocus={true} onFocus={this.handleFocus} />
</div>
</div>
{ name_error }
</div>
<p>{"Your " + strings.Team + " name shows in menus and headings. It may include the name of your " + strings.Company + ", but it's not required."}</p>
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div>
);
}
});
TeamUrlPage = React.createClass({
TeamURLPage = React.createClass({
submitBack: function (e) {
e.preventDefault();
this.props.state.wizard = "team_name";
this.props.state.wizard = "team_display_name";
this.props.updateParent(this.props.state);
},
submitNext: function (e) {
@@ -172,18 +190,18 @@ TeamUrlPage = React.createClass({
return;
}
else if (cleaned_name.length <= 3 || cleaned_name.length > 15) {
this.setState({name_error: "Domain must be 4 or more characters up to a maximum of 15"})
this.setState({name_error: "Name must be 4 or more characters up to a maximum of 15"})
return;
}
for (var index = 0; index < constants.RESERVED_DOMAINS.length; index++) {
if (cleaned_name.indexOf(constants.RESERVED_DOMAINS[index]) == 0) {
this.setState({name_error: "This Team URL name is unavailable"})
for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) {
if (cleaned_name.indexOf(constants.RESERVED_TEAM_NAMES[index]) == 0) {
this.setState({name_error: "This team name is unavailable"})
return;
}
}
client.findTeamByDomain(name,
client.findTeamByName(name,
function(data) {
if (!data) {
if (config.AllowSignupDomainsWizard) {
@@ -193,7 +211,7 @@ TeamUrlPage = React.createClass({
this.props.state.team.type = 'O';
}
this.props.state.team.domain = name;
this.props.state.team.name = name;
this.props.updateParent(this.props.state);
}
else {
@@ -210,6 +228,11 @@ TeamUrlPage = React.createClass({
getInitialState: function() {
return { };
},
handleFocus: function(e) {
e.preventDefault();
e.currentTarget.select();
},
render: function() {
client.track('signup', 'signup_team_03_url');
@@ -218,14 +241,15 @@ TeamUrlPage = React.createClass({
return (
<div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>{utils.toTitleCase(strings.Team) + " URL"}</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
<div className="input-group">
<input type="text" ref="name" className="form-control text-right" placeholder="" maxLength="128" defaultValue={this.props.state.team.domain} />
<span className="input-group-addon">.{ utils.getDomainWithOutSub() }</span>
<span className="input-group-addon">{ window.location.origin + "/" }</span>
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.name} autoFocus={true} onFocus={this.handleFocus}/>
</div>
</div>
</div>
@@ -233,8 +257,9 @@ TeamUrlPage = React.createClass({
</div>
<p className="black">{"Pick something short and memorable for your " + strings.Team + "'s web address."}</p>
<p>{"Your " + strings.Team + " URL can only contain lowercase letters, numbers and dashes. Also, it needs to start with a letter and cannot end in a dash."}</p>
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div>
);
}
@@ -291,6 +316,7 @@ AllowedDomainsPage = React.createClass({
return (
<div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Email Domain</h2>
<p>
@@ -303,7 +329,7 @@ AllowedDomainsPage = React.createClass({
<div className="col-sm-9">
<div className="input-group">
<span className="input-group-addon">@</span>
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.allowed_domains} />
<input type="text" ref="name" className="form-control" placeholder="" maxLength="128" defaultValue={this.props.state.team.allowed_domains} autoFocus={true} onFocus={this.handleFocus}/>
</div>
</div>
</div>
@@ -313,8 +339,9 @@ AllowedDomainsPage = React.createClass({
<p>
<div className="checkbox"><label><input type="checkbox" ref="open_network" defaultChecked={this.props.state.team.type == 'O'} /> Allow anyone to signup to this domain without an invitation.</label></div>
</p>
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div>
);
}
@@ -356,7 +383,7 @@ EmailItem = React.createClass({
return (
<div className={ email_error ? "form-group has-error" : "form-group" }>
<input type="email" ref="email" className="form-control" placeholder="Email Address" defaultValue={this.props.email} maxLength="128" />
<input autoFocus={this.props.focus} type="email" ref="email" className="form-control" placeholder="Email Address" defaultValue={this.props.email} maxLength="128" />
{ email_error }
</div>
);
@@ -424,16 +451,22 @@ SendInivtesPage = React.createClass({
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
emails.push(<EmailItem key={i} ref={'email_' + i} email={this.props.state.invites[i]} />);
if (i == 0) {
emails.push(<EmailItem focus={true} key={i} ref={'email_' + i} email={this.props.state.invites[i]} />);
} else {
emails.push(<EmailItem focus={false} key={i} ref={'email_' + i} email={this.props.state.invites[i]} />);
}
}
return (
<div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Send Invitations</h2>
{ emails }
<div className="form-group"><button className="btn-default btn" onClick={this.submitAddInvite}>Add Invitation</button></div>
<div className="form btn-default-group"><button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;<button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button></div>
<div className="form-group"><button type="button" className="btn-default btn" onClick={this.submitAddInvite}>Add Invitation</button></div>
<div className="form btn-default-group"><button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button></div>
</form>
<p>{"If you'd prefer, you can send invitations after you finish setting up the "+ strings.Team + "."}</p>
<div><a href="#" onClick={this.submitSkip}>Skip this step</a></div>
</div>
@@ -477,20 +510,22 @@ UsernamePage = React.createClass({
return (
<div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a username</h2>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
<input type="text" ref="name" className="form-control" placeholder="" defaultValue={this.props.state.user.username} maxLength="128" />
<input autoFocus={true} type="text" ref="name" className="form-control" placeholder="" defaultValue={this.props.state.user.username} maxLength="128" />
</div>
</div>
{ name_error }
</div>
<p>{"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others."}</p>
<p>It can be made of lowercase letters and numbers.</p>
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button type="submit" className="btn-primary btn" onClick={this.submitNext}>Next<i className="glyphicon glyphicon-chevron-right"></i></button>
</form>
</div>
);
}
@@ -531,18 +566,11 @@ PasswordPage = React.createClass({
props.state.wizard = "finished";
props.updateParent(props.state, true);
if (utils.isTestDomain()) {
UserStore.setLastDomain(teamSignup.team.domain);
UserStore.setLastEmail(teamSignup.team.email);
window.location.href = window.location.protocol + '//' + utils.getDomainWithOutSub() + '/login?email=' + encodeURIComponent(teamSignup.team.email);
}
else {
window.location.href = window.location.protocol + '//' + teamSignup.team.domain + '.' + utils.getDomainWithOutSub() + '/login?email=' + encodeURIComponent(teamSignup.team.email);
}
window.location.href = window.location.origin + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email);
// client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password,
// function(data) {
// UserStore.setLastDomain(teamSignup.team.domain);
// TeamStore.setLastName(teamSignup.team.domain);
// UserStore.setLastEmail(teamSignup.team.email);
// UserStore.setCurrentUser(data);
// window.location.href = '/channels/town-square';
@@ -570,13 +598,14 @@ PasswordPage = React.createClass({
return (
<div>
<form>
<img className="signup-team-logo" src="/static/images/logo.png" />
<h2>Choose a password</h2>
<p>You'll use your email address ({this.props.state.team.email}) and password to log into {config.SiteName}.</p>
<div className={ name_error ? "form-group has-error" : "form-group" }>
<div className="row">
<div className="col-sm-9">
<input type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
<input autoFocus={true} type="password" ref="password" className="form-control" placeholder="" maxLength="128" />
</div>
</div>
{ name_error }
@@ -585,10 +614,11 @@ PasswordPage = React.createClass({
<label><input type="checkbox" ref="email_service" /> It's ok to send me occassional email with updates about the {config.SiteName} service.</label>
</div>
<div className="form-group">
<button className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button className="btn-primary btn" id="finish-button" data-loading-text={"<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Creating "+strings.Team+"..."} onClick={this.submitNext}>Finish</button>
<button type="button" className="btn btn-default" onClick={this.submitBack}><i className="glyphicon glyphicon-chevron-left"></i> Back</button>&nbsp;
<button type="submit" className="btn-primary btn" id="finish-button" data-loading-text={"<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Creating "+strings.Team+"..."} onClick={this.submitNext}>Finish</button>
</div>
<p>By proceeding to create your account and use { config.SiteName }, you agree to our <a href={ config.TermsLink }>Terms of Service</a> and <a href={ config.PrivacyLink }>Privacy Policy</a>. If you do not agree, you cannot use {config.SiteName}.</p>
</form>
</div>
);
}
@@ -596,28 +626,23 @@ PasswordPage = React.createClass({
module.exports = React.createClass({
updateParent: function(state, skipSet) {
BrowserStore.setGlobalItem(this.props.hash, JSON.stringify(state));
BrowserStore.setGlobalItem(this.props.hash, state);
if (!skipSet) {
this.setState(state);
}
},
getInitialState: function() {
var props = null;
try {
props = JSON.parse(BrowserStore.getGlobalItem(this.props.hash));
}
catch(parse_error) {
}
var props = BrowserStore.getGlobalItem(this.props.hash);
if (!props) {
props = {};
props.wizard = "welcome";
props.team = {};
props.team.email = this.props.email;
props.team.name = this.props.name;
props.team.display_name = this.props.name;
props.team.company_name = this.props.name;
props.team.domain = utils.cleanUpUrlable(this.props.name);
props.team.name = utils.cleanUpUrlable(this.props.name);
props.team.allowed_domains = "";
props.invites = [];
props.invites.push("");
@@ -628,19 +653,19 @@ module.exports = React.createClass({
props.data = this.props.data;
}
return props ;
return props;
},
render: function() {
if (this.state.wizard == "welcome") {
return <WelcomePage state={this.state} updateParent={this.updateParent} />
}
if (this.state.wizard == "team_name") {
return <TeamNamePage state={this.state} updateParent={this.updateParent} />
if (this.state.wizard == "team_display_name") {
return <TeamDisplayNamePage state={this.state} updateParent={this.updateParent} />
}
if (this.state.wizard == "team_url") {
return <TeamUrlPage state={this.state} updateParent={this.updateParent} />
return <TeamURLPage state={this.state} updateParent={this.updateParent} />
}
if (this.state.wizard == "allowed_domains") {

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