Merge branch 'release-3.1'
Conflicts: webapp/components/create_comment.jsx
Этот коммит содержится в:
76
api/oauth.go
76
api/oauth.go
@@ -204,7 +204,10 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
break
|
||||
case model.OAUTH_ACTION_LOGIN:
|
||||
LoginByOAuth(c, w, r, service, body)
|
||||
user := LoginByOAuth(c, w, r, service, body)
|
||||
if len(teamId) > 0 {
|
||||
c.Err = JoinUserToTeamById(teamId, user)
|
||||
}
|
||||
if c.Err == nil {
|
||||
http.Redirect(w, r, GetProtocol(r)+"://"+r.Host, http.StatusTemporaryRedirect)
|
||||
}
|
||||
@@ -424,8 +427,17 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
service := params["service"]
|
||||
loginHint := r.URL.Query().Get("login_hint")
|
||||
|
||||
teamId, err := getTeamIdFromQuery(r.URL.Query())
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = model.OAUTH_ACTION_LOGIN
|
||||
if len(teamId) != 0 {
|
||||
stateProps["team_id"] = teamId
|
||||
}
|
||||
|
||||
if authUrl, err := GetAuthorizationCode(c, service, stateProps, loginHint); err != nil {
|
||||
c.Err = err
|
||||
@@ -435,6 +447,36 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamIdFromQuery(query url.Values) (string, *model.AppError) {
|
||||
hash := query.Get("h")
|
||||
inviteId := query.Get("id")
|
||||
|
||||
if len(hash) > 0 {
|
||||
data := query.Get("d")
|
||||
props := model.MapFromJson(strings.NewReader(data))
|
||||
|
||||
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
|
||||
return "", model.NewLocAppError("getTeamIdFromQuery", "web.singup_with_oauth.invalid_link.app_error", nil, "")
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(props["time"], 10, 64)
|
||||
if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
|
||||
return "", model.NewLocAppError("getTeamIdFromQuery", "web.singup_with_oauth.expired_link.app_error", nil, "")
|
||||
}
|
||||
|
||||
return props["id"], nil
|
||||
} else if len(inviteId) > 0 {
|
||||
if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
|
||||
// soft fail, so we still create user but don't auto-join team
|
||||
l4g.Error("%v", result.Err)
|
||||
} else {
|
||||
return result.Data.(*model.Team).Id, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
@@ -445,34 +487,10 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
hash := r.URL.Query().Get("h")
|
||||
|
||||
teamId := ""
|
||||
inviteId := r.URL.Query().Get("id")
|
||||
|
||||
if len(hash) > 0 {
|
||||
data := r.URL.Query().Get("d")
|
||||
props := model.MapFromJson(strings.NewReader(data))
|
||||
|
||||
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
|
||||
c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.invalid_link.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(props["time"], 10, 64)
|
||||
if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
|
||||
c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.expired_link.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
teamId = props["id"]
|
||||
} else if len(inviteId) != 0 {
|
||||
if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
|
||||
// soft fail, so we still create user but don't auto-join team
|
||||
l4g.Error("%v", result.Err)
|
||||
} else {
|
||||
teamId = result.Data.(*model.Team).Id
|
||||
}
|
||||
teamId, err := getTeamIdFromQuery(r.URL.Query())
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
stateProps := map[string]string{}
|
||||
|
||||
12
api/team.go
12
api/team.go
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
@@ -247,6 +248,14 @@ func CreateTeam(c *Context, team *model.Team) *model.Team {
|
||||
}
|
||||
}
|
||||
|
||||
func JoinUserToTeamById(teamId string, user *model.User) *model.AppError {
|
||||
if result := <-Srv.Store.Team().Get(teamId); result.Err != nil {
|
||||
return result.Err
|
||||
} else {
|
||||
return JoinUserToTeam(result.Data.(*model.Team), user)
|
||||
}
|
||||
}
|
||||
|
||||
func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
|
||||
|
||||
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id}
|
||||
@@ -258,6 +267,9 @@ func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
|
||||
}
|
||||
|
||||
if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil {
|
||||
if tmr.Err.Id == store.TEAM_MEMBER_EXISTS_ERROR {
|
||||
return nil
|
||||
}
|
||||
return tmr.Err
|
||||
}
|
||||
|
||||
|
||||
23
api/user.go
23
api/user.go
@@ -285,11 +285,6 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service
|
||||
suchan := Srv.Store.User().GetByAuth(user.AuthData, service)
|
||||
euchan := Srv.Store.User().GetByEmail(user.Email)
|
||||
|
||||
var tchan store.StoreChannel
|
||||
if len(teamId) != 0 {
|
||||
tchan = Srv.Store.Team().Get(teamId)
|
||||
}
|
||||
|
||||
found := true
|
||||
count := 0
|
||||
for found {
|
||||
@@ -319,20 +314,14 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service
|
||||
return nil
|
||||
}
|
||||
|
||||
if tchan != nil {
|
||||
if result := <-tchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
if len(teamId) > 0 {
|
||||
err = JoinUserToTeamById(teamId, user)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return nil
|
||||
} else {
|
||||
team := result.Data.(*model.Team)
|
||||
err = JoinUserToTeam(team, user)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return nil
|
||||
}
|
||||
|
||||
go addDirectChannels(team.Id, user)
|
||||
}
|
||||
|
||||
go addDirectChannels(teamId, user)
|
||||
}
|
||||
|
||||
doLogin(c, w, r, ruser, "")
|
||||
|
||||
@@ -868,6 +868,7 @@ func cmdVersion() {
|
||||
fmt.Fprintln(os.Stderr, "Build Date: "+model.BuildDate)
|
||||
fmt.Fprintln(os.Stderr, "Build Hash: "+model.BuildHash)
|
||||
fmt.Fprintln(os.Stderr, "Build Enterprise Ready: "+model.BuildEnterpriseReady)
|
||||
fmt.Fprintln(os.Stderr, "DB Version: "+api.Srv.Store.(*store.SqlStore).SchemaVersion)
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
100
model/config.go
100
model/config.go
@@ -6,6 +6,7 @@ package model
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,6 +36,15 @@ const (
|
||||
FAKE_SETTING = "********************************"
|
||||
)
|
||||
|
||||
// should match the values in webapp/i18n/i18n.jsx
|
||||
var LOCALES = []string{
|
||||
"en",
|
||||
"es",
|
||||
"fr",
|
||||
"ja",
|
||||
"pt-BR",
|
||||
}
|
||||
|
||||
type ServiceSettings struct {
|
||||
ListenAddress string
|
||||
MaximumLoginAttempts int
|
||||
@@ -420,19 +430,39 @@ func (o *Config) SetDefaults() {
|
||||
*o.SupportSettings.SupportEmail = "feedback@mattermost.com"
|
||||
}
|
||||
|
||||
if o.LdapSettings.Enable == nil {
|
||||
o.LdapSettings.Enable = new(bool)
|
||||
*o.LdapSettings.Enable = false
|
||||
}
|
||||
|
||||
if o.LdapSettings.LdapServer == nil {
|
||||
o.LdapSettings.LdapServer = new(string)
|
||||
*o.LdapSettings.LdapServer = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.LdapPort == nil {
|
||||
o.LdapSettings.LdapPort = new(int)
|
||||
*o.LdapSettings.LdapPort = 389
|
||||
}
|
||||
|
||||
if o.LdapSettings.QueryTimeout == nil {
|
||||
o.LdapSettings.QueryTimeout = new(int)
|
||||
*o.LdapSettings.QueryTimeout = 60
|
||||
if o.LdapSettings.ConnectionSecurity == nil {
|
||||
o.LdapSettings.ConnectionSecurity = new(string)
|
||||
*o.LdapSettings.ConnectionSecurity = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.Enable == nil {
|
||||
o.LdapSettings.Enable = new(bool)
|
||||
*o.LdapSettings.Enable = false
|
||||
if o.LdapSettings.BaseDN == nil {
|
||||
o.LdapSettings.BaseDN = new(string)
|
||||
*o.LdapSettings.BaseDN = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.BindUsername == nil {
|
||||
o.LdapSettings.BindUsername = new(string)
|
||||
*o.LdapSettings.BindUsername = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.BindPassword == nil {
|
||||
o.LdapSettings.BindPassword = new(string)
|
||||
*o.LdapSettings.BindPassword = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.UserFilter == nil {
|
||||
@@ -440,9 +470,29 @@ func (o *Config) SetDefaults() {
|
||||
*o.LdapSettings.UserFilter = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.LoginFieldName == nil {
|
||||
o.LdapSettings.LoginFieldName = new(string)
|
||||
*o.LdapSettings.LoginFieldName = ""
|
||||
if o.LdapSettings.FirstNameAttribute == nil {
|
||||
o.LdapSettings.FirstNameAttribute = new(string)
|
||||
*o.LdapSettings.FirstNameAttribute = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.LastNameAttribute == nil {
|
||||
o.LdapSettings.LastNameAttribute = new(string)
|
||||
*o.LdapSettings.LastNameAttribute = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.EmailAttribute == nil {
|
||||
o.LdapSettings.EmailAttribute = new(string)
|
||||
*o.LdapSettings.EmailAttribute = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.NicknameAttribute == nil {
|
||||
o.LdapSettings.NicknameAttribute = new(string)
|
||||
*o.LdapSettings.NicknameAttribute = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.IdAttribute == nil {
|
||||
o.LdapSettings.IdAttribute = new(string)
|
||||
*o.LdapSettings.IdAttribute = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.SyncIntervalMinutes == nil {
|
||||
@@ -450,6 +500,21 @@ func (o *Config) SetDefaults() {
|
||||
*o.LdapSettings.SyncIntervalMinutes = 60
|
||||
}
|
||||
|
||||
if o.LdapSettings.SkipCertificateVerification == nil {
|
||||
o.LdapSettings.SkipCertificateVerification = new(bool)
|
||||
*o.LdapSettings.SkipCertificateVerification = false
|
||||
}
|
||||
|
||||
if o.LdapSettings.QueryTimeout == nil {
|
||||
o.LdapSettings.QueryTimeout = new(int)
|
||||
*o.LdapSettings.QueryTimeout = 60
|
||||
}
|
||||
|
||||
if o.LdapSettings.LoginFieldName == nil {
|
||||
o.LdapSettings.LoginFieldName = new(string)
|
||||
*o.LdapSettings.LoginFieldName = ""
|
||||
}
|
||||
|
||||
if o.ServiceSettings.SessionLengthWebInDays == nil {
|
||||
o.ServiceSettings.SessionLengthWebInDays = new(int)
|
||||
*o.ServiceSettings.SessionLengthWebInDays = 30
|
||||
@@ -515,21 +580,6 @@ func (o *Config) SetDefaults() {
|
||||
*o.ComplianceSettings.EnableDaily = false
|
||||
}
|
||||
|
||||
if o.LdapSettings.ConnectionSecurity == nil {
|
||||
o.LdapSettings.ConnectionSecurity = new(string)
|
||||
*o.LdapSettings.ConnectionSecurity = ""
|
||||
}
|
||||
|
||||
if o.LdapSettings.SkipCertificateVerification == nil {
|
||||
o.LdapSettings.SkipCertificateVerification = new(bool)
|
||||
*o.LdapSettings.SkipCertificateVerification = false
|
||||
}
|
||||
|
||||
if o.LdapSettings.NicknameAttribute == nil {
|
||||
o.LdapSettings.NicknameAttribute = new(string)
|
||||
*o.LdapSettings.NicknameAttribute = ""
|
||||
}
|
||||
|
||||
if o.LocalizationSettings.DefaultServerLocale == nil {
|
||||
o.LocalizationSettings.DefaultServerLocale = new(string)
|
||||
*o.LocalizationSettings.DefaultServerLocale = DEFAULT_LOCALE
|
||||
@@ -542,7 +592,7 @@ func (o *Config) SetDefaults() {
|
||||
|
||||
if o.LocalizationSettings.AvailableLocales == nil {
|
||||
o.LocalizationSettings.AvailableLocales = new(string)
|
||||
*o.LocalizationSettings.AvailableLocales = *o.LocalizationSettings.DefaultClientLocale
|
||||
*o.LocalizationSettings.AvailableLocales = strings.Join(LOCALES, ",")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
// It should be maitained in chronological order with most current
|
||||
// release at the front of the list.
|
||||
var versions = []string{
|
||||
"3.1.0",
|
||||
"3.0.0",
|
||||
"2.2.0",
|
||||
"2.1.0",
|
||||
|
||||
@@ -10,6 +10,10 @@ import (
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
TEAM_MEMBER_EXISTS_ERROR = "store.sql_team.save_member.exists.app_error"
|
||||
)
|
||||
|
||||
type SqlTeamStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
@@ -372,8 +376,8 @@ func (s SqlTeamStore) SaveMember(member *model.TeamMember) StoreChannel {
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(member); err != nil {
|
||||
if IsUniqueConstraintError(err.Error(), []string{"TeamId", "teammembers_pkey"}) {
|
||||
result.Err = model.NewLocAppError("SqlTeamStore.SaveMember", "store.sql_team.save_member.exists.app_error", nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error())
|
||||
if IsUniqueConstraintError(err.Error(), []string{"TeamId", "teammembers_pkey", "PRIMARY"}) {
|
||||
result.Err = model.NewLocAppError("SqlTeamStore.SaveMember", TEAM_MEMBER_EXISTS_ERROR, nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error())
|
||||
} else {
|
||||
result.Err = model.NewLocAppError("SqlTeamStore.SaveMember", "store.sql_team.save_member.save.app_error", nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error())
|
||||
}
|
||||
|
||||
@@ -139,8 +139,8 @@ export function doFocusPost(channelId, postId, data) {
|
||||
});
|
||||
AsyncClient.getChannels(true);
|
||||
AsyncClient.getChannelExtraInfo(channelId);
|
||||
AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
||||
AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
||||
AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true);
|
||||
AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS, true);
|
||||
}
|
||||
|
||||
export function emitPostFocusEvent(postId) {
|
||||
|
||||
@@ -239,6 +239,22 @@ export default class AdminSidebar extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let customBranding = null;
|
||||
|
||||
if (window.mm_license.IsLicensed === 'true') {
|
||||
customBranding = (
|
||||
<AdminSidebarSection
|
||||
name='custom_brand'
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='admin.sidebar.customBrand'
|
||||
defaultMessage='Custom Branding'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='admin-sidebar'>
|
||||
<AdminSidebarHeader/>
|
||||
@@ -515,16 +531,7 @@ export default class AdminSidebar extends React.Component {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AdminSidebarSection
|
||||
name='custom_brand'
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='admin.sidebar.customBrand'
|
||||
defaultMessage='Custom Branding'
|
||||
/>
|
||||
|
||||
}
|
||||
/>
|
||||
{customBranding}
|
||||
<AdminSidebarSection
|
||||
name='legal_and_support'
|
||||
title={
|
||||
|
||||
@@ -343,7 +343,7 @@ export default class LdapSettings extends AdminSettings {
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.ldap.syncIntervalHelpText'
|
||||
defaultMessage='LDAP Synchronization is the process by which Mattermost updates its users to reflect any updated data on the LDAP server. For example if a name for a user is updated on the LDAP server, the change will be reflected in Mattermost when the synchronization is performed. Accounts that have been removed from the LDAP server will have their active sessions cleared and no longer be able to login to Mattermost. Mattermost will perform this synchronization regularly according to the interval supplied here. For example, if 60 is supplied, Mattermost will update the users every hour.'
|
||||
defaultMessage='LDAP Synchronization is the process by which Mattermost updates its users to reflect any updated data on the LDAP server. For example if a name for a user is updated on the LDAP server, the change will be reflected in Mattermost when the synchronization is performed. Accounts that have been removed from the LDAP server will have their active sessions cleared and no longer be able to login to Mattermost. Mattermost will perform this synchronization regularly according to the interval supplied here. For example, if 60 is supplied, Mattermost will update the users every hour. Changing this will require a server restart before taking effect.'
|
||||
/>
|
||||
}
|
||||
value={this.state.syncIntervalMinutes}
|
||||
|
||||
@@ -50,6 +50,7 @@ export default class MultiSelectSetting extends React.Component {
|
||||
labelKey='text'
|
||||
options={this.props.values}
|
||||
joinValues={true}
|
||||
clearable={false}
|
||||
disabled={this.props.disabled}
|
||||
noResultsText={this.props.noResultText}
|
||||
onChange={this.handleChange}
|
||||
|
||||
@@ -22,8 +22,8 @@ export default class StorageSettings extends AdminSettings {
|
||||
|
||||
this.renderSettings = this.renderSettings.bind(this);
|
||||
|
||||
//maxFileSize: props.config.FileSettings.MaxFileSize,
|
||||
this.state = Object.assign(this.state, {
|
||||
maxFileSize: props.config.FileSettings.MaxFileSize,
|
||||
driverName: props.config.FileSettings.DriverName,
|
||||
directory: props.config.FileSettings.Directory,
|
||||
amazonS3AccessKeyId: props.config.FileSettings.AmazonS3AccessKeyId,
|
||||
@@ -34,7 +34,7 @@ export default class StorageSettings extends AdminSettings {
|
||||
}
|
||||
|
||||
getConfigFromState(config) {
|
||||
config.FileSettings.MaxFileSize = this.parseInt(this.state.maxFileSize);
|
||||
//config.FileSettings.MaxFileSize = this.parseInt(this.state.maxFileSize);
|
||||
config.FileSettings.DriverName = this.state.driverName;
|
||||
config.FileSettings.Directory = this.state.directory;
|
||||
config.FileSettings.AmazonS3AccessKeyId = this.state.amazonS3AccessKeyId;
|
||||
@@ -57,26 +57,27 @@ export default class StorageSettings extends AdminSettings {
|
||||
}
|
||||
|
||||
renderSettings() {
|
||||
/*<TextSetting
|
||||
id='maxFileSize'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.image.maxFileSizeTitle'
|
||||
defaultMessage='Max File Size:'
|
||||
/>
|
||||
}
|
||||
placeholder={Utils.localizeMessage('admin.image.maxFileSizeExample', 'Ex "52428800"')}
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.image.maxFileSizeDescription'
|
||||
defaultMessage='Max File Size in bytes. If blank, will be set to 52428800 (50MB).'
|
||||
/>
|
||||
}
|
||||
value={this.state.maxFileSize}
|
||||
onChange={this.handleChange}
|
||||
/>*/
|
||||
|
||||
return (
|
||||
<SettingsGroup>
|
||||
<TextSetting
|
||||
id='maxFileSize'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.image.maxFileSizeTitle'
|
||||
defaultMessage='Max File Size:'
|
||||
/>
|
||||
}
|
||||
placeholder={Utils.localizeMessage('admin.image.maxFileSizeExample', 'Ex "52428800"')}
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.image.maxFileSizeDescription'
|
||||
defaultMessage='Max File Size in bytes. If blank, will be set to 52428800 (50MB).'
|
||||
/>
|
||||
}
|
||||
value={this.state.maxFileSize}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<DropdownSetting
|
||||
id='driverName'
|
||||
values={[
|
||||
|
||||
@@ -23,26 +23,26 @@ export default class DoughnutChart extends React.Component {
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!Utils.areObjectsEqual(prevProps.data, this.props.data) || !Utils.areObjectsEqual(prevProps.options, this.props.options)) {
|
||||
if (this.chart) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
this.initChart();
|
||||
this.initChart(true);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.chart) {
|
||||
if (this.chart && this.refs.canvas) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
initChart() {
|
||||
initChart(update) {
|
||||
if (!this.refs.canvas) {
|
||||
return;
|
||||
}
|
||||
var el = ReactDOM.findDOMNode(this.refs.canvas);
|
||||
var ctx = el.getContext('2d');
|
||||
this.chart = new Chart(ctx, {type: 'doughnut', data: this.props.data, options: this.props.options || {}}); //eslint-disable-line new-cap
|
||||
if (update) {
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -23,26 +23,26 @@ export default class LineChart extends React.Component {
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!Utils.areObjectsEqual(prevProps.data, this.props.data) || !Utils.areObjectsEqual(prevProps.options, this.props.options)) {
|
||||
if (this.chart) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
this.initChart();
|
||||
this.initChart(true);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.chart) {
|
||||
if (this.chart && this.refs.canvas) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
initChart() {
|
||||
initChart(update) {
|
||||
if (!this.refs.canvas) {
|
||||
return;
|
||||
}
|
||||
var el = ReactDOM.findDOMNode(this.refs.canvas);
|
||||
var ctx = el.getContext('2d');
|
||||
this.chart = new Chart(ctx, {type: 'line', data: this.props.data, options: this.props.options || {}}); //eslint-disable-line new-cap
|
||||
if (update) {
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -147,7 +147,7 @@ class CreateComment extends React.Component {
|
||||
Client.createPost(
|
||||
post,
|
||||
() => {
|
||||
// DO nothing. Websockets will handle this.
|
||||
PostStore.removePendingPost(post.channel_id, post.pending_post_id);
|
||||
},
|
||||
(err) => {
|
||||
if (err.id === 'api.post.create_post.root_id.app_error') {
|
||||
|
||||
@@ -180,7 +180,7 @@ class CreatePost extends React.Component {
|
||||
|
||||
Client.createPost(post,
|
||||
() => {
|
||||
// DO nothing. Websockets will handle this.
|
||||
PostStore.removePendingPost(post.pending_post_id);
|
||||
},
|
||||
(err) => {
|
||||
if (err.id === 'api.post.create_post.root_id.app_error') {
|
||||
|
||||
@@ -67,6 +67,7 @@ class EditPostModal extends React.Component {
|
||||
Client.updatePost(
|
||||
updatedPost,
|
||||
() => {
|
||||
AsyncClient.getPosts(updatedPost.channel_id);
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
(err) => {
|
||||
|
||||
@@ -69,13 +69,13 @@ export default class LoginController extends React.Component {
|
||||
this.setState({loginId});
|
||||
}
|
||||
|
||||
let password = this.refs.password.value;
|
||||
const password = this.refs.password.value;
|
||||
if (password !== this.state.password) {
|
||||
this.setState({password});
|
||||
}
|
||||
|
||||
// don't trim the password since we support spaces in passwords
|
||||
loginId = loginId.trim();
|
||||
password = password.trim();
|
||||
|
||||
if (!loginId) {
|
||||
// it's slightly weird to be constructing the message ID, but it's a bit nicer than triply nested if statements
|
||||
@@ -444,7 +444,7 @@ export default class LoginController extends React.Component {
|
||||
<a
|
||||
className='btn btn-custom-login gitlab'
|
||||
key='gitlab'
|
||||
href={Client.getOAuthRoute() + '/gitlab/login'}
|
||||
href={Client.getOAuthRoute() + '/gitlab/login' + this.props.location.search}
|
||||
>
|
||||
<span className='icon'/>
|
||||
<span>
|
||||
@@ -529,4 +529,4 @@ export default class LoginController extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@ export default class RhsRootPost extends React.Component {
|
||||
filenames={post.filenames}
|
||||
channelId={post.channel_id}
|
||||
userId={post.user_id}
|
||||
compactDisplay={this.props.compactDisplay}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -173,11 +173,24 @@ export default class SignupUserComplete extends React.Component {
|
||||
this.state.ldapPassword,
|
||||
null,
|
||||
() => {
|
||||
GlobalActions.emitInitialLoad(
|
||||
() => {
|
||||
browserHistory.push('/select_team');
|
||||
}
|
||||
);
|
||||
if (this.props.location.query.id || this.props.location.query.h) {
|
||||
Client.addUserToTeamFromInvite(
|
||||
this.props.location.query.d,
|
||||
this.props.location.query.h,
|
||||
this.props.location.query.id,
|
||||
() => {
|
||||
this.finishSignup();
|
||||
},
|
||||
() => {
|
||||
// there's not really a good way to deal with this, so just let the user log in like normal
|
||||
this.finishSignup();
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.finishSignup();
|
||||
},
|
||||
(err) => {
|
||||
if (err.id === 'ent.ldap.do_login.user_not_registered.app_error' || err.id === 'ent.ldap.do_login.user_filtered.app_error') {
|
||||
@@ -205,6 +218,15 @@ export default class SignupUserComplete extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
finishSignup() {
|
||||
GlobalActions.emitInitialLoad(
|
||||
() => {
|
||||
GlobalActions.loadDefaultLocale();
|
||||
browserHistory.push('/select_team');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
handleUserCreated(user, data) {
|
||||
track('signup', 'signup_user_02_complete');
|
||||
Client.loginById(
|
||||
@@ -761,4 +783,4 @@ export default class SignupUserComplete extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
"admin.email.smtpUsernameTitle": "SMTP Username:",
|
||||
"admin.email.testing": "Testing...",
|
||||
"admin.false": "false",
|
||||
"admin.general.localization": "Localization",
|
||||
"admin.general.localization.availableLocalesDescription": "Determines which languages are available for users in Account Settings.",
|
||||
"admin.general.localization.clientLocaleDescription": "Default language for newly created users and pages where the user hasn't logged in.",
|
||||
"admin.general.localization.serverLocaleDescription": "Default language for system messages and logs. Changing this will require a server restart before taking effect.",
|
||||
@@ -283,7 +284,7 @@
|
||||
"admin.ldap.serverTitle": "LDAP Server:",
|
||||
"admin.ldap.skipCertificateVerification": "Skip Certificate Verification",
|
||||
"admin.ldap.skipCertificateVerificationDesc": "Skips the certificate verification step for TLS or STARTTLS connections. Not recommended for production environments where TLS is required. For testing only.",
|
||||
"admin.ldap.syncIntervalHelpText": "LDAP Synchronization is the process by which Mattermost updates its users to reflect any updated data on the LDAP server. For example if a name for a user is updated on the LDAP server, the change will be reflected in Mattermost when the synchronization is performed. Accounts that have been removed from the LDAP server will have their active sessions cleared and no longer be able to login to Mattermost. Mattermost will perform this synchronization regularly according to the interval supplied here. For example, if 60 is supplied, Mattermost will update the users every hour.",
|
||||
"admin.ldap.syncIntervalHelpText": "LDAP Synchronization is the process by which Mattermost updates its users to reflect any updated data on the LDAP server. For example if a name for a user is updated on the LDAP server, the change will be reflected in Mattermost when the synchronization is performed. Accounts that have been removed from the LDAP server will have their active sessions cleared and no longer be able to login to Mattermost. Mattermost will perform this synchronization regularly according to the interval supplied here. For example, if 60 is supplied, Mattermost will update the users every hour. Changing this will require a server restart before taking effect.",
|
||||
"admin.ldap.syncIntervalTitle": "Synchronization Interval (In Minutes)",
|
||||
"admin.ldap.uernameAttrDesc": "The attribute in the LDAP server that will be used to populate the username field in Mattermost. This may be the same as the ID Attribute.",
|
||||
"admin.ldap.userFilterDisc": "Optionally enter an LDAP Filter to use when searching for user objects. Only the users selected by the query will be able to access Mattermost. For Active Directory, the query to filter out disabled users is (&(objectCategory=Person)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))).",
|
||||
@@ -433,6 +434,7 @@
|
||||
"admin.sidebar.integrations": "Integrations",
|
||||
"admin.sidebar.ldap": "LDAP",
|
||||
"admin.sidebar.license": "Edition and License",
|
||||
"admin.sidebar.localization": "Localization",
|
||||
"admin.sidebar.logging": "Logging",
|
||||
"admin.sidebar.login": "Login",
|
||||
"admin.sidebar.logs": "Logs",
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
"admin.email.smtpUsernameTitle": "Usuario SMTP:",
|
||||
"admin.email.testing": "Probando...",
|
||||
"admin.false": "falso",
|
||||
"admin.general.localization": "Idiomas",
|
||||
"admin.general.localization.availableLocalesDescription": "Determina qué idiomas están disponibles para los usuarios en la Configuración de la Cuenta.",
|
||||
"admin.general.localization.clientLocaleDescription": "Idioma predeterminado para nuevos usuarios y páginas donde el usuario no ha iniciado sesión.",
|
||||
"admin.general.localization.serverLocaleDescription": "Idioma predeterminado para los mensajes del sistema y los registros. Cambiar esto requerirá un reinicio del servidor antes de tomar efecto.",
|
||||
@@ -431,6 +432,7 @@
|
||||
"admin.sidebar.integrations": "Integraciones",
|
||||
"admin.sidebar.ldap": "LDAP",
|
||||
"admin.sidebar.license": "Edición y Licencia",
|
||||
"admin.sidebar.localization": "Idiomas",
|
||||
"admin.sidebar.logging": "Registros",
|
||||
"admin.sidebar.login": "Inicio de Sesión",
|
||||
"admin.sidebar.logs": "Registros",
|
||||
|
||||
@@ -13,6 +13,7 @@ import frLocaleData from 'react-intl/locale-data/fr';
|
||||
import jaLocaleData from 'react-intl/locale-data/ja';
|
||||
import ptLocaleData from 'react-intl/locale-data/pt';
|
||||
|
||||
// should match the values in model/config.go
|
||||
const languages = {
|
||||
en: {
|
||||
value: 'en',
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"keymirror": "0.1.1",
|
||||
"marked": "mattermost/marked#2d92082cd542dc0adc175a1407194f6a4c3cb621",
|
||||
"match-at": "0.1.0",
|
||||
"mattermost": "mattermost/mattermost-javascript#master",
|
||||
"mattermost": "mattermost/mattermost-javascript#release-3.1",
|
||||
"object-assign": "4.1.0",
|
||||
"perfect-scrollbar": "0.6.11",
|
||||
"react": "15.0.2",
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
@include clearfix;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
margin: 5px 0;
|
||||
padding: 2px;
|
||||
height: 39px;
|
||||
line-height: 35px;
|
||||
margin: 0;
|
||||
padding: 3px 8px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
|
||||
@@ -552,7 +552,7 @@ PostStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||
|
||||
switch (action.type) {
|
||||
case ActionTypes.RECEIVED_POSTS: {
|
||||
const id = PostStore.currentFocusedPostId == null ? action.id : PostStore.currentFocusedPostId;
|
||||
const id = PostStore.currentFocusedPostId !== null && action.isPost ? PostStore.currentFocusedPostId : action.id;
|
||||
PostStore.storePosts(id, makePostListNonNull(action.post_list));
|
||||
PostStore.checkBounds(id, action.numRequested, makePostListNonNull(action.post_list), action.before);
|
||||
PostStore.emitChange();
|
||||
|
||||
@@ -104,7 +104,7 @@ class UserStoreClass extends EventEmitter {
|
||||
this.currentUserId = user.id;
|
||||
global.window.mm_current_user_id = this.currentUserId;
|
||||
if (LocalizationStore.getLocale() !== user.locale) {
|
||||
GlobalActions.newLocalizationSelected(user.locale);
|
||||
setTimeout(() => GlobalActions.newLocalizationSelected(user.locale), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -603,7 +603,7 @@ export function getPosts(id) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getPostsBefore(postId, offset, numPost) {
|
||||
export function getPostsBefore(postId, offset, numPost, isPost) {
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
@@ -624,7 +624,8 @@ export function getPostsBefore(postId, offset, numPost) {
|
||||
id: channelId,
|
||||
before: true,
|
||||
numRequested: numPost,
|
||||
post_list: data
|
||||
post_list: data,
|
||||
isPost
|
||||
});
|
||||
|
||||
getProfiles();
|
||||
@@ -638,7 +639,7 @@ export function getPostsBefore(postId, offset, numPost) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getPostsAfter(postId, offset, numPost) {
|
||||
export function getPostsAfter(postId, offset, numPost, isPost) {
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
if (channelId == null) {
|
||||
return;
|
||||
@@ -659,7 +660,8 @@ export function getPostsAfter(postId, offset, numPost) {
|
||||
id: channelId,
|
||||
before: false,
|
||||
numRequested: numPost,
|
||||
post_list: data
|
||||
post_list: data,
|
||||
isPost
|
||||
});
|
||||
|
||||
getProfiles();
|
||||
|
||||
@@ -163,7 +163,7 @@ class MattermostMarkdownRenderer extends marked.Renderer {
|
||||
}
|
||||
|
||||
// remove any links added to the text by hashtag or mention parsing since they'll break this link
|
||||
output += '>' + text.replace(/<\/?a[^>]*>/, '') + '</a>';
|
||||
output += '>' + text.replace(/<\/?a[^>]*>/g, '') + '</a>';
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user