mirror of
https://github.com/TryGhost/Ghost-Admin.git
synced 2023-12-14 02:33:04 +01:00
Improved Password Reset Tool
Closes #1471 - add api and User model methods for generating and validating tokens - add routes and handlers for reset password pages - add client styles and views for reset password form - some basic integration tests for User model methods
This commit is contained in:
parent
f510ab8310
commit
41eff2c4db
4 changed files with 118 additions and 17 deletions
|
@ -15,7 +15,8 @@
|
|||
|
||||
.ghost-login,
|
||||
.ghost-signup,
|
||||
.ghost-forgotten {
|
||||
.ghost-forgotten,
|
||||
.ghost-reset {
|
||||
color: $midgrey;
|
||||
background: $darkgrey;
|
||||
|
||||
|
@ -35,7 +36,8 @@
|
|||
|
||||
.login-box,
|
||||
.signup-box,
|
||||
.forgotten-box {
|
||||
.forgotten-box,
|
||||
.reset-box {
|
||||
max-width: 530px;
|
||||
height: 90%;
|
||||
margin: 0 auto;
|
||||
|
@ -177,10 +179,10 @@
|
|||
|
||||
|
||||
/* =============================================================================
|
||||
2. Signup
|
||||
2. Signup and Reset
|
||||
============================================================================= */
|
||||
|
||||
#signup {
|
||||
#signup, #reset {
|
||||
@include box-sizing(border-box);
|
||||
max-width: 280px;
|
||||
color: lighten($midgrey, 15%);
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
'register/' : 'register',
|
||||
'signup/' : 'signup',
|
||||
'signin/' : 'login',
|
||||
'forgotten/' : 'forgotten'
|
||||
'forgotten/' : 'forgotten',
|
||||
'reset/:token/' : 'reset'
|
||||
},
|
||||
|
||||
signup: function () {
|
||||
|
@ -28,6 +29,10 @@
|
|||
Ghost.currentView = new Ghost.Views.Forgotten({ el: '.js-forgotten-box' });
|
||||
},
|
||||
|
||||
reset: function (token) {
|
||||
Ghost.currentView = new Ghost.Views.ResetPassword({ el: '.js-reset-box', token: token });
|
||||
},
|
||||
|
||||
blog: function () {
|
||||
var posts = new Ghost.Collections.Posts();
|
||||
NProgress.start();
|
||||
|
|
9
tpl/reset.hbs
Normal file
9
tpl/reset.hbs
Normal file
|
@ -0,0 +1,9 @@
|
|||
<form id="reset" method="post" novalidate="novalidate">
|
||||
<div class="password-wrap">
|
||||
<input class="password" type="password" placeholder="Password" name="newpassword" />
|
||||
</div>
|
||||
<div class="password-wrap">
|
||||
<input class="password" type="password" placeholder="Confirm Password" name="ne2password" />
|
||||
</div>
|
||||
<button class="button-save" type="submit">Reset Password</button>
|
||||
</form>
|
109
views/login.js
109
views/login.js
|
@ -6,9 +6,6 @@
|
|||
|
||||
initialize: function () {
|
||||
this.render();
|
||||
$(".js-login-box").css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
|
||||
$("[name='email']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
templateName: "login",
|
||||
|
@ -17,6 +14,13 @@
|
|||
'submit #login': 'submitHandler'
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
var self = this;
|
||||
this.$el.css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
|
||||
self.$("[name='email']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
submitHandler: function (event) {
|
||||
event.preventDefault();
|
||||
var email = this.$el.find('.email').val(),
|
||||
|
@ -61,9 +65,6 @@
|
|||
|
||||
initialize: function () {
|
||||
this.render();
|
||||
$(".js-signup-box").css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
|
||||
$("[name='name']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
templateName: "signup",
|
||||
|
@ -72,11 +73,21 @@
|
|||
'submit #signup': 'submitHandler'
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
var self = this;
|
||||
|
||||
this.$el
|
||||
.css({"opacity": 0})
|
||||
.animate({"opacity": 1}, 500, function () {
|
||||
self.$("[name='name']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
submitHandler: function (event) {
|
||||
event.preventDefault();
|
||||
var name = this.$el.find('.name').val(),
|
||||
email = this.$el.find('.email').val(),
|
||||
password = this.$el.find('.password').val();
|
||||
var name = this.$('.name').val(),
|
||||
email = this.$('.email').val(),
|
||||
password = this.$('.password').val();
|
||||
|
||||
// This is needed due to how error handling is done. If this is not here, there will not be a time
|
||||
// when there is no error.
|
||||
|
@ -119,9 +130,6 @@
|
|||
|
||||
initialize: function () {
|
||||
this.render();
|
||||
$(".js-forgotten-box").css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
|
||||
$("[name='email']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
templateName: "forgotten",
|
||||
|
@ -130,6 +138,13 @@
|
|||
'submit #forgotten': 'submitHandler'
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
var self = this;
|
||||
this.$el.css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
|
||||
self.$("[name='email']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
submitHandler: function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -166,4 +181,74 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ghost.Views.ResetPassword = Ghost.View.extend({
|
||||
templateName: 'reset',
|
||||
|
||||
events: {
|
||||
'submit #reset': 'submitHandler'
|
||||
},
|
||||
|
||||
initialize: function (attrs) {
|
||||
attrs = attrs || {};
|
||||
|
||||
this.token = attrs.token;
|
||||
|
||||
this.render();
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
var self = this;
|
||||
this.$el.css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
|
||||
self.$("[name='newpassword']").focus();
|
||||
});
|
||||
},
|
||||
|
||||
submitHandler: function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var self = this,
|
||||
newPassword = this.$('input[name="newpassword"]').val(),
|
||||
ne2Password = this.$('input[name="ne2password"]').val();
|
||||
|
||||
if (newPassword !== ne2Password) {
|
||||
Ghost.notifications.addItem({
|
||||
type: 'error',
|
||||
message: "Your passwords do not match.",
|
||||
status: 'passive'
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.$('input, button').prop('disabled', true);
|
||||
|
||||
$.ajax({
|
||||
url: '/ghost/reset/' + this.token + '/',
|
||||
type: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
|
||||
},
|
||||
data: {
|
||||
newpassword: newPassword,
|
||||
ne2password: ne2Password
|
||||
},
|
||||
success: function (msg) {
|
||||
window.location.href = msg.redirect;
|
||||
},
|
||||
error: function (xhr) {
|
||||
self.$('input, button').prop('disabled', false);
|
||||
|
||||
Ghost.notifications.clearEverything();
|
||||
Ghost.notifications.addItem({
|
||||
type: 'error',
|
||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||
status: 'passive'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
|
Loading…
Reference in a new issue