Implement simple team-based registration (#620)

* Implement simple team-based registration

Config required goes in the `teams` top-level property in config.json. This consists of an array of team objects:
```json
{
  "name": "University of Bath",
  "emailSuffix": "bath.ac.uk"
}
```
These can be selected on registration and require a user to have a certain email address in order to register as part of a team. This is for vector-im/riot-web#2940. The next step would be sending users with emails matching the emailSuffix of a team to the correct welcome page as in vector-im/riot-web#2430.
This commit is contained in:
Luke Barnard 2017-01-18 12:48:28 +01:00 committed by GitHub
parent fcb1d7a664
commit 5ef5204c8c
3 changed files with 108 additions and 12 deletions

View file

@ -1003,6 +1003,7 @@ module.exports = React.createClass({
defaultHsUrl={this.getDefaultHsUrl()} defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()} defaultIsUrl={this.getDefaultIsUrl()}
brand={this.props.config.brand} brand={this.props.config.brand}
teamsConfig={this.props.config.teamsConfig}
customHsUrl={this.getCurrentHsUrl()} customHsUrl={this.getCurrentHsUrl()}
customIsUrl={this.getCurrentIsUrl()} customIsUrl={this.getCurrentIsUrl()}
registrationUrl={this.props.registrationUrl} registrationUrl={this.props.registrationUrl}

View file

@ -49,6 +49,16 @@ module.exports = React.createClass({
email: React.PropTypes.string, email: React.PropTypes.string,
username: React.PropTypes.string, username: React.PropTypes.string,
guestAccessToken: React.PropTypes.string, guestAccessToken: React.PropTypes.string,
teamsConfig: React.PropTypes.shape({
// Email address to request new teams
supportEmail: React.PropTypes.string,
teams: React.PropTypes.arrayOf(React.PropTypes.shape({
// The displayed name of the team
"name": React.PropTypes.string,
// The suffix with which every team email address ends
"emailSuffix": React.PropTypes.string,
})).required,
}),
defaultDeviceDisplayName: React.PropTypes.string, defaultDeviceDisplayName: React.PropTypes.string,
@ -254,6 +264,7 @@ module.exports = React.createClass({
defaultUsername={this.state.formVals.username} defaultUsername={this.state.formVals.username}
defaultEmail={this.state.formVals.email} defaultEmail={this.state.formVals.email}
defaultPassword={this.state.formVals.password} defaultPassword={this.state.formVals.password}
teamsConfig={this.props.teamsConfig}
guestUsername={this.props.username} guestUsername={this.props.username}
minPasswordLength={MIN_PASSWORD_LENGTH} minPasswordLength={MIN_PASSWORD_LENGTH}
onError={this.onFormValidationFailed} onError={this.onFormValidationFailed}

View file

@ -38,6 +38,16 @@ module.exports = React.createClass({
defaultEmail: React.PropTypes.string, defaultEmail: React.PropTypes.string,
defaultUsername: React.PropTypes.string, defaultUsername: React.PropTypes.string,
defaultPassword: React.PropTypes.string, defaultPassword: React.PropTypes.string,
teamsConfig: React.PropTypes.shape({
// Email address to request new teams
supportEmail: React.PropTypes.string,
teams: React.PropTypes.arrayOf(React.PropTypes.shape({
// The displayed name of the team
"name": React.PropTypes.string,
// The suffix with which every team email address ends
"emailSuffix": React.PropTypes.string,
})).required,
}),
// A username that will be used if no username is entered. // A username that will be used if no username is entered.
// Specifying this param will also warn the user that entering // Specifying this param will also warn the user that entering
@ -62,7 +72,8 @@ module.exports = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
fieldValid: {} fieldValid: {},
selectedTeam: null,
}; };
}, },
@ -119,6 +130,25 @@ module.exports = React.createClass({
} }
}, },
onSelectTeam: function(teamIndex) {
let team = this._getSelectedTeam(teamIndex);
if (team) {
this.refs.email.value = this.refs.email.value.split("@")[0];
}
this.setState({
selectedTeam: team,
showSupportEmail: teamIndex === "other",
});
},
_getSelectedTeam: function(teamIndex) {
if (this.props.teamsConfig &&
this.props.teamsConfig.teams[teamIndex]) {
return this.props.teamsConfig.teams[teamIndex];
}
return null;
},
/** /**
* Returns true if all fields were valid last time * Returns true if all fields were valid last time
* they were validated. * they were validated.
@ -139,11 +169,15 @@ module.exports = React.createClass({
switch (field_id) { switch (field_id) {
case FIELD_EMAIL: case FIELD_EMAIL:
this.markFieldValid( let email = this.refs.email.value;
field_id, if (this.props.teamsConfig) {
this.refs.email.value == '' || Email.looksValid(this.refs.email.value), let team = this.state.selectedTeam;
"RegistrationForm.ERR_EMAIL_INVALID" if (team) {
); email = email + "@" + team.emailSuffix;
}
}
let valid = email === '' || Email.looksValid(email);
this.markFieldValid(field_id, valid, "RegistrationForm.ERR_EMAIL_INVALID");
break; break;
case FIELD_USERNAME: case FIELD_USERNAME:
// XXX: SPEC-1 // XXX: SPEC-1
@ -222,17 +256,64 @@ module.exports = React.createClass({
return cls; return cls;
}, },
_renderEmailInputSuffix: function() {
let suffix = null;
if (!this.state.selectedTeam) {
return suffix;
}
let team = this.state.selectedTeam;
if (team) {
suffix = "@" + team.emailSuffix;
}
return suffix;
},
render: function() { render: function() {
var self = this; var self = this;
var emailSection, registerButton; var emailSection, teamSection, teamAdditionSupport, registerButton;
if (this.props.showEmail) { if (this.props.showEmail) {
let emailSuffix = this._renderEmailInputSuffix();
emailSection = ( emailSection = (
<input type="text" ref="email" <div>
autoFocus={true} placeholder="Email address (optional)" <input type="text" ref="email"
defaultValue={this.props.defaultEmail} autoFocus={true} placeholder="Email address (optional)"
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')} defaultValue={this.props.defaultEmail}
onBlur={function() {self.validateField(FIELD_EMAIL)}} /> className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_EMAIL)}}
value={self.state.email}/>
{emailSuffix ? <input className="mx_Login_field" value={emailSuffix} disabled/> : null }
</div>
); );
if (this.props.teamsConfig) {
teamSection = (
<select
defaultValue="-1"
className="mx_Login_field"
onBlur={function() {self.validateField(FIELD_EMAIL)}}
onChange={function(ev) {self.onSelectTeam(ev.target.value)}}
>
<option key="-1" value="-1">No team</option>
{this.props.teamsConfig.teams.map((t, index) => {
return (
<option key={index} value={index}>
{t.name}
</option>
);
})}
<option key="-2" value="other">Other</option>
</select>
);
if (this.props.teamsConfig.supportEmail && this.state.showSupportEmail) {
teamAdditionSupport = (
<span>
If your team is not listed, email&nbsp;
<a href={"mailto:" + this.props.teamsConfig.supportEmail}>
{this.props.teamsConfig.supportEmail}
</a>
</span>
);
}
}
} }
if (this.props.onRegisterClick) { if (this.props.onRegisterClick) {
registerButton = ( registerButton = (
@ -248,6 +329,9 @@ module.exports = React.createClass({
return ( return (
<div> <div>
<form onSubmit={this.onSubmit}> <form onSubmit={this.onSubmit}>
{teamSection}
{teamAdditionSupport}
<br />
{emailSection} {emailSection}
<br /> <br />
<input type="text" ref="username" <input type="text" ref="username"