Merge pull request #1129 from mattermost/PLT-44

PLT-44 Inital support for multi-tab multi-team login
Этот коммит содержится в:
Corey Hulen
2015-10-22 21:35:29 -07:00
родитель 5ecf45e109 7384b0791e
Коммит 6a1934192f
88 изменённых файлов: 658 добавлений и 491 удалений

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

@@ -24,7 +24,7 @@ func InitAdmin(r *mux.Router) {
sr.Handle("/config", ApiUserRequired(getConfig)).Methods("GET") sr.Handle("/config", ApiUserRequired(getConfig)).Methods("GET")
sr.Handle("/save_config", ApiUserRequired(saveConfig)).Methods("POST") sr.Handle("/save_config", ApiUserRequired(saveConfig)).Methods("POST")
sr.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST") sr.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST")
sr.Handle("/client_props", ApiAppHandler(getClientProperties)).Methods("GET") sr.Handle("/client_props", ApiAppHandler(getClientConfig)).Methods("GET")
sr.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST") sr.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST")
} }
@@ -57,8 +57,8 @@ func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.ArrayToJson(lines))) w.Write([]byte(model.ArrayToJson(lines)))
} }
func getClientProperties(c *Context, w http.ResponseWriter, r *http.Request) { func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.MapToJson(utils.ClientProperties))) w.Write([]byte(model.MapToJson(utils.ClientCfg)))
} }
func logClient(c *Context, w http.ResponseWriter, r *http.Request) { func logClient(c *Context, w http.ResponseWriter, r *http.Request) {

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

@@ -20,7 +20,7 @@ func NewServerTemplatePage(templateName string) *ServerTemplatePage {
return &ServerTemplatePage{ return &ServerTemplatePage{
TemplateName: templateName, TemplateName: templateName,
Props: make(map[string]string), Props: make(map[string]string),
ClientProps: utils.ClientProperties, ClientCfg: utils.ClientCfg,
} }
} }

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

@@ -8,6 +8,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
l4g "code.google.com/p/log4go" l4g "code.google.com/p/log4go"
@@ -19,20 +20,24 @@ import (
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE) var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
type Context struct { type Context struct {
Session model.Session Session model.Session
RequestId string RequestId string
IpAddress string IpAddress string
Path string Path string
Err *model.AppError Err *model.AppError
teamURLValid bool teamURLValid bool
teamURL string teamURL string
siteURL string siteURL string
SessionTokenIndex int64
} }
type Page struct { type Page struct {
TemplateName string TemplateName string
Props map[string]string Props map[string]string
ClientProps map[string]string ClientCfg map[string]string
User *model.User
Team *model.Team
SessionTokenIndex int64
} }
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
@@ -96,8 +101,37 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Attempt to parse the token from the cookie // Attempt to parse the token from the cookie
if len(token) == 0 { if len(token) == 0 {
if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil { tokens := GetMultiSessionCookieTokens(r)
token = cookie.Value if len(tokens) > 0 {
// If there is only 1 token in the cookie then just use it like normal
if len(tokens) == 1 {
token = tokens[0]
} else {
// If it is a multi-session token then find the correct session
sessionTokenIndexStr := r.URL.Query().Get(model.SESSION_TOKEN_INDEX)
sessionTokenIndex := int64(-1)
if len(sessionTokenIndexStr) > 0 {
if index, err := strconv.ParseInt(sessionTokenIndexStr, 10, 64); err == nil {
sessionTokenIndex = index
}
} else {
sessionTokenIndexStr := r.Header.Get(model.HEADER_MM_SESSION_TOKEN_INDEX)
if len(sessionTokenIndexStr) > 0 {
if index, err := strconv.ParseInt(sessionTokenIndexStr, 10, 64); err == nil {
sessionTokenIndex = index
}
}
}
if sessionTokenIndex >= 0 && sessionTokenIndex < int64(len(tokens)) {
token = tokens[sessionTokenIndex]
c.SessionTokenIndex = sessionTokenIndex
} else {
c.SessionTokenIndex = -1
}
}
} else {
c.SessionTokenIndex = -1
} }
} }
@@ -123,18 +157,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
if len(token) != 0 { if len(token) != 0 {
var session *model.Session session := GetSession(token)
if ts, ok := sessionCache.Get(token); ok {
session = ts.(*model.Session)
}
if session == nil {
if sessionResult := <-Srv.Store.Session().Get(token); sessionResult.Err != nil {
c.LogError(model.NewAppError("ServeHTTP", "Invalid session", "token="+token+", err="+sessionResult.Err.DetailedError))
} else {
session = sessionResult.Data.(*model.Session)
}
}
if session == nil || session.IsExpired() { if session == nil || session.IsExpired() {
c.RemoveSessionCookie(w, r) c.RemoveSessionCookie(w, r)
@@ -318,10 +341,23 @@ func (c *Context) IsTeamAdmin() bool {
func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
sessionCache.Remove(c.Session.Token) // multiToken := ""
// if oldMultiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
// multiToken = oldMultiCookie.Value
// }
// multiCookie := &http.Cookie{
// Name: model.SESSION_COOKIE_TOKEN,
// Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
// Path: "/",
// MaxAge: model.SESSION_TIME_WEB_IN_SECS,
// HttpOnly: true,
// }
//http.SetCookie(w, multiCookie)
cookie := &http.Cookie{ cookie := &http.Cookie{
Name: model.SESSION_TOKEN, Name: model.SESSION_COOKIE_TOKEN,
Value: "", Value: "",
Path: "/", Path: "/",
MaxAge: -1, MaxAge: -1,
@@ -329,21 +365,6 @@ func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
} }
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
multiToken := ""
if oldMultiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil {
multiToken = oldMultiCookie.Value
}
multiCookie := &http.Cookie{
Name: model.MULTI_SESSION_TOKEN,
Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
Path: "/",
MaxAge: model.SESSION_TIME_WEB_IN_SECS,
HttpOnly: true,
}
http.SetCookie(w, multiCookie)
} }
func (c *Context) SetInvalidParam(where string, name string) { func (c *Context) SetInvalidParam(where string, name string) {
@@ -479,7 +500,7 @@ func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request)
} }
w.WriteHeader(err.StatusCode) w.WriteHeader(err.StatusCode)
ServerTemplates.ExecuteTemplate(w, "error.html", Page{Props: props, ClientProps: utils.ClientProperties}) ServerTemplates.ExecuteTemplate(w, "error.html", Page{Props: props, ClientCfg: utils.ClientCfg})
} }
func Handle404(w http.ResponseWriter, r *http.Request) { func Handle404(w http.ResponseWriter, r *http.Request) {
@@ -489,6 +510,46 @@ func Handle404(w http.ResponseWriter, r *http.Request) {
RenderWebError(err, w, r) RenderWebError(err, w, r)
} }
func GetSession(token string) *model.Session {
var session *model.Session
if ts, ok := sessionCache.Get(token); ok {
session = ts.(*model.Session)
}
if session == nil {
if sessionResult := <-Srv.Store.Session().Get(token); sessionResult.Err != nil {
l4g.Error("Invalid session token=" + token + ", err=" + sessionResult.Err.DetailedError)
} else {
session = sessionResult.Data.(*model.Session)
}
}
return session
}
func GetMultiSessionCookieTokens(r *http.Request) []string {
if multiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
multiToken := multiCookie.Value
if len(multiToken) > 0 {
return strings.Split(multiToken, " ")
}
}
return []string{}
}
func FindMultiSessionForTeamId(r *http.Request, teamId string) (int64, *model.Session) {
for index, token := range GetMultiSessionCookieTokens(r) {
s := GetSession(token)
if s != nil && !s.IsExpired() && s.TeamId == teamId {
return int64(index), s
}
}
return -1, nil
}
func AddSessionToCache(session *model.Session) { func AddSessionToCache(session *model.Session) {
sessionCache.Add(session.Token, session) sessionCache.Add(session.Token, session)
} }

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

@@ -281,7 +281,7 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
// copy the context and create a mock session for posting the message // copy the context and create a mock session for posting the message
mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false} mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false}
newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL} newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, 0}
if text, ok := respProps["text"]; ok { if text, ok := respProps["text"]; ok {
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"]); err != nil { if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"]); err != nil {

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

@@ -426,9 +426,9 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
} }
subjectPage := NewServerTemplatePage("find_teams_subject") subjectPage := NewServerTemplatePage("find_teams_subject")
subjectPage.ClientProps["SiteURL"] = c.GetSiteURL() subjectPage.ClientCfg["SiteURL"] = c.GetSiteURL()
bodyPage := NewServerTemplatePage("find_teams_body") bodyPage := NewServerTemplatePage("find_teams_body")
bodyPage.ClientProps["SiteURL"] = c.GetSiteURL() bodyPage.ClientCfg["SiteURL"] = c.GetSiteURL()
if result := <-Srv.Store.Team().GetTeamsForEmail(email); result.Err != nil { if result := <-Srv.Store.Team().GetTeamsForEmail(email); result.Err != nil {
c.Err = result.Err c.Err = result.Err

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

@@ -23,9 +23,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -38,7 +38,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "email_change_subject"}}[{{.ClientProps.SiteName}}] Your email address has changed for {{.Props.TeamDisplayName}}{{end}} {{define "email_change_subject"}}[{{.ClientCfg.SiteName}}] Your email address has changed for {{.Props.TeamDisplayName}}{{end}}

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

@@ -26,9 +26,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -41,7 +41,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "email_change_verify_subject"}}[{{.ClientProps.SiteName}}] Verify new email address for {{.Props.TeamDisplayName}}{{end}} {{define "email_change_verify_subject"}}[{{.ClientCfg.SiteName}}] Verify new email address for {{.Props.TeamDisplayName}}{{end}}

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

@@ -1,7 +1,7 @@
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{{ .ClientProps.SiteName }} - Error</title> <title>{{ .ClientCfg.SiteName }} - Error</title>
<link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css"> <link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css">
<link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css" rel="stylesheet">
@@ -22,7 +22,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="error__container"> <div class="error__container">
<div class="error__icon"><i class="fa fa-exclamation-triangle"></i></div> <div class="error__icon"><i class="fa fa-exclamation-triangle"></i></div>
<h2>{{ .ClientProps.SiteName }} needs your help:</h2> <h2>{{ .ClientCfg.SiteName }} needs your help:</h2>
<p>{{ .Props.Message }}</p> <p>{{ .Props.Message }}</p>
<a href="{{.Props.SiteURL}}">Go back to team site</a> <a href="{{.Props.SiteURL}}">Go back to team site</a>
</div> </div>

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

@@ -9,7 +9,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;"> <table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
<tr> <tr>
<td style="padding: 20px 20px 10px; text-align:left;"> <td style="padding: 20px 20px 10px; text-align:left;">
<img src="{{.ClientProps.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt=""> <img src="{{.ClientCfg.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -31,9 +31,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -42,11 +42,11 @@
<tr> <tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;"> <td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;"> <p style="margin: 25px 0;">
<img width="65" src="{{.ClientProps.SiteURL}}/static/images/circles.png" alt=""> <img width="65" src="{{.ClientCfg.SiteURL}}/static/images/circles.png" alt="">
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "find_teams_subject"}}Your {{ .ClientProps.SiteName }} Teams{{end}} {{define "find_teams_subject"}}Your {{ .ClientCfg.SiteName }} Teams{{end}}

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

@@ -18,7 +18,7 @@
<tr> <tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;"> <td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2> <h2 style="font-weight: normal; margin-top: 10px;">You've been invited</h2>
<p>{{.Props.TeamDisplayName}} started using {{.ClientProps.SiteName}}.<br> The team {{.Props.SenderStatus}} <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamDisplayName}}</strong>.</p> <p>{{.Props.TeamDisplayName}} started using {{.ClientCfg.SiteName}}.<br> The team {{.Props.SenderStatus}} <strong>{{.Props.SenderName}}</strong>, has invited you to join <strong>{{.Props.TeamDisplayName}}</strong>.</p>
<p style="margin: 20px 0 15px"> <p style="margin: 20px 0 15px">
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Join Team</a> <a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Join Team</a>
</p> </p>
@@ -26,9 +26,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -41,7 +41,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamDisplayName }} Team on {{.ClientProps.SiteName}}{{end}} {{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamDisplayName }} Team on {{.ClientCfg.SiteName}}{{end}}

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

@@ -23,9 +23,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -38,7 +38,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "password_change_subject"}}You updated your password for {{.Props.TeamDisplayName}} on {{ .ClientProps.SiteName }}{{end}} {{define "password_change_subject"}}You updated your password for {{.Props.TeamDisplayName}} on {{ .ClientCfg.SiteName }}{{end}}

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

@@ -26,9 +26,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -41,7 +41,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "post_subject"}}[{{.ClientProps.SiteName}}] {{.Props.TeamDisplayName}} Team Notifications for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}} {{define "post_subject"}}[{{.ClientCfg.SiteName}}] {{.Props.TeamDisplayName}} Team Notifications for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}}

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

@@ -26,9 +26,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -41,7 +41,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -21,14 +21,14 @@
<p style="margin: 20px 0 25px"> <p style="margin: 20px 0 25px">
<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;">Set up your team</a> <a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Set up your team</a>
</p> </p>
{{ .ClientProps.SiteName }} is one place for all your team communication, searchable and available anywhere.<br>You'll get more out of {{ .ClientProps.SiteName }} when your team is in constant communication--let's get them on board.<br></p> {{ .ClientCfg.SiteName }} is one place for all your team communication, searchable and available anywhere.<br>You'll get more out of {{ .ClientCfg.SiteName }} when your team is in constant communication--let's get them on board.<br></p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -41,7 +41,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -1 +1 @@
{{define "signup_team_subject"}}Invitation to {{ .ClientProps.SiteName }}{{end}} {{define "signup_team_subject"}}Invitation to {{ .ClientCfg.SiteName }}{{end}}

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

@@ -26,9 +26,9 @@
</tr> </tr>
<tr> <tr>
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> Any questions at all, mail us any time: <a href="mailto:{{.ClientCfg.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientCfg.FeedbackEmail}}</a>.<br>
Best wishes,<br> Best wishes,<br>
The {{.ClientProps.SiteName}} Team<br> The {{.ClientCfg.SiteName}} Team<br>
</td> </td>
</tr> </tr>
</table> </table>
@@ -41,7 +41,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

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

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

@@ -43,7 +43,7 @@
</p> </p>
<p style="padding: 0 50px;"> <p style="padding: 0 50px;">
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br> (c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientProps.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a> If you no longer wish to receive these emails, click on the following link: <a href="mailto:{{.ClientCfg.FeedbackEmail}}?subject=Unsubscribe&body=Unsubscribe" style="text-decoration: none; color:#2389D7;">Unsubscribe</a>
</p> </p>
</td> </td>
</tr> </tr>

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

@@ -428,43 +428,23 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
} }
w.Header().Set(model.HEADER_TOKEN, session.Token) w.Header().Set(model.HEADER_TOKEN, session.Token)
sessionCookie := &http.Cookie{
Name: model.SESSION_TOKEN,
Value: session.Token,
Path: "/",
MaxAge: maxAge,
HttpOnly: true,
}
http.SetCookie(w, sessionCookie)
tokens := GetMultiSessionCookieTokens(r)
multiToken := "" multiToken := ""
if originalMultiSessionCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil { seen := make(map[string]string)
multiToken = originalMultiSessionCookie.Value seen[session.TeamId] = session.TeamId
} for _, token := range tokens {
s := GetSession(token)
// Attempt to clean all the old tokens or duplicate tokens if s != nil && !s.IsExpired() && seen[s.TeamId] == "" {
if len(multiToken) > 0 { multiToken += " " + token
tokens := strings.Split(multiToken, " ") seen[s.TeamId] = s.TeamId
multiToken = ""
seen := make(map[string]string)
seen[session.TeamId] = session.TeamId
for _, token := range tokens {
if sr := <-Srv.Store.Session().Get(token); sr.Err == nil {
s := sr.Data.(*model.Session)
if !s.IsExpired() && seen[s.TeamId] == "" {
multiToken += " " + token
seen[s.TeamId] = s.TeamId
}
}
} }
} }
multiToken = strings.TrimSpace(session.Token + " " + multiToken) multiToken = strings.TrimSpace(multiToken + " " + session.Token)
multiSessionCookie := &http.Cookie{ multiSessionCookie := &http.Cookie{
Name: model.MULTI_SESSION_TOKEN, Name: model.SESSION_COOKIE_TOKEN,
Value: multiToken, Value: multiToken,
Path: "/", Path: "/",
MaxAge: maxAge, MaxAge: maxAge,

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

@@ -111,7 +111,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
// Respond with an auth token this can be overriden by a specific test as required // Respond with an auth token this can be overriden by a specific test as required
sessionCookie := &http.Cookie{ sessionCookie := &http.Cookie{
Name: model.SESSION_TOKEN, Name: model.SESSION_COOKIE_TOKEN,
Value: client.AuthToken, Value: client.AuthToken,
Path: "/", Path: "/",
MaxAge: model.SESSION_TIME_WEB_IN_SECS, MaxAge: model.SESSION_TIME_WEB_IN_SECS,

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

@@ -16,17 +16,19 @@ import (
) )
const ( const (
HEADER_REQUEST_ID = "X-Request-ID" HEADER_REQUEST_ID = "X-Request-ID"
HEADER_VERSION_ID = "X-Version-ID" HEADER_VERSION_ID = "X-Version-ID"
HEADER_ETAG_SERVER = "ETag" HEADER_ETAG_SERVER = "ETag"
HEADER_ETAG_CLIENT = "If-None-Match" HEADER_ETAG_CLIENT = "If-None-Match"
HEADER_FORWARDED = "X-Forwarded-For" HEADER_FORWARDED = "X-Forwarded-For"
HEADER_REAL_IP = "X-Real-IP" HEADER_REAL_IP = "X-Real-IP"
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto" HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
HEADER_TOKEN = "token" HEADER_TOKEN = "token"
HEADER_BEARER = "BEARER" HEADER_BEARER = "BEARER"
HEADER_AUTH = "Authorization" HEADER_AUTH = "Authorization"
API_URL_SUFFIX = "/api/v1" HEADER_MM_SESSION_TOKEN_INDEX = "X-MM-TokenIndex"
SESSION_TOKEN_INDEX = "session_token_index"
API_URL_SUFFIX = "/api/v1"
) )
type Result struct { type Result struct {
@@ -293,7 +295,7 @@ func (c *Client) login(m map[string]string) (*Result, *AppError) {
} else { } else {
c.AuthToken = r.Header.Get(HEADER_TOKEN) c.AuthToken = r.Header.Get(HEADER_TOKEN)
c.AuthType = HEADER_BEARER c.AuthType = HEADER_BEARER
sessionToken := getCookie(SESSION_TOKEN, r) sessionToken := getCookie(SESSION_COOKIE_TOKEN, r)
if c.AuthToken != sessionToken.Value { if c.AuthToken != sessionToken.Value {
NewAppError("/users/login", "Authentication tokens didn't match", "") NewAppError("/users/login", "Authentication tokens didn't match", "")

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

@@ -9,8 +9,7 @@ import (
) )
const ( const (
SESSION_TOKEN = "MMSID" SESSION_COOKIE_TOKEN = "MMTOKEN"
MULTI_SESSION_TOKEN = "MMSIDMU"
SESSION_TIME_WEB_IN_DAYS = 30 SESSION_TIME_WEB_IN_DAYS = 30
SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS
SESSION_TIME_MOBILE_IN_DAYS = 30 SESSION_TIME_MOBILE_IN_DAYS = 30

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

@@ -26,7 +26,7 @@ const (
var Cfg *model.Config = &model.Config{} var Cfg *model.Config = &model.Config{}
var CfgLastModified int64 = 0 var CfgLastModified int64 = 0
var CfgFileName string = "" var CfgFileName string = ""
var ClientProperties map[string]string = map[string]string{} var ClientCfg map[string]string = map[string]string{}
var SanitizeOptions map[string]bool = map[string]bool{} var SanitizeOptions map[string]bool = map[string]bool{}
func FindConfigFile(fileName string) string { func FindConfigFile(fileName string) string {
@@ -161,7 +161,7 @@ func LoadConfig(fileName string) {
Cfg = &config Cfg = &config
SanitizeOptions = getSanitizeOptions(Cfg) SanitizeOptions = getSanitizeOptions(Cfg)
ClientProperties = getClientProperties(Cfg) ClientCfg = getClientConfig(Cfg)
} }
func getSanitizeOptions(c *model.Config) map[string]bool { func getSanitizeOptions(c *model.Config) map[string]bool {
@@ -172,7 +172,7 @@ func getSanitizeOptions(c *model.Config) map[string]bool {
return options return options
} }
func getClientProperties(c *model.Config) map[string]string { func getClientConfig(c *model.Config) map[string]string {
props := make(map[string]string) props := make(map[string]string)
props["Version"] = model.CurrentVersion props["Version"] = model.CurrentVersion

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

@@ -14,7 +14,7 @@ export default class AboutBuildModal extends React.Component {
} }
render() { render() {
const config = global.window.config; const config = global.window.mm_config;
return ( return (
<Modal <Modal

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

@@ -36,7 +36,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = ( profilePicture = (
<img <img
className='user__picture' className='user__picture'
src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at} src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at + '&' + Utils.getSessionIndex()}
/> />
); );
} }

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

@@ -215,7 +215,7 @@ export default class UserItem extends React.Component {
<div className='row member-div'> <div className='row member-div'>
<img <img
className='post-profile-img pull-left' className='post-profile-img pull-left'
src={`/api/v1/users/${user.id}/image?time=${user.update_at}`} src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
height='36' height='36'
width='36' width='36'
/> />

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

@@ -26,7 +26,6 @@ export default class ChannelLoader extends React.Component {
} }
componentDidMount() { componentDidMount() {
/* Initial aysnc loads */ /* Initial aysnc loads */
AsyncClient.getMe();
AsyncClient.getPosts(ChannelStore.getCurrentId()); AsyncClient.getPosts(ChannelStore.getCurrentId());
AsyncClient.getChannels(true, true); AsyncClient.getChannels(true, true);
AsyncClient.getChannelExtraInfo(true); AsyncClient.getChannelExtraInfo(true);

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

@@ -19,10 +19,10 @@ export default class EmailVerify extends React.Component {
var resend = ''; var resend = '';
var resendConfirm = ''; var resendConfirm = '';
if (this.props.isVerified === 'true') { if (this.props.isVerified === 'true') {
title = global.window.config.SiteName + ' Email Verified'; title = global.window.mm_config.SiteName + ' Email Verified';
body = <p>Your email has been verified! Click <a href={this.props.teamURL + '?email=' + this.props.userEmail}>here</a> to log in.</p>; body = <p>Your email has been verified! Click <a href={this.props.teamURL + '?email=' + this.props.userEmail}>here</a> to log in.</p>;
} else { } else {
title = global.window.config.SiteName + ': You are almost done'; title = global.window.mm_config.SiteName + ': You are almost done';
body = <p>Please verify your email address. Check your inbox for an email.</p>; body = <p>Please verify your email address. Check your inbox for an email.</p>;
resend = ( resend = (
<button <button

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

@@ -36,7 +36,7 @@ export default class FileAttachment extends React.Component {
if (type === 'image') { if (type === 'image') {
var self = this; // Need this reference since we use the given "this" var self = this; // Need this reference since we use the given "this"
$('<img/>').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) { $('<img/>').attr('src', fileInfo.path + '_thumb.jpg?' + utils.getSessionIndex()).load(function loadWrapper(path, name) {
return function loader() { return function loader() {
$(this).remove(); $(this).remove();
if (name in self.refs) { if (name in self.refs) {
@@ -147,7 +147,7 @@ export default class FileAttachment extends React.Component {
var re3 = new RegExp('\\)', 'g'); var re3 = new RegExp('\\)', 'g');
var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
$(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)'); $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg?' + utils.getSessionIndex() + ')');
} }
} }
removeBackgroundImage(name) { removeBackgroundImage(name) {

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

@@ -34,7 +34,7 @@ export default class FilePreview extends React.Component {
if (filename.indexOf('/api/v1/files/get') !== -1) { if (filename.indexOf('/api/v1/files/get') !== -1) {
filename = filename.split('/api/v1/files/get')[1]; filename = filename.split('/api/v1/files/get')[1];
} }
filename = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename; filename = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename + '?' + Utils.getSessionIndex();
if (type === 'image') { if (type === 'image') {
previews.push( previews.push(

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

@@ -21,7 +21,7 @@ export default class InviteMemberModal extends React.Component {
emailErrors: {}, emailErrors: {},
firstNameErrors: {}, firstNameErrors: {},
lastNameErrors: {}, lastNameErrors: {},
emailEnabled: global.window.config.SendEmailNotifications === 'true' emailEnabled: global.window.mm_config.SendEmailNotifications === 'true'
}; };
} }

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

@@ -16,7 +16,7 @@ export default class Login extends React.Component {
} }
handleSubmit(e) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
let state = {}; var state = {};
const name = this.props.teamName; const name = this.props.teamName;
if (!name) { if (!name) {
@@ -49,8 +49,7 @@ export default class Login extends React.Component {
this.setState(state); this.setState(state);
Client.loginByEmail(name, email, password, Client.loginByEmail(name, email, password,
function loggedIn(data) { () => {
UserStore.setCurrentUser(data);
UserStore.setLastEmail(email); UserStore.setLastEmail(email);
const redirect = Utils.getUrlParameter('redirect'); const redirect = Utils.getUrlParameter('redirect');
@@ -60,7 +59,7 @@ export default class Login extends React.Component {
window.location.href = '/' + name + '/channels/town-square'; window.location.href = '/' + name + '/channels/town-square';
} }
}, },
function loginFailed(err) { (err) => {
if (err.message === 'Login failed because email address has not been verified') { if (err.message === 'Login failed because email address has not been verified') {
window.location.href = '/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email); window.location.href = '/verify_email?teamname=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email);
return; return;
@@ -68,7 +67,7 @@ export default class Login extends React.Component {
state.serverError = err.message; state.serverError = err.message;
this.valid = false; this.valid = false;
this.setState(state); this.setState(state);
}.bind(this) }
); );
} }
render() { render() {
@@ -95,7 +94,7 @@ export default class Login extends React.Component {
} }
let loginMessage = []; let loginMessage = [];
if (global.window.config.EnableSignUpWithGitLab === 'true') { if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
loginMessage.push( loginMessage.push(
<a <a
className='btn btn-custom-login gitlab' className='btn btn-custom-login gitlab'
@@ -124,7 +123,7 @@ export default class Login extends React.Component {
} }
let emailSignup; let emailSignup;
if (global.window.config.EnableSignUpWithEmail === 'true') { if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
emailSignup = ( emailSignup = (
<div> <div>
<div className={'form-group' + errorClass}> <div className={'form-group' + errorClass}>
@@ -186,7 +185,7 @@ export default class Login extends React.Component {
<div className='signup-team__container'> <div className='signup-team__container'>
<h5 className='margin--less'>Sign in to:</h5> <h5 className='margin--less'>Sign in to:</h5>
<h2 className='signup-team__name'>{teamDisplayName}</h2> <h2 className='signup-team__name'>{teamDisplayName}</h2>
<h2 className='signup-team__subdomain'>on {global.window.config.SiteName}</h2> <h2 className='signup-team__subdomain'>on {global.window.mm_config.SiteName}</h2>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
{verifiedBox} {verifiedBox}
<div className={'form-group' + errorClass}> <div className={'form-group' + errorClass}>

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

@@ -105,7 +105,7 @@ export default class MemberListItem extends React.Component {
<div className='row member-div'> <div className='row member-div'>
<img <img
className='post-profile-img pull-left' className='post-profile-img pull-left'
src={'/api/v1/users/' + member.id + '/image?time=' + timestamp} src={'/api/v1/users/' + member.id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
height='36' height='36'
width='36' width='36'
/> />

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

@@ -169,7 +169,7 @@ export default class MemberListTeamItem extends React.Component {
<div className='row member-div'> <div className='row member-div'>
<img <img
className='post-profile-img pull-left' className='post-profile-img pull-left'
src={`/api/v1/users/${user.id}/image?time=${timestamp}`} src={`/api/v1/users/${user.id}/image?time=${timestamp}&${Utils.getSessionIndex()}`}
height='36' height='36'
width='36' width='36'
/> />

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

@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
const Utils = require('../utils/utils.jsx');
export default class Mention extends React.Component { export default class Mention extends React.Component {
constructor(props) { constructor(props) {
@@ -25,7 +26,7 @@ export default class Mention extends React.Component {
<span> <span>
<img <img
className='mention-img' className='mention-img'
src={'/api/v1/users/' + this.props.id + '/image?time=' + timestamp} src={'/api/v1/users/' + this.props.id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
/> />
</span> </span>
); );

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

@@ -178,7 +178,7 @@ export default class MoreDirectChannels extends React.Component {
className='profile-img pull-left' className='profile-img pull-left'
width='38' width='38'
height='38' height='38'
src={`/api/v1/users/${user.id}/image?time=${user.update_at}`} src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
/> />
<div className='more-name'> <div className='more-name'>
{user.username} {user.username}

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

@@ -152,7 +152,7 @@ export default class NavbarDropdown extends React.Component {
sysAdminLink = ( sysAdminLink = (
<li> <li>
<a <a
href='/admin_console' href={'/admin_console?' + Utils.getSessionIndex()}
> >
{'System Console'} {'System Console'}
</a> </a>
@@ -178,7 +178,7 @@ export default class NavbarDropdown extends React.Component {
}); });
} }
if (global.window.config.EnableTeamCreation === 'true') { if (global.window.mm_config.EnableTeamCreation === 'true') {
teams.push( teams.push(
<li key='newTeam_li'> <li key='newTeam_li'>
<a <a

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

@@ -61,7 +61,7 @@ export default class PasswordResetForm extends React.Component {
<div className='signup-team__container'> <div className='signup-team__container'>
<h3>Password Reset</h3> <h3>Password Reset</h3>
<form onSubmit={this.handlePasswordReset}> <form onSubmit={this.handlePasswordReset}>
<p>{'Enter a new password for your ' + this.props.teamDisplayName + ' ' + global.window.config.SiteName + ' account.'}</p> <p>{'Enter a new password for your ' + this.props.teamDisplayName + ' ' + global.window.mm_config.SiteName + ' account.'}</p>
<div className={formClass}> <div className={formClass}>
<input <input
type='password' type='password'

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

@@ -158,8 +158,8 @@ export default class Post extends React.Component {
var profilePic = null; var profilePic = null;
if (!this.props.hideProfilePic) { if (!this.props.hideProfilePic) {
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp; let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
if (post.props && post.props.from_webhook && global.window.config.EnablePostIconOverride === 'true') { if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
if (post.props.override_icon_url) { if (post.props.override_icon_url) {
src = post.props.override_icon_url; src = post.props.override_icon_url;
} }

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

@@ -119,12 +119,12 @@ export default class PostBody extends React.Component {
this.setState({youtubeTitle: metadata.title}); this.setState({youtubeTitle: metadata.title});
} }
if (global.window.config.GoogleDeveloperKey && !this.receivedYoutubeData) { if (global.window.mm_config.GoogleDeveloperKey && !this.receivedYoutubeData) {
$.ajax({ $.ajax({
async: true, async: true,
url: 'https://www.googleapis.com/youtube/v3/videos', url: 'https://www.googleapis.com/youtube/v3/videos',
type: 'GET', type: 'GET',
data: {part: 'snippet', id: youtubeId, key: global.window.config.GoogleDeveloperKey}, data: {part: 'snippet', id: youtubeId, key: global.window.mm_config.GoogleDeveloperKey},
success: success.bind(this) success: success.bind(this)
}); });
} }

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

@@ -16,7 +16,7 @@ export default class PostHeader extends React.Component {
let botIndicator; let botIndicator;
if (post.props && post.props.from_webhook) { if (post.props && post.props.from_webhook) {
if (post.props.override_username && global.window.config.EnablePostUsernameOverride === 'true') { if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
userProfile = ( userProfile = (
<UserProfile <UserProfile
userId={post.user_id} userId={post.user_id}

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

@@ -334,7 +334,7 @@ export default class PostList extends React.Component {
<div className='post-profile-img__container channel-intro-img'> <div className='post-profile-img__container channel-intro-img'>
<img <img
className='post-profile-img' className='post-profile-img'
src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at} src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at + '&' + Utils.getSessionIndex()}
height='50' height='50'
width='50' width='50'
/> />

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

@@ -199,7 +199,7 @@ export default class RhsComment extends React.Component {
<div className='post-profile-img__container'> <div className='post-profile-img__container'>
<img <img
className='post-profile-img' className='post-profile-img'
src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp} src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
height='36' height='36'
width='36' width='36'
/> />

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

@@ -121,7 +121,7 @@ export default class RhsRootPost extends React.Component {
let botIndicator; let botIndicator;
if (post.props && post.props.from_webhook) { if (post.props && post.props.from_webhook) {
if (post.props.override_username && global.window.config.EnablePostUsernameOverride === 'true') { if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
userProfile = ( userProfile = (
<UserProfile <UserProfile
userId={post.user_id} userId={post.user_id}
@@ -134,8 +134,8 @@ export default class RhsRootPost extends React.Component {
botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>; botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>;
} }
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp; let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
if (post.props && post.props.from_webhook && global.window.config.EnablePostIconOverride === 'true') { if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
if (post.props.override_icon_url) { if (post.props.override_icon_url) {
src = post.props.override_icon_url; src = post.props.override_icon_url;
} }

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

@@ -77,7 +77,7 @@ export default class SearchResultsItem extends React.Component {
<div className='post-profile-img__container'> <div className='post-profile-img__container'>
<img <img
className='post-profile-img' className='post-profile-img'
src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp} src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex()}
height='36' height='36'
width='36' width='36'
/> />

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

@@ -79,7 +79,7 @@ export default class SettingPicture extends React.Component {
>Save</a> >Save</a>
); );
} }
var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + global.window.config.ProfileWidth + 'px in width and ' + global.window.config.ProfileHeight + 'px height.'; var helpText = 'Upload a profile picture in either JPG or PNG format, at least ' + global.window.mm_config.ProfileWidth + 'px in width and ' + global.window.mm_config.ProfileHeight + 'px height.';
var self = this; var self = this;
return ( return (

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

@@ -183,8 +183,8 @@ export default class Sidebar extends React.Component {
const channel = ChannelStore.getCurrent(); const channel = ChannelStore.getCurrent();
if (channel) { if (channel) {
let currentSiteName = ''; let currentSiteName = '';
if (global.window.config.SiteName != null) { if (global.window.mm_config.SiteName != null) {
currentSiteName = global.window.config.SiteName; currentSiteName = global.window.mm_config.SiteName;
} }
let currentChannelName = channel.display_name; let currentChannelName = channel.display_name;

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

@@ -3,6 +3,7 @@
var NavbarDropdown = require('./navbar_dropdown.jsx'); var NavbarDropdown = require('./navbar_dropdown.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
const Utils = require('../utils/utils.jsx');
export default class SidebarHeader extends React.Component { export default class SidebarHeader extends React.Component {
constructor(props) { constructor(props) {
@@ -32,7 +33,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = ( profilePicture = (
<img <img
className='user__picture' className='user__picture'
src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at} src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at + '&' + Utils.getSessionIndex()}
/> />
); );
} }
@@ -61,7 +62,7 @@ export default class SidebarHeader extends React.Component {
} }
SidebarHeader.defaultProps = { SidebarHeader.defaultProps = {
teamDisplayName: global.window.config.SiteName, teamDisplayName: global.window.mm_config.SiteName,
teamType: '' teamType: ''
}; };
SidebarHeader.propTypes = { SidebarHeader.propTypes = {

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

@@ -84,7 +84,7 @@ export default class SidebarRightMenu extends React.Component {
consoleLink = ( consoleLink = (
<li> <li>
<a <a
href='/admin_console' href={'/admin_console?' + utils.getSessionIndex()}
> >
<i className='glyphicon glyphicon-wrench'></i>System Console</a> <i className='glyphicon glyphicon-wrench'></i>System Console</a>
</li> </li>
@@ -92,8 +92,8 @@ export default class SidebarRightMenu extends React.Component {
} }
var siteName = ''; var siteName = '';
if (global.window.config.SiteName != null) { if (global.window.mm_config.SiteName != null) {
siteName = global.window.config.SiteName; siteName = global.window.mm_config.SiteName;
} }
var teamDisplayName = siteName; var teamDisplayName = siteName;
if (this.props.teamDisplayName) { if (this.props.teamDisplayName) {

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

@@ -14,19 +14,19 @@ export default class TeamSignUp extends React.Component {
var count = 0; var count = 0;
if (global.window.config.EnableSignUpWithEmail === 'true') { if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
count = count + 1; count = count + 1;
} }
if (global.window.config.EnableSignUpWithGitLab === 'true') { if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
count = count + 1; count = count + 1;
} }
if (count > 1) { if (count > 1) {
this.state = {page: 'choose'}; this.state = {page: 'choose'};
} else if (global.window.config.EnableSignUpWithEmail === 'true') { } else if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
this.state = {page: 'email'}; this.state = {page: 'email'};
} else if (global.window.config.EnableSignUpWithGitLab === 'true') { } else if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
this.state = {page: 'gitlab'}; this.state = {page: 'gitlab'};
} }
} }

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

@@ -82,30 +82,29 @@ export default class SignupUserComplete extends React.Component {
}); });
client.createUser(user, this.props.data, this.props.hash, client.createUser(user, this.props.data, this.props.hash,
function createUserSuccess() { () => {
client.track('signup', 'signup_user_02_complete'); client.track('signup', 'signup_user_02_complete');
client.loginByEmail(this.props.teamName, user.email, user.password, client.loginByEmail(this.props.teamName, user.email, user.password,
function emailLoginSuccess(data) { () => {
UserStore.setLastEmail(user.email); UserStore.setLastEmail(user.email);
UserStore.setCurrentUser(data);
if (this.props.hash > 0) { if (this.props.hash > 0) {
BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'})); BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
} }
window.location.href = '/' + this.props.teamName + '/channels/town-square'; window.location.href = '/' + this.props.teamName + '/channels/town-square';
}.bind(this), },
function emailLoginFailure(err) { (err) => {
if (err.message === 'Login failed because email address has not been verified') { if (err.message === 'Login failed because email address has not been verified') {
window.location.href = '/verify_email?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.props.teamName); window.location.href = '/verify_email?email=' + encodeURIComponent(user.email) + '&teamname=' + encodeURIComponent(this.props.teamName);
} else { } else {
this.setState({serverError: err.message}); this.setState({serverError: err.message});
} }
}.bind(this) }
); );
}.bind(this), },
function createUserFailure(err) { (err) => {
this.setState({serverError: err.message}); this.setState({serverError: err.message});
}.bind(this) }
); );
} }
render() { render() {
@@ -149,7 +148,7 @@ export default class SignupUserComplete extends React.Component {
// set up the email entry and hide it if an email was provided // set up the email entry and hide it if an email was provided
var yourEmailIs = ''; var yourEmailIs = '';
if (this.state.user.email) { if (this.state.user.email) {
yourEmailIs = <span>Your email address is <strong>{this.state.user.email}</strong>. You'll use this address to sign in to {global.window.config.SiteName}.</span>; yourEmailIs = <span>Your email address is <strong>{this.state.user.email}</strong>. You'll use this address to sign in to {global.window.mm_config.SiteName}.</span>;
} }
var emailContainerStyle = 'margin--extra'; var emailContainerStyle = 'margin--extra';
@@ -177,7 +176,7 @@ export default class SignupUserComplete extends React.Component {
); );
var signupMessage = []; var signupMessage = [];
if (global.window.config.EnableSignUpWithGitLab === 'true') { if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
signupMessage.push( signupMessage.push(
<a <a
className='btn btn-custom-login gitlab' className='btn btn-custom-login gitlab'
@@ -190,7 +189,7 @@ export default class SignupUserComplete extends React.Component {
} }
var emailSignup; var emailSignup;
if (global.window.config.EnableSignUpWithEmail === 'true') { if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
emailSignup = ( emailSignup = (
<div> <div>
<div className='inner__content'> <div className='inner__content'>
@@ -259,7 +258,7 @@ export default class SignupUserComplete extends React.Component {
/> />
<h5 className='margin--less'>Welcome to:</h5> <h5 className='margin--less'>Welcome to:</h5>
<h2 className='signup-team__name'>{this.props.teamDisplayName}</h2> <h2 className='signup-team__name'>{this.props.teamDisplayName}</h2>
<h2 className='signup-team__subdomain'>on {global.window.config.SiteName}</h2> <h2 className='signup-team__subdomain'>on {global.window.mm_config.SiteName}</h2>
<h4 className='color--light'>Let's create your account</h4> <h4 className='color--light'>Let's create your account</h4>
{signupMessage} {signupMessage}
{emailSignup} {emailSignup}

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

@@ -8,7 +8,7 @@ export default class ChooseAuthPage extends React.Component {
} }
render() { render() {
var buttons = []; var buttons = [];
if (global.window.config.EnableSignUpWithGitLab === 'true') { if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
buttons.push( buttons.push(
<a <a
className='btn btn-custom-login gitlab btn-full' className='btn btn-custom-login gitlab btn-full'
@@ -26,7 +26,7 @@ export default class ChooseAuthPage extends React.Component {
); );
} }
if (global.window.config.EnableSignUpWithEmail === 'true') { if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
buttons.push( buttons.push(
<a <a
className='btn btn-custom-login email btn-full' className='btn btn-custom-login email btn-full'

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

@@ -36,15 +36,14 @@ export default class TeamSignupPasswordPage extends React.Component {
delete teamSignup.wizard; delete teamSignup.wizard;
Client.createTeamFromSignup(teamSignup, Client.createTeamFromSignup(teamSignup,
function success() { () => {
Client.track('signup', 'signup_team_08_complete'); Client.track('signup', 'signup_team_08_complete');
var props = this.props; var props = this.props;
Client.loginByEmail(teamSignup.team.name, teamSignup.team.email, teamSignup.user.password, Client.loginByEmail(teamSignup.team.name, teamSignup.team.email, teamSignup.user.password,
function loginSuccess(data) { () => {
UserStore.setLastEmail(teamSignup.team.email); UserStore.setLastEmail(teamSignup.team.email);
UserStore.setCurrentUser(data);
if (this.props.hash > 0) { if (this.props.hash > 0) {
BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'})); BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
} }
@@ -54,21 +53,21 @@ export default class TeamSignupPasswordPage extends React.Component {
props.updateParent(props.state, true); props.updateParent(props.state, true);
window.location.href = '/' + teamSignup.team.name + '/channels/town-square'; window.location.href = '/' + teamSignup.team.name + '/channels/town-square';
}.bind(this), },
function loginFail(err) { (err) => {
if (err.message === 'Login failed because email address has not been verified') { if (err.message === 'Login failed because email address has not been verified') {
window.location.href = '/verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name); window.location.href = '/verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name);
} else { } else {
this.setState({serverError: err.message}); this.setState({serverError: err.message});
$('#finish-button').button('reset'); $('#finish-button').button('reset');
} }
}.bind(this) }
); );
}.bind(this), },
function error(err) { (err) => {
this.setState({serverError: err.message}); this.setState({serverError: err.message});
$('#finish-button').button('reset'); $('#finish-button').button('reset');
}.bind(this) }
); );
} }
render() { render() {
@@ -129,7 +128,7 @@ export default class TeamSignupPasswordPage extends React.Component {
Finish Finish
</button> </button>
</div> </div>
<p>By proceeding to create your account and use {global.window.config.SiteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {global.window.config.SiteName}.</p> <p>By proceeding to create your account and use {global.window.mm_config.SiteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {global.window.mm_config.SiteName}.</p>
<div className='margin--extra'> <div className='margin--extra'>
<a <a
href='#' href='#'

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

@@ -13,7 +13,7 @@ export default class TeamSignupSendInvitesPage extends React.Component {
this.submitSkip = this.submitSkip.bind(this); this.submitSkip = this.submitSkip.bind(this);
this.keySubmit = this.keySubmit.bind(this); this.keySubmit = this.keySubmit.bind(this);
this.state = { this.state = {
emailEnabled: global.window.config.SendEmailNotifications === 'true' emailEnabled: global.window.mm_config.SendEmailNotifications === 'true'
}; };
if (!this.state.emailEnabled) { if (!this.state.emailEnabled) {

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

@@ -40,7 +40,7 @@ export default class TeamSignupUrlPage extends React.Component {
return; return;
} }
if (global.window.config.RestrictTeamNames === 'true') { if (global.window.mm_config.RestrictTeamNames === 'true') {
for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) { for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) {
if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) { if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) {
this.setState({nameError: 'URL is taken or contains a reserved word'}); this.setState({nameError: 'URL is taken or contains a reserved word'});

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

@@ -15,7 +15,7 @@ export default class TeamSignupUsernamePage extends React.Component {
} }
submitBack(e) { submitBack(e) {
e.preventDefault(); e.preventDefault();
if (global.window.config.SendEmailNotifications === 'true') { if (global.window.mm_config.SendEmailNotifications === 'true') {
this.props.state.wizard = 'send_invites'; this.props.state.wizard = 'send_invites';
} else { } else {
this.props.state.wizard = 'team_url'; this.props.state.wizard = 'team_url';

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

@@ -110,7 +110,7 @@ export default class TeamSignupWelcomePage extends React.Component {
src='/static/images/logo.png' src='/static/images/logo.png'
/> />
<h3 className='sub-heading'>Welcome to:</h3> <h3 className='sub-heading'>Welcome to:</h3>
<h1 className='margin--top-none'>{global.window.config.SiteName}</h1> <h1 className='margin--top-none'>{global.window.mm_config.SiteName}</h1>
</p> </p>
<p className='margin--less'>Let's set up your new team</p> <p className='margin--less'>Let's set up your new team</p>
<p> <p>

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

@@ -67,13 +67,14 @@ export default class UserProfile extends React.Component {
dataContent.push( dataContent.push(
<img <img
className='user-popover__image' className='user-popover__image'
src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at} src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '&' + Utils.getSessionIndex()}
height='128' height='128'
width='128' width='128'
key='user-popover-image' key='user-popover-image'
/> />
); );
if (!global.window.config.ShowEmailAddress === 'true') {
if (!global.window.mm_config.ShowEmailAddress === 'true') {
dataContent.push( dataContent.push(
<div <div
className='text-nowrap' className='text-nowrap'

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

@@ -122,7 +122,7 @@ export default class UserSettingsGeneralTab extends React.Component {
() => { () => {
this.updateSection(''); this.updateSection('');
AsyncClient.getMe(); AsyncClient.getMe();
const verificationEnabled = global.window.config.SendEmailNotifications === 'true' && global.window.config.RequireEmailVerification === 'true' && emailUpdated; const verificationEnabled = global.window.mm_config.SendEmailNotifications === 'true' && global.window.mm_config.RequireEmailVerification === 'true' && emailUpdated;
if (verificationEnabled) { if (verificationEnabled) {
ErrorStore.storeLastError({message: 'Check your email at ' + user.email + ' to verify the address.'}); ErrorStore.storeLastError({message: 'Check your email at ' + user.email + ' to verify the address.'});
@@ -451,8 +451,8 @@ export default class UserSettingsGeneralTab extends React.Component {
} }
var emailSection; var emailSection;
if (this.props.activeSection === 'email') { if (this.props.activeSection === 'email') {
const emailEnabled = global.window.config.SendEmailNotifications === 'true'; const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
const emailVerificationEnabled = global.window.config.RequireEmailVerification === 'true'; const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
let helpText = 'Email is used for notifications, and requires verification if changed.'; let helpText = 'Email is used for notifications, and requires verification if changed.';
if (!emailEnabled) { if (!emailEnabled) {
@@ -542,7 +542,7 @@ export default class UserSettingsGeneralTab extends React.Component {
<SettingPicture <SettingPicture
title='Profile Picture' title='Profile Picture'
submit={this.submitPicture} submit={this.submitPicture}
src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update} src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + utils.getSessionIndex()}
server_error={serverError} server_error={serverError}
client_error={clientError} client_error={clientError}
updateSection={function clearSection(e) { updateSection={function clearSection(e) {

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

@@ -34,7 +34,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
let outgoingHooksSection; let outgoingHooksSection;
var inputs = []; var inputs = [];
if (global.window.config.EnableIncomingWebhooks === 'true') { if (global.window.mm_config.EnableIncomingWebhooks === 'true') {
if (this.props.activeSection === 'incoming-hooks') { if (this.props.activeSection === 'incoming-hooks') {
inputs.push( inputs.push(
<ManageIncomingHooks /> <ManageIncomingHooks />
@@ -65,7 +65,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
} }
} }
if (global.window.config.EnableOutgoingWebhooks === 'true') { if (global.window.mm_config.EnableOutgoingWebhooks === 'true') {
if (this.props.activeSection === 'outgoing-hooks') { if (this.props.activeSection === 'outgoing-hooks') {
inputs.push( inputs.push(
<ManageOutgoingHooks /> <ManageOutgoingHooks />

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

@@ -35,10 +35,11 @@ export default class UserSettingsModal extends React.Component {
tabs.push({name: 'security', uiName: 'Security', icon: 'glyphicon glyphicon-lock'}); tabs.push({name: 'security', uiName: 'Security', icon: 'glyphicon glyphicon-lock'});
tabs.push({name: 'notifications', uiName: 'Notifications', icon: 'glyphicon glyphicon-exclamation-sign'}); tabs.push({name: 'notifications', uiName: 'Notifications', icon: 'glyphicon glyphicon-exclamation-sign'});
tabs.push({name: 'appearance', uiName: 'Appearance', icon: 'glyphicon glyphicon-wrench'}); tabs.push({name: 'appearance', uiName: 'Appearance', icon: 'glyphicon glyphicon-wrench'});
if (global.window.config.EnableOAuthServiceProvider === 'true') { if (global.window.mm_config.EnableOAuthServiceProvider === 'true') {
tabs.push({name: 'developer', uiName: 'Developer', icon: 'glyphicon glyphicon-th'}); tabs.push({name: 'developer', uiName: 'Developer', icon: 'glyphicon glyphicon-th'});
} }
if (global.window.config.EnableIncomingWebhooks === 'true' || global.window.config.EnableOutgoingWebhooks === 'true') {
if (global.window.mm_config.EnableIncomingWebhooks === 'true' || global.window.mm_config.EnableOutgoingWebhooks === 'true') {
tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'}); tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'});
} }
tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'}); tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'});

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

@@ -413,7 +413,7 @@ export default class NotificationsTab extends React.Component {
</label> </label>
<br/> <br/>
</div> </div>
<div><br/>{'Email notifications are sent for mentions and direct messages after youve been offline for more than 60 seconds or away from ' + global.window.config.SiteName + ' for more than 5 minutes.'}</div> <div><br/>{'Email notifications are sent for mentions and direct messages after youve been offline for more than 60 seconds or away from ' + global.window.mm_config.SiteName + ' for more than 5 minutes.'}</div>
</div> </div>
); );

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

@@ -197,7 +197,7 @@ export default class ViewImageModal extends React.Component {
} }
fileInfo.path = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path; fileInfo.path = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
return fileInfo.path + '_preview.jpg'; return fileInfo.path + '_preview.jpg' + '?' + Utils.getSessionIndex();
} }
// only images have proper previews, so just use a placeholder icon for non-images // only images have proper previews, so just use a placeholder icon for non-images
@@ -306,7 +306,7 @@ export default class ViewImageModal extends React.Component {
width={width} width={width}
height={height} height={height}
> >
<source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename} /> <source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename + '?' + Utils.getSessionIndex()} />
</video> </video>
); );
} else { } else {

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

@@ -7,7 +7,7 @@ export default class ViewImagePopoverBar extends React.Component {
} }
render() { render() {
var publicLink = ''; var publicLink = '';
if (global.window.config.EnablePublicLink === 'true') { if (global.window.mm_config.EnablePublicLink === 'true') {
publicLink = ( publicLink = (
<div> <div>
<a <a

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

@@ -35,26 +35,18 @@ var RemovedFromChannelModal = require('../components/removed_from_channel_modal.
var FileUploadOverlay = require('../components/file_upload_overlay.jsx'); var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
var RegisterAppModal = require('../components/register_app_modal.jsx'); var RegisterAppModal = require('../components/register_app_modal.jsx');
var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx'); var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx');
var TeamStore = require('../stores/team_store.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
function setupChannelPage(props) { function setupChannelPage(props) {
TeamStore.setCurrentId(props.TeamId);
AppDispatcher.handleViewAction({ AppDispatcher.handleViewAction({
type: ActionTypes.CLICK_CHANNEL, type: ActionTypes.CLICK_CHANNEL,
name: props.ChannelName, name: props.ChannelName,
id: props.ChannelId id: props.ChannelId
}); });
AppDispatcher.handleViewAction({
type: ActionTypes.CLICK_TEAM,
id: props.TeamId
});
AsyncClient.getAllPreferences(); AsyncClient.getAllPreferences();
// ChannelLoader must be rendered first // ChannelLoader must be rendered first
@@ -237,7 +229,7 @@ function setupChannelPage(props) {
document.getElementById('register_app_modal') document.getElementById('register_app_modal')
); );
if (global.window.config.SendEmailNotifications === 'false') { if (global.window.mm_config.SendEmailNotifications === 'false') {
ErrorStore.storeLastError({message: 'Preview Mode: Email notifications have not been configured'}); ErrorStore.storeLastError({message: 'Preview Mode: Email notifications have not been configured'});
ErrorStore.emitChange(); ErrorStore.emitChange();
} }

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

@@ -2,14 +2,15 @@
// See License.txt for license information. // See License.txt for license information.
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
function setupHomePage(props) { function setupHomePage() {
var last = ChannelStore.getLastVisitedName(); var last = ChannelStore.getLastVisitedName();
if (last == null || last.length === 0) { if (last == null || last.length === 0) {
window.location = props.TeamURL + '/channels/' + Constants.DEFAULT_CHANNEL; window.location = TeamStore.getCurrentTeamUrl() + '/channels/' + Constants.DEFAULT_CHANNEL;
} else { } else {
window.location = props.TeamURL + '/channels/' + last; window.location = TeamStore.getCurrentTeamUrl() + '/channels/' + last;
} }
} }

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

@@ -1,12 +1,12 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information. // See License.txt for license information.
var UserStore;
function getPrefix() { function getPrefix() {
if (!UserStore) { if (global.window.mm_user) {
UserStore = require('./user_store.jsx'); //eslint-disable-line global-require return global.window.mm_user.id + '_';
} }
return UserStore.getCurrentId() + '_';
return 'unknown_';
} }
class BrowserStoreClass { class BrowserStoreClass {
@@ -17,35 +17,55 @@ class BrowserStoreClass {
this.setGlobalItem = this.setGlobalItem.bind(this); this.setGlobalItem = this.setGlobalItem.bind(this);
this.getGlobalItem = this.getGlobalItem.bind(this); this.getGlobalItem = this.getGlobalItem.bind(this);
this.removeGlobalItem = this.removeGlobalItem.bind(this); this.removeGlobalItem = this.removeGlobalItem.bind(this);
this.clear = this.clear.bind(this);
this.actionOnItemsWithPrefix = this.actionOnItemsWithPrefix.bind(this); this.actionOnItemsWithPrefix = this.actionOnItemsWithPrefix.bind(this);
this.actionOnGlobalItemsWithPrefix = this.actionOnGlobalItemsWithPrefix.bind(this);
this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this); this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this);
this.getLastServerVersion = this.getLastServerVersion.bind(this);
this.setLastServerVersion = this.setLastServerVersion.bind(this);
this.clear = this.clear.bind(this);
this.clearAll = this.clearAll.bind(this);
var currentVersion = localStorage.getItem('local_storage_version'); var currentVersion = sessionStorage.getItem('storage_version');
if (currentVersion !== global.window.config.Version) { if (currentVersion !== global.window.mm_config.Version) {
this.clear(); sessionStorage.clear();
localStorage.setItem('local_storage_version', global.window.config.Version); sessionStorage.setItem('storage_version', global.window.mm_config.Version);
} }
} }
getItem(name, defaultValue) { getItem(name, defaultValue) {
return this.getGlobalItem(getPrefix() + name, defaultValue); var result = null;
try {
result = JSON.parse(sessionStorage.getItem(getPrefix() + name));
} catch (err) {
result = null;
}
if (result === null && typeof defaultValue !== 'undefined') {
result = defaultValue;
}
return result;
} }
setItem(name, value) { setItem(name, value) {
this.setGlobalItem(getPrefix() + name, value); sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
} }
removeItem(name) { removeItem(name) {
localStorage.removeItem(getPrefix() + name); sessionStorage.removeItem(getPrefix() + name);
} }
setGlobalItem(name, value) { setGlobalItem(name, value) {
try { try {
localStorage.setItem(name, JSON.stringify(value)); if (this.isLocalStorageSupported()) {
localStorage.setItem(getPrefix() + name, JSON.stringify(value));
} else {
sessionStorage.setItem(getPrefix() + name, JSON.stringify(value));
}
} catch (err) { } catch (err) {
console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console console.log('An error occurred while setting local storage, clearing all props'); //eslint-disable-line no-console
localStorage.clear(); localStorage.clear();
sessionStorage.clear();
window.location.href = window.location.href; window.location.href = window.location.href;
} }
} }
@@ -53,7 +73,11 @@ class BrowserStoreClass {
getGlobalItem(name, defaultValue) { getGlobalItem(name, defaultValue) {
var result = null; var result = null;
try { try {
result = JSON.parse(localStorage.getItem(name)); if (this.isLocalStorageSupported()) {
result = JSON.parse(getPrefix() + localStorage.getItem(name));
} else {
result = JSON.parse(getPrefix() + sessionStorage.getItem(name));
}
} catch (err) { } catch (err) {
result = null; result = null;
} }
@@ -66,22 +90,35 @@ class BrowserStoreClass {
} }
removeGlobalItem(name) { removeGlobalItem(name) {
localStorage.removeItem(name); if (this.isLocalStorageSupported()) {
localStorage.removeItem(getPrefix() + name);
} else {
sessionStorage.removeItem(getPrefix() + name);
}
} }
clear() { getLastServerVersion() {
localStorage.clear(); return sessionStorage.getItem('last_server_version');
sessionStorage.clear(); }
setLastServerVersion(version) {
sessionStorage.setItem('last_server_version', version);
} }
/** /**
* Preforms the given action on each item that has the given prefix * Preforms the given action on each item that has the given prefix
* Signature for action is action(key, value) * Signature for action is action(key, value)
*/ */
actionOnItemsWithPrefix(prefix, action) { actionOnGlobalItemsWithPrefix(prefix, action) {
var globalPrefix = getPrefix(); var globalPrefix = getPrefix();
var globalPrefixiLen = globalPrefix.length; var globalPrefixiLen = globalPrefix.length;
for (var key in localStorage) {
var storage = sessionStorage;
if (this.isLocalStorageSupported()) {
storage = localStorage;
}
for (var key in storage) {
if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) { if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
var userkey = key.substring(globalPrefixiLen); var userkey = key.substring(globalPrefixiLen);
action(userkey, this.getGlobalItem(key)); action(userkey, this.getGlobalItem(key));
@@ -89,6 +126,26 @@ class BrowserStoreClass {
} }
} }
actionOnItemsWithPrefix(prefix, action) {
var globalPrefix = getPrefix();
var globalPrefixiLen = globalPrefix.length;
for (var key in sessionStorage) {
if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) {
var userkey = key.substring(globalPrefixiLen);
action(userkey, this.getGlobalItem(key));
}
}
}
clear() {
sessionStorage.clear();
}
clearAll() {
sessionStorage.clear();
localStorage.clear();
}
isLocalStorageSupported() { isLocalStorageSupported() {
try { try {
sessionStorage.setItem('testSession', '1'); sessionStorage.setItem('testSession', '1');

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

@@ -34,9 +34,11 @@ class ErrorStoreClass extends EventEmitter {
removeChangeListener(callback) { removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback); this.removeListener(CHANGE_EVENT, callback);
} }
handledError() { handledError() {
BrowserStore.removeItem('last_error'); BrowserStore.removeItem('last_error');
} }
getLastError() { getLastError() {
return BrowserStore.getItem('last_error'); return BrowserStore.getItem('last_error');
} }

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

@@ -324,10 +324,10 @@ class PostStoreClass extends EventEmitter {
return 0; return 0;
}); });
BrowserStore.setItem('pending_posts_' + channelId, postList); BrowserStore.setGlobalItem('pending_posts_' + channelId, postList);
} }
getPendingPosts(channelId) { getPendingPosts(channelId) {
return BrowserStore.getItem('pending_posts_' + channelId); return BrowserStore.getGlobalItem('pending_posts_' + channelId);
} }
storeUnseenDeletedPost(post) { storeUnseenDeletedPost(post) {
var posts = this.getUnseenDeletedPosts(post.channel_id); var posts = this.getUnseenDeletedPosts(post.channel_id);
@@ -371,7 +371,7 @@ class PostStoreClass extends EventEmitter {
this.pStorePendingPosts(channelId, postList); this.pStorePendingPosts(channelId, postList);
} }
clearPendingPosts() { clearPendingPosts() {
BrowserStore.actionOnItemsWithPrefix('pending_posts_', function clearPending(key) { BrowserStore.actionOnGlobalItemsWithPrefix('pending_posts_', function clearPending(key) {
BrowserStore.removeItem(key); BrowserStore.removeItem(key);
}); });
} }
@@ -414,26 +414,26 @@ class PostStoreClass extends EventEmitter {
} }
storeCurrentDraft(draft) { storeCurrentDraft(draft) {
var channelId = ChannelStore.getCurrentId(); var channelId = ChannelStore.getCurrentId();
BrowserStore.setItem('draft_' + channelId, draft); BrowserStore.setGlobalItem('draft_' + channelId, draft);
} }
getCurrentDraft() { getCurrentDraft() {
var channelId = ChannelStore.getCurrentId(); var channelId = ChannelStore.getCurrentId();
return this.getDraft(channelId); return this.getDraft(channelId);
} }
storeDraft(channelId, draft) { storeDraft(channelId, draft) {
BrowserStore.setItem('draft_' + channelId, draft); BrowserStore.setGlobalItem('draft_' + channelId, draft);
} }
getDraft(channelId) { getDraft(channelId) {
return BrowserStore.getItem('draft_' + channelId, this.getEmptyDraft()); return BrowserStore.getGlobalItem('draft_' + channelId, this.getEmptyDraft());
} }
storeCommentDraft(parentPostId, draft) { storeCommentDraft(parentPostId, draft) {
BrowserStore.setItem('comment_draft_' + parentPostId, draft); BrowserStore.setGlobalItem('comment_draft_' + parentPostId, draft);
} }
getCommentDraft(parentPostId) { getCommentDraft(parentPostId) {
return BrowserStore.getItem('comment_draft_' + parentPostId, this.getEmptyDraft()); return BrowserStore.getGlobalItem('comment_draft_' + parentPostId, this.getEmptyDraft());
} }
clearDraftUploads() { clearDraftUploads() {
BrowserStore.actionOnItemsWithPrefix('draft_', function clearUploads(key, value) { BrowserStore.actionOnGlobalItemsWithPrefix('draft_', function clearUploads(key, value) {
if (value) { if (value) {
value.uploadsInProgress = []; value.uploadsInProgress = [];
BrowserStore.setItem(key, value); BrowserStore.setItem(key, value);
@@ -441,7 +441,7 @@ class PostStoreClass extends EventEmitter {
}); });
} }
clearCommentDraftUploads() { clearCommentDraftUploads() {
BrowserStore.actionOnItemsWithPrefix('comment_draft_', function clearUploads(key, value) { BrowserStore.actionOnGlobalItemsWithPrefix('comment_draft_', function clearUploads(key, value) {
if (value) { if (value) {
value.uploadsInProgress = []; value.uploadsInProgress = [];
BrowserStore.setItem(key, value); BrowserStore.setItem(key, value);

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

@@ -38,6 +38,10 @@ class SocketStoreClass extends EventEmitter {
return; return;
} }
if (!global.window.mm_session_token_index) {
return;
}
this.setMaxListeners(0); this.setMaxListeners(0);
if (window.WebSocket && !conn) { if (window.WebSocket && !conn) {
@@ -45,7 +49,9 @@ class SocketStoreClass extends EventEmitter {
if (window.location.protocol === 'https:') { if (window.location.protocol === 'https:') {
protocol = 'wss://'; protocol = 'wss://';
} }
var connUrl = protocol + location.host + '/api/v1/websocket';
var connUrl = protocol + location.host + '/api/v1/websocket?' + Utils.getSessionIndex();
if (this.failCount === 0) { if (this.failCount === 0) {
console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
} }

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

@@ -28,29 +28,31 @@ class TeamStoreClass extends EventEmitter {
this.get = this.get.bind(this); this.get = this.get.bind(this);
this.getByName = this.getByName.bind(this); this.getByName = this.getByName.bind(this);
this.getAll = this.getAll.bind(this); this.getAll = this.getAll.bind(this);
this.setCurrentId = this.setCurrentId.bind(this);
this.getCurrentId = this.getCurrentId.bind(this); this.getCurrentId = this.getCurrentId.bind(this);
this.getCurrent = this.getCurrent.bind(this); this.getCurrent = this.getCurrent.bind(this);
this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this); this.getCurrentTeamUrl = this.getCurrentTeamUrl.bind(this);
this.storeTeam = this.storeTeam.bind(this); this.saveTeam = this.saveTeam.bind(this);
this.pStoreTeams = this.pStoreTeams.bind(this);
this.pGetTeams = this.pGetTeams.bind(this);
} }
emitChange() { emitChange() {
this.emit(CHANGE_EVENT); this.emit(CHANGE_EVENT);
} }
addChangeListener(callback) { addChangeListener(callback) {
this.on(CHANGE_EVENT, callback); this.on(CHANGE_EVENT, callback);
} }
removeChangeListener(callback) { removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback); this.removeListener(CHANGE_EVENT, callback);
} }
get(id) { get(id) {
var c = this.pGetTeams(); var c = this.getAll();
return c[id]; return c[id];
} }
getByName(name) { getByName(name) {
var t = this.pGetTeams(); var t = this.getAll();
for (var id in t) { for (var id in t) {
if (t[id].name === name) { if (t[id].name === name) {
@@ -60,59 +62,51 @@ class TeamStoreClass extends EventEmitter {
return null; return null;
} }
getAll() {
return this.pGetTeams();
}
setCurrentId(id) {
if (id === null) {
BrowserStore.removeItem('current_team_id');
} else {
BrowserStore.setItem('current_team_id', id);
}
}
getCurrentId() {
return BrowserStore.getItem('current_team_id');
}
getCurrent() {
var currentId = this.getCurrentId();
if (currentId !== null) { getAll() {
return this.get(currentId); return BrowserStore.getItem('user_teams', {});
}
getCurrentId() {
var team = global.window.mm_team;
if (team) {
return team.id;
} }
return null; return null;
} }
getCurrent() {
if (global.window.mm_team != null && this.get(global.window.mm_team.id) == null) {
this.saveTeam(global.window.mm_team);
}
return global.window.mm_team;
}
getCurrentTeamUrl() { getCurrentTeamUrl() {
if (this.getCurrent()) { if (this.getCurrent()) {
return getWindowLocationOrigin() + '/' + this.getCurrent().name; return getWindowLocationOrigin() + '/' + this.getCurrent().name;
} }
return null; return null;
} }
storeTeam(team) {
var teams = this.pGetTeams(); saveTeam(team) {
var teams = this.getAll();
teams[team.id] = team; teams[team.id] = team;
this.pStoreTeams(teams);
}
pStoreTeams(teams) {
BrowserStore.setItem('user_teams', teams); BrowserStore.setItem('user_teams', teams);
} }
pGetTeams() {
return BrowserStore.getItem('user_teams', {});
}
} }
var TeamStore = new TeamStoreClass(); var TeamStore = new TeamStoreClass();
TeamStore.dispatchToken = AppDispatcher.register(function registry(payload) { TeamStore.dispatchToken = AppDispatcher.register((payload) => {
var action = payload.action; var action = payload.action;
switch (action.type) { switch (action.type) {
case ActionTypes.CLICK_TEAM:
TeamStore.setCurrentId(action.id);
TeamStore.emitChange();
break;
case ActionTypes.RECIEVED_TEAM: case ActionTypes.RECIEVED_TEAM:
TeamStore.storeTeam(action.team); TeamStore.saveTeam(action.team);
TeamStore.emitChange(); TeamStore.emitChange();
break; break;

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

@@ -3,7 +3,6 @@
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var client = require('../utils/client.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
@@ -38,13 +37,11 @@ class UserStoreClass extends EventEmitter {
this.emitToggleImportModal = this.emitToggleImportModal.bind(this); this.emitToggleImportModal = this.emitToggleImportModal.bind(this);
this.addImportModalListener = this.addImportModalListener.bind(this); this.addImportModalListener = this.addImportModalListener.bind(this);
this.removeImportModalListener = this.removeImportModalListener.bind(this); this.removeImportModalListener = this.removeImportModalListener.bind(this);
this.setCurrentId = this.setCurrentId.bind(this);
this.getCurrentId = this.getCurrentId.bind(this); this.getCurrentId = this.getCurrentId.bind(this);
this.getCurrentUser = this.getCurrentUser.bind(this); this.getCurrentUser = this.getCurrentUser.bind(this);
this.setCurrentUser = this.setCurrentUser.bind(this); this.setCurrentUser = this.setCurrentUser.bind(this);
this.getLastEmail = this.getLastEmail.bind(this); this.getLastEmail = this.getLastEmail.bind(this);
this.setLastEmail = this.setLastEmail.bind(this); this.setLastEmail = this.setLastEmail.bind(this);
this.removeCurrentUser = this.removeCurrentUser.bind(this);
this.hasProfile = this.hasProfile.bind(this); this.hasProfile = this.hasProfile.bind(this);
this.getProfile = this.getProfile.bind(this); this.getProfile = this.getProfile.bind(this);
this.getProfileByUsername = this.getProfileByUsername.bind(this); this.getProfileByUsername = this.getProfileByUsername.bind(this);
@@ -52,9 +49,6 @@ class UserStoreClass extends EventEmitter {
this.getProfiles = this.getProfiles.bind(this); this.getProfiles = this.getProfiles.bind(this);
this.getActiveOnlyProfiles = this.getActiveOnlyProfiles.bind(this); this.getActiveOnlyProfiles = this.getActiveOnlyProfiles.bind(this);
this.saveProfile = this.saveProfile.bind(this); this.saveProfile = this.saveProfile.bind(this);
this.pStoreProfiles = this.pStoreProfiles.bind(this);
this.pGetProfiles = this.pGetProfiles.bind(this);
this.pGetProfilesUsernameMap = this.pGetProfilesUsernameMap.bind(this);
this.setSessions = this.setSessions.bind(this); this.setSessions = this.setSessions.bind(this);
this.getSessions = this.getSessions.bind(this); this.getSessions = this.getSessions.bind(this);
this.setAudits = this.setAudits.bind(this); this.setAudits = this.setAudits.bind(this);
@@ -62,138 +56,155 @@ class UserStoreClass extends EventEmitter {
this.setTeams = this.setTeams.bind(this); this.setTeams = this.setTeams.bind(this);
this.getTeams = this.getTeams.bind(this); this.getTeams = this.getTeams.bind(this);
this.getCurrentMentionKeys = this.getCurrentMentionKeys.bind(this); this.getCurrentMentionKeys = this.getCurrentMentionKeys.bind(this);
this.getLastVersion = this.getLastVersion.bind(this);
this.setLastVersion = this.setLastVersion.bind(this);
this.setStatuses = this.setStatuses.bind(this); this.setStatuses = this.setStatuses.bind(this);
this.pSetStatuses = this.pSetStatuses.bind(this); this.pSetStatuses = this.pSetStatuses.bind(this);
this.setStatus = this.setStatus.bind(this); this.setStatus = this.setStatus.bind(this);
this.getStatuses = this.getStatuses.bind(this); this.getStatuses = this.getStatuses.bind(this);
this.getStatus = this.getStatus.bind(this); this.getStatus = this.getStatus.bind(this);
this.gCurrentId = null;
} }
emitChange(userId) { emitChange(userId) {
this.emit(CHANGE_EVENT, userId); this.emit(CHANGE_EVENT, userId);
} }
addChangeListener(callback) { addChangeListener(callback) {
this.on(CHANGE_EVENT, callback); this.on(CHANGE_EVENT, callback);
} }
removeChangeListener(callback) { removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback); this.removeListener(CHANGE_EVENT, callback);
} }
emitSessionsChange() { emitSessionsChange() {
this.emit(CHANGE_EVENT_SESSIONS); this.emit(CHANGE_EVENT_SESSIONS);
} }
addSessionsChangeListener(callback) { addSessionsChangeListener(callback) {
this.on(CHANGE_EVENT_SESSIONS, callback); this.on(CHANGE_EVENT_SESSIONS, callback);
} }
removeSessionsChangeListener(callback) { removeSessionsChangeListener(callback) {
this.removeListener(CHANGE_EVENT_SESSIONS, callback); this.removeListener(CHANGE_EVENT_SESSIONS, callback);
} }
emitAuditsChange() { emitAuditsChange() {
this.emit(CHANGE_EVENT_AUDITS); this.emit(CHANGE_EVENT_AUDITS);
} }
addAuditsChangeListener(callback) { addAuditsChangeListener(callback) {
this.on(CHANGE_EVENT_AUDITS, callback); this.on(CHANGE_EVENT_AUDITS, callback);
} }
removeAuditsChangeListener(callback) { removeAuditsChangeListener(callback) {
this.removeListener(CHANGE_EVENT_AUDITS, callback); this.removeListener(CHANGE_EVENT_AUDITS, callback);
} }
emitTeamsChange() { emitTeamsChange() {
this.emit(CHANGE_EVENT_TEAMS); this.emit(CHANGE_EVENT_TEAMS);
} }
addTeamsChangeListener(callback) { addTeamsChangeListener(callback) {
this.on(CHANGE_EVENT_TEAMS, callback); this.on(CHANGE_EVENT_TEAMS, callback);
} }
removeTeamsChangeListener(callback) { removeTeamsChangeListener(callback) {
this.removeListener(CHANGE_EVENT_TEAMS, callback); this.removeListener(CHANGE_EVENT_TEAMS, callback);
} }
emitStatusesChange() { emitStatusesChange() {
this.emit(CHANGE_EVENT_STATUSES); this.emit(CHANGE_EVENT_STATUSES);
} }
addStatusesChangeListener(callback) { addStatusesChangeListener(callback) {
this.on(CHANGE_EVENT_STATUSES, callback); this.on(CHANGE_EVENT_STATUSES, callback);
} }
removeStatusesChangeListener(callback) { removeStatusesChangeListener(callback) {
this.removeListener(CHANGE_EVENT_STATUSES, callback); this.removeListener(CHANGE_EVENT_STATUSES, callback);
} }
emitToggleImportModal(value) { emitToggleImportModal(value) {
this.emit(TOGGLE_IMPORT_MODAL_EVENT, value); this.emit(TOGGLE_IMPORT_MODAL_EVENT, value);
} }
addImportModalListener(callback) { addImportModalListener(callback) {
this.on(TOGGLE_IMPORT_MODAL_EVENT, callback); this.on(TOGGLE_IMPORT_MODAL_EVENT, callback);
} }
removeImportModalListener(callback) { removeImportModalListener(callback) {
this.removeListener(TOGGLE_IMPORT_MODAL_EVENT, callback); this.removeListener(TOGGLE_IMPORT_MODAL_EVENT, callback);
} }
setCurrentId(id) {
this.gCurrentId = id; getCurrentUser() {
if (id == null) { if (this.getProfiles()[global.window.mm_user.id] == null) {
BrowserStore.removeGlobalItem('current_user_id'); this.saveProfile(global.window.mm_user);
}
return global.window.mm_user;
}
setCurrentUser(user) {
var oldUser = global.window.mm_user;
if (oldUser.id === user.id) {
global.window.mm_user = user;
this.saveProfile(user);
} else { } else {
BrowserStore.setGlobalItem('current_user_id', id); throw new Error('Problem with setCurrentUser old_user_id=' + oldUser.id + ' new_user_id=' + user.id);
} }
} }
getCurrentId(skipFetch) {
var currentId = this.gCurrentId;
if (currentId == null) { getCurrentId() {
currentId = BrowserStore.getGlobalItem('current_user_id'); var user = global.window.mm_user;
this.gCurrentId = currentId;
if (user) {
return user.id;
} }
// this is a special case to force fetch the return null;
// current user if it's missing }
// it's synchronous to block rendering
if (currentId == null && !skipFetch) { getLastEmail() {
var me = client.getMeSynchronous(); return BrowserStore.getGlobalItem('last_email', '');
if (me != null) { }
this.setCurrentUser(me);
currentId = me.id; setLastEmail(email) {
BrowserStore.setGlobalItem('last_email', email);
}
hasProfile(userId) {
return this.getProfiles()[userId] != null;
}
getProfile(userId) {
return this.getProfiles()[userId];
}
getProfileByUsername(username) {
return this.getProfilesUsernameMap()[username];
}
getProfilesUsernameMap() {
var profileUsernameMap = {};
var profiles = this.getProfiles();
for (var key in profiles) {
if (profiles.hasOwnProperty(key)) {
var profile = profiles[key];
profileUsernameMap[profile.username] = profile;
} }
} }
return currentId; return profileUsernameMap;
} }
getCurrentUser() {
if (this.getCurrentId() == null) {
return null;
}
return this.pGetProfiles()[this.getCurrentId()];
}
setCurrentUser(user) {
this.setCurrentId(user.id);
this.saveProfile(user);
}
getLastEmail() {
return BrowserStore.getItem('last_email', '');
}
setLastEmail(email) {
BrowserStore.setItem('last_email', email);
}
removeCurrentUser() {
this.setCurrentId(null);
}
hasProfile(userId) {
return this.pGetProfiles()[userId] != null;
}
getProfile(userId) {
return this.pGetProfiles()[userId];
}
getProfileByUsername(username) {
return this.pGetProfilesUsernameMap()[username];
}
getProfilesUsernameMap() {
return this.pGetProfilesUsernameMap();
}
getProfiles() { getProfiles() {
return this.pGetProfiles(); return BrowserStore.getItem('profiles', {});
} }
getActiveOnlyProfiles() { getActiveOnlyProfiles() {
var active = {}; var active = {};
var current = this.pGetProfiles(); var current = this.getProfiles();
for (var key in current) { for (var key in current) {
if (current[key].delete_at === 0) { if (current[key].delete_at === 0) {
@@ -203,45 +214,37 @@ class UserStoreClass extends EventEmitter {
return active; return active;
} }
saveProfile(profile) { saveProfile(profile) {
var ps = this.pGetProfiles(); var ps = this.getProfiles();
ps[profile.id] = profile; ps[profile.id] = profile;
this.pStoreProfiles(ps); BrowserStore.setItem('profiles', ps);
}
pStoreProfiles(profiles) {
BrowserStore.setItem('profiles', profiles);
var profileUsernameMap = {};
for (var id in profiles) {
if (profiles.hasOwnProperty(id)) {
profileUsernameMap[profiles[id].username] = profiles[id];
}
}
BrowserStore.setItem('profileUsernameMap', profileUsernameMap);
}
pGetProfiles() {
return BrowserStore.getItem('profiles', {});
}
pGetProfilesUsernameMap() {
return BrowserStore.getItem('profileUsernameMap', {});
} }
setSessions(sessions) { setSessions(sessions) {
BrowserStore.setItem('sessions', sessions); BrowserStore.setItem('sessions', sessions);
} }
getSessions() { getSessions() {
return BrowserStore.getItem('sessions', {loading: true}); return BrowserStore.getItem('sessions', {loading: true});
} }
setAudits(audits) { setAudits(audits) {
BrowserStore.setItem('audits', audits); BrowserStore.setItem('audits', audits);
} }
getAudits() { getAudits() {
return BrowserStore.getItem('audits', {loading: true}); return BrowserStore.getItem('audits', {loading: true});
} }
setTeams(teams) { setTeams(teams) {
BrowserStore.setItem('teams', teams); BrowserStore.setItem('teams', teams);
} }
getTeams() { getTeams() {
return BrowserStore.getItem('teams', []); return BrowserStore.getItem('teams', []);
} }
getCurrentMentionKeys() { getCurrentMentionKeys() {
var user = this.getCurrentUser(); var user = this.getCurrentUser();
@@ -269,28 +272,27 @@ class UserStoreClass extends EventEmitter {
return keys; return keys;
} }
getLastVersion() {
return BrowserStore.getItem('last_version', '');
}
setLastVersion(version) {
BrowserStore.setItem('last_version', version);
}
setStatuses(statuses) { setStatuses(statuses) {
this.pSetStatuses(statuses); this.pSetStatuses(statuses);
this.emitStatusesChange(); this.emitStatusesChange();
} }
pSetStatuses(statuses) { pSetStatuses(statuses) {
BrowserStore.setItem('statuses', statuses); BrowserStore.setItem('statuses', statuses);
} }
setStatus(userId, status) { setStatus(userId, status) {
var statuses = this.getStatuses(); var statuses = this.getStatuses();
statuses[userId] = status; statuses[userId] = status;
this.pSetStatuses(statuses); this.pSetStatuses(statuses);
this.emitStatusesChange(); this.emitStatusesChange();
} }
getStatuses() { getStatuses() {
return BrowserStore.getItem('statuses', {}); return BrowserStore.getItem('statuses', {});
} }
getStatus(id) { getStatus(id) {
return this.getStatuses()[id]; return this.getStatuses()[id];
} }
@@ -299,7 +301,7 @@ class UserStoreClass extends EventEmitter {
var UserStore = new UserStoreClass(); var UserStore = new UserStoreClass();
UserStore.setMaxListeners(0); UserStore.setMaxListeners(0);
UserStore.dispatchToken = AppDispatcher.register(function registry(payload) { UserStore.dispatchToken = AppDispatcher.register((payload) => {
var action = payload.action; var action = payload.action;
switch (action.type) { switch (action.type) {

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

@@ -3,6 +3,7 @@
var client = require('./client.jsx'); var client = require('./client.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var PostStore = require('../stores/post_store.jsx'); var PostStore = require('../stores/post_store.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
@@ -50,18 +51,18 @@ export function getChannels(force, updateLastViewed, checkVersion) {
callTracker.getChannels = utils.getTimestamp(); callTracker.getChannels = utils.getTimestamp();
client.getChannels( client.getChannels(
function getChannelsSuccess(data, textStatus, xhr) { (data, textStatus, xhr) => {
callTracker.getChannels = 0; callTracker.getChannels = 0;
if (checkVersion) { if (checkVersion) {
var serverVersion = xhr.getResponseHeader('X-Version-ID'); var serverVersion = xhr.getResponseHeader('X-Version-ID');
if (!UserStore.getLastVersion()) { if (!BrowserStore.getLastServerVersion()) {
UserStore.setLastVersion(serverVersion); BrowserStore.setLastServerVersion(serverVersion);
} }
if (serverVersion !== UserStore.getLastVersion()) { if (serverVersion !== BrowserStore.getLastServerVersion()) {
UserStore.setLastVersion(serverVersion); BrowserStore.setLastServerVersion(serverVersion);
window.location.href = window.location.href; window.location.href = window.location.href;
console.log('Detected version update refreshing the page'); //eslint-disable-line no-console console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
} }
@@ -77,7 +78,7 @@ export function getChannels(force, updateLastViewed, checkVersion) {
members: data.members members: data.members
}); });
}, },
function getChannelsFailure(err) { (err) => {
callTracker.getChannels = 0; callTracker.getChannels = 0;
dispatchError(err, 'getChannels'); dispatchError(err, 'getChannels');
} }
@@ -566,8 +567,8 @@ export function getMe() {
} }
callTracker.getMe = utils.getTimestamp(); callTracker.getMe = utils.getTimestamp();
client.getMeSynchronous( client.getMe(
function getMeSyncSuccess(data, textStatus, xhr) { (data, textStatus, xhr) => {
callTracker.getMe = 0; callTracker.getMe = 0;
if (xhr.status === 304 || !data) { if (xhr.status === 304 || !data) {
@@ -579,7 +580,7 @@ export function getMe() {
me: data me: data
}); });
}, },
function getMeSyncFailure(err) { (err) => {
callTracker.getMe = 0; callTracker.getMe = 0;
dispatchError(err, 'getMe'); dispatchError(err, 'getMe');
} }

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

@@ -4,8 +4,8 @@ var BrowserStore = require('../stores/browser_store.jsx');
var TeamStore = require('../stores/team_store.jsx'); var TeamStore = require('../stores/team_store.jsx');
var ErrorStore = require('../stores/error_store.jsx'); var ErrorStore = require('../stores/error_store.jsx');
export function track(category, action, label, prop, val) { export function track(category, action, label, property, value) {
global.window.analytics.track(action, {category: category, label: label, property: prop, value: val}); global.window.analytics.track(action, {category, label, property, value});
} }
export function trackPage() { export function trackPage() {
@@ -232,6 +232,7 @@ export function logout() {
track('api', 'api_users_logout'); track('api', 'api_users_logout');
var currentTeamUrl = TeamStore.getCurrentTeamUrl(); var currentTeamUrl = TeamStore.getCurrentTeamUrl();
BrowserStore.clear(); BrowserStore.clear();
ErrorStore.storeLastError(null);
window.location.href = currentTeamUrl + '/logout'; window.location.href = currentTeamUrl + '/logout';
} }
@@ -385,10 +386,9 @@ export function getAllTeams(success, error) {
}); });
} }
export function getMeSynchronous(success, error) { export function getMe(success, error) {
var currentUser = null; var currentUser = null;
$.ajax({ $.ajax({
async: false,
cache: false, cache: false,
url: '/api/v1/users/me', url: '/api/v1/users/me',
dataType: 'json', dataType: 'json',
@@ -402,7 +402,7 @@ export function getMeSynchronous(success, error) {
}, },
error: function onError(xhr, status, err) { error: function onError(xhr, status, err) {
if (error) { if (error) {
var e = handleError('getMeSynchronous', xhr, status, err); var e = handleError('getMe', xhr, status, err);
error(e); error(e);
} }
} }

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

@@ -33,7 +33,6 @@ module.exports = {
RECIEVED_MSG: null, RECIEVED_MSG: null,
CLICK_TEAM: null,
RECIEVED_TEAM: null, RECIEVED_TEAM: null,
RECIEVED_CONFIG: null, RECIEVED_CONFIG: null,

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

@@ -873,7 +873,7 @@ export function getFileUrl(filename) {
if (url.indexOf('/api/v1/files/get') !== -1) { if (url.indexOf('/api/v1/files/get') !== -1) {
url = filename.split('/api/v1/files/get')[1]; url = filename.split('/api/v1/files/get')[1];
} }
url = getWindowLocationOrigin() + '/api/v1/files/get' + url; url = getWindowLocationOrigin() + '/api/v1/files/get' + url + '?' + getSessionIndex();
return url; return url;
} }
@@ -884,6 +884,14 @@ export function getFileName(path) {
return split[split.length - 1]; return split[split.length - 1];
} }
export function getSessionIndex() {
if (global.window.mm_session_token_index >= 0) {
return 'session_token_index=' + global.window.mm_session_token_index;
}
return '';
}
// Generates a RFC-4122 version 4 compliant globally unique identifier. // Generates a RFC-4122 version 4 compliant globally unique identifier.
export function generateId() { export function generateId() {
// implementation taken from http://stackoverflow.com/a/2117523 // implementation taken from http://stackoverflow.com/a/2117523

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

@@ -1,7 +1,7 @@
{{define "footer"}} {{define "footer"}}
<div class="footer-pane col-xs-12"> <div class="footer-pane col-xs-12">
<div class="col-xs-12"> <div class="col-xs-12">
<span class="pull-right footer-site-name">{{ .ClientProps.SiteName }}</span> <span class="pull-right footer-site-name">{{ .ClientCfg.SiteName }}</span>
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
<span class="pull-right footer-link copyright">© 2015 Mattermost, Inc.</span> <span class="pull-right footer-link copyright">© 2015 Mattermost, Inc.</span>

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

@@ -18,10 +18,6 @@
<link rel="manifest" href="/static/config/manifest.json"> <link rel="manifest" href="/static/config/manifest.json">
<!-- Android add to homescreen --> <!-- Android add to homescreen -->
<script>
window.config = {{ .ClientProps }};
</script>
<!-- CSS Should always go first --> <!-- CSS Should always go first -->
<link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css"> <link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css">
<link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css"> <link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css">
@@ -43,6 +39,19 @@
<style id="antiClickjack">body{display:none !important;}</style> <style id="antiClickjack">body{display:none !important;}</style>
<script>
window.mm_config = {{ .ClientCfg }};
window.mm_team = {{ .Team }};
window.mm_user = {{ .User }};
if ({{.SessionTokenIndex}} >= 0) {
window.mm_session_token_index = {{.SessionTokenIndex}};
$.ajaxSetup({
headers: { 'X-MM-TokenIndex': mm_session_token_index }
});
}
</script>
<script> <script>
window.onerror = function(msg, url, line, column, stack) { window.onerror = function(msg, url, line, column, stack) {
var l = {}; var l = {};
@@ -70,9 +79,9 @@
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
if (window.config.SegmentDeveloperKey != null && window.config.SegmentDeveloperKey !== "") { if (window.mm_config.SegmentDeveloperKey != null && window.mm_config.SegmentDeveloperKey !== "") {
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.0.1"; !function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.0.1";
analytics.load(window.config.SegmentDeveloperKey); analytics.load(window.mm_config.SegmentDeveloperKey);
var user = window.UserStore.getCurrentUser(true); var user = window.UserStore.getCurrentUser(true);
if (user) { if (user) {
analytics.identify(user.id, { analytics.identify(user.id, {

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

@@ -17,7 +17,7 @@
</div> </div>
</div> </div>
<script> <script>
window.setup_home_page({{ .Props }}); window.setup_home_page();
</script> </script>
</body> </body>
</html> </html>

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

@@ -9,7 +9,7 @@
<div class="col-sm-12"> <div class="col-sm-12">
<div class="signup-team__container"> <div class="signup-team__container">
<img class="signup-team-logo" src="/static/images/logo.png" /> <img class="signup-team-logo" src="/static/images/logo.png" />
<h1>{{ .ClientProps.SiteName }}</h1> <h1>{{ .ClientCfg.SiteName }}</h1>
<h4 class="color--light">All team communication in one place, searchable and accessible anywhere</h4> <h4 class="color--light">All team communication in one place, searchable and accessible anywhere</h4>
<div id="signup-team"></div> <div id="signup-team"></div>
</div> </div>

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

@@ -11,7 +11,7 @@
<div class="row main"> <div class="row main">
<div class="app__content"> <div class="app__content">
<div class="welcome-info"> <div class="welcome-info">
<h1>Welcome to {{ .ClientProps.SiteName }}!</h1> <h1>Welcome to {{ .ClientCfg.SiteName }}!</h1>
<p> <p>
You do not appear to be part of any teams. Please contact your You do not appear to be part of any teams. Please contact your
administrator to have him send you an invitation to a private team. administrator to have him send you an invitation to a private team.

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

@@ -15,6 +15,7 @@ import (
"gopkg.in/fsnotify.v1" "gopkg.in/fsnotify.v1"
"html/template" "html/template"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
) )
@@ -31,10 +32,20 @@ func NewHtmlTemplatePage(templateName string, title string) *HtmlTemplatePage {
props := make(map[string]string) props := make(map[string]string)
props["Title"] = title props["Title"] = title
return &HtmlTemplatePage{TemplateName: templateName, Props: props, ClientProps: utils.ClientProperties} return &HtmlTemplatePage{TemplateName: templateName, Props: props, ClientCfg: utils.ClientCfg}
} }
func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) { func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) {
if me.Team != nil {
me.Team.Sanitize()
}
if me.User != nil {
me.User.Sanitize(map[string]bool{})
}
me.SessionTokenIndex = c.SessionTokenIndex
if err := Templates.ExecuteTemplate(w, me.TemplateName, me); err != nil { if err := Templates.ExecuteTemplate(w, me.TemplateName, me); err != nil {
c.SetUnknownError(me.TemplateName, err.Error()) c.SetUnknownError(me.TemplateName, err.Error())
} }
@@ -78,9 +89,9 @@ func InitWeb() {
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET") mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET") mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET") mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here. mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here. mainrouter.Handle("/{team}/channels/{channelname}", api.AppHandler(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here. mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
watchAndParseTemplates() watchAndParseTemplates()
} }
@@ -141,6 +152,20 @@ func CheckBrowserCompatability(c *api.Context, r *http.Request) bool {
} }
// func getTeamAndUser(c *api.Context) (*model.Team, *model.User) {
// if tr := <-api.Srv.Store.Team().Get(c.Session.TeamId); tr.Err != nil {
// c.Err = tr.Err
// return nil, nil
// } else {
// if ur := <-api.Srv.Store.User().Get(c.Session.UserId); ur.Err != nil {
// c.Err = ur.Err
// return nil, nil
// } else {
// return tr.Data.(*model.Team), ur.Data.(*model.User)
// }
// }
// }
func root(c *api.Context, w http.ResponseWriter, r *http.Request) { func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
if !CheckBrowserCompatability(c, r) { if !CheckBrowserCompatability(c, r) {
@@ -151,8 +176,29 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
page := NewHtmlTemplatePage("signup_team", "Signup") page := NewHtmlTemplatePage("signup_team", "Signup")
page.Render(c, w) page.Render(c, w)
} else { } else {
teamChan := api.Srv.Store.Team().Get(c.Session.TeamId)
userChan := api.Srv.Store.User().Get(c.Session.UserId)
var team *model.Team
if tr := <-teamChan; tr.Err != nil {
c.Err = tr.Err
return
} else {
team = tr.Data.(*model.Team)
}
var user *model.User
if ur := <-userChan; ur.Err != nil {
c.Err = ur.Err
return
} else {
user = ur.Data.(*model.User)
}
page := NewHtmlTemplatePage("home", "Home") page := NewHtmlTemplatePage("home", "Home")
page.Props["TeamURL"] = c.GetTeamURL() page.Team = team
page.User = user
page.Render(c, w) page.Render(c, w)
} }
} }
@@ -176,50 +222,19 @@ func login(c *api.Context, w http.ResponseWriter, r *http.Request) {
var team *model.Team var team *model.Team
if tResult := <-api.Srv.Store.Team().GetByName(teamName); tResult.Err != nil { if tResult := <-api.Srv.Store.Team().GetByName(teamName); tResult.Err != nil {
l4g.Error("Couldn't find team name=%v, teamURL=%v, err=%v", teamName, c.GetTeamURL(), tResult.Err.Message) l4g.Error("Couldn't find team name=%v, err=%v", teamName, tResult.Err.Message)
http.Redirect(w, r, api.GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect) http.Redirect(w, r, api.GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect)
return return
} else { } else {
team = tResult.Data.(*model.Team) team = tResult.Data.(*model.Team)
} }
// If we are already logged into this team then go to home
if len(c.Session.UserId) != 0 && c.Session.TeamId == team.Id {
page := NewHtmlTemplatePage("home", "Home")
page.Props["TeamURL"] = c.GetTeamURL()
page.Render(c, w)
return
}
// We still might be able to switch to this team because we've logged in before // We still might be able to switch to this team because we've logged in before
if multiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil { _, session := api.FindMultiSessionForTeamId(r, team.Id)
multiToken := multiCookie.Value if session != nil {
w.Header().Set(model.HEADER_TOKEN, session.Token)
if len(multiToken) > 0 { http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusTemporaryRedirect)
tokens := strings.Split(multiToken, " ") return
for _, token := range tokens {
if sr := <-api.Srv.Store.Session().Get(token); sr.Err == nil {
s := sr.Data.(*model.Session)
if !s.IsExpired() && s.TeamId == team.Id {
w.Header().Set(model.HEADER_TOKEN, s.Token)
sessionCookie := &http.Cookie{
Name: model.SESSION_TOKEN,
Value: s.Token,
Path: "/",
MaxAge: model.SESSION_TIME_WEB_IN_SECS,
HttpOnly: true,
}
http.SetCookie(w, sessionCookie)
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusTemporaryRedirect)
return
}
}
}
}
} }
page := NewHtmlTemplatePage("login", "Login") page := NewHtmlTemplatePage("login", "Login")
@@ -315,7 +330,7 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request)
func logout(c *api.Context, w http.ResponseWriter, r *http.Request) { func logout(c *api.Context, w http.ResponseWriter, r *http.Request) {
api.Logout(c, w, r) api.Logout(c, w, r)
http.Redirect(w, r, c.GetTeamURL(), http.StatusFound) http.Redirect(w, r, c.GetTeamURL(), http.StatusTemporaryRedirect)
} }
func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
@@ -324,7 +339,27 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
teamName := params["team"] teamName := params["team"]
var team *model.Team var team *model.Team
teamChan := api.Srv.Store.Team().Get(c.Session.TeamId) if result := <-api.Srv.Store.Team().GetByName(teamName); result.Err != nil {
c.Err = result.Err
return
} else {
team = result.Data.(*model.Team)
}
// We are logged into a different team. Lets see if we have another
// session in the cookie that will give us access.
if c.Session.TeamId != team.Id {
index, session := api.FindMultiSessionForTeamId(r, team.Id)
if session == nil {
// redirect to login
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
} else {
c.Session = *session
c.SessionTokenIndex = index
}
}
userChan := api.Srv.Store.User().Get(c.Session.UserId)
var channelId string var channelId string
if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil { if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil {
@@ -334,17 +369,14 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
channelId = result.Data.(string) channelId = result.Data.(string)
} }
if tResult := <-teamChan; tResult.Err != nil { var user *model.User
c.Err = tResult.Err if ur := <-userChan; ur.Err != nil {
c.Err = ur.Err
c.RemoveSessionCookie(w, r)
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
return return
} else { } else {
team = tResult.Data.(*model.Team) user = ur.Data.(*model.User)
}
if team.Name != teamName {
l4g.Error("It appears you are logged into " + team.Name + ", but are trying to access " + teamName)
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusFound)
return
} }
if len(channelId) == 0 { if len(channelId) == 0 {
@@ -365,15 +397,6 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
channelId = sc.Id channelId = sc.Id
} }
} else { } else {
// lets make sure the user is valid
if result := <-api.Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
c.Err = result.Err
c.RemoveSessionCookie(w, r)
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
return
}
// We will attempt to auto-join open channels // We will attempt to auto-join open channels
if cr := <-api.Srv.Store.Channel().GetByName(c.Session.TeamId, name); cr.Err != nil { if cr := <-api.Srv.Store.Channel().GetByName(c.Session.TeamId, name); cr.Err != nil {
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound) http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
@@ -394,7 +417,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
} }
page := NewHtmlTemplatePage("channel", "") page := NewHtmlTemplatePage("channel", "")
page.Props["Title"] = name + " - " + team.DisplayName + " " + page.ClientProps["SiteName"] page.Props["Title"] = name + " - " + team.DisplayName + " " + page.ClientCfg["SiteName"]
page.Props["TeamDisplayName"] = team.DisplayName page.Props["TeamDisplayName"] = team.DisplayName
page.Props["TeamName"] = team.Name page.Props["TeamName"] = team.Name
page.Props["TeamType"] = team.Type page.Props["TeamType"] = team.Type
@@ -402,6 +425,8 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
page.Props["ChannelName"] = name page.Props["ChannelName"] = name
page.Props["ChannelId"] = channelId page.Props["ChannelId"] = channelId
page.Props["UserId"] = c.Session.UserId page.Props["UserId"] = c.Session.UserId
page.Team = team
page.User = user
page.Render(c, w) page.Render(c, w)
} }
@@ -500,7 +525,7 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) {
} }
page := NewHtmlTemplatePage("password_reset", "") page := NewHtmlTemplatePage("password_reset", "")
page.Props["Title"] = "Reset Password " + page.ClientProps["SiteName"] page.Props["Title"] = "Reset Password " + page.ClientCfg["SiteName"]
page.Props["TeamDisplayName"] = teamDisplayName page.Props["TeamDisplayName"] = teamDisplayName
page.Props["TeamName"] = teamName page.Props["TeamName"] = teamName
page.Props["Hash"] = hash page.Props["Hash"] = hash
@@ -627,7 +652,10 @@ func signupCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request)
return return
} }
root(c, w, r) page := NewHtmlTemplatePage("home", "Home")
page.Team = team
page.User = ruser
page.Render(c, w)
} }
} }
@@ -690,6 +718,11 @@ func loginCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request)
return return
} }
page := NewHtmlTemplatePage("home", "Home")
page.Team = team
page.User = user
page.Render(c, w)
root(c, w, r) root(c, w, r)
} }
} }
@@ -701,12 +734,33 @@ func adminConsole(c *api.Context, w http.ResponseWriter, r *http.Request) {
return return
} }
teamChan := api.Srv.Store.Team().Get(c.Session.TeamId)
userChan := api.Srv.Store.User().Get(c.Session.UserId)
var team *model.Team
if tr := <-teamChan; tr.Err != nil {
c.Err = tr.Err
return
} else {
team = tr.Data.(*model.Team)
}
var user *model.User
if ur := <-userChan; ur.Err != nil {
c.Err = ur.Err
return
} else {
user = ur.Data.(*model.User)
}
params := mux.Vars(r) params := mux.Vars(r)
activeTab := params["tab"] activeTab := params["tab"]
teamId := params["team"] teamId := params["team"]
page := NewHtmlTemplatePage("admin_console", "Admin Console") page := NewHtmlTemplatePage("admin_console", "Admin Console")
page.User = user
page.Team = team
page.Props["ActiveTab"] = activeTab page.Props["ActiveTab"] = activeTab
page.Props["TeamId"] = teamId page.Props["TeamId"] = teamId
page.Render(c, w) page.Render(c, w)