diff --git a/config/config.json b/config/config.json index a51e8f60ee..2209a9656b 100644 --- a/config/config.json +++ b/config/config.json @@ -186,6 +186,7 @@ "UsernameAttribute": "", "NicknameAttribute": "", "IdAttribute": "", + "PositionAttribute": "", "SyncIntervalMinutes": 60, "SkipCertificateVerification": false, "QueryTimeout": 60, @@ -218,6 +219,7 @@ "UsernameAttribute": "", "NicknameAttribute": "", "LocaleAttribute": "", + "PositionAttribute": "", "LoginButtonText": "With SAML" }, "NativeAppSettings": { diff --git a/i18n/en.json b/i18n/en.json index 37acda9398..a035da711d 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -3875,6 +3875,10 @@ "id": "model.user.is_valid.nickname.app_error", "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", "translation": "Your password must contain at least {{.Min}} characters." diff --git a/model/config.go b/model/config.go index 7d3cb93d6d..0a3fcb33ef 100644 --- a/model/config.go +++ b/model/config.go @@ -251,6 +251,7 @@ type LdapSettings struct { UsernameAttribute *string NicknameAttribute *string IdAttribute *string + PositionAttribute *string // Syncronization SyncIntervalMinutes *int @@ -297,6 +298,7 @@ type SamlSettings struct { UsernameAttribute *string NicknameAttribute *string LocaleAttribute *string + PositionAttribute *string LoginButtonText *string } @@ -690,6 +692,11 @@ func (o *Config) SetDefaults() { *o.LdapSettings.IdAttribute = "" } + if o.LdapSettings.PositionAttribute == nil { + o.LdapSettings.PositionAttribute = new(string) + *o.LdapSettings.PositionAttribute = "" + } + if o.LdapSettings.SyncIntervalMinutes == nil { o.LdapSettings.SyncIntervalMinutes = new(int) *o.LdapSettings.SyncIntervalMinutes = 60 @@ -911,6 +918,11 @@ func (o *Config) SetDefaults() { *o.SamlSettings.NicknameAttribute = "" } + if o.SamlSettings.PositionAttribute == nil { + o.SamlSettings.PositionAttribute = new(string) + *o.SamlSettings.PositionAttribute = "" + } + if o.SamlSettings.LocaleAttribute == nil { o.SamlSettings.LocaleAttribute = new(string) *o.SamlSettings.LocaleAttribute = "" diff --git a/model/user.go b/model/user.go index 330d26d82e..76c3772cb4 100644 --- a/model/user.go +++ b/model/user.go @@ -37,6 +37,7 @@ type User struct { Nickname string `json:"nickname"` FirstName string `json:"first_name"` LastName string `json:"last_name"` + Position string `json:"position"` Roles string `json:"roles"` AllowMarketing bool `json:"allow_marketing,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) } + 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 { return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id) } diff --git a/model/user_test.go b/model/user_test.go index 2f6524c052..15b1aae6f4 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -128,6 +128,17 @@ func TestUserIsValid(t *testing.T) { if err := user.IsValid(); err == nil { 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) { diff --git a/store/sql_upgrade.go b/store/sql_upgrade.go index e4b1906e0b..a275f664c4 100644 --- a/store/sql_upgrade.go +++ b/store/sql_upgrade.go @@ -222,6 +222,9 @@ func UpgradeDatabaseToVersion36(sqlStore *SqlStore) { // Create Team Description column 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) //} } diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 5882ed454b..b71d8214ca 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -66,6 +66,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore { table.ColMap("NotifyProps").SetMaxSize(2000) table.ColMap("Locale").SetMaxSize(5) table.ColMap("MfaSecret").SetMaxSize(128) + table.ColMap("Position").SetMaxSize(64) } return us diff --git a/webapp/components/admin_console/ldap_settings.jsx b/webapp/components/admin_console/ldap_settings.jsx index 5aa23fde66..b774d34f37 100644 --- a/webapp/components/admin_console/ldap_settings.jsx +++ b/webapp/components/admin_console/ldap_settings.jsx @@ -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} /> + + } + placeholder={Utils.localizeMessage('admin.ldap.positionAttrEx', 'E.g.: "title"')} + helpText={ + + } + value={this.state.positionAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> + + } + placeholder={Utils.localizeMessage('admin.saml.positionAttrEx', 'E.g.: "Role"')} + helpText={ + + } + value={this.state.positionAttribute} + onChange={this.handleChange} + disabled={!this.state.enable} + /> +

+ {position} +

+ + ); + } + if (global.window.mm_config.ShowEmailAddress === 'true' || UserStore.isSystemAdminForCurrentUser() || this.props.user === UserStore.getCurrentUser()) { dataContent.push(
+ + + ); + } else { + let positionLabel = ( + + ); + if (Utils.isMobile()) { + positionLabel = ''; + } + + inputs.push( +
+ +
+ +
+
+ ); + + extraInfo = ( + + + + ); + + submit = this.submitPosition; + } + + positionSection = ( + { + this.updateSection(''); + e.preventDefault(); + }} + extraInfo={extraInfo} + /> + ); + } else { + let describe = ''; + if (user.position) { + describe = user.position; + } else { + describe = ( + + ); + } + + positionSection = ( + { + this.updateSection('position'); + }} + /> + ); + } + const emailSection = this.createEmailSection(); let pictureSection; @@ -1030,6 +1150,8 @@ class UserSettingsGeneralTab extends React.Component {
{nicknameSection}
+ {positionSection} +
{emailSection}
{pictureSection} diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index c7198817fc..3658b40ae9 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -441,6 +441,9 @@ "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.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.queryEx": "E.g.: \"60\"", "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.nicknameAttrEx": "E.g.: \"Nickname\"", "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.privateKeyFileFileRemoveDesc": "Remove the private key used to decrypt SAML Assertions from the Identity Provider.", "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.emptyName": "Click 'Edit' to add your full name", "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.firstName": "First 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.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.position": "Position", + "user.settings.general.positionExtra": "Tell your teammates what you do.", "user.settings.general.primaryEmail": "Primary Email", "user.settings.general.profilePicture": "Profile Picture", "user.settings.general.title": "General Settings", diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index 1eac2732b0..94fa19ea9b 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -264,7 +264,8 @@ export const Constants = { FULLNAME: 'fullname', NICKNAME: 'nickname', EMAIL: 'email', - LANGUAGE: 'language' + LANGUAGE: 'language', + POSITION: 'position' }, ScrollTypes: { @@ -835,6 +836,7 @@ export const Constants = { MAX_NICKNAME_LENGTH: 22, MIN_PASSWORD_LENGTH: 5, MAX_PASSWORD_LENGTH: 64, + MAX_POSITION_LENGTH: 35, MIN_TRIGGER_LENGTH: 1, MAX_TRIGGER_LENGTH: 128, MAX_TEXTSETTING_LENGTH: 1024,