PLT-4332 Position field for Users (#4632)
* Add User.Position field to store & model. * GOFMT * Add Position to user settings. * Unit tests. * Add position to profile popup. * i18n * Fix log message for invalid position. * Add Position field attribute to LDAP config. * Add Position field attribute to SAML config. * Reword empty position message. * Change Position Max Length to 35. * Better invalid position error message. * Add new fields to config.json. * Ensure position is never longer than max when displayed. * Hard limit of 64 chars with soft limit still 35 * Put field with other attributes.
Этот коммит содержится в:
коммит произвёл
enahum
родитель
9735854503
Коммит
8406e854aa
@@ -186,6 +186,7 @@
|
|||||||
"UsernameAttribute": "",
|
"UsernameAttribute": "",
|
||||||
"NicknameAttribute": "",
|
"NicknameAttribute": "",
|
||||||
"IdAttribute": "",
|
"IdAttribute": "",
|
||||||
|
"PositionAttribute": "",
|
||||||
"SyncIntervalMinutes": 60,
|
"SyncIntervalMinutes": 60,
|
||||||
"SkipCertificateVerification": false,
|
"SkipCertificateVerification": false,
|
||||||
"QueryTimeout": 60,
|
"QueryTimeout": 60,
|
||||||
@@ -218,6 +219,7 @@
|
|||||||
"UsernameAttribute": "",
|
"UsernameAttribute": "",
|
||||||
"NicknameAttribute": "",
|
"NicknameAttribute": "",
|
||||||
"LocaleAttribute": "",
|
"LocaleAttribute": "",
|
||||||
|
"PositionAttribute": "",
|
||||||
"LoginButtonText": "With SAML"
|
"LoginButtonText": "With SAML"
|
||||||
},
|
},
|
||||||
"NativeAppSettings": {
|
"NativeAppSettings": {
|
||||||
|
|||||||
@@ -3875,6 +3875,10 @@
|
|||||||
"id": "model.user.is_valid.nickname.app_error",
|
"id": "model.user.is_valid.nickname.app_error",
|
||||||
"translation": "Invalid nickname"
|
"translation": "Invalid nickname"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "model.user.is_valid.position.app_error",
|
||||||
|
"translation": "Invalid position: must not be longer than 35 characters."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "model.user.is_valid.pwd.app_error",
|
"id": "model.user.is_valid.pwd.app_error",
|
||||||
"translation": "Your password must contain at least {{.Min}} characters."
|
"translation": "Your password must contain at least {{.Min}} characters."
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ type LdapSettings struct {
|
|||||||
UsernameAttribute *string
|
UsernameAttribute *string
|
||||||
NicknameAttribute *string
|
NicknameAttribute *string
|
||||||
IdAttribute *string
|
IdAttribute *string
|
||||||
|
PositionAttribute *string
|
||||||
|
|
||||||
// Syncronization
|
// Syncronization
|
||||||
SyncIntervalMinutes *int
|
SyncIntervalMinutes *int
|
||||||
@@ -297,6 +298,7 @@ type SamlSettings struct {
|
|||||||
UsernameAttribute *string
|
UsernameAttribute *string
|
||||||
NicknameAttribute *string
|
NicknameAttribute *string
|
||||||
LocaleAttribute *string
|
LocaleAttribute *string
|
||||||
|
PositionAttribute *string
|
||||||
|
|
||||||
LoginButtonText *string
|
LoginButtonText *string
|
||||||
}
|
}
|
||||||
@@ -690,6 +692,11 @@ func (o *Config) SetDefaults() {
|
|||||||
*o.LdapSettings.IdAttribute = ""
|
*o.LdapSettings.IdAttribute = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.LdapSettings.PositionAttribute == nil {
|
||||||
|
o.LdapSettings.PositionAttribute = new(string)
|
||||||
|
*o.LdapSettings.PositionAttribute = ""
|
||||||
|
}
|
||||||
|
|
||||||
if o.LdapSettings.SyncIntervalMinutes == nil {
|
if o.LdapSettings.SyncIntervalMinutes == nil {
|
||||||
o.LdapSettings.SyncIntervalMinutes = new(int)
|
o.LdapSettings.SyncIntervalMinutes = new(int)
|
||||||
*o.LdapSettings.SyncIntervalMinutes = 60
|
*o.LdapSettings.SyncIntervalMinutes = 60
|
||||||
@@ -911,6 +918,11 @@ func (o *Config) SetDefaults() {
|
|||||||
*o.SamlSettings.NicknameAttribute = ""
|
*o.SamlSettings.NicknameAttribute = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.SamlSettings.PositionAttribute == nil {
|
||||||
|
o.SamlSettings.PositionAttribute = new(string)
|
||||||
|
*o.SamlSettings.PositionAttribute = ""
|
||||||
|
}
|
||||||
|
|
||||||
if o.SamlSettings.LocaleAttribute == nil {
|
if o.SamlSettings.LocaleAttribute == nil {
|
||||||
o.SamlSettings.LocaleAttribute = new(string)
|
o.SamlSettings.LocaleAttribute = new(string)
|
||||||
*o.SamlSettings.LocaleAttribute = ""
|
*o.SamlSettings.LocaleAttribute = ""
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type User struct {
|
|||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
|
Position string `json:"position"`
|
||||||
Roles string `json:"roles"`
|
Roles string `json:"roles"`
|
||||||
AllowMarketing bool `json:"allow_marketing,omitempty"`
|
AllowMarketing bool `json:"allow_marketing,omitempty"`
|
||||||
Props StringMap `json:"props,omitempty"`
|
Props StringMap `json:"props,omitempty"`
|
||||||
@@ -78,6 +79,10 @@ func (u *User) IsValid() *AppError {
|
|||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id)
|
return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if utf8.RuneCountInString(u.Position) > 35 {
|
||||||
|
return NewLocAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id)
|
||||||
|
}
|
||||||
|
|
||||||
if utf8.RuneCountInString(u.FirstName) > 64 {
|
if utf8.RuneCountInString(u.FirstName) > 64 {
|
||||||
return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id)
|
return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,17 @@ func TestUserIsValid(t *testing.T) {
|
|||||||
if err := user.IsValid(); err == nil {
|
if err := user.IsValid(); err == nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.LastName = ""
|
||||||
|
user.Position = ""
|
||||||
|
if err := user.IsValid(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Position = strings.Repeat("01234567890", 20)
|
||||||
|
if err := user.IsValid(); err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserGetFullName(t *testing.T) {
|
func TestUserGetFullName(t *testing.T) {
|
||||||
|
|||||||
@@ -222,6 +222,9 @@ func UpgradeDatabaseToVersion36(sqlStore *SqlStore) {
|
|||||||
// Create Team Description column
|
// Create Team Description column
|
||||||
sqlStore.CreateColumnIfNotExists("Teams", "Description", "varchar(255)", "varchar(255)", "")
|
sqlStore.CreateColumnIfNotExists("Teams", "Description", "varchar(255)", "varchar(255)", "")
|
||||||
|
|
||||||
|
// Add a Position column to users.
|
||||||
|
sqlStore.CreateColumnIfNotExists("Users", "Position", "varchar(64)", "varchar(64)", "")
|
||||||
|
|
||||||
//saveSchemaVersion(sqlStore, VERSION_3_6_0)
|
//saveSchemaVersion(sqlStore, VERSION_3_6_0)
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
|
|||||||
table.ColMap("NotifyProps").SetMaxSize(2000)
|
table.ColMap("NotifyProps").SetMaxSize(2000)
|
||||||
table.ColMap("Locale").SetMaxSize(5)
|
table.ColMap("Locale").SetMaxSize(5)
|
||||||
table.ColMap("MfaSecret").SetMaxSize(128)
|
table.ColMap("MfaSecret").SetMaxSize(128)
|
||||||
|
table.ColMap("Position").SetMaxSize(64)
|
||||||
}
|
}
|
||||||
|
|
||||||
return us
|
return us
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export default class LdapSettings extends AdminSettings {
|
|||||||
config.LdapSettings.NicknameAttribute = this.state.nicknameAttribute;
|
config.LdapSettings.NicknameAttribute = this.state.nicknameAttribute;
|
||||||
config.LdapSettings.EmailAttribute = this.state.emailAttribute;
|
config.LdapSettings.EmailAttribute = this.state.emailAttribute;
|
||||||
config.LdapSettings.UsernameAttribute = this.state.usernameAttribute;
|
config.LdapSettings.UsernameAttribute = this.state.usernameAttribute;
|
||||||
|
config.LdapSettings.PositionAttribute = this.state.positionAttribute;
|
||||||
config.LdapSettings.IdAttribute = this.state.idAttribute;
|
config.LdapSettings.IdAttribute = this.state.idAttribute;
|
||||||
config.LdapSettings.SyncIntervalMinutes = this.parseIntNonZero(this.state.syncIntervalMinutes);
|
config.LdapSettings.SyncIntervalMinutes = this.parseIntNonZero(this.state.syncIntervalMinutes);
|
||||||
config.LdapSettings.SkipCertificateVerification = this.state.skipCertificateVerification;
|
config.LdapSettings.SkipCertificateVerification = this.state.skipCertificateVerification;
|
||||||
@@ -63,6 +64,7 @@ export default class LdapSettings extends AdminSettings {
|
|||||||
nicknameAttribute: config.LdapSettings.NicknameAttribute,
|
nicknameAttribute: config.LdapSettings.NicknameAttribute,
|
||||||
emailAttribute: config.LdapSettings.EmailAttribute,
|
emailAttribute: config.LdapSettings.EmailAttribute,
|
||||||
usernameAttribute: config.LdapSettings.UsernameAttribute,
|
usernameAttribute: config.LdapSettings.UsernameAttribute,
|
||||||
|
positionAttribute: config.LdapSettings.PositionAttribute,
|
||||||
idAttribute: config.LdapSettings.IdAttribute,
|
idAttribute: config.LdapSettings.IdAttribute,
|
||||||
syncIntervalMinutes: config.LdapSettings.SyncIntervalMinutes,
|
syncIntervalMinutes: config.LdapSettings.SyncIntervalMinutes,
|
||||||
skipCertificateVerification: config.LdapSettings.SkipCertificateVerification,
|
skipCertificateVerification: config.LdapSettings.SkipCertificateVerification,
|
||||||
@@ -299,6 +301,25 @@ export default class LdapSettings extends AdminSettings {
|
|||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
disabled={!this.state.enable}
|
disabled={!this.state.enable}
|
||||||
/>
|
/>
|
||||||
|
<TextSetting
|
||||||
|
id='positionAttribute'
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.ldap.positionAttrTitle'
|
||||||
|
defaultMessage='Position Attribute:'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder={Utils.localizeMessage('admin.ldap.positionAttrEx', 'E.g.: "title"')}
|
||||||
|
helpText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.ldap.positionAttrDesc'
|
||||||
|
defaultMessage='(Optional) The attribute in the AD/LDAP server that will be used to populate the position field in Mattermost.'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
value={this.state.positionAttribute}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={!this.state.enable}
|
||||||
|
/>
|
||||||
<TextSetting
|
<TextSetting
|
||||||
id='emailAttribute'
|
id='emailAttribute'
|
||||||
label={
|
label={
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default class SamlSettings extends AdminSettings {
|
|||||||
config.SamlSettings.EmailAttribute = this.state.emailAttribute;
|
config.SamlSettings.EmailAttribute = this.state.emailAttribute;
|
||||||
config.SamlSettings.UsernameAttribute = this.state.usernameAttribute;
|
config.SamlSettings.UsernameAttribute = this.state.usernameAttribute;
|
||||||
config.SamlSettings.NicknameAttribute = this.state.nicknameAttribute;
|
config.SamlSettings.NicknameAttribute = this.state.nicknameAttribute;
|
||||||
|
config.SamlSettings.PositionAttribute = this.state.positionAttribute;
|
||||||
config.SamlSettings.LocaleAttribute = this.state.localeAttribute;
|
config.SamlSettings.LocaleAttribute = this.state.localeAttribute;
|
||||||
config.SamlSettings.LoginButtonText = this.state.loginButtonText;
|
config.SamlSettings.LoginButtonText = this.state.loginButtonText;
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ export default class SamlSettings extends AdminSettings {
|
|||||||
emailAttribute: settings.EmailAttribute,
|
emailAttribute: settings.EmailAttribute,
|
||||||
usernameAttribute: settings.UsernameAttribute,
|
usernameAttribute: settings.UsernameAttribute,
|
||||||
nicknameAttribute: settings.NicknameAttribute,
|
nicknameAttribute: settings.NicknameAttribute,
|
||||||
|
positionAttribute: settings.PositionAttribute,
|
||||||
localeAttribute: settings.LocaleAttribute,
|
localeAttribute: settings.LocaleAttribute,
|
||||||
loginButtonText: settings.LoginButtonText
|
loginButtonText: settings.LoginButtonText
|
||||||
};
|
};
|
||||||
@@ -508,6 +510,25 @@ export default class SamlSettings extends AdminSettings {
|
|||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
disabled={!this.state.enable}
|
disabled={!this.state.enable}
|
||||||
/>
|
/>
|
||||||
|
<TextSetting
|
||||||
|
id='positionAttribute'
|
||||||
|
label={
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.saml.positionAttrTitle'
|
||||||
|
defaultMessage='Position Attribute:'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder={Utils.localizeMessage('admin.saml.positionAttrEx', 'E.g.: "Role"')}
|
||||||
|
helpText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='admin.saml.positionAttrDesc'
|
||||||
|
defaultMessage='(Optional) The attribute in the SAML Assertion that will be used to populate the position of users in Mattermost.'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
value={this.state.positionAttribute}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
disabled={!this.state.enable}
|
||||||
|
/>
|
||||||
<TextSetting
|
<TextSetting
|
||||||
id='localeAttribute'
|
id='localeAttribute'
|
||||||
label={
|
label={
|
||||||
|
|||||||
@@ -182,6 +182,23 @@ export default class UserProfile extends React.Component {
|
|||||||
|
|
||||||
dataContent.push(webrtc);
|
dataContent.push(webrtc);
|
||||||
|
|
||||||
|
const position = this.props.user.position.substring(0, Constants.MAX_POSITION_LENGTH);
|
||||||
|
if (position) {
|
||||||
|
dataContent.push(
|
||||||
|
<div
|
||||||
|
data-toggle='tooltip'
|
||||||
|
title={position}
|
||||||
|
key='user-popover-position'
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className='text-nowrap'
|
||||||
|
>
|
||||||
|
{position}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (global.window.mm_config.ShowEmailAddress === 'true' || UserStore.isSystemAdminForCurrentUser() || this.props.user === UserStore.getCurrentUser()) {
|
if (global.window.mm_config.ShowEmailAddress === 'true' || UserStore.isSystemAdminForCurrentUser() || this.props.user === UserStore.getCurrentUser()) {
|
||||||
dataContent.push(
|
dataContent.push(
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ const holders = defineMessages({
|
|||||||
close: {
|
close: {
|
||||||
id: 'user.settings.general.close',
|
id: 'user.settings.general.close',
|
||||||
defaultMessage: 'Close'
|
defaultMessage: 'Close'
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
id: 'user.settings.general.position',
|
||||||
|
defaultMessage: 'Position'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -85,6 +89,7 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
this.submitEmail = this.submitEmail.bind(this);
|
this.submitEmail = this.submitEmail.bind(this);
|
||||||
this.submitUser = this.submitUser.bind(this);
|
this.submitUser = this.submitUser.bind(this);
|
||||||
this.submitPicture = this.submitPicture.bind(this);
|
this.submitPicture = this.submitPicture.bind(this);
|
||||||
|
this.submitPosition = this.submitPosition.bind(this);
|
||||||
|
|
||||||
this.updateUsername = this.updateUsername.bind(this);
|
this.updateUsername = this.updateUsername.bind(this);
|
||||||
this.updateFirstName = this.updateFirstName.bind(this);
|
this.updateFirstName = this.updateFirstName.bind(this);
|
||||||
@@ -94,6 +99,7 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
this.updateConfirmEmail = this.updateConfirmEmail.bind(this);
|
this.updateConfirmEmail = this.updateConfirmEmail.bind(this);
|
||||||
this.updatePicture = this.updatePicture.bind(this);
|
this.updatePicture = this.updatePicture.bind(this);
|
||||||
this.updateSection = this.updateSection.bind(this);
|
this.updateSection = this.updateSection.bind(this);
|
||||||
|
this.updatePosition = this.updatePosition.bind(this);
|
||||||
|
|
||||||
this.state = this.setupInitialState(props);
|
this.state = this.setupInitialState(props);
|
||||||
}
|
}
|
||||||
@@ -249,6 +255,22 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submitPosition(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const user = Object.assign({}, this.props.user);
|
||||||
|
const position = this.state.position.trim();
|
||||||
|
|
||||||
|
if (user.position === position) {
|
||||||
|
this.updateSection('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.position = position;
|
||||||
|
|
||||||
|
this.submitUser(user, Constants.UserUpdateEvents.Position, false);
|
||||||
|
}
|
||||||
|
|
||||||
updateUsername(e) {
|
updateUsername(e) {
|
||||||
this.setState({username: e.target.value});
|
this.setState({username: e.target.value});
|
||||||
}
|
}
|
||||||
@@ -265,6 +287,10 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
this.setState({nickname: e.target.value});
|
this.setState({nickname: e.target.value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePosition(e) {
|
||||||
|
this.setState({position: e.target.value});
|
||||||
|
}
|
||||||
|
|
||||||
updateEmail(e) {
|
updateEmail(e) {
|
||||||
this.setState({email: e.target.value});
|
this.setState({email: e.target.value});
|
||||||
}
|
}
|
||||||
@@ -302,6 +328,7 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
firstName: user.first_name,
|
firstName: user.first_name,
|
||||||
lastName: user.last_name,
|
lastName: user.last_name,
|
||||||
nickname: user.nickname,
|
nickname: user.nickname,
|
||||||
|
position: user.position,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
confirmEmail: '',
|
confirmEmail: '',
|
||||||
picture: null,
|
picture: null,
|
||||||
@@ -936,6 +963,99 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let positionSection;
|
||||||
|
if (this.props.activeSection === 'position') {
|
||||||
|
let extraInfo;
|
||||||
|
let submit = null;
|
||||||
|
if ((this.props.user.auth_service === 'ldap' || this.props.user.auth_service === Constants.SAML_SERVICE) && global.window.mm_config.PositionAttributeSet === 'true') {
|
||||||
|
extraInfo = (
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.general.field_handled_externally'
|
||||||
|
defaultMessage='This field is handled through your login provider. If you want to change it, you need to do so though your login provider.'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let positionLabel = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.general.position'
|
||||||
|
defaultMessage='Position'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (Utils.isMobile()) {
|
||||||
|
positionLabel = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.push(
|
||||||
|
<div
|
||||||
|
key='positionSetting'
|
||||||
|
className='form-group'
|
||||||
|
>
|
||||||
|
<label className='col-sm-5 control-label'>{positionLabel}</label>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
type='text'
|
||||||
|
onChange={this.updatePosition}
|
||||||
|
value={this.state.position}
|
||||||
|
maxLength={Constants.MAX_POSITION_LENGTH}
|
||||||
|
autoCapitalize='off'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
extraInfo = (
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.general.positionExtra'
|
||||||
|
defaultMessage='Tell your teammates what you do.'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
submit = this.submitPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
positionSection = (
|
||||||
|
<SettingItemMax
|
||||||
|
title={formatMessage(holders.position)}
|
||||||
|
inputs={inputs}
|
||||||
|
submit={submit}
|
||||||
|
server_error={serverError}
|
||||||
|
client_error={clientError}
|
||||||
|
updateSection={(e) => {
|
||||||
|
this.updateSection('');
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
extraInfo={extraInfo}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let describe = '';
|
||||||
|
if (user.position) {
|
||||||
|
describe = user.position;
|
||||||
|
} else {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.general.emptyPosition'
|
||||||
|
defaultMessage="Click 'Edit' to add your job title / position"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
positionSection = (
|
||||||
|
<SettingItemMin
|
||||||
|
title={formatMessage(holders.position)}
|
||||||
|
describe={describe}
|
||||||
|
updateSection={() => {
|
||||||
|
this.updateSection('position');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const emailSection = this.createEmailSection();
|
const emailSection = this.createEmailSection();
|
||||||
|
|
||||||
let pictureSection;
|
let pictureSection;
|
||||||
@@ -1030,6 +1150,8 @@ class UserSettingsGeneralTab extends React.Component {
|
|||||||
<div className='divider-light'/>
|
<div className='divider-light'/>
|
||||||
{nicknameSection}
|
{nicknameSection}
|
||||||
<div className='divider-light'/>
|
<div className='divider-light'/>
|
||||||
|
{positionSection}
|
||||||
|
<div className='divider-light'/>
|
||||||
{emailSection}
|
{emailSection}
|
||||||
<div className='divider-light'/>
|
<div className='divider-light'/>
|
||||||
{pictureSection}
|
{pictureSection}
|
||||||
|
|||||||
@@ -441,6 +441,9 @@
|
|||||||
"admin.ldap.portDesc": "The port Mattermost will use to connect to the AD/LDAP server. Default is 389.",
|
"admin.ldap.portDesc": "The port Mattermost will use to connect to the AD/LDAP server. Default is 389.",
|
||||||
"admin.ldap.portEx": "E.g.: \"389\"",
|
"admin.ldap.portEx": "E.g.: \"389\"",
|
||||||
"admin.ldap.portTitle": "AD/LDAP Port:",
|
"admin.ldap.portTitle": "AD/LDAP Port:",
|
||||||
|
"admin.ldap.positionAttrEx": "E.g.: \"title\"",
|
||||||
|
"admin.ldap.positionAttrDesc": "(Optional) The attribute in the AD/LDAP server that will be used to populate the position field in Mattermost.",
|
||||||
|
"admin.ldap.positionAttrTitle": "Position Attribute:",
|
||||||
"admin.ldap.queryDesc": "The timeout value for queries to the AD/LDAP server. Increase if you are getting timeout errors caused by a slow AD/LDAP server.",
|
"admin.ldap.queryDesc": "The timeout value for queries to the AD/LDAP server. Increase if you are getting timeout errors caused by a slow AD/LDAP server.",
|
||||||
"admin.ldap.queryEx": "E.g.: \"60\"",
|
"admin.ldap.queryEx": "E.g.: \"60\"",
|
||||||
"admin.ldap.queryTitle": "Query Timeout (seconds):",
|
"admin.ldap.queryTitle": "Query Timeout (seconds):",
|
||||||
@@ -614,6 +617,9 @@
|
|||||||
"admin.saml.nicknameAttrDesc": "(Optional) The attribute in the SAML Assertion that will be used to populate the nickname of users in Mattermost.",
|
"admin.saml.nicknameAttrDesc": "(Optional) The attribute in the SAML Assertion that will be used to populate the nickname of users in Mattermost.",
|
||||||
"admin.saml.nicknameAttrEx": "E.g.: \"Nickname\"",
|
"admin.saml.nicknameAttrEx": "E.g.: \"Nickname\"",
|
||||||
"admin.saml.nicknameAttrTitle": "Nickname Attribute:",
|
"admin.saml.nicknameAttrTitle": "Nickname Attribute:",
|
||||||
|
"admin.saml.positionAttrDesc": "(Optional) The attribute in the SAML Assertion that will be used to populate the position of users in Mattermost.",
|
||||||
|
"admin.saml.positionAttrEx": "E.g.: \"Role\"",
|
||||||
|
"admin.saml.positionAttrTitle": "Position Attribute:",
|
||||||
"admin.saml.privateKeyFileFileDesc": "The private key used to decrypt SAML Assertions from the Identity Provider.",
|
"admin.saml.privateKeyFileFileDesc": "The private key used to decrypt SAML Assertions from the Identity Provider.",
|
||||||
"admin.saml.privateKeyFileFileRemoveDesc": "Remove the private key used to decrypt SAML Assertions from the Identity Provider.",
|
"admin.saml.privateKeyFileFileRemoveDesc": "Remove the private key used to decrypt SAML Assertions from the Identity Provider.",
|
||||||
"admin.saml.privateKeyFileTitle": "Service Provider Private Key:",
|
"admin.saml.privateKeyFileTitle": "Service Provider Private Key:",
|
||||||
@@ -1953,6 +1959,7 @@
|
|||||||
"user.settings.general.emailUnchanged": "Your new email address is the same as your old email address.",
|
"user.settings.general.emailUnchanged": "Your new email address is the same as your old email address.",
|
||||||
"user.settings.general.emptyName": "Click 'Edit' to add your full name",
|
"user.settings.general.emptyName": "Click 'Edit' to add your full name",
|
||||||
"user.settings.general.emptyNickname": "Click 'Edit' to add a nickname",
|
"user.settings.general.emptyNickname": "Click 'Edit' to add a nickname",
|
||||||
|
"user.settings.general.emptyPosition": "Click 'Edit' to add your job title / position",
|
||||||
"user.settings.general.field_handled_externally": "This field is handled through your login provider. If you want to change it, you need to do so through your login provider.",
|
"user.settings.general.field_handled_externally": "This field is handled through your login provider. If you want to change it, you need to do so through your login provider.",
|
||||||
"user.settings.general.firstName": "First Name",
|
"user.settings.general.firstName": "First Name",
|
||||||
"user.settings.general.fullName": "Full Name",
|
"user.settings.general.fullName": "Full Name",
|
||||||
@@ -1969,6 +1976,8 @@
|
|||||||
"user.settings.general.nicknameExtra": "Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.",
|
"user.settings.general.nicknameExtra": "Use Nickname for a name you might be called that is different from your first name and username. This is most often used when two or more people have similar sounding names and usernames.",
|
||||||
"user.settings.general.notificationsExtra": "By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.",
|
"user.settings.general.notificationsExtra": "By default, you will receive mention notifications when someone types your first name. Go to {notify} settings to change this default.",
|
||||||
"user.settings.general.notificationsLink": "Notifications",
|
"user.settings.general.notificationsLink": "Notifications",
|
||||||
|
"user.settings.general.position": "Position",
|
||||||
|
"user.settings.general.positionExtra": "Tell your teammates what you do.",
|
||||||
"user.settings.general.primaryEmail": "Primary Email",
|
"user.settings.general.primaryEmail": "Primary Email",
|
||||||
"user.settings.general.profilePicture": "Profile Picture",
|
"user.settings.general.profilePicture": "Profile Picture",
|
||||||
"user.settings.general.title": "General Settings",
|
"user.settings.general.title": "General Settings",
|
||||||
|
|||||||
@@ -264,7 +264,8 @@ export const Constants = {
|
|||||||
FULLNAME: 'fullname',
|
FULLNAME: 'fullname',
|
||||||
NICKNAME: 'nickname',
|
NICKNAME: 'nickname',
|
||||||
EMAIL: 'email',
|
EMAIL: 'email',
|
||||||
LANGUAGE: 'language'
|
LANGUAGE: 'language',
|
||||||
|
POSITION: 'position'
|
||||||
},
|
},
|
||||||
|
|
||||||
ScrollTypes: {
|
ScrollTypes: {
|
||||||
@@ -835,6 +836,7 @@ export const Constants = {
|
|||||||
MAX_NICKNAME_LENGTH: 22,
|
MAX_NICKNAME_LENGTH: 22,
|
||||||
MIN_PASSWORD_LENGTH: 5,
|
MIN_PASSWORD_LENGTH: 5,
|
||||||
MAX_PASSWORD_LENGTH: 64,
|
MAX_PASSWORD_LENGTH: 64,
|
||||||
|
MAX_POSITION_LENGTH: 35,
|
||||||
MIN_TRIGGER_LENGTH: 1,
|
MIN_TRIGGER_LENGTH: 1,
|
||||||
MAX_TRIGGER_LENGTH: 128,
|
MAX_TRIGGER_LENGTH: 128,
|
||||||
MAX_TEXTSETTING_LENGTH: 1024,
|
MAX_TEXTSETTING_LENGTH: 1024,
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user