Wire up account settings forms

This commit is contained in:
James Allen
2014-06-20 11:15:25 +01:00
parent 0ef7e54ad3
commit 81845dec32
19 changed files with 134 additions and 147 deletions

View File

@@ -37,6 +37,10 @@ module.exports =
user.first_name = req.body.first_name.trim()
if req.body.last_name?
user.last_name = req.body.last_name.trim()
if req.body.role?
user.role = req.body.role.trim()
if req.body.institution?
user.institution = req.body.institution.trim()
if req.body.mode?
user.ace.mode = req.body.mode
if req.body.theme?

View File

@@ -28,21 +28,6 @@ module.exports = UserController =
UserController.sendFormattedPersonalInfo(user, res, next)
req.session.destroy()
updatePersonalInfo: (req, res, next = (error)->) ->
{first_name, last_name, role, institution} = req.body
user_id = req.session.user._id
logger.log data:req.body, user_id:user_id, "getting update for user personal info"
update =
first_name:sanitize.escape(first_name)
last_name:sanitize.escape(last_name)
role:sanitize.escape(role)
institution:sanitize.escape(institution)
UserUpdater.updatePersonalInfo user_id, update, (err)->
if err?
res.send 500
else
res.send 204
sendFormattedPersonalInfo: (user, res, next = (error) ->) ->
UserController._formatPersonalInfo user, (error, info) ->
return next(error) if error?

View File

@@ -28,14 +28,3 @@ module.exports = UserUpdater =
return callback(err)
callback()
updatePersonalInfo: (user_id, info, callback)->
self = @
update =
$set:
"first_name": info.first_name || ""
"last_name": info.last_name || ""
"role": info.role || ""
"institution": info.institution || ""
self.updateUser user_id.toString(), update, (err)->
callback(err)

View File

@@ -91,7 +91,6 @@ module.exports = class Router
app.get '/user/auth_token', AuthenticationController.requireLogin(), AuthenticationController.getAuthToken
app.get '/user/personal_info', AuthenticationController.requireLogin(allow_auth_token: true), UserInfoController.getLoggedInUsersPersonalInfo
app.post '/user/personal_info', AuthenticationController.requireLogin(), UserInfoController.updatePersonalInfo
app.get '/user/:user_id/personal_info', httpAuth, UserInfoController.getPersonalInfo
app.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage

View File

@@ -101,7 +101,7 @@ block content
| complete
.progress
.bar.bar-success(ng-style="{'width' : (percentComplete+'%')}")
.progress-bar.progress-bar-info(ng-style="{'width' : (percentComplete+'%')}")
button#completeUserProfileInformation.btn.btn-primary(
ng-hide="formVisable",

View File

@@ -11,7 +11,7 @@ block content
form(async-form="login", name="loginForm", action='/login', ng-cloak)
input(name='_csrf', type='hidden', value=csrfToken)
input(name='redir', type='hidden', value=redir)
form-messages
form-messages(for="loginForm")
.form-group
input.form-control(
type='email',

View File

@@ -16,8 +16,8 @@ block content
ng-cloak
)
input(type="hidden", name="_csrf", value=csrfToken)
form-messages
.alert.alert-success(ng-show="success")
form-messages(for="passwordResetForm")
.alert.alert-success(ng-show="passwordResetForm.response.success")
| You have been sent an email to complete your password reset.
.form-group
label(for='email') Please enter your email address

View File

@@ -21,16 +21,16 @@ block content
form(async-form="register", name="registerForm", action="/register", novalidate, ng-cloak)
input(name='_csrf', type='hidden', value=csrfToken)
input(name='redir', type='hidden', value=redir)
form-messages
form-messages(for="registerForm")
.form-group
label(for='email') Email
input.form-control(
type='email',
name='email',
value='#{new_email}',
placeholder="email@example.com"
required,
ng-model="email"
ng-model="email",
ng-init="email = #{JSON.stringify(new_email)}"
)
span.small.text-primary(ng-show="registerForm.email.$invalid && registerForm.email.$dirty")
| Must be an email address

View File

@@ -15,8 +15,8 @@ block content
ng-cloak
)
input(type="hidden", name="_csrf", value=csrfToken)
form-messages
.alert.alert-success(ng-show="success")
form-messages(for="passwordResetForm")
.alert.alert-success(ng-show="passwordResetForm.response.success")
| Your password has been reset.
a(href='/login') Login here

View File

@@ -5,43 +5,93 @@ block content
.container
.row
.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
.card
.card.account-settings
.page-header
h1 Account Settings
.messageArea
form-messages(ng-cloak, for="settingsForm")
.alert.alert-success(ng-show="settingsForm.response.success")
| Thanks, your settings have been updated.
form-messages(ng-cloak, for="changePasswordForm")
.container-fluid
.row
.row(ng-cloak)
.col-md-5
h3 Update Account Info
form#userSettings
form(async-form="settings", name="settingsForm", action="/user/settings", novalidate)
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label(for='email') Email
input#emailAddress.form-control(type='email', name='email', value=user.email)
input.form-control(
type='email',
name='email',
placeholder="email@example.com"
required,
ng-model="email",
ng-init="email = #{JSON.stringify(user.email)}"
)
span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
| Must be an email address
.form-group
label(for='firstName').control-label First Name
input#firstName.form-control(type='text', name='first_name', value=user.first_name)
input.form-control(
type='text',
name='first_name',
value=user.first_name
)
.form-group
label(for='lastName').control-label Last Name
input#lastName.form-control(type='text', name='last_name', value=user.last_name)
input.form-control(
type='text',
name='last_name',
value=user.last_name
)
.actions
button.btn.btn-primary(type='submit') Update
button.btn.btn-primary(
type='submit',
ng-disabled="settingsForm.$invalid"
) Update
.col-md-5.col-md-offset-1
h3 Change Password
form#changePasswordForm(method="post", action="/user/password/update")
form(async-form="changepassword", name="changePasswordForm", action="/user/password/update", novalidate)
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label(for='currentPassword') Current Password
input#currentPassword.form-control(type='password', name='currentPassword', placeholder='*********')
input.form-control(
type='password',
name='currentPassword',
placeholder='*********',
ng-model="currentPassword",
required
)
span.small.text-primary(ng-show="changePasswordForm.currentPassword.$invalid && changePasswordForm.currentPassword.$dirty")
| Required
.form-group
label(for='newPassword1') New Password
input#newPassword1.form-control(type='password', name='newPassword1', placeholder='************')
input.form-control(
type='password',
name='newPassword1',
placeholder='*********',
ng-model="newPassword1",
required
)
span.small.text-primary(ng-show="changePasswordForm.newPassword1.$invalid && changePasswordForm.newPassword1.$dirty")
| Required
.form-group
label(for='newPassword2') Confirm New Password
input#newPassword2.form-control(type='password', name='newPassword2', placeholder='************')
input.form-control(
type='password',
name='newPassword2',
placeholder='*********',
ng-model="newPassword2",
equals="{{newPassword1}}"
)
span.small.text-primary(ng-show="changePasswordForm.newPassword2.$invalid && changePasswordForm.newPassword2.$dirty")
| Doesn't match
.actions
button.btn.btn-primary(type='submit') Change
button.btn.btn-primary(
type='submit',
ng-disabled="changePasswordForm.$invalid"
) Change
hr.soften

View File

@@ -6,6 +6,8 @@ define [
link: (scope, element, attrs) ->
formName = attrs.asyncForm
scope[attrs.name].response = response = {}
element.on "submit", (e) ->
e.preventDefault()
@@ -16,29 +18,29 @@ define [
$http
.post(element.attr('action'), formData)
.success (data, status, headers, config) ->
scope.success = true
scope.error = false
response.success = true
response.error = false
if data.redir?
ga('send', 'event', formName, 'success')
window.location = data.redir
else if data.message?
scope.message = data.message
response.message = data.message
if data.message.type == "error"
scope.success = false
scope.error = true
response.success = false
response.error = true
ga('send', 'event', formName, 'failure', data.message)
else
ga('send', 'event', formName, 'success')
.error (data, status, headers, config) ->
scope.success = false
scope.error = true
ga('send', 'event', formName, 'failure', data.message)
scope.message =
response.success = false
response.error = true
response.message =
text: data.message or "Something went wrong talking to the server :(. Please try again."
type: 'error'
ga('send', 'event', formName, 'failure', data.message)
}
App.directive "formMessages", () ->
@@ -46,12 +48,16 @@ define [
restrict: "E"
template: """
<div class="alert" ng-class="{
'alert-danger': message.type == 'error',
'alert-success': message.type != 'error'
}" ng-show="!!message">
{{message.text}}
'alert-danger': form.response.message.type == 'error',
'alert-success': form.response.message.type != 'error'
}" ng-show="!!form.response.message">
{{form.response.message.text}}
</div>
<div ng-transclude></div>
"""
transclude: true
scope: {
form: "=for"
}
}

View File

@@ -0,0 +1,15 @@
define [
"base"
], (App) ->
App.directive 'equals', () ->
return {
require: "ngModel",
link: (scope, element, attrs, ngModel) ->
scope.$watch attrs.ngModel, () -> validate()
attrs.$observe 'equals', () -> validate()
validate = () ->
equal = (attrs.equals == ngModel.$viewValue)
ngModel.$setValidity('areEqual', equal)
}

View File

@@ -5,5 +5,6 @@ define [
"directives/stopPropagation"
"directives/focusInput"
"directives/focusOn"
"directives/equals"
], () ->
angular.bootstrap(document.body, ["SharelatexApp"])

View File

@@ -27,7 +27,7 @@ define [
$scope.formVisable = true
$scope.sendUpdate = ->
request = $http.post "/user/personal_info", $scope.userInfoForm
request = $http.post "/user/settings", $scope.userInfoForm
request.success (data, status)->
request.error (data, status)->
console.log "the request failed"

View File

@@ -0,0 +1,5 @@
.account-settings {
.alert {
margin-bottom: 0;
}
}

View File

@@ -53,5 +53,6 @@
// ShareLaTeX app classes
@import "app/base.less";
@import "app/account-settings.less";
@import "app/project-list.less";
@import "app/editor.less";

View File

@@ -95,6 +95,22 @@ describe "UserController", ->
done()
@UserController.updateUserSettings @req, @res
it "should set the role", (done)->
@req.body =
role: "student"
@res.send = (code)=>
@user.role.should.equal "student"
done()
@UserController.updateUserSettings @req, @res
it "should set the institution", (done)->
@req.body =
institution: "MIT"
@res.send = (code)=>
@user.institution.should.equal "MIT"
done()
@UserController.updateUserSettings @req, @res
it "should set some props on ace", (done)->
@req.body =
theme: "something"

View File

@@ -124,51 +124,3 @@ describe "UserInfoController", ->
institution: @user.institution
}
describe "setPersonalInfo", ->
beforeEach ->
@req =
session:
user:
_id:"123123j321jikuj90jlk"
@req.body =
first_name: "bob"
last_name: "smith"
role:"student"
institution: "Sheffield"
notWanted: "something"
it "should send the data from the body to the user updater", (done)->
@UserUpdater.updatePersonalInfo.callsArgWith(2, null)
@res.send = (statusCode)=>
statusCode.should.equal 204
@UserUpdater.updatePersonalInfo.args[0][0].should.equal @req.session.user._id
args = @UserUpdater.updatePersonalInfo.args[0][1]
args.first_name.should.equal @req.body.first_name
args.last_name.should.equal @req.body.last_name
args.role.should.equal @req.body.role
args.institution.should.equal @req.body.institution
assert.equal args.notWanted, undefined
done()
@UserInfoController.updatePersonalInfo @req, @res
it "should sanitize the data", (done)->
@UserUpdater.updatePersonalInfo.callsArgWith(2, null)
@res.send = (statusCode)=>
@sanitizer.escape.calledWith(@req.body.first_name).should.equal true
@sanitizer.escape.calledWith(@req.body.last_name).should.equal true
@sanitizer.escape.calledWith(@req.body.role).should.equal true
@sanitizer.escape.calledWith(@req.body.institution).should.equal true
done()
@UserInfoController.updatePersonalInfo @req, @res
it "should send an error if the UpserUpdater returns on", (done)->
@UserUpdater.updatePersonalInfo.callsArgWith(2, "error")
@res.send = (statusCode)->
statusCode.should.equal 500
done()
@UserInfoController.updatePersonalInfo @req, @res

View File

@@ -46,39 +46,3 @@ describe "UserUpdater", ->
@UserUpdater.updateUser.calledWith(@user_id, $set: { "email": @newEmail}).should.equal true
done()
describe "updatePersonalInfo", ->
beforeEach ->
@info =
first_name:"billy"
last_name:"brag"
role:"student"
institution:"sheffield"
it "should set the names role and institution", (done)->
@UserUpdater.updateUser = sinon.stub().callsArgWith(2)
@UserUpdater.updatePersonalInfo @user_id, @info, (err)=>
@UserUpdater.updateUser.args[0][0].should.equal @user_id
args = @UserUpdater.updateUser.args[0][1]
args["$set"].first_name.should.equal @info.first_name
args["$set"].last_name.should.equal @info.last_name
args["$set"].role.should.equal @info.role
args["$set"].institution.should.equal @info.institution
done()
it "should return the error", (done)->
@UserUpdater.updateUser = sinon.stub().callsArgWith(2, "error")
@UserUpdater.updatePersonalInfo @user_id, @info, (err)=>
should.exist(err)
done()
it "should default them to empty strings", (done)->
@UserUpdater.updateUser = sinon.stub().callsArgWith(2)
@UserUpdater.updatePersonalInfo @user_id, {}, (err)=>
args = @UserUpdater.updateUser.args[0][1]
args["$set"].first_name.should.equal ""
args["$set"].last_name.should.equal ""
args["$set"].role.should.equal ""
args["$set"].institution.should.equal ""
done()