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.
Этот коммит содержится в:
George Goldberg
2016-12-14 11:11:51 +00:00
коммит произвёл enahum
родитель 9735854503
Коммит 8406e854aa
13 изменённых файлов: 231 добавлений и 1 удалений

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

@@ -38,6 +38,7 @@ export default class LdapSettings extends AdminSettings {
config.LdapSettings.NicknameAttribute = this.state.nicknameAttribute;
config.LdapSettings.EmailAttribute = this.state.emailAttribute;
config.LdapSettings.UsernameAttribute = this.state.usernameAttribute;
config.LdapSettings.PositionAttribute = this.state.positionAttribute;
config.LdapSettings.IdAttribute = this.state.idAttribute;
config.LdapSettings.SyncIntervalMinutes = this.parseIntNonZero(this.state.syncIntervalMinutes);
config.LdapSettings.SkipCertificateVerification = this.state.skipCertificateVerification;
@@ -63,6 +64,7 @@ export default class LdapSettings extends AdminSettings {
nicknameAttribute: config.LdapSettings.NicknameAttribute,
emailAttribute: config.LdapSettings.EmailAttribute,
usernameAttribute: config.LdapSettings.UsernameAttribute,
positionAttribute: config.LdapSettings.PositionAttribute,
idAttribute: config.LdapSettings.IdAttribute,
syncIntervalMinutes: config.LdapSettings.SyncIntervalMinutes,
skipCertificateVerification: config.LdapSettings.SkipCertificateVerification,
@@ -299,6 +301,25 @@ export default class LdapSettings extends AdminSettings {
onChange={this.handleChange}
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
id='emailAttribute'
label={

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

@@ -41,6 +41,7 @@ export default class SamlSettings extends AdminSettings {
config.SamlSettings.EmailAttribute = this.state.emailAttribute;
config.SamlSettings.UsernameAttribute = this.state.usernameAttribute;
config.SamlSettings.NicknameAttribute = this.state.nicknameAttribute;
config.SamlSettings.PositionAttribute = this.state.positionAttribute;
config.SamlSettings.LocaleAttribute = this.state.localeAttribute;
config.SamlSettings.LoginButtonText = this.state.loginButtonText;
@@ -65,6 +66,7 @@ export default class SamlSettings extends AdminSettings {
emailAttribute: settings.EmailAttribute,
usernameAttribute: settings.UsernameAttribute,
nicknameAttribute: settings.NicknameAttribute,
positionAttribute: settings.PositionAttribute,
localeAttribute: settings.LocaleAttribute,
loginButtonText: settings.LoginButtonText
};
@@ -508,6 +510,25 @@ export default class SamlSettings extends AdminSettings {
onChange={this.handleChange}
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
id='localeAttribute'
label={

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

@@ -182,6 +182,23 @@ export default class UserProfile extends React.Component {
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()) {
dataContent.push(
<div

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

@@ -69,6 +69,10 @@ const holders = defineMessages({
close: {
id: 'user.settings.general.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.submitUser = this.submitUser.bind(this);
this.submitPicture = this.submitPicture.bind(this);
this.submitPosition = this.submitPosition.bind(this);
this.updateUsername = this.updateUsername.bind(this);
this.updateFirstName = this.updateFirstName.bind(this);
@@ -94,6 +99,7 @@ class UserSettingsGeneralTab extends React.Component {
this.updateConfirmEmail = this.updateConfirmEmail.bind(this);
this.updatePicture = this.updatePicture.bind(this);
this.updateSection = this.updateSection.bind(this);
this.updatePosition = this.updatePosition.bind(this);
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) {
this.setState({username: e.target.value});
}
@@ -265,6 +287,10 @@ class UserSettingsGeneralTab extends React.Component {
this.setState({nickname: e.target.value});
}
updatePosition(e) {
this.setState({position: e.target.value});
}
updateEmail(e) {
this.setState({email: e.target.value});
}
@@ -302,6 +328,7 @@ class UserSettingsGeneralTab extends React.Component {
firstName: user.first_name,
lastName: user.last_name,
nickname: user.nickname,
position: user.position,
email: user.email,
confirmEmail: '',
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();
let pictureSection;
@@ -1030,6 +1150,8 @@ class UserSettingsGeneralTab extends React.Component {
<div className='divider-light'/>
{nicknameSection}
<div className='divider-light'/>
{positionSection}
<div className='divider-light'/>
{emailSection}
<div className='divider-light'/>
{pictureSection}