mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added client side validation
Closes #581. * Basically adds the client side of node validator, that we're already using * Validator is plonked onto `Ghost.Validator` * Usage is identical as to https://github.com/chriso/node-validator * Has sanitizing values et al * `Ghost.Validator.error` is redefined, it populates Ghost.Validator._errors (Array) * `Ghost.Validator.handleErrors` is supposed to print out the multiple error messages, if there are multiple (this is broken due to how notifications are presented `.html` instead of `.append`), and also apply class to element * The ajax calls are wrapped in an if to prevent network traffic if something's not right on client side * Added validation to general settings and user settings screens. * On validation error, optionally adds `.input-error` to whatever element you reference, see below (if `el` exists on the error object). This is the only place where usage is different to the original implementation. Redeclared `error()` function in `init.js` * Usage: `Ghost.Validate.check(valueToCheck, {message: "the error message", el: $('#the element')}).isEmail()` * The element above will receive the `.input-error` class. `isEmail()` is one of the stuff you can check against.
This commit is contained in:
parent
d00392973f
commit
6c99b67ab3
6 changed files with 1209 additions and 119 deletions
1010
core/client/assets/vendor/validator-client.js
vendored
Normal file
1010
core/client/assets/vendor/validator-client.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
/*globals window, $, _, Backbone */
|
/*globals window, $, _, Backbone, Validator */
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
||||||
Views : {},
|
Views : {},
|
||||||
Collections : {},
|
Collections : {},
|
||||||
Models : {},
|
Models : {},
|
||||||
|
Validate : new Validator(),
|
||||||
|
|
||||||
settings: {
|
settings: {
|
||||||
apiRoot: '/api/v0.1'
|
apiRoot: '/api/v0.1'
|
||||||
|
@ -36,6 +37,25 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ghost.Validate.error = function (object) {
|
||||||
|
this._errors.push(object);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ghost.Validate.handleErrors = function () {
|
||||||
|
_.each(Ghost.Validate._errors, function (errorObj) {
|
||||||
|
Ghost.notifications.addItem({
|
||||||
|
type: 'error',
|
||||||
|
message: errorObj.message,
|
||||||
|
status: 'passive'
|
||||||
|
});
|
||||||
|
if (errorObj.hasOwnProperty('el')) {
|
||||||
|
errorObj.el.addClass('input-error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
window.Ghost = Ghost;
|
window.Ghost = Ghost;
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<input type="password" id="user-new-password-verification">
|
<input type="password" id="user-new-password-verification">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="button-delete">Change Password</button>
|
<button class="button-delete button-change-password">Change Password</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
|
@ -23,25 +23,33 @@
|
||||||
password = this.$el.find('.password').val(),
|
password = this.$el.find('.password').val(),
|
||||||
redirect = Ghost.Views.Utils.getUrlVariables().r;
|
redirect = Ghost.Views.Utils.getUrlVariables().r;
|
||||||
|
|
||||||
$.ajax({
|
Ghost.Validate._errors = [];
|
||||||
url: '/ghost/signin/',
|
Ghost.Validate.check(email).isEmail();
|
||||||
type: 'POST',
|
Ghost.Validate.check(password, "Password too short").len(5);
|
||||||
data: {
|
|
||||||
email: email,
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
password: password,
|
Ghost.Validate.handleErrors();
|
||||||
redirect: redirect
|
} else {
|
||||||
},
|
$.ajax({
|
||||||
success: function (msg) {
|
url: '/ghost/signin/',
|
||||||
window.location.href = msg.redirect;
|
type: 'POST',
|
||||||
},
|
data: {
|
||||||
error: function (xhr) {
|
email: email,
|
||||||
Ghost.notifications.addItem({
|
password: password,
|
||||||
type: 'error',
|
redirect: redirect
|
||||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
},
|
||||||
status: 'passive'
|
success: function (msg) {
|
||||||
});
|
window.location.href = msg.redirect;
|
||||||
}
|
},
|
||||||
});
|
error: function (xhr) {
|
||||||
|
Ghost.notifications.addItem({
|
||||||
|
type: 'error',
|
||||||
|
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||||
|
status: 'passive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,24 +74,15 @@
|
||||||
email = this.$el.find('.email').val(),
|
email = this.$el.find('.email').val(),
|
||||||
password = this.$el.find('.password').val();
|
password = this.$el.find('.password').val();
|
||||||
|
|
||||||
if (!name) {
|
// This is needed due to how error handling is done. If this is not here, there will not be a time
|
||||||
Ghost.notifications.addItem({
|
// when there is no error.
|
||||||
type: 'error',
|
Ghost.Validate._errors = [];
|
||||||
message: "Please enter a name",
|
Ghost.Validate.check(name, "Please enter a name").len(1);
|
||||||
status: 'passive'
|
Ghost.Validate.check(email, "Please enter a correct email address").isEmail();
|
||||||
});
|
Ghost.Validate.check(password, "Please enter a password").len(5);
|
||||||
} else if (!email) {
|
|
||||||
Ghost.notifications.addItem({
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
type: 'error',
|
Ghost.Validate.handleErrors();
|
||||||
message: "Please enter an email",
|
|
||||||
status: 'passive'
|
|
||||||
});
|
|
||||||
} else if (!password) {
|
|
||||||
Ghost.notifications.addItem({
|
|
||||||
type: 'error',
|
|
||||||
message: "Please enter a password",
|
|
||||||
status: 'passive'
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/ghost/signup/',
|
url: '/ghost/signup/',
|
||||||
|
@ -128,24 +127,31 @@
|
||||||
|
|
||||||
var email = this.$el.find('.email').val();
|
var email = this.$el.find('.email').val();
|
||||||
|
|
||||||
$.ajax({
|
Ghost.Validate._errors = [];
|
||||||
url: '/ghost/forgotten/',
|
Ghost.Validate.check(email).isEmail();
|
||||||
type: 'POST',
|
|
||||||
data: {
|
|
||||||
email: email
|
|
||||||
},
|
|
||||||
success: function (msg) {
|
|
||||||
|
|
||||||
window.location.href = msg.redirect;
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
},
|
Ghost.Validate.handleErrors();
|
||||||
error: function (xhr) {
|
} else {
|
||||||
Ghost.notifications.addItem({
|
$.ajax({
|
||||||
type: 'error',
|
url: '/ghost/forgotten/',
|
||||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
type: 'POST',
|
||||||
status: 'passive'
|
data: {
|
||||||
});
|
email: email
|
||||||
}
|
},
|
||||||
});
|
success: function (msg) {
|
||||||
|
|
||||||
|
window.location.href = msg.redirect;
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
Ghost.notifications.addItem({
|
||||||
|
type: 'error',
|
||||||
|
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||||
|
status: 'passive'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -157,21 +157,45 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
saveSettings: function () {
|
saveSettings: function () {
|
||||||
var themes = this.model.get('availableThemes');
|
var themes = this.model.get('availableThemes'),
|
||||||
this.model.unset('availableThemes');
|
title = this.$('#blog-title').val(),
|
||||||
this.model.save({
|
description = this.$('#blog-description').val(),
|
||||||
title: this.$('#blog-title').val(),
|
email = this.$('#email-address').val(),
|
||||||
description: $('#blog-description').val(),
|
postsPerPage = this.$('#postsPerPage').val();
|
||||||
logo: this.$('#blog-logo').attr("src"),
|
|
||||||
cover: this.$('#blog-cover').attr("src"),
|
Ghost.Validate._errors = [];
|
||||||
email: this.$('#email-address').val(),
|
Ghost.Validate
|
||||||
postsPerPage: this.$('#postsPerPage').val(),
|
.check(title, {message: "Title is too long", el: $('#blog-title')})
|
||||||
activeTheme: this.$('#activeTheme').val()
|
.len(0, 150);
|
||||||
}, {
|
Ghost.Validate
|
||||||
success: this.saveSuccess,
|
.check(description, {message: "Description is too long", el: $('#blog-description')})
|
||||||
error: this.saveError
|
.len(0, 200);
|
||||||
});
|
Ghost.Validate
|
||||||
this.model.set({availableThemes: themes});
|
.check(email, {message: "Please supply a valid email address", el: $('#email-address')})
|
||||||
|
.isEmail().len(0, 254);
|
||||||
|
Ghost.Validate
|
||||||
|
.check(postsPerPage, {message: "Please use a number", el: $('postsPerPage')})
|
||||||
|
.isInt();
|
||||||
|
|
||||||
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
|
Ghost.Validate.handleErrors();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.model.unset('availableThemes');
|
||||||
|
this.model.save({
|
||||||
|
title: title,
|
||||||
|
description: description,
|
||||||
|
logo: this.$('#blog-logo').attr("src"),
|
||||||
|
cover: this.$('#blog-cover').attr("src"),
|
||||||
|
email: email,
|
||||||
|
postsPerPage: postsPerPage,
|
||||||
|
activeTheme: this.$('#activeTheme').val()
|
||||||
|
}, {
|
||||||
|
success: this.saveSuccess,
|
||||||
|
error: this.saveError
|
||||||
|
});
|
||||||
|
this.model.set({availableThemes: themes});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
showLogo: function () {
|
showLogo: function () {
|
||||||
var settings = this.model.toJSON();
|
var settings = this.model.toJSON();
|
||||||
|
@ -259,63 +283,92 @@
|
||||||
|
|
||||||
|
|
||||||
saveUser: function () {
|
saveUser: function () {
|
||||||
this.model.save({
|
var userName = this.$('#user-name').val(),
|
||||||
'full_name': this.$('#user-name').val(),
|
userEmail = this.$('#user-email').val(),
|
||||||
'email_address': this.$('#user-email').val(),
|
userLocation = this.$('#user-location').val(),
|
||||||
'location': this.$('#user-location').val(),
|
userWebsite = this.$('#user-website').val(),
|
||||||
'url': this.$('#user-website').val(),
|
userBio = this.$('#user-bio').val();
|
||||||
'bio': this.$('#user-bio').val(),
|
|
||||||
'profile_picture': this.$('#user-profile-picture').attr('src'),
|
Ghost.Validate._errors = [];
|
||||||
'cover_picture': this.$('#user-cover-picture').attr('src')
|
Ghost.Validate
|
||||||
}, {
|
.check(userName, {message: "Name is too long", el: $('#user-name')})
|
||||||
success: this.saveSuccess,
|
.len(0, 150);
|
||||||
error: this.saveError
|
Ghost.Validate
|
||||||
});
|
.check(userBio, {message: "Bio is too long", el: $('#user-bio')})
|
||||||
|
.len(0, 200);
|
||||||
|
Ghost.Validate
|
||||||
|
.check(userEmail, {message: "Please supply a valid email address", el: $('#user-email')})
|
||||||
|
.isEmail();
|
||||||
|
Ghost.Validate
|
||||||
|
.check(userLocation, {message: "Location is too long", el: $('#user-location')})
|
||||||
|
.len(0, 150);
|
||||||
|
if (userWebsite.length > 0) {
|
||||||
|
Ghost.Validate
|
||||||
|
.check(userWebsite, {message: "Please use a valid url", el: $('#user-website')})
|
||||||
|
.isUrl()
|
||||||
|
.len(0, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
|
Ghost.Validate.handleErrors();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.model.save({
|
||||||
|
'full_name': userName,
|
||||||
|
'email_address': userEmail,
|
||||||
|
'location': userLocation,
|
||||||
|
'url': userWebsite,
|
||||||
|
'bio': userBio,
|
||||||
|
'profile_picture': this.$('#user-profile-picture').attr('src'),
|
||||||
|
'cover_picture': this.$('#user-cover-picture').attr('src')
|
||||||
|
}, {
|
||||||
|
success: this.saveSuccess,
|
||||||
|
error: this.saveError
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
changePassword: function (event) {
|
changePassword: function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
var self = this,
|
var self = this,
|
||||||
oldPassword = this.$('#user-password-old').val(),
|
oldPassword = this.$('#user-password-old').val(),
|
||||||
newPassword = this.$('#user-password-new').val(),
|
newPassword = this.$('#user-password-new').val(),
|
||||||
ne2Password = this.$('#user-new-password-verification').val();
|
ne2Password = this.$('#user-new-password-verification').val();
|
||||||
|
|
||||||
if (newPassword !== ne2Password) {
|
Ghost.Validate._errors = [];
|
||||||
this.validationError('Your new passwords do not match');
|
Ghost.Validate.check(newPassword, {message: 'Your new passwords do not match'}).equals(ne2Password);
|
||||||
return;
|
Ghost.Validate.check(newPassword, {message: 'Your password is not long enough. It must be at least 8 chars long.'}).len(8);
|
||||||
}
|
|
||||||
|
|
||||||
if (newPassword.length < 8) {
|
if (Ghost.Validate._errors.length > 0) {
|
||||||
this.validationError('Your password is not long enough. It must be at least 8 chars long.');
|
Ghost.Validate.handleErrors();
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/ghost/changepw/',
|
url: '/ghost/changepw/',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {
|
data: {
|
||||||
password: oldPassword,
|
password: oldPassword,
|
||||||
newpassword: newPassword,
|
newpassword: newPassword,
|
||||||
ne2password: ne2Password
|
ne2password: ne2Password
|
||||||
},
|
},
|
||||||
success: function (msg) {
|
success: function (msg) {
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: msg.msg,
|
message: msg.msg,
|
||||||
status: 'passive',
|
status: 'passive',
|
||||||
id: 'success-98'
|
id: 'success-98'
|
||||||
});
|
});
|
||||||
self.$('#user-password-old, #user-password-new, #user-new-password-verification').val('');
|
self.$('#user-password-old, #user-password-new, #user-new-password-verification').val('');
|
||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
|
||||||
status: 'passive'
|
status: 'passive'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
templateName: 'settings/user-profile',
|
templateName: 'settings/user-profile',
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
<script src="/public/vendor/showdown/extensions/ghostdown.js"></script>
|
<script src="/public/vendor/showdown/extensions/ghostdown.js"></script>
|
||||||
<script src="/shared/vendor/showdown/extensions/github.js"></script>
|
<script src="/shared/vendor/showdown/extensions/github.js"></script>
|
||||||
<script src="/public/vendor/shortcuts.js"></script>
|
<script src="/public/vendor/shortcuts.js"></script>
|
||||||
|
<script src="/public/vendor/validator-client.js"></script>
|
||||||
<script src="/public/vendor/countable.js"></script>
|
<script src="/public/vendor/countable.js"></script>
|
||||||
<script src="/public/vendor/to-title-case.js"></script>
|
<script src="/public/vendor/to-title-case.js"></script>
|
||||||
<script src="/public/vendor/packery.pkgd.min.js"></script>
|
<script src="/public/vendor/packery.pkgd.min.js"></script>
|
||||||
|
@ -83,8 +84,8 @@
|
||||||
<script src="/public/models/uploadModal.js"></script>
|
<script src="/public/models/uploadModal.js"></script>
|
||||||
<!-- // require '/public/views/*' -->
|
<!-- // require '/public/views/*' -->
|
||||||
<script src="/public/views/base.js"></script>
|
<script src="/public/views/base.js"></script>
|
||||||
<script src="/public/models/uploadModal.js"></script>
|
|
||||||
<script src="/public/views/post-settings.js"></script>
|
<script src="/public/views/post-settings.js"></script>
|
||||||
|
<script src="/public/models/uploadModal.js"></script>
|
||||||
<script src="/public/views/blog.js"></script>
|
<script src="/public/views/blog.js"></script>
|
||||||
<script src="/public/views/editor.js"></script>
|
<script src="/public/views/editor.js"></script>
|
||||||
<script src="/public/views/editor-tag-widget.js"></script>
|
<script src="/public/views/editor-tag-widget.js"></script>
|
||||||
|
|
Loading…
Add table
Reference in a new issue