@@ -842,6 +850,47 @@ module.exports = {
);
}
})),
+ ClassInviteExistingStudentStep: intl.injectIntl(React.createClass({
+ getDefaultProps: function () {
+ return {
+ classroom: null,
+ onHandleLogOut: function () {},
+ studentUsername: null,
+ waiting: false
+ };
+ },
+ onNextStep: function () {
+ this.props.onNextStep();
+ },
+ render: function () {
+ var formatMessage = this.props.intl.formatMessage;
+ return (
+
+ {this.props.waiting ? [
+
+ ] : [
+ {this.props.studentUsername}
,
+
+ {formatMessage({id: 'registration.classroomInviteExistingStudentStepDescription'})}
+
,
+
+
+
{this.props.classroom.title}
+
+
{formatMessage({id: 'registration.invitedBy'})}
+
{this.props.classroom.educator.username}
+
+
+ ,
+ {formatMessage({id: 'registration.notYou'})}
,
+
+ ]}
+
+ );
+ }
+ })),
ClassWelcomeStep: intl.injectIntl(React.createClass({
getDefaultProps: function () {
return {
diff --git a/src/components/registration/steps.scss b/src/components/registration/steps.scss
index 6deca7956..21d0ff0a5 100644
--- a/src/components/registration/steps.scss
+++ b/src/components/registration/steps.scss
@@ -29,6 +29,15 @@
color: $ui-dark-gray;
}
+ &.class-invite-step {
+ text-align: center;
+
+ > p a {
+ text-decoration: underline;
+ color: $ui-white;
+ font-weight: inherit;
+ }
+ }
&.class-invite-step,
&.class-welcome-step {
@@ -41,6 +50,12 @@
}
}
+ &.username-step {
+ .username-label {
+ margin-bottom: .75rem;
+ }
+ }
+
&.demographics-step {
.gender-input {
margin-top: -5.5rem;
@@ -66,26 +81,12 @@
margin-bottom: 1.25rem;
}
}
-
- .validation-message {
- margin-top: .5rem;
- }
-
- .checkbox-row {
- .validation-message {
- margin-top: 0;
- }
- }
}
&.organization-step {
- .validation-message {
- transform: translate(16rem, -4rem);
- }
-
.checkbox-group {
.validation-message {
- transform: translate(16rem, -16rem);
+ transform: translate(16rem, 8rem);
}
}
@@ -100,14 +101,6 @@
}
}
- &.address-step {
- .select {
- .validation-message {
- transform: translate(0, .5rem);
- }
- }
- }
-
&.usescratch-step {
.form {
.form-group {
@@ -119,12 +112,6 @@
}
}
}
-
-
- }
-
- .validation-message {
- margin-top: .75rem;
}
p {
diff --git a/src/l10n.json b/src/l10n.json
index 80c0e4a29..098cccf48 100644
--- a/src/l10n.json
+++ b/src/l10n.json
@@ -111,19 +111,24 @@
"registration.choosePasswordStepDescription": "Type in a new password for your account. You will use this password the next time you log into Scratch.",
"registration.choosePasswordStepTitle": "Create a password",
"registration.choosePasswordStepTooltip": "Don't use your name or anything that's easy for someone else to guess.",
- "registration.classroomInviteStepDescription": "has invited you to join the class:",
+ "registration.classroomApiGeneralError": "Sorry, we could not find the registration information for this class",
+ "registration.classroomInviteExistingStudentStepDescription": "you have been invited to join the class:",
+ "registration.classroomInviteNewStudentStepDescription": "has invited you to join the class:",
"registration.confirmYourEmail": "Confirm Your Email",
"registration.confirmYourEmailDescription": "If you haven't already, please click the link in the confirmation email sent to:",
"registration.createUsername": "Create a Username",
"registration.goToClass": "Go to Class",
+ "registration.invitedBy": "invited by",
"registration.lastStepTitle": "Thank you for requesting a Scratch Teacher Account",
"registration.lastStepDescription": "We are currently processing your application. ",
"registration.mustBeNewStudent": "You must be a new student to complete your registration",
"registration.nameStepTooltip": "This information is used for verification and to aggregate usage statistics.",
"registration.newPassword": "New Password",
"registration.nextStep": "Next Step",
+ "registration.notYou": "Not you? Log in as another user",
"registration.personalStepTitle": "Personal Information",
"registration.personalStepDescription": "Your individual responses will not be displayed publicly, and will be kept confidential and secure",
+ "registration.selectCountry": "select country",
"registration.studentPersonalStepDescription": "This information will not appear on the Scratch website.",
"registration.showPassword": "Show password",
"registration.usernameStepDescription": "Fill in the following forms to request an account. The approval process may take up to 24 hours.",
diff --git a/src/redux/session.js b/src/redux/session.js
index 6447bc943..553228d6b 100644
--- a/src/redux/session.js
+++ b/src/redux/session.js
@@ -79,6 +79,12 @@ module.exports.refreshSession = function () {
body.flags.must_complete_registration &&
window.location.pathname !== '/classes/complete_registration') {
return window.location = '/classes/complete_registration';
+ } else if (
+ body.flags &&
+ body.flags.must_reset_password &&
+ !body.flags.must_complete_registration &&
+ window.location.pathname !== '/classes/student_password_reset/') {
+ return window.location = '/classes/student_password_reset/';
} else {
dispatch(tokenActions.getToken());
dispatch(module.exports.setSession(body));
diff --git a/src/views/studentcompleteregistration/studentcompleteregistration.jsx b/src/views/studentcompleteregistration/studentcompleteregistration.jsx
index 4d1289981..50df16443 100644
--- a/src/views/studentcompleteregistration/studentcompleteregistration.jsx
+++ b/src/views/studentcompleteregistration/studentcompleteregistration.jsx
@@ -6,6 +6,7 @@ var render = require('../../lib/render.jsx');
var sessionStatus = require('../../redux/session').Status;
var api = require('../../lib/api');
var intl = require('../../lib/intl.jsx');
+var log = require('../../lib/log.js');
var Deck = require('../../components/deck/deck.jsx');
var Progression = require('../../components/progression/progression.jsx');
@@ -33,17 +34,16 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
});
},
componentDidUpdate: function (prevProps) {
- if (prevProps.session.session !== this.props.session.session &&
- this.props.session.session.permissions &&
- this.props.session.session.permissions.student) {
- var classroomId = this.props.session.session.user.classroomId;
+ if (prevProps.studentUsername !== this.props.studentUsername && this.props.newStudent) {
+ this.setState({waiting: true});
api({
- uri: '/classrooms/' + classroomId
+ uri: '/classrooms/' + this.props.classroomId
}, function (err, body, res) {
- if (err || res.statusCode === 404) {
+ this.setState({waiting: false});
+ if (err || res.statusCode !== 200) {
return this.setState({
registrationErrors: {
- __all__: this.props.intl.formatMessage({id: 'studentRegistration.classroomApiGeneralError'})
+ __all__: this.props.intl.formatMessage({id: 'registration.classroomApiGeneralError'})
}
});
}
@@ -51,6 +51,18 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
}.bind(this));
}
},
+ handleLogOut: function (e) {
+ e.preventDefault();
+ api({
+ host: '',
+ method: 'post',
+ uri: '/accounts/logout/',
+ useCsrf: true
+ }, function (err) {
+ if (err) return log.error(err);
+ window.location = '/';
+ }.bind(this));
+ },
register: function (formData) {
this.setState({waiting: true});
formData = defaults({}, formData || {}, this.state.formData);
@@ -65,7 +77,7 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
country: formData.user.country,
is_robot: formData.user.isRobot
};
- if (this.props.session.session.flags.must_reset_password) {
+ if (this.props.must_reset_password) {
submittedData.password = formData.user.password;
}
api({
@@ -88,36 +100,36 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
var demographicsDescription = this.props.intl.formatMessage({
id: 'registration.studentPersonalStepDescription'});
var registrationErrors = this.state.registrationErrors;
- var sessionFetched = this.props.session.status === sessionStatus.FETCHED;
- if (sessionFetched &&
- !(this.props.session.session.permissions.student &&
- this.props.session.session.flags.must_complete_registration)) {
+ if (!this.props.newStudent) {
registrationErrors = {
__all__: this.props.intl.formatMessage({id: 'registration.mustBeNewStudent'})
};
}
return (
- {sessionFetched && this.state.classroom ?
- (registrationErrors ?
-
-
- {Object.keys(registrationErrors).map(function (field) {
- var label = field + ': ';
- if (field === '__all__') {
- label = '';
- }
- return (- {label}{registrationErrors[field]}
);
- })}
-
-
- :
+ {registrationErrors ? (
+
+
+ {Object.keys(registrationErrors).map(function (field) {
+ var label = field + ': ';
+ if (field === '__all__') {
+ label = '';
+ }
+ return (- {label}{registrationErrors[field]}
);
+ })}
+
+
+ ) : (
+ this.state.waiting || !this.state.classroom ? (
+
+ ) : (
-
- {this.props.session.session.flags.must_reset_password ?
+ {this.props.must_reset_password ?
@@ -132,9 +144,7 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
waiting={this.state.waiting} />
)
- :
-
- }
+ )}
);
}
@@ -142,7 +152,14 @@ var StudentCompleteRegistration = intl.injectIntl(React.createClass({
var mapStateToProps = function (state) {
return {
- session: state.session
+ classroomId: state.session.session.user && state.session.session.user.classroomId,
+ must_reset_password: state.session.session.flags && state.session.session.flags.must_reset_password,
+ newStudent: (
+ state.session.session.permissions &&
+ state.session.session.permissions.student &&
+ state.session.session.flags.must_complete_registration),
+ sessionFetched: state.session.status === sessionStatus.FETCHED,
+ studentUsername: state.session.session.user && state.session.session.user.username
};
};
diff --git a/src/views/studentregistration/l10n.json b/src/views/studentregistration/l10n.json
deleted file mode 100644
index ede12528d..000000000
--- a/src/views/studentregistration/l10n.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "studentRegistration.classroomApiGeneralError": "Sorry, we could not find the registration information for this class"
-}
diff --git a/src/views/studentregistration/studentregistration.jsx b/src/views/studentregistration/studentregistration.jsx
index bbfd7f719..5400b11d6 100644
--- a/src/views/studentregistration/studentregistration.jsx
+++ b/src/views/studentregistration/studentregistration.jsx
@@ -35,14 +35,16 @@ var StudentRegistration = intl.injectIntl(React.createClass({
});
},
componentDidMount: function () {
+ this.setState({waiting: true});
api({
uri: '/classrooms/' + this.props.classroomId,
params: {token: this.props.classroomToken}
}, function (err, body, res) {
+ this.setState({waiting: false});
if (err) {
return this.setState({
registrationError: this.props.intl.formatMessage({
- id: 'studentRegistration.classroomApiGeneralError'
+ id: 'registration.classroomApiGeneralError'
})
});
}
@@ -104,9 +106,9 @@ var StudentRegistration = intl.injectIntl(React.createClass({
:
-
+