From 15ad24d160cb4604d0605ebbfa53d11a57820706 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Thu, 6 Jul 2017 17:28:38 -0400 Subject: [PATCH 01/52] Minor fix --- api/oauth.go | 4 +- api/user.go | 2 +- api4/oauth.go | 8 ++-- api4/user.go | 2 +- app/oauth.go | 111 ++++++++++++++++++++++++++++++++++++++++++----- i18n/en.json | 4 ++ model/token.go | 1 + webapp/yarn.lock | 2 +- 8 files changed, 114 insertions(+), 20 deletions(-) diff --git a/api/oauth.go b/api/oauth.go index 84d30ee61a..a239e889bb 100644 --- a/api/oauth.go +++ b/api/oauth.go @@ -157,7 +157,7 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - if authUrl, err := app.GetOAuthLoginEndpoint(service, teamId, model.OAUTH_ACTION_LOGIN, redirectTo, loginHint); err != nil { + if authUrl, err := app.GetOAuthLoginEndpoint(w, r, service, teamId, model.OAUTH_ACTION_LOGIN, redirectTo, loginHint); err != nil { c.Err = err return } else { @@ -180,7 +180,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - if authUrl, err := app.GetOAuthSignupEndpoint(service, teamId); err != nil { + if authUrl, err := app.GetOAuthSignupEndpoint(w, r, service, teamId); err != nil { c.Err = err return } else { diff --git a/api/user.go b/api/user.go index eb249cb394..0b2fbfba88 100644 --- a/api/user.go +++ b/api/user.go @@ -866,7 +866,7 @@ func emailToOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - link, err := app.SwitchEmailToOAuth(email, password, mfaToken, service) + link, err := app.SwitchEmailToOAuth(w, r, email, password, mfaToken, service) if err != nil { c.Err = err return diff --git a/api4/oauth.go b/api4/oauth.go index 402651b929..d00b4a666c 100644 --- a/api4/oauth.go +++ b/api4/oauth.go @@ -400,7 +400,7 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) { uri := c.GetSiteURLHeader() + "/signup/" + service + "/complete" - body, teamId, props, err := app.AuthorizeOAuthUser(service, code, state, uri) + body, teamId, props, err := app.AuthorizeOAuthUser(w, r, service, code, state, uri) if err != nil { c.Err = err return @@ -455,7 +455,7 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - if authUrl, err := app.GetOAuthLoginEndpoint(c.Params.Service, teamId, model.OAUTH_ACTION_LOGIN, redirectTo, loginHint); err != nil { + if authUrl, err := app.GetOAuthLoginEndpoint(w, r, c.Params.Service, teamId, model.OAUTH_ACTION_LOGIN, redirectTo, loginHint); err != nil { c.Err = err return } else { @@ -475,7 +475,7 @@ func mobileLoginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - if authUrl, err := app.GetOAuthLoginEndpoint(c.Params.Service, teamId, model.OAUTH_ACTION_MOBILE, "", ""); err != nil { + if authUrl, err := app.GetOAuthLoginEndpoint(w, r, c.Params.Service, teamId, model.OAUTH_ACTION_MOBILE, "", ""); err != nil { c.Err = err return } else { @@ -500,7 +500,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) { return } - if authUrl, err := app.GetOAuthSignupEndpoint(c.Params.Service, teamId); err != nil { + if authUrl, err := app.GetOAuthSignupEndpoint(w, r, c.Params.Service, teamId); err != nil { c.Err = err return } else { diff --git a/api4/user.go b/api4/user.go index 04faf13c41..f13c33f0bd 100644 --- a/api4/user.go +++ b/api4/user.go @@ -1056,7 +1056,7 @@ func switchAccountType(c *Context, w http.ResponseWriter, r *http.Request) { var err *model.AppError if switchRequest.EmailToOAuth() { - link, err = app.SwitchEmailToOAuth(switchRequest.Email, switchRequest.Password, switchRequest.MfaCode, switchRequest.NewService) + link, err = app.SwitchEmailToOAuth(w, r, switchRequest.Email, switchRequest.Password, switchRequest.MfaCode, switchRequest.NewService) } else if switchRequest.OAuthToEmail() { c.SessionRequired() if c.Err != nil { diff --git a/app/oauth.go b/app/oauth.go index a2edae8bee..4bc84272b6 100644 --- a/app/oauth.go +++ b/app/oauth.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "strings" + "time" l4g "github.com/alecthomas/log4go" "github.com/mattermost/platform/einterfaces" @@ -20,6 +21,11 @@ import ( "github.com/mattermost/platform/utils" ) +const ( + OAUTH_COOKIE_MAX_AGE_SECONDS = 30 * 60 // 30 minutes + COOKIE_OAUTH = "MMOAUTH" +) + func CreateOAuthApp(app *model.OAuthApp) (*model.OAuthApp, *model.AppError) { if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider { return nil, model.NewAppError("CreateOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "", http.StatusNotImplemented) @@ -289,7 +295,7 @@ func newSessionUpdateToken(appName string, accessData *model.AccessData, user *m return accessRsp, nil } -func GetOAuthLoginEndpoint(service, teamId, action, redirectTo, loginHint string) (string, *model.AppError) { +func GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamId, action, redirectTo, loginHint string) (string, *model.AppError) { stateProps := map[string]string{} stateProps["action"] = action if len(teamId) != 0 { @@ -300,21 +306,21 @@ func GetOAuthLoginEndpoint(service, teamId, action, redirectTo, loginHint string stateProps["redirect_to"] = redirectTo } - if authUrl, err := GetAuthorizationCode(service, stateProps, loginHint); err != nil { + if authUrl, err := GetAuthorizationCode(w, r, service, stateProps, loginHint); err != nil { return "", err } else { return authUrl, nil } } -func GetOAuthSignupEndpoint(service, teamId string) (string, *model.AppError) { +func GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamId string) (string, *model.AppError) { stateProps := map[string]string{} stateProps["action"] = model.OAUTH_ACTION_SIGNUP if len(teamId) != 0 { stateProps["team_id"] = teamId } - if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil { + if authUrl, err := GetAuthorizationCode(w, r, service, stateProps, ""); err != nil { return "", err } else { return authUrl, nil @@ -519,17 +525,69 @@ func CompleteSwitchWithOAuth(service string, userData io.ReadCloser, email strin return user, nil } -func GetAuthorizationCode(service string, props map[string]string, loginHint string) (string, *model.AppError) { +func CreateOAuthStateToken(extra string) (*model.Token, *model.AppError) { + token := model.NewToken(model.TOKEN_TYPE_OAUTH, extra) + + if result := <-Srv.Store.Token().Save(token); result.Err != nil { + return nil, result.Err + } + + return token, nil +} + +func GetOAuthStateToken(token string) (*model.Token, *model.AppError) { + if result := <-Srv.Store.Token().GetByToken(token); result.Err != nil { + return nil, model.NewAppError("GetOAuthStateToken", "api.oauth.invalid_state_token.app_error", nil, result.Err.Error(), http.StatusBadRequest) + } else { + token := result.Data.(*model.Token) + if token.Type != model.TOKEN_TYPE_OAUTH { + return nil, model.NewAppError("GetOAuthStateToken", "api.oauth.invalid_state_token.app_error", nil, "", http.StatusBadRequest) + } + + return token, nil + } +} + +func generateOAuthStateTokenExtra(email, action, cookie string) string { + return email + ":" + action + ":" + cookie +} + +func GetAuthorizationCode(w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError) { sso := utils.Cfg.GetSSOService(service) if sso != nil && !sso.Enable { return "", model.NewAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service, http.StatusNotImplemented) } + secure := false + if GetProtocol(r) == "https" { + secure = true + } + + cookieValue := model.NewId() + expiresAt := time.Unix(model.GetMillis()/1000+int64(OAUTH_COOKIE_MAX_AGE_SECONDS), 0) + oauthCookie := &http.Cookie{ + Name: COOKIE_OAUTH, + Value: cookieValue, + Path: "/", + MaxAge: OAUTH_COOKIE_MAX_AGE_SECONDS, + Expires: expiresAt, + HttpOnly: true, + Secure: secure, + } + + http.SetCookie(w, oauthCookie) + clientId := sso.Id endpoint := sso.AuthEndpoint scope := sso.Scope - props["hash"] = utils.HashSha256(clientId) + tokenExtra := generateOAuthStateTokenExtra(props["email"], props["action"], cookieValue) + stateToken, err := CreateOAuthStateToken(tokenExtra) + if err != nil { + return "", err + } + + props["token"] = stateToken.Token state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props))) redirectUri := utils.GetSiteURL() + "/signup/" + service + "/complete" @@ -547,7 +605,7 @@ func GetAuthorizationCode(service string, props map[string]string, loginHint str return authUrl, nil } -func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) { +func AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectUri string) (io.ReadCloser, string, map[string]string, *model.AppError) { sso := utils.Cfg.GetSSOService(service) if sso == nil || !sso.Enable { return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service) @@ -562,10 +620,41 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser stateProps := model.MapFromJson(strings.NewReader(stateStr)) - if stateProps["hash"] != utils.HashSha256(sso.Id) { - return nil, "", nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "") + expectedToken, err := GetOAuthStateToken(stateProps["token"]) + if err != nil { + return nil, "", nil, err } + stateEmail := stateProps["email"] + stateAction := stateProps["action"] + if stateAction == model.OAUTH_ACTION_EMAIL_TO_SSO && stateEmail == "" { + return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest) + } + + cookieValue := "" + if cookie, err := r.Cookie(COOKIE_OAUTH); err != nil { + return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest) + } else { + cookieValue = cookie.Value + } + + expectedTokenExtra := generateOAuthStateTokenExtra(stateEmail, stateAction, cookieValue) + if expectedTokenExtra != expectedToken.Extra { + return nil, "", nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "", http.StatusBadRequest) + } + + DeleteToken(expectedToken) + + cookie := &http.Cookie{ + Name: COOKIE_OAUTH, + Value: "", + Path: "/", + MaxAge: -1, + HttpOnly: true, + } + + http.SetCookie(w, cookie) + teamId := stateProps["team_id"] p := url.Values{} @@ -617,7 +706,7 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser } -func SwitchEmailToOAuth(email, password, code, service string) (string, *model.AppError) { +func SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) { var user *model.User var err *model.AppError if user, err = GetUserByEmail(email); err != nil { @@ -635,7 +724,7 @@ func SwitchEmailToOAuth(email, password, code, service string) (string, *model.A if service == model.USER_AUTH_SERVICE_SAML { return utils.GetSiteURL() + "/login/sso/saml?action=" + model.OAUTH_ACTION_EMAIL_TO_SSO + "&email=" + email, nil } else { - if authUrl, err := GetAuthorizationCode(service, stateProps, ""); err != nil { + if authUrl, err := GetAuthorizationCode(w, r, service, stateProps, ""); err != nil { return "", err } else { return authUrl, nil diff --git a/i18n/en.json b/i18n/en.json index 7d23a13c1a..6784bcd756 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2699,6 +2699,10 @@ "id": "api.user.permanent_delete_user.system_admin.warn", "translation": "You are deleting %v that is a system administrator. You may need to set another account as the system administrator using the command line tools." }, + { + "id": "api.oauth.invalid_state_token.app_error", + "translation": "Invalid state token" + }, { "id": "api.user.reset_password.invalid_link.app_error", "translation": "The reset password link does not appear to be valid" diff --git a/model/token.go b/model/token.go index 6666e112b2..a4d10c7f8d 100644 --- a/model/token.go +++ b/model/token.go @@ -8,6 +8,7 @@ import "net/http" const ( TOKEN_SIZE = 64 MAX_TOKEN_EXIPRY_TIME = 1000 * 60 * 60 * 24 // 24 hour + TOKEN_TYPE_OAUTH = "oauth" ) type Token struct { diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 66774deb5a..dde0b98d31 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5043,7 +5043,7 @@ math-expression-evaluator@^1.2.14: mattermost-redux@mattermost/mattermost-redux#webapp-master: version "0.0.1" - resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/dd48556075c8be41aa5ac4a0165bbe830d496875" + resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/b4bab66d36f10ace06bcd3243d68807bfbca9c48" dependencies: deep-equal "1.0.1" harmony-reflect "1.5.1" From 0bb4add8a17fc1b0984094cf48da711b68a26d55 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 7 Jul 2017 19:25:26 -0400 Subject: [PATCH 02/52] PLT-6999 Stopped mutating style object provided by react-bootstrap (#6869) * PLT-6999 Stopped mutating style object provided by react-bootstrap * Fixed indentation to satisfy eslint --- .../components/emoji_picker/emoji_picker.jsx | 102 +++++++++++------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/webapp/components/emoji_picker/emoji_picker.jsx b/webapp/components/emoji_picker/emoji_picker.jsx index a047c12776..0d9b341767 100644 --- a/webapp/components/emoji_picker/emoji_picker.jsx +++ b/webapp/components/emoji_picker/emoji_picker.jsx @@ -309,7 +309,7 @@ export default class EmojiPicker extends React.Component { right: this.props.rightOffset }; } else { - pickerStyle = this.props.style; + pickerStyle = {...this.props.style}; } } @@ -325,91 +325,111 @@ export default class EmojiPicker extends React.Component {
} + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'recent'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'people'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'nature'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'food'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'activity'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'travel'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'objects'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'symbols'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'flags'} /> } + icon={ + + } onCategoryClick={this.handleCategoryClick} selected={this.state.category === 'custom'} /> From 40c0c0bd13380acff2defd8644b714a4641d44c4 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Sat, 8 Jul 2017 07:25:57 +0800 Subject: [PATCH 03/52] Enable emoji picker by default in config.json (#6871) --- config/config.json | 2 +- model/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.json b/config/config.json index 3401a5e4f0..a07cf5c196 100644 --- a/config/config.json +++ b/config/config.json @@ -38,7 +38,7 @@ "WebsocketPort": 80, "WebserverMode": "gzip", "EnableCustomEmoji": false, - "EnableEmojiPicker": false, + "EnableEmojiPicker": true, "RestrictCustomEmojiCreation": "all", "RestrictPostDelete": "all", "AllowEditPost": "always", diff --git a/model/config.go b/model/config.go index b7526925f3..38d27d8bb3 100644 --- a/model/config.go +++ b/model/config.go @@ -1033,7 +1033,7 @@ func (o *Config) SetDefaults() { if o.ServiceSettings.EnableEmojiPicker == nil { o.ServiceSettings.EnableEmojiPicker = new(bool) - *o.ServiceSettings.EnableEmojiPicker = false + *o.ServiceSettings.EnableEmojiPicker = true } if o.ServiceSettings.RestrictCustomEmojiCreation == nil { From 61adde51127bd85c16fec022f41be0ec0c5b5c3f Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Sat, 8 Jul 2017 07:26:28 +0800 Subject: [PATCH 04/52] fix "@" mention doesn't open recent mentions search results if RHS is closed (#6872) --- webapp/components/channel_header.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx index 34d58f5aa0..10e5687944 100644 --- a/webapp/components/channel_header.jsx +++ b/webapp/components/channel_header.jsx @@ -964,7 +964,10 @@ export default class ChannelHeader extends React.Component { placement='bottom' overlay={recentMentionsTooltip} > -
+
Date: Fri, 7 Jul 2017 19:27:13 -0400 Subject: [PATCH 05/52] Only show unreads below indicator after first load is complete (#6874) --- webapp/components/post_view/post_list.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/post_view/post_list.jsx b/webapp/components/post_view/post_list.jsx index 13cc28da32..20d1ce0ffb 100644 --- a/webapp/components/post_view/post_list.jsx +++ b/webapp/components/post_view/post_list.jsx @@ -152,7 +152,7 @@ export default class PostList extends React.PureComponent { return; } - if (!this.wasAtBottom() && this.props.posts !== nextProps.posts) { + if (!this.wasAtBottom() && this.props.posts !== nextProps.posts && this.hasScrolledToNewMessageSeparator) { const unViewedCount = nextProps.posts.reduce((count, post) => { if (post.create_at > this.state.lastViewed && post.user_id !== nextProps.currentUserId && From 06814885a050c9a0cae92c73c48e334272d30281 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Sat, 8 Jul 2017 04:27:38 +0500 Subject: [PATCH 06/52] UI changes (#6876) * PLT-7011 - Long channel name truncation * PLT-7013 - Fixing channel header mobile * PLT-7012 - Updating quick switch modal on mobile * PLT-7008 - Channel preferences mobile fix * PLT-7014 - Increasing tap area for channel desc --- webapp/components/navbar.jsx | 2 +- .../quick_switch_modal/quick_switch_modal.jsx | 2 +- webapp/sass/layout/_headers.scss | 17 ++++++++++++----- webapp/sass/layout/_navigation.scss | 2 +- webapp/sass/layout/_sidebar-right.scss | 5 +++++ webapp/sass/responsive/_mobile.scss | 5 +---- webapp/utils/utils.jsx | 4 ++-- 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/webapp/components/navbar.jsx b/webapp/components/navbar.jsx index f61f58a8de..6305f870e2 100644 --- a/webapp/components/navbar.jsx +++ b/webapp/components/navbar.jsx @@ -698,7 +698,7 @@ export default class Navbar extends React.Component { />
@@ -439,10 +434,7 @@ export default class RhsThread extends React.Component { renderView={renderView} onScroll={this.handleScroll} > -
+
@@ -458,7 +450,6 @@ export default class RhsThread extends React.Component { status={rootStatus} previewCollapsed={this.state.previewsCollapsed} isBusy={this.state.isBusy} - getPostList={this.getPostListContainer} />
Date: Thu, 13 Jul 2017 15:12:28 -0400 Subject: [PATCH 40/52] Postgres fix --- store/sql_oauth_store.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/store/sql_oauth_store.go b/store/sql_oauth_store.go index 8e23a8cb2a..2e6fe2655c 100644 --- a/store/sql_oauth_store.go +++ b/store/sql_oauth_store.go @@ -9,6 +9,7 @@ import ( "github.com/mattermost/gorp" "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" ) type SqlOAuthStore struct { @@ -527,7 +528,14 @@ func (as SqlOAuthStore) deleteApp(transaction *gorp.Transaction, clientId string func (as SqlOAuthStore) deleteOAuthAppSessions(transaction *gorp.Transaction, clientId string) StoreResult { result := StoreResult{} - if _, err := transaction.Exec("DELETE s.* FROM Sessions s INNER JOIN OAuthAccessData o ON o.Token = s.Token WHERE o.ClientId = :Id", map[string]interface{}{"Id": clientId}); err != nil { + query := "" + if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES { + query = "DELETE FROM Sessions s USING OAuthAccessData o WHERE o.Token = s.Token AND o.ClientId = :Id" + } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL { + query = "DELETE s.* FROM Sessions s INNER JOIN OAuthAccessData o ON o.Token = s.Token WHERE o.ClientId = :Id" + } + + if _, err := transaction.Exec(query, map[string]interface{}{"Id": clientId}); err != nil { result.Err = model.NewLocAppError("SqlOAuthStore.DeleteApp", "store.sql_oauth.delete_app.app_error", nil, "id="+clientId+", err="+err.Error()) return result } From 764ff4cb64eb86c87a28a076eed28d8778f194d6 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Thu, 13 Jul 2017 19:55:45 -0400 Subject: [PATCH 41/52] PLT-7116/PLT-7126 Some final release fixes (#6933) * Some final release fixes * Fix team switching with image in channel bug --- webapp/components/admin_console/saml_settings.jsx | 6 +++--- webapp/components/markdown_image.jsx | 2 +- webapp/components/post_view/post_list.jsx | 11 ++++++++++- webapp/yarn.lock | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/webapp/components/admin_console/saml_settings.jsx b/webapp/components/admin_console/saml_settings.jsx index 4c0c0c8fd2..2358660dac 100644 --- a/webapp/components/admin_console/saml_settings.jsx +++ b/webapp/components/admin_console/saml_settings.jsx @@ -77,15 +77,15 @@ export default class SamlSettings extends AdminSettings { AdminActions.samlCertificateStatus( (data) => { const files = {}; - if (!data.IdpCertificateFile) { + if (!data.idp_certificate_file) { files.idpCertificateFile = ''; } - if (!data.PublicCertificateFile) { + if (!data.public_certificate_file) { files.publicCertificateFile = ''; } - if (!data.PrivateKeyFile) { + if (!data.private_key_file) { files.privateKeyFile = ''; } this.setState(files); diff --git a/webapp/components/markdown_image.jsx b/webapp/components/markdown_image.jsx index 4d86354578..2634ef3f6f 100644 --- a/webapp/components/markdown_image.jsx +++ b/webapp/components/markdown_image.jsx @@ -39,7 +39,7 @@ export default class MarkdownImage extends React.PureComponent { waitForHeight = () => { if (this.refs.image.height) { - postListScrollChange(); + setTimeout(postListScrollChange, 0); this.heightTimeout = 0; } else { diff --git a/webapp/components/post_view/post_list.jsx b/webapp/components/post_view/post_list.jsx index c42c623775..d8a56fe83f 100644 --- a/webapp/components/post_view/post_list.jsx +++ b/webapp/components/post_view/post_list.jsx @@ -181,6 +181,10 @@ export default class PostList extends React.PureComponent { const posts = this.props.posts; const postList = this.refs.postlist; + if (!postList) { + return; + } + // Scroll to focused post on first load const focusedPost = this.refs[this.props.focusedPostId]; if (focusedPost && this.props.posts) { @@ -262,7 +266,7 @@ export default class PostList extends React.PureComponent { checkBottom = () => { if (!this.refs.postlist) { - return false; + return true; } // No scroll bar so we're at the bottom @@ -329,7 +333,12 @@ export default class PostList extends React.PureComponent { handleScroll = () => { // Only count as user scroll if we've already performed our first load scroll this.hasScrolled = this.hasScrolledToNewMessageSeparator || this.hasScrolledToFocusedPost; + if (!this.refs.postlist) { + return; + } + this.previousScrollTop = this.refs.postlist.scrollTop; + if (this.refs.postlist.scrollHeight === this.previousScrollHeight) { this.atBottom = this.checkBottom(); } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 503b802543..0192200f8e 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5043,7 +5043,7 @@ math-expression-evaluator@^1.2.14: mattermost-redux@mattermost/mattermost-redux#webapp-4.0: version "0.0.1" - resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/2f816b1b1374f7d10f7c160cd392f323d8cebcc6" + resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/a68c49c57130ed64e5a8fb3bdbd004ced437790b" dependencies: deep-equal "1.0.1" harmony-reflect "1.5.1" From a18479df0940be8503c9b88993490741793eba9e Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 13 Jul 2017 14:02:33 -0700 Subject: [PATCH 42/52] Tweak WebSocket header-processing (#6929) * fix * consolidate code --- api/websocket_test.go | 9 +++++++++ app/server.go | 5 ++--- utils/api.go | 10 +++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/api/websocket_test.go b/api/websocket_test.go index a65ebc02e0..18e1a64262 100644 --- a/api/websocket_test.go +++ b/api/websocket_test.go @@ -362,6 +362,15 @@ func TestWebsocketOriginSecurity(t *testing.T) { t.Fatal("Should have errored because Origin contain AllowCorsFrom") } + // Should fail because non-matching CORS + *utils.Cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" + _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ + "Origin": []string{"http://www.good.co"}, + }) + if err == nil { + t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!") + } + *utils.Cfg.ServiceSettings.AllowCorsFrom = "" } diff --git a/app/server.go b/app/server.go index a5090a5974..a5b2dbda94 100644 --- a/app/server.go +++ b/app/server.go @@ -53,9 +53,8 @@ type CorsWrapper struct { func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { if len(*utils.Cfg.ServiceSettings.AllowCorsFrom) > 0 { - origin := r.Header.Get("Origin") - if *utils.Cfg.ServiceSettings.AllowCorsFrom == "*" || strings.Contains(*utils.Cfg.ServiceSettings.AllowCorsFrom, origin) { - w.Header().Set("Access-Control-Allow-Origin", origin) + if utils.OriginChecker(r) { + w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) if r.Method == "OPTIONS" { w.Header().Set( diff --git a/utils/api.go b/utils/api.go index 663f53c168..d175e0c13d 100644 --- a/utils/api.go +++ b/utils/api.go @@ -15,7 +15,15 @@ type OriginCheckerProc func(*http.Request) bool func OriginChecker(r *http.Request) bool { origin := r.Header.Get("Origin") - return *Cfg.ServiceSettings.AllowCorsFrom == "*" || strings.Contains(*Cfg.ServiceSettings.AllowCorsFrom, origin) + if *Cfg.ServiceSettings.AllowCorsFrom == "*" { + return true + } + for _, allowed := range strings.Split(*Cfg.ServiceSettings.AllowCorsFrom, " ") { + if allowed == origin { + return true + } + } + return false } func GetOriginChecker(r *http.Request) OriginCheckerProc { From 10cde61958b2b241f745152d661a0bc0040e63d5 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Fri, 14 Jul 2017 13:10:20 -0400 Subject: [PATCH 43/52] Only apply edit policy setting if message changed (#6930) --- app/post.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/post.go b/app/post.go index f5eb293675..1e8c721ecb 100644 --- a/app/post.go +++ b/app/post.go @@ -256,7 +256,7 @@ func UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError } if utils.IsLicensed { - if *utils.Cfg.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_TIME_LIMIT && model.GetMillis() > oldPost.CreateAt+int64(*utils.Cfg.ServiceSettings.PostEditTimeLimit*1000) { + if *utils.Cfg.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_TIME_LIMIT && model.GetMillis() > oldPost.CreateAt+int64(*utils.Cfg.ServiceSettings.PostEditTimeLimit*1000) && post.Message != oldPost.Message { err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_time_limit.app_error", map[string]interface{}{"timeLimit": *utils.Cfg.ServiceSettings.PostEditTimeLimit}, "", http.StatusBadRequest) return nil, err } From cd5703c0273c11345063c3355f87f6486c5f04a6 Mon Sep 17 00:00:00 2001 From: enahum Date: Fri, 14 Jul 2017 13:10:44 -0400 Subject: [PATCH 44/52] translations PR 20170714 (#6938) --- i18n/it.json | 4 ++-- webapp/i18n/de.json | 4 ++-- webapp/i18n/es.json | 4 ++-- webapp/i18n/fr.json | 4 ++-- webapp/i18n/it.json | 34 +++++++++++++++++----------------- webapp/i18n/ja.json | 6 +++--- webapp/i18n/pl.json | 4 ++-- webapp/i18n/pt-BR.json | 4 ++-- webapp/i18n/ru.json | 4 ++-- webapp/i18n/tr.json | 4 ++-- webapp/i18n/zh-CN.json | 4 ++-- webapp/i18n/zh-TW.json | 10 +++++----- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/i18n/it.json b/i18n/it.json index 75c891d50b..95ec55b3b3 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -1873,11 +1873,11 @@ }, { "id": "api.slackimport.slack_add_bot_user.email_pwd", - "translation": "Slack Bot/Post di Integrazione Importa Utente: Email, Password: {{.Email}}, {{.Password}}\r\n" + "translation": "Slack Bot/Integra pubblicazioni Importa Utente: Email, Password: {{.Email}}, {{.Password}}\r\n" }, { "id": "api.slackimport.slack_add_bot_user.unable_import", - "translation": "Impossibile importare Slack Bot/Post di integrazione Utente: {{.Username}}\r\n" + "translation": "Impossibile importare Slack Bot/Integra pubblicazioni Utente: {{.Username}}\r\n" }, { "id": "api.slackimport.slack_add_channels.added", diff --git a/webapp/i18n/de.json b/webapp/i18n/de.json index 6604576076..311ee0b652 100644 --- a/webapp/i18n/de.json +++ b/webapp/i18n/de.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Z.B.: \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "Salt für E-Mail-Einladung:", "admin.email.mhpns": "Benutze verschlüsselte, production-quality HPNS Verbindung zu iOS und Android Apps", - "admin.email.mhpnsHelp": "Mattermost iOS App bei iTunes herunterladen. Mattermost Android App bei Google Play herunterladen. Erfahren Sie mehr über HPNS.", + "admin.email.mhpnsHelp": "Mattermost iOS App bei iTunes herunterladen. Mattermost Android App bei Google Play herunterladen. Erfahren Sie mehr über HPNS.", "admin.email.mtpns": "Benutze iOS und Android Apps von iTunes und Google Play mit TPNS", - "admin.email.mtpnsHelp": "Mattermost iOS App bei iTunes herunterladen. Mattermost Android App bei Google Play herunterladen. Erfahren Sie mehr über TPNS.", + "admin.email.mtpnsHelp": "Mattermost iOS App bei iTunes herunterladen. Mattermost Android App bei Google Play herunterladen. Erfahren Sie mehr über TPNS.", "admin.email.nofificationOrganizationExample": "Z.B. \"© Musterfirma GmbH, Musterstraße 23, 59424 Musterhausen, Deutschland\"", "admin.email.notificationDisplayDescription": "Anzeigename des E-Mail-Kontos, welches zum Senden von Benachrichtigungsmails von Mattermost verwendet wird.", "admin.email.notificationDisplayExample": "Z.B.: \"Mattermost-Nachricht\", \"System\", \"No-Reply\"", diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json index febfe2a345..e75fffcec6 100644 --- a/webapp/i18n/es.json +++ b/webapp/i18n/es.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Ej.: \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "Salt para correos electrónicos de invitación:", "admin.email.mhpns": "Utiliza conexiones cifradas, con calidad de producción de HPNS para tus aplicaciones en iOS y Android", - "admin.email.mhpnsHelp": "Descarga la app de Mattermost para iOS desde iTunes. Descarga la app de Mattermost para Android desde Google Play. Conoce más acerca de HPNS.", + "admin.email.mhpnsHelp": "Descarga la app de Mattermost para iOS desde iTunes. Descarga la app de Mattermost para Android desde Google Play. Conoce más acerca de HPNS.", "admin.email.mtpns": "Utiliza las apps de iOS y Android en iTunes y Google Play con TPNS", - "admin.email.mtpnsHelp": "Descarga la app de Mattermost para iOS desde iTunes. Descarga la app de Mattermost para Android desde Google Play. Conoce más acerca de TPNS.", + "admin.email.mtpnsHelp": "Descarga la app de Mattermost para iOS desde iTunes. Descarga la app de Mattermost para Android desde Google Play. Conoce más acerca de TPNS.", "admin.email.nofificationOrganizationExample": "Ej: \"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "Muestra el nombre en la cuenta del email utilizada para enviar notificaciones por correo electrónico desde Mattermost.", "admin.email.notificationDisplayExample": "Ej: \"Notificación de Mattermost\", \"Sistema\", \"No-Responder\"", diff --git a/webapp/i18n/fr.json b/webapp/i18n/fr.json index 1384f4ca68..e9ec2a7a28 100644 --- a/webapp/i18n/fr.json +++ b/webapp/i18n/fr.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Ex. : \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "Clé de salage des e-mails d'invitation :", "admin.email.mhpns": "La connexion à iOS et aux applications Android est cryptée", - "admin.email.mhpnsHelp": "Téléchargez l'application iOS Mattermost depuis iTunes. Téléchargez l'application Android Mattermost depuis le Google Play. Apprenez-en davantage sur HPNS.", + "admin.email.mhpnsHelp": "Téléchargez l'application iOS Mattermost depuis iTunes. Téléchargez l'application Android Mattermost depuis le Google Play. Apprenez-en davantage sur HPNS.", "admin.email.mtpns": "Utilisez iOS et Android sur iTunes et Google Play avec TPNS", - "admin.email.mtpnsHelp": "Téléchargez l'application iOS Mattermost depuis iTunes. Téléchargez l'application Android Mattermost depuis le Google Play. Apprenez-en davantage sur TPNS.", + "admin.email.mtpnsHelp": "Téléchargez l'application iOS Mattermost depuis iTunes. Téléchargez l'application Android Mattermost depuis le Google Play. Apprenez-en davantage sur TPNS.", "admin.email.nofificationOrganizationExample": "Ex. : \"© MonEntreprise, 12 avenue Niel, 75017 Paris, France\"", "admin.email.notificationDisplayDescription": "Afficher le nom du compte de messagerie utilisé lors de l'envoi d'e-mails de notification par Mattermost.", "admin.email.notificationDisplayExample": "Ex. : \"Notification Mattermost\", \"Système\", \"No-reply\"", diff --git a/webapp/i18n/it.json b/webapp/i18n/it.json index ee12129e32..acda13894f 100644 --- a/webapp/i18n/it.json +++ b/webapp/i18n/it.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Es. \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "Seme per inviti email:", "admin.email.mhpns": "USa connesioni criptate HPNS per le applicazioni iOS and Android", - "admin.email.mhpnsHelp": "Scarica Mattermost iOS app from iTunes. Scarica Mattermost Android app da Google Play. Learn more about HPNS.", + "admin.email.mhpnsHelp": "Scarica Mattermost iOS app da iTunes. Scarica Mattermost Android app da Google Play. Scopri di più su HPNS.", "admin.email.mtpns": "Usare le app iOS e Android su iTunes e Google Play con TPNS", - "admin.email.mtpnsHelp": "Scarica Mattermost per iOS da iTunes. Scarica Mattermost per Android da Google Play. Informazioni su TPNS.", + "admin.email.mtpnsHelp": "Scarica Mattermost iOS app da iTunes. Scarica Mattermost Android app da Google Play. Scopri di più su TPNS.", "admin.email.nofificationOrganizationExample": "Es. \"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "Nome visualizzato nell'account di posta, usato per le email di notifica di Mattermost.", "admin.email.notificationDisplayExample": "Es: \"Notifica Mattermost\", \"System\", \"No-Reply\"", @@ -847,7 +847,7 @@ "admin.support.termsTitle": "Collegamento Termini di Utilizzo:", "admin.system_analytics.activeUsers": "Utenti attivi con post", "admin.system_analytics.title": "il Sistema", - "admin.system_analytics.totalPosts": "Post totali", + "admin.system_analytics.totalPosts": "Pubblicazioni totali", "admin.system_users.allUsers": "Tutti gli utenti", "admin.system_users.noTeams": "Nessun gruppo", "admin.system_users.title": "Utenti {siteName}", @@ -900,7 +900,7 @@ "admin.team.userCreationDescription": "Se false, la creazione di nuovi account è disattivata. Il pulsate di creazione account visualizza un errore se cliccato.", "admin.team.userCreationTitle": "Abilita creazione account: ", "admin.team_analytics.activeUsers": "Utenti attivi con post", - "admin.team_analytics.totalPosts": "Post totali", + "admin.team_analytics.totalPosts": "Pubblicazioni totali", "admin.true": "vero", "admin.user_item.authServiceEmail": "Metodo di accesso: Email", "admin.user_item.authServiceNotEmail": "Metodo di accesso: {service}", @@ -961,26 +961,26 @@ "analytics.system.channelTypes": "Tipi di canale", "analytics.system.dailyActiveUsers": "Utenti attivi quotidianamente", "analytics.system.monthlyActiveUsers": "Utenti attivi mensilmente", - "analytics.system.postTypes": "Post, File e Hashtag", + "analytics.system.postTypes": "Pubblicazioni, File e Hashtag", "analytics.system.privateGroups": "Canali Privati", "analytics.system.publicChannels": "Canali Pubblici", "analytics.system.skippedIntensiveQueries": "Per massimizzare le performance, alcune statistiche sono state disattivate. Puoi riattivarle nel file config.json. Vedi https://docs.mattermost.com/administration/statistics.html", - "analytics.system.textPosts": "Post con solo testo", + "analytics.system.textPosts": "Pubblicazioni con solo testo", "analytics.system.title": "Statistiche di Sistema", "analytics.system.totalChannels": "Canali totali", "analytics.system.totalCommands": "Comandi totali", - "analytics.system.totalFilePosts": "Post con file", - "analytics.system.totalHashtagPosts": "Post con Hashtags", + "analytics.system.totalFilePosts": "Pubblicazioni con file", + "analytics.system.totalHashtagPosts": "Pubblicazioni con Hashtags", "analytics.system.totalIncomingWebhooks": "Webhooks in ingresso", "analytics.system.totalMasterDbConnections": "Connessioni al database principale", "analytics.system.totalOutgoingWebhooks": "Webhooks in uscita", - "analytics.system.totalPosts": "Post totali", + "analytics.system.totalPosts": "Pubblicazioni totali", "analytics.system.totalReadDbConnections": "Connessioni al database di replica", "analytics.system.totalSessions": "Sessioni totali", "analytics.system.totalTeams": "Gruppi totali", "analytics.system.totalUsers": "Utenti totali", "analytics.system.totalWebsockets": "Connessioni WebSocket", - "analytics.team.activeUsers": "Utenti Attivi Con Post", + "analytics.team.activeUsers": "Utenti Attivi Con Pubblicazioni", "analytics.team.newlyCreated": "Utenti Creati Recentemente", "analytics.team.noTeams": "Non sono presenti gruppi su questo server per i quali visualizzare statistiche.", "analytics.team.privateGroups": "Canali Privati", @@ -988,7 +988,7 @@ "analytics.team.recentActive": "Utenti Attivi Recentemente", "analytics.team.recentUsers": "Utenti Attivi Recentemente", "analytics.team.title": "Statistiche gruppo per {team}", - "analytics.team.totalPosts": "Post totali", + "analytics.team.totalPosts": "Pubblicazioni totali", "analytics.team.totalUsers": "Utenti totali", "api.channel.add_member.added": "{addedUsername} aggiunto al canale da {username}", "api.channel.delete_channel.archived": "{username} ha archiviato il canale.", @@ -1093,7 +1093,7 @@ "channel_header.channelHeader": "Modifica titolo canale", "channel_header.channelMembers": "Membri", "channel_header.delete": "Elimina Canale", - "channel_header.flagged": "Post segnati", + "channel_header.flagged": "Pubblicazioni segnati", "channel_header.leave": "Abbandona canale", "channel_header.manageMembers": "Gestione Membri", "channel_header.notificationPreferences": "Preferenze delle Notifiche", @@ -1748,7 +1748,7 @@ "mobile.open_dm.error": "Impossibile aprire un messaggio diretto con {displayName}. Controlla la connessione e riprova.", "mobile.post.cancel": "Annulla", "mobile.post.delete_question": "Sei sicuro di voler eliminare questo post?", - "mobile.post.delete_title": "Elimina Post", + "mobile.post.delete_title": "Elimina Pubblicazione", "mobile.post.failed_delete": "Elimina messaggio", "mobile.post.failed_retry": "Riprova", "mobile.post.failed_title": "Impossibile inviare il messaggio", @@ -1766,7 +1766,7 @@ "mobile.routes.login": "Login", "mobile.routes.loginOptions": "Selezionatore Login", "mobile.routes.mfa": "Forza Autenticazione Multi-fattore", - "mobile.routes.postsList": "Elenco Post", + "mobile.routes.postsList": "Elenco Pubblicazioni", "mobile.routes.saml": "Single SignOn", "mobile.routes.selectTeam": "Seleziona gruppo", "mobile.routes.settings": "Impostazioni", @@ -1962,7 +1962,7 @@ "search_header.results": "Risultati di Ricerca", "search_header.title2": "Citazioni Recenti", "search_header.title3": "Pubblicazioni contrassegnate", - "search_header.title4": "Post bloccati in {channelDisplayName}", + "search_header.title4": "Pubblicazioni bloccate in {channelDisplayName}", "search_item.direct": "Messaggio diretto (with {username})", "search_item.jump": "Salta", "search_results.because": "
  • Se stai cercando una frase parziale (es. cerca \"one\" per trovare \"riunione\" o \" reazione\"), aggiungi un * al tuo termine di ricerca.
  • Le ricerche a due lettere o con parole comuni come \"questo\", \"e\" ed \"è\" non compaiono nei risultati causa dell'elevato numero di occorrenze trovate.
", @@ -1999,8 +1999,8 @@ "sidebar.tutorialScreen1": "

Canali

I canali organizzano le conversazioni per argomenti. Sono aperti a tutto il gruppo. Per inviare messaggi privati usa Messaggi Privati verso una persona o Canale privato verso un gruppo di persone.

", "sidebar.tutorialScreen2": "

I canali \"{townsquare}\" e \"{offtopic}\"

Questi sono due canali pubblici di partenza:

{townsquare} è un canale per le comunicazioni intragruppo. Tutti i membri del gruppo accedono a questo canale.

{offtopic} è un canale per divertirsi e trattare argomenti diversi dal lavoro. Tu e il tuo gruppo potete scegliere quali altri canali creare.

", "sidebar.tutorialScreen3": "

Creare e Entrare nei canali

Clicca \"Di più...\" per creare un nuovo canale o entrare su un canale esistente.

Puoi anche creare un nuovo canale cliccando sul simbolo \"+\" vicino al titolo del canale.

", - "sidebar.unreadAbove": "Post non letti sopra", - "sidebar.unreadBelow": "Post non letti sotto", + "sidebar.unreadAbove": "Pubblicazioni non lette sopra", + "sidebar.unreadBelow": "Pubblicazioni non lette sotto", "sidebar_header.tutorial": "

Menu principale

Il Menu principale è dove puoi Invitare nuovi membri, accedere alle tue Impostazioni account e impostare il tuo colore tema.

Gli amministratori di gruppo possono anche accedere alle Impostazioni di gruppo.

Gli Amministratori di Sistema troveranno la Console di Sistema per gestire l'intera installazione.

", "sidebar_right_menu.accountSettings": "Impostazioni Account", "sidebar_right_menu.addMemberToTeam": "Aggiungi membri al gruppo", diff --git a/webapp/i18n/ja.json b/webapp/i18n/ja.json index 3ad0a09090..2c94b0a468 100644 --- a/webapp/i18n/ja.json +++ b/webapp/i18n/ja.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "例: \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "電子メール招待ソルト:", "admin.email.mhpns": "iOSとAndroidアプリで、暗号化され高品質なHPNS接続を使用する", - "admin.email.mhpnsHelp": "iTunesからMattermost iOSアプリをダウンロードし、Google PlayからMattermost Androidアプリをダウンロードしてください。詳しくはHPNSを参照してください。", + "admin.email.mhpnsHelp": "iTunesからMattermost iOSアプリをダウンロードし、Google PlayからMattermost Androidアプリをダウンロードしてください。詳しくはHPNSを参照してください。", "admin.email.mtpns": "TPNSが有効化されたiOS/AndroidアプリをiTunes/Google Playから入手する", - "admin.email.mtpnsHelp": "iTunesからMattermost iOSアプリをダウンロードしてください。Google PlayからMattermost Androidアプリをダウンロードしてください。詳しくはTPNSを参照してください。", + "admin.email.mtpnsHelp": "iTunesからMattermost iOSアプリをダウンロードしてください。Google PlayからMattermost Androidアプリをダウンロードしてください。詳しくはTPNSを参照してください。", "admin.email.nofificationOrganizationExample": "例: \"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "Mattermostから電子メールによる通知を送信する際に使用される電子メールアカウントの表示名です。", "admin.email.notificationDisplayExample": "例: \"Mattermost Notification\", \"System\", \"No-Reply\"", @@ -2142,7 +2142,7 @@ "tutorial_intro.screenOne": "

ようこそ

Mattermostへ

あなたのチームの全てのコミュニケーションを一箇所で、すぐに検索可能で、どこからでもアクセスできるものにします。

チームがつながり、互いに助け合うことで、大切なこと(what matters most)を成し遂げましょう

", "tutorial_intro.screenTwo": "

Mattermostの使い方

公開チャンネル、非公開チャンネル、ダイレクトメッセージでコミュニケーションを行います。

全てがアーカイブされ、ウェブにアクセスできるデスクトップ、ラップトップ、スマートフォンのいずれからでも検索できます。

", "tutorial_intro.skip": "チュートリアルをスキップする", - "tutorial_intro.support": "必要なことがあったら、電子メールを出してください: ", + "tutorial_intro.support": "何かありましたら私たちにメールしてください ", "tutorial_intro.teamInvite": "チームメイトを招待する", "tutorial_intro.whenReady": " 用意ができた時に。", "tutorial_tip.next": "次へ", diff --git a/webapp/i18n/pl.json b/webapp/i18n/pl.json index bc845dd21c..8bd4c96cc8 100644 --- a/webapp/i18n/pl.json +++ b/webapp/i18n/pl.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Np. \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "Wartość losowa do zaproszeń Email:", "admin.email.mhpns": "Użyj szyfrowanego, produkcyjnej jakości połączenia HPNS do aplikacji iOS i Android", - "admin.email.mhpnsHelp": "Pobierz aplikację Mattermost na iOS z iTunes. Pobierz aplikację Mattermost na Androida z Google Play. Dowiedz się więcej o HPNS (jęz. angielski).", + "admin.email.mhpnsHelp": "Pobierz aplikację Mattermost na iOS z iTunes. Pobierz aplikację Mattermost na Androida z Google Play. Dowiedz się więcej o HPNS (jęz. angielski).", "admin.email.mtpns": "Użyj aplikacji na iOS i Android na iTunes i Google Play z TPNS", - "admin.email.mtpnsHelp": "Pobierz aplikację Mattermost na iOS z iTunes. Pobierz aplikację Mattermost na Androida z Google Play. Dowiedz się więcej o TPNS (jęz. angielski). ", + "admin.email.mtpnsHelp": "Pobierz aplikację Mattermost na iOS z iTunes. Pobierz aplikację Mattermost na Androida z Google Play. Dowiedz się więcej o TPNS (jęz. angielski). ", "admin.email.nofificationOrganizationExample": "Np. \"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "Nazwa wyświetlana w powiadomieniach mailowych wysyłanych z Mattermost.", "admin.email.notificationDisplayExample": "np: \"Powiadomienia Mattermost\", \"System\", \"No-Reply\", \"Nie odpowiadać\"", diff --git a/webapp/i18n/pt-BR.json b/webapp/i18n/pt-BR.json index e4ac4f2d20..9d73425190 100644 --- a/webapp/i18n/pt-BR.json +++ b/webapp/i18n/pt-BR.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Ex.: \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "Salt Email Convite:", "admin.email.mhpns": "Use criptografado, com qualidade de produção conexão HPNS com aplicativos Android e iOS", - "admin.email.mhpnsHelp": "Download Mattermost iOS app no iTunes. Download Mattermost Android app no Google Play. Leia mais sobre HPNS.", + "admin.email.mhpnsHelp": "Download Mattermost iOS app no iTunes. Download Mattermost Android app no Google Play. Leia mais sobre HPNS.", "admin.email.mtpns": "Use apps iOS e Android no iTunes e Google Play com TPMS", - "admin.email.mtpnsHelp": "Download Mattermost iOS app no iTunes. Download Mattermost Android app no Google Play. Leia mais sobre TPNS.", + "admin.email.mtpnsHelp": "Download Mattermost iOS app no iTunes. Download Mattermost Android app no Google Play. Leia mais sobre TPNS.", "admin.email.nofificationOrganizationExample": "Ex. \"® Empresa ABC, Av. Paulista, 1000, São Paulo, SP, 12345-150, BRA\"", "admin.email.notificationDisplayDescription": "Mostra o nome da conta de e-mail usada quando a notificação de e-mail é enviado do Mattermost.", "admin.email.notificationDisplayExample": "Ex: \"Mattermost Notificação\", \"Sistema\", \"Não-Responda\"", diff --git a/webapp/i18n/ru.json b/webapp/i18n/ru.json index 1f1854bcd5..2c00ff662a 100644 --- a/webapp/i18n/ru.json +++ b/webapp/i18n/ru.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Например: \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "\"Соль\" для почтового приглашения:", "admin.email.mhpns": "Используйте шифрованное, качественное HPNS соединение с iOS и Android приложениями", - "admin.email.mhpnsHelp": "Загрузить Mattermost из iTunes. Загрузить Mattermost из Google Play. Узнать больше о HPNS.", + "admin.email.mhpnsHelp": "Загрузить Mattermost iOS app из iTunes. Загрузить Mattermost Android из Google Play. Узнать больше о HPNS.", "admin.email.mtpns": "Используйте iOS и Android приложения из iTunes и Google Play с TPNS", - "admin.email.mtpnsHelp": "Загрузить Mattermost из iTunes. Загрузить Mattermost из Google Play. Узнать больше о TPNS.", + "admin.email.mtpnsHelp": "Загрузить Mattermost iOS app из iTunes. Загрузить Mattermost Android из Google Play. Узнать больше о TPNS.", "admin.email.nofificationOrganizationExample": "Например: \"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "Отображаемое имя пользователя учётной записи электронной почты от которого происходит отправка уведомлений Mattermost.", "admin.email.notificationDisplayExample": "Например: \"Mattermost Notification\", \"System\", \"No-Reply\"", diff --git a/webapp/i18n/tr.json b/webapp/i18n/tr.json index 8de2f7cbb1..536688696d 100644 --- a/webapp/i18n/tr.json +++ b/webapp/i18n/tr.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "Örnek: \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "E-posta Çağrı Çeşnisi:", "admin.email.mhpns": "iOS ve Android uygulamaları için üretim kalitesinde şifrelenmiş HPNS bağlantısı kullanılsın", - "admin.email.mhpnsHelp": "Mattermost iOS uygulamasını iTunes üzerinden indirin. Mattermost Android uygulamasını Google Play üzerinden indirin. HPNS hakkında ayrıntılı bilgi alın.", + "admin.email.mhpnsHelp": "Mattermost iOS uygulamasını iTunes üzerinden indirin. Mattermost Android appMattermost Android uygulamasını Google Play üzerinden indirin. HPNS hakkında ayrıntılı bilgi alın.", "admin.email.mtpns": "iOS ve Android uygulamalarını iTunes ve Google Play üzerinden TPNS ile kullanın", - "admin.email.mtpnsHelp": "Mattermost iOS uygulamasını iTunes üzerinden indirin. Mattermost Android uygulamasını Google Play üzerinden indirin. TPNS hakkında ayrıntılı bilgi alın.", + "admin.email.mtpnsHelp": "Mattermost iOS uygulamasını iTunes üzerinden indirin. Mattermost Android uygulamasını Google Play üzerinden indirin. TPNS hakkında ayrıntılı bilgi alın.", "admin.email.nofificationOrganizationExample": "Örnek \"© ABC Ltd., 565 Atatürk Cad, Ankara, 06000, Türkiye\"", "admin.email.notificationDisplayDescription": "Matermost bildirim e-postaları gönderilirken kullanılacak e-posta hesabı için görüntülenecek ad.", "admin.email.notificationDisplayExample": "Örnek: \"Mattermost Bildirimi\", \"Sistem\", \"No-Reply\"", diff --git a/webapp/i18n/zh-CN.json b/webapp/i18n/zh-CN.json index 956dc15f4a..796cc7b2ad 100644 --- a/webapp/i18n/zh-CN.json +++ b/webapp/i18n/zh-CN.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "例如 \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "电子邮件邀请盐值:", "admin.email.mhpns": "使用加密的,产品及质量的HPNS连接到iOS和Android应用程序", - "admin.email.mhpnsHelp": "从 iTunes下载 Mattermost iOS app。从 Google Play 下载 Mattermost Android app。 了解更多 HPNS。", + "admin.email.mhpnsHelp": "从 iTunes下载 Mattermost iOS app。从 Google Play 下载 Mattermost Android app。 了解更多 HPNS。", "admin.email.mtpns": "在iTunes和TPNS的谷歌Play使用iOS和Android应用程序", - "admin.email.mtpnsHelp": "从 iTunes下载 Mattermost iOS app。从 Google Play 下载 Mattermost Android app。 了解更多 TPNS。", + "admin.email.mtpnsHelp": "从 iTunes下载 Mattermost iOS app。从 Google Play 下载 Mattermost Android app。 了解更多 TPNS。", "admin.email.nofificationOrganizationExample": "例如:\"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "从 Mattermost 发送的电子邮件通知时显示的电子邮件帐号名。", "admin.email.notificationDisplayExample": "例如:\"Mattermost通知\", \"系统\", \"无答复\"", diff --git a/webapp/i18n/zh-TW.json b/webapp/i18n/zh-TW.json index ea4f3b5b9d..cb2d07a8e5 100644 --- a/webapp/i18n/zh-TW.json +++ b/webapp/i18n/zh-TW.json @@ -256,9 +256,9 @@ "admin.email.inviteSaltExample": "如:\"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"", "admin.email.inviteSaltTitle": "電子郵件邀請 Salt:", "admin.email.mhpns": "使用有加密、產品品質的 HPNS 連線到 iOS 與 Android 應用程式", - "admin.email.mhpnsHelp": "從iTunes下載 Mattermost iOS 應用程式。從Google Play 下載 Mattermost Android 應用程式。瞭解更多關於HPNS.", + "admin.email.mhpnsHelp": "從iTunes下載 Mattermost iOS 應用程式。從Google Play 下載 Mattermost Android 應用程式。瞭解更多關於HPNS.", "admin.email.mtpns": "使用 TPNS 以及 iTunes 和 GooglePlay 上的 iOS 與 Android 應用程式", - "admin.email.mtpnsHelp": "從 iTunes 下載 Mattermost iOS 應用程式。從Google Play下載 Mattermost Android 應用程式。瞭解更多關於 TPNS。", + "admin.email.mtpnsHelp": "從 iTunes 下載 Mattermost iOS 應用程式。從Google Play下載 Mattermost Android 應用程式。瞭解更多關於 TPNS。", "admin.email.nofificationOrganizationExample": "如:\"© ABC Corporation, 565 Knight Way, Palo Alto, California, 94305, USA\"", "admin.email.notificationDisplayDescription": "從 Mattermost 傳送通知電子郵件時發件者的顯示名稱。", "admin.email.notificationDisplayExample": "例如:\"Mattermost 通知\"、\"系統\"、\"勿回信\"", @@ -1784,9 +1784,9 @@ "mobile.server_upgrade.title": "需要伺服器更新", "mobile.server_url.invalid_format": "網址開頭必須是 http:// 或 https://", "mobile.session_expired": "工作階段過期:請登入以繼續接收通知。", - "mobile.settings.clear": "Clear Offline Store", - "mobile.settings.clear_button": "Clear", - "mobile.settings.clear_message": "\nThis will clear all offline data and restart the app. You will be automatically logged back in once the app restarts.\n", + "mobile.settings.clear": "清除離線儲存資料", + "mobile.settings.clear_button": "清除", + "mobile.settings.clear_message": "\n這將會清除所有離線資料並重新啟動 app 。在重啟 app 後會自動重新登入。\n", "mobile.settings.team_selection": "選擇團隊", "modal.manaul_status.ask": "別再問我", "modal.manaul_status.button": "是,將狀態設定為\"線上\"", From 3ad3aa653f1bba0b8051782bf7a7bb03a7519c82 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Fri, 14 Jul 2017 13:12:52 -0400 Subject: [PATCH 45/52] Add back blue bar error for editing post after time limit (#6939) --- webapp/actions/post_actions.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/webapp/actions/post_actions.jsx b/webapp/actions/post_actions.jsx index 836d3e3808..09cc14e405 100644 --- a/webapp/actions/post_actions.jsx +++ b/webapp/actions/post_actions.jsx @@ -182,6 +182,13 @@ export function updatePost(post, success) { (data) => { if (data && success) { success(); + } else { + const serverError = getState().requests.posts.editPost.error; + AppDispatcher.handleServerAction({ + type: ActionTypes.RECEIVED_ERROR, + err: {id: serverError.server_error_id, ...serverError}, + method: 'editPost' + }); } } ); From 22d34476e5d8d98baeec506f24e78ea0ff8932b9 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 14 Jul 2017 13:47:13 -0400 Subject: [PATCH 46/52] PLT-7133 Updated marked to fix escaping of autolinked email addresses (#6942) --- webapp/package.json | 2 +- webapp/yarn.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 9e1dd4dcb1..4870c5ce81 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -22,7 +22,7 @@ "jquery": "3.2.1", "key-mirror": "1.0.1", "localforage": "1.5.0", - "marked": "mattermost/marked#8f5902fff9bad793cd6c66e0c44002c9e79e1317", + "marked": "mattermost/marked#c0b5f4a651b0af63e974522b20b93b7999490c53", "match-at": "0.1.0", "mattermost-redux": "mattermost/mattermost-redux#webapp-4.0", "object-assign": "4.1.1", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 0192200f8e..4bde0079e9 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5025,9 +5025,9 @@ map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" -marked@mattermost/marked#8f5902fff9bad793cd6c66e0c44002c9e79e1317: +marked@mattermost/marked#c0b5f4a651b0af63e974522b20b93b7999490c53: version "0.3.5" - resolved "https://codeload.github.com/mattermost/marked/tar.gz/8f5902fff9bad793cd6c66e0c44002c9e79e1317" + resolved "https://codeload.github.com/mattermost/marked/tar.gz/c0b5f4a651b0af63e974522b20b93b7999490c53" match-at@0.1.0: version "0.1.0" From a20ddb40476837f8686d9f73b449920f4e465d4a Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 14 Jul 2017 14:42:08 -0400 Subject: [PATCH 47/52] Fixed downloading of image files (#6934) * Fixed downloading of image files * Fixed captitalization * Fixed missing import * Rename image to media --- api/file.go | 24 ++++++++++++-- api4/file.go | 93 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/api/file.go b/api/file.go index 1e7c7d66d7..3b49be5e06 100644 --- a/api/file.go +++ b/api/file.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "strconv" + "strings" l4g "github.com/alecthomas/log4go" "github.com/gorilla/mux" @@ -15,6 +16,15 @@ import ( "github.com/mattermost/platform/utils" ) +var UNSAFE_CONTENT_TYPES = [...]string{ + "application/javascript", + "application/ecmascript", + "text/javascript", + "text/ecmascript", + "application/x-javascript", + "text/html", +} + func InitFile() { l4g.Debug(utils.T("api.file.init.debug")) @@ -282,13 +292,21 @@ func getPublicFileOld(c *Context, w http.ResponseWriter, r *http.Request) { func writeFileResponse(filename string, contentType string, bytes []byte, w http.ResponseWriter, r *http.Request) *model.AppError { w.Header().Set("Cache-Control", "max-age=2592000, private") w.Header().Set("Content-Length", strconv.Itoa(len(bytes))) + w.Header().Set("X-Content-Type-Options", "nosniff") - if contentType != "" { - w.Header().Set("Content-Type", contentType) + if contentType == "" { + contentType = "application/octet-stream" } else { - w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer + for _, unsafeContentType := range UNSAFE_CONTENT_TYPES { + if strings.HasPrefix(contentType, unsafeContentType) { + contentType = "text/plain" + break + } + } } + w.Header().Set("Content-Type", contentType) + w.Header().Set("Content-Disposition", "attachment;filename=\""+filename+"\"; filename*=UTF-8''"+url.QueryEscape(filename)) // prevent file links from being embedded in iframes diff --git a/api4/file.go b/api4/file.go index a395fff655..4b39a18128 100644 --- a/api4/file.go +++ b/api4/file.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "strconv" + "strings" l4g "github.com/alecthomas/log4go" "github.com/mattermost/platform/app" @@ -18,6 +19,27 @@ const ( FILE_TEAM_ID = "noteam" ) +var UNSAFE_CONTENT_TYPES = [...]string{ + "application/javascript", + "application/ecmascript", + "text/javascript", + "text/ecmascript", + "application/x-javascript", + "text/html", +} + +var MEDIA_CONTENT_TYPES = [...]string{ + "image/jpeg", + "image/png", + "image/bmp", + "image/gif", + "video/avi", + "video/mpeg", + "video/mp4", + "audio/mpeg", + "audio/wav", +} + func InitFile() { l4g.Debug(utils.T("api.file.init.debug")) @@ -82,9 +104,9 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { return } - toDownload, failConv := strconv.ParseBool(r.URL.Query().Get("download")) - if failConv != nil { - toDownload = false + forceDownload, convErr := strconv.ParseBool(r.URL.Query().Get("download")) + if convErr != nil { + forceDownload = false } info, err := app.GetFileInfo(c.Params.FileId) @@ -105,22 +127,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) { return } - contentTypeToCheck := []string{"image/jpeg", "image/png", "image/bmp", "image/gif", - "video/avi", "video/mpeg", "audio/mpeg3", "audio/wav"} - - contentType := http.DetectContentType(data) - foundContentType := false - for _, contentTypeFromList := range contentTypeToCheck { - if contentType == contentTypeFromList && toDownload == false { - foundContentType = true - break - } - } - if !foundContentType { - toDownload = true - } - - err = writeFileResponse(info.Name, info.MimeType, data, toDownload, w, r) + err = writeFileResponse(info.Name, info.MimeType, data, forceDownload, w, r) if err != nil { c.Err = err return @@ -133,9 +140,9 @@ func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) { return } - toDownload, failConv := strconv.ParseBool(r.URL.Query().Get("download")) - if failConv != nil { - toDownload = false + forceDownload, convErr := strconv.ParseBool(r.URL.Query().Get("download")) + if convErr != nil { + forceDownload = false } info, err := app.GetFileInfo(c.Params.FileId) @@ -158,7 +165,7 @@ func getFileThumbnail(c *Context, w http.ResponseWriter, r *http.Request) { if data, err := app.ReadFile(info.ThumbnailPath); err != nil { c.Err = err c.Err.StatusCode = http.StatusNotFound - } else if err := writeFileResponse(info.Name, info.MimeType, data, toDownload, w, r); err != nil { + } else if err := writeFileResponse(info.Name, info.MimeType, data, forceDownload, w, r); err != nil { c.Err = err return } @@ -205,9 +212,9 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) { return } - toDownload, failConv := strconv.ParseBool(r.URL.Query().Get("download")) - if failConv != nil { - toDownload = false + forceDownload, convErr := strconv.ParseBool(r.URL.Query().Get("download")) + if convErr != nil { + forceDownload = false } info, err := app.GetFileInfo(c.Params.FileId) @@ -230,7 +237,7 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) { if data, err := app.ReadFile(info.PreviewPath); err != nil { c.Err = err c.Err.StatusCode = http.StatusNotFound - } else if err := writeFileResponse(info.Name, info.MimeType, data, toDownload, w, r); err != nil { + } else if err := writeFileResponse(info.Name, info.MimeType, data, forceDownload, w, r); err != nil { c.Err = err return } @@ -298,14 +305,38 @@ func getPublicFile(c *Context, w http.ResponseWriter, r *http.Request) { } } -func writeFileResponse(filename string, contentType string, bytes []byte, toDownload bool, w http.ResponseWriter, r *http.Request) *model.AppError { +func writeFileResponse(filename string, contentType string, bytes []byte, forceDownload bool, w http.ResponseWriter, r *http.Request) *model.AppError { w.Header().Set("Cache-Control", "max-age=2592000, private") w.Header().Set("Content-Length", strconv.Itoa(len(bytes))) + w.Header().Set("X-Content-Type-Options", "nosniff") - if contentType != "" { - w.Header().Set("Content-Type", contentType) + if contentType == "" { + contentType = "application/octet-stream" } else { - w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer + for _, unsafeContentType := range UNSAFE_CONTENT_TYPES { + if strings.HasPrefix(contentType, unsafeContentType) { + contentType = "text/plain" + break + } + } + } + + w.Header().Set("Content-Type", contentType) + + var toDownload bool + if forceDownload { + toDownload = true + } else { + isMediaType := false + + for _, mediaContentType := range MEDIA_CONTENT_TYPES { + if strings.HasPrefix(contentType, mediaContentType) { + isMediaType = true + break + } + } + + toDownload = !isMediaType } if toDownload { From 9a23519d07664f252fe5a568034abf6b720511d0 Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Fri, 14 Jul 2017 17:06:59 -0400 Subject: [PATCH 48/52] PLT-6983 Allowed team invite IDs of arbitrary lengths to match API v3 (#6944) --- api4/context.go | 2 +- api4/team_test.go | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/api4/context.go b/api4/context.go index 06eee67153..61c3182661 100644 --- a/api4/context.go +++ b/api4/context.go @@ -355,7 +355,7 @@ func (c *Context) RequireInviteId() *Context { return c } - if len(c.Params.InviteId) != 26 { + if len(c.Params.InviteId) == 0 { c.SetInvalidUrlParam("invite_id") } return c diff --git a/api4/team_test.go b/api4/team_test.go index 440b2feb26..421428afa2 100644 --- a/api4/team_test.go +++ b/api4/team_test.go @@ -12,10 +12,10 @@ import ( "strings" "testing" + "encoding/base64" "github.com/mattermost/platform/app" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" - "encoding/base64" ) func TestCreateTeam(t *testing.T) { @@ -1475,7 +1475,7 @@ func TestInviteUsersToTeam(t *testing.T) { } func TestGetTeamInviteInfo(t *testing.T) { - th := Setup().InitBasic() + th := Setup().InitBasic().InitSystemAdmin() defer TearDown() Client := th.Client team := th.BasicTeam @@ -1491,6 +1491,13 @@ func TestGetTeamInviteInfo(t *testing.T) { t.Fatal("should be empty") } + team.InviteId = "12345678901234567890123456789012" + team, resp = th.SystemAdminClient.UpdateTeam(team) + CheckNoError(t, resp) + + team, resp = Client.GetTeamInviteInfo(team.InviteId) + CheckNoError(t, resp) + _, resp = Client.GetTeamInviteInfo("junk") - CheckBadRequestStatus(t, resp) + CheckNotFoundStatus(t, resp) } From 70bfcfb9d78f2aa07a36d24b092a477ca2b680ef Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 17 Jul 2017 18:22:28 -0400 Subject: [PATCH 49/52] Only apply never edit policy setting when message changes (#6947) --- app/post.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/post.go b/app/post.go index 1e8c721ecb..dd0b24bac5 100644 --- a/app/post.go +++ b/app/post.go @@ -227,19 +227,19 @@ func SendEphemeralPost(teamId, userId string, post *model.Post) *model.Post { } func UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) { - if utils.IsLicensed { - if *utils.Cfg.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_NEVER { - err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_denied.app_error", nil, "", http.StatusForbidden) - return nil, err - } - } - var oldPost *model.Post if result := <-Srv.Store.Post().Get(post.Id); result.Err != nil { return nil, result.Err } else { oldPost = result.Data.(*model.PostList).Posts[post.Id] + if utils.IsLicensed { + if *utils.Cfg.ServiceSettings.AllowEditPost == model.ALLOW_EDIT_POST_NEVER && post.Message != oldPost.Message { + err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_denied.app_error", nil, "", http.StatusForbidden) + return nil, err + } + } + if oldPost == nil { err := model.NewAppError("UpdatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id, http.StatusBadRequest) return nil, err From 3e090162aaac7cbc75900635b800d96e9e9050e3 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 17 Jul 2017 18:22:49 -0400 Subject: [PATCH 50/52] PLT-7126 Do not version detect on saml endpoints and remove config reloading (#6955) * Do not version detect on saml endpoints and remove config reloading * Update mattermost-redux --- app/saml.go | 6 ------ webapp/yarn.lock | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/saml.go b/app/saml.go index 730e29efcc..e5d1e8b3e6 100644 --- a/app/saml.go +++ b/app/saml.go @@ -68,7 +68,6 @@ func AddSamlPublicCertificate(fileData *multipart.FileHeader) *model.AppError { } utils.SaveConfig(utils.CfgFileName, cfg) - utils.LoadConfig(utils.CfgFileName) return nil } @@ -88,7 +87,6 @@ func AddSamlPrivateCertificate(fileData *multipart.FileHeader) *model.AppError { } utils.SaveConfig(utils.CfgFileName, cfg) - utils.LoadConfig(utils.CfgFileName) return nil } @@ -108,7 +106,6 @@ func AddSamlIdpCertificate(fileData *multipart.FileHeader) *model.AppError { } utils.SaveConfig(utils.CfgFileName, cfg) - utils.LoadConfig(utils.CfgFileName) return nil } @@ -144,7 +141,6 @@ func RemoveSamlPublicCertificate() *model.AppError { } utils.SaveConfig(utils.CfgFileName, cfg) - utils.LoadConfig(utils.CfgFileName) return nil } @@ -165,7 +161,6 @@ func RemoveSamlPrivateCertificate() *model.AppError { } utils.SaveConfig(utils.CfgFileName, cfg) - utils.LoadConfig(utils.CfgFileName) return nil } @@ -186,7 +181,6 @@ func RemoveSamlIdpCertificate() *model.AppError { } utils.SaveConfig(utils.CfgFileName, cfg) - utils.LoadConfig(utils.CfgFileName) return nil } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 4bde0079e9..7583c43126 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5043,7 +5043,7 @@ math-expression-evaluator@^1.2.14: mattermost-redux@mattermost/mattermost-redux#webapp-4.0: version "0.0.1" - resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/a68c49c57130ed64e5a8fb3bdbd004ced437790b" + resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/ea67cb97d1e34b251e13a356583223284f544aa5" dependencies: deep-equal "1.0.1" harmony-reflect "1.5.1" From 41969ae66c2d140b004e6dbced597749f34aedf8 Mon Sep 17 00:00:00 2001 From: Saturnino Abril Date: Tue, 18 Jul 2017 06:27:29 +0800 Subject: [PATCH 51/52] [PLT-7141] Fix deactivated user from appearing in channel view members list filtering (#6957) * fix deactivated user from appearing in channel view members list filtering * add deactivated user filters to channels, teams and DM --- webapp/components/add_users_to_team/add_users_to_team.jsx | 7 ++++++- .../channel_invite_modal/channel_invite_modal.jsx | 7 ++++++- .../components/member_list_channel/member_list_channel.jsx | 2 +- .../more_direct_channels/more_direct_channels.jsx | 7 ++++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/webapp/components/add_users_to_team/add_users_to_team.jsx b/webapp/components/add_users_to_team/add_users_to_team.jsx index 19e0d674b4..e3eb8477bc 100644 --- a/webapp/components/add_users_to_team/add_users_to_team.jsx +++ b/webapp/components/add_users_to_team/add_users_to_team.jsx @@ -215,6 +215,11 @@ export default class AddUsersToTeam extends React.Component { /> ); + let users = []; + if (this.state.users) { + users = this.state.users.filter((user) => user.delete_at === 0); + } + return ( {this.state.inviteError}); } + let users = []; + if (this.state.users) { + users = this.state.users.filter((user) => user.delete_at === 0); + } + let content; if (this.state.loading) { content = (); } else { content = ( ); + let users = []; + if (this.state.users) { + users = this.state.users.filter((user) => user.delete_at === 0); + } + return ( Date: Tue, 18 Jul 2017 15:48:47 -0400 Subject: [PATCH 52/52] Fix PDF preview urls (#6976) --- webapp/components/pdf_preview.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/components/pdf_preview.jsx b/webapp/components/pdf_preview.jsx index 790355561d..09913afcf0 100644 --- a/webapp/components/pdf_preview.jsx +++ b/webapp/components/pdf_preview.jsx @@ -84,7 +84,7 @@ export default class PDFPreview extends React.Component { success: false }); - PDFJS.getDocument(window.mm_config.SiteURL + props.fileUrl).then(this.onDocumentLoad); + PDFJS.getDocument(props.fileUrl).then(this.onDocumentLoad); } onDocumentLoad(pdf) {