Conflicts:
	webapp/components/create_comment.jsx
Этот коммит содержится в:
JoramWilander
2016-06-13 15:29:24 -04:00
родитель bb5ca5a878 974238231b
Коммит 5af526c259
29 изменённых файлов: 258 добавлений и 143 удалений

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

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

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

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

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

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

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

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