mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 14:43:08 +03:00
Create SettingsUserController
addresses #2422 - creates settings user controller - creates user model object - updates user fixture to be compatible with user model - updates settings/user template - add validator to Ember Admin - use validator to validate user model is valid - add mock response to /users/me/ path - creates models/base file for all models to inherit from - add mock response to /ghost/changepw/ path
This commit is contained in:
parent
79a333b480
commit
81eb705a37
12
Gruntfile.js
12
Gruntfile.js
@ -170,7 +170,9 @@ var path = require('path'),
|
|||||||
"Ember": true,
|
"Ember": true,
|
||||||
"Em": true,
|
"Em": true,
|
||||||
"DS": true,
|
"DS": true,
|
||||||
"$": true
|
"$": true,
|
||||||
|
"validator": true,
|
||||||
|
"ic": true
|
||||||
},
|
},
|
||||||
// node environment
|
// node environment
|
||||||
node: false,
|
node: false,
|
||||||
@ -577,17 +579,17 @@ var path = require('path'),
|
|||||||
'bower_components/ember/ember.js',
|
'bower_components/ember/ember.js',
|
||||||
'bower_components/ember-resolver/dist/ember-resolver.js',
|
'bower_components/ember-resolver/dist/ember-resolver.js',
|
||||||
'bower_components/ic-ajax/dist/globals/main.js',
|
'bower_components/ic-ajax/dist/globals/main.js',
|
||||||
|
'bower_components/validator-js/validator.js',
|
||||||
'bower_components/codemirror/lib/codemirror.js',
|
'bower_components/codemirror/lib/codemirror.js',
|
||||||
'bower_components/codemirror/addon/mode/overlay.js',
|
'bower_components/codemirror/addon/mode/overlay.js',
|
||||||
'bower_components/codemirror/mode/markdown/markdown.js',
|
'bower_components/codemirror/mode/markdown/markdown.js',
|
||||||
'bower_components/codemirror/mode/gfm/gfm.js',
|
'bower_components/codemirror/mode/gfm/gfm.js',
|
||||||
'bower_components/showdown/src/showdown.js',
|
'bower_components/showdown/src/showdown.js',
|
||||||
|
'bower_components/moment/moment.js',
|
||||||
|
|
||||||
'core/clientold/assets/lib/showdown/extensions/ghostdown.js',
|
'core/clientold/assets/lib/showdown/extensions/ghostdown.js',
|
||||||
'core/shared/lib/showdown/extensions/typography.js',
|
'core/shared/lib/showdown/extensions/typography.js',
|
||||||
'core/shared/lib/showdown/extensions/github.js',
|
'core/shared/lib/showdown/extensions/github.js'
|
||||||
|
|
||||||
'bower_components/moment/moment.js'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
65
core/client/controllers/settings/user.js
Normal file
65
core/client/controllers/settings/user.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*global alert */
|
||||||
|
|
||||||
|
var SettingsUserController = Ember.Controller.extend({
|
||||||
|
cover: function () {
|
||||||
|
// @TODO: add {{asset}} subdir path
|
||||||
|
return this.user.getWithDefault('cover', '/shared/img/user-cover.png');
|
||||||
|
}.property('user.cover'),
|
||||||
|
|
||||||
|
coverTitle: function () {
|
||||||
|
return this.get('user.name') + '\'s Cover Image';
|
||||||
|
}.property('user.name'),
|
||||||
|
|
||||||
|
image: function () {
|
||||||
|
// @TODO: add {{asset}} subdir path
|
||||||
|
return 'background-image: url(' + this.user.getWithDefault('image', '/shared/img/user-image.png') + ')';
|
||||||
|
}.property('user.image'),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
cover: function () {
|
||||||
|
alert('@TODO: Show Upload modal for cover');
|
||||||
|
},
|
||||||
|
|
||||||
|
image: function () {
|
||||||
|
alert('@TODO: Show Upload modal for image');
|
||||||
|
},
|
||||||
|
|
||||||
|
save: function () {
|
||||||
|
alert('@TODO: Saving user...');
|
||||||
|
|
||||||
|
if (this.user.validate().get('isValid')) {
|
||||||
|
this.user.save().then(function (response) {
|
||||||
|
alert('Done saving' + JSON.stringify(response));
|
||||||
|
}, function () {
|
||||||
|
alert('Error saving.');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert('Errors found! ' + JSON.stringify(this.user.get('errors')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
password: function () {
|
||||||
|
alert('@TODO: Changing password...');
|
||||||
|
var passwordProperties = this.getProperties('password', 'newpassword', 'ne2password');
|
||||||
|
|
||||||
|
if (this.user.validatePassword(passwordProperties).get('passwordIsValid')) {
|
||||||
|
this.user.saveNewPassword(passwordProperties).then(function () {
|
||||||
|
alert('Success!');
|
||||||
|
// Clear properties from view
|
||||||
|
this.setProperties({
|
||||||
|
'password': '',
|
||||||
|
'newpassword': '',
|
||||||
|
'ne2password': ''
|
||||||
|
});
|
||||||
|
}.bind(this), function (errors) {
|
||||||
|
alert('Errors ' + JSON.stringify(errors));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert('Errors found! ' + JSON.stringify(this.user.get('passwordErrors')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SettingsUserController;
|
@ -1,4 +1,3 @@
|
|||||||
/*global ic */
|
|
||||||
import postFixtures from 'ghost/fixtures/posts';
|
import postFixtures from 'ghost/fixtures/posts';
|
||||||
import userFixtures from 'ghost/fixtures/users';
|
import userFixtures from 'ghost/fixtures/users';
|
||||||
|
|
||||||
@ -36,6 +35,10 @@ var defineFixtures = function (status) {
|
|||||||
ic.ajax.defineFixture('/ghost/api/v0.1/posts/1', post(1, status));
|
ic.ajax.defineFixture('/ghost/api/v0.1/posts/1', post(1, status));
|
||||||
ic.ajax.defineFixture('/ghost/api/v0.1/posts/2', post(2, status));
|
ic.ajax.defineFixture('/ghost/api/v0.1/posts/2', post(2, status));
|
||||||
ic.ajax.defineFixture('/ghost/api/v0.1/signin', user(status));
|
ic.ajax.defineFixture('/ghost/api/v0.1/signin', user(status));
|
||||||
|
ic.ajax.defineFixture('/ghost/api/v0.1/users/me/', user(status));
|
||||||
|
ic.ajax.defineFixture('/ghost/changepw/', response({
|
||||||
|
msg: 'Password changed successfully'
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineFixtures;
|
export default defineFixtures;
|
@ -5,16 +5,16 @@ var users = [
|
|||||||
"name": "some-user",
|
"name": "some-user",
|
||||||
"slug": "some-user",
|
"slug": "some-user",
|
||||||
"email": "some@email.com",
|
"email": "some@email.com",
|
||||||
"image": null,
|
"image": undefined,
|
||||||
"cover": null,
|
"cover": undefined,
|
||||||
"bio": "",
|
"bio": "Example bio",
|
||||||
"website": "",
|
"website": "",
|
||||||
"location": "",
|
"location": "Imaginationland",
|
||||||
"accessibility": null,
|
"accessibility": undefined,
|
||||||
"status": "active",
|
"status": "active",
|
||||||
"language": "en_US",
|
"language": "en_US",
|
||||||
"meta_title": null,
|
"meta_title": undefined,
|
||||||
"meta_description": null,
|
"meta_description": undefined,
|
||||||
"created_at": "2014-02-15T20:02:25.000Z",
|
"created_at": "2014-02-15T20:02:25.000Z",
|
||||||
"updated_at": "2014-03-11T14:06:43.000Z"
|
"updated_at": "2014-03-11T14:06:43.000Z"
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
|
import User from 'ghost/models/user';
|
||||||
import userFixtures from 'ghost/fixtures/users';
|
import userFixtures from 'ghost/fixtures/users';
|
||||||
|
|
||||||
var currentUser = {
|
var currentUser = {
|
||||||
name: 'currentUser',
|
name: 'currentUser',
|
||||||
|
|
||||||
initialize: function (container) {
|
initialize: function (container) {
|
||||||
var userFixture = userFixtures.findBy("id", 1);
|
container.register('user:current', User);
|
||||||
|
|
||||||
container.register('user:current', Ember.Object.extend(userFixture));
|
|
||||||
// Todo: remove userFixture
|
|
||||||
// Todo: use model User instead of Ember.Object once model layer exists
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,6 +14,9 @@ var injectCurrentUser = {
|
|||||||
|
|
||||||
initialize: function (container) {
|
initialize: function (container) {
|
||||||
if (container.lookup('user:current')) {
|
if (container.lookup('user:current')) {
|
||||||
|
// @TODO: remove userFixture
|
||||||
|
container.lookup('user:current').setProperties(userFixtures.findBy('id', 1));
|
||||||
|
|
||||||
container.injection('route', 'user', 'user:current');
|
container.injection('route', 'user', 'user:current');
|
||||||
container.injection('controller', 'user', 'user:current');
|
container.injection('controller', 'user', 'user:current');
|
||||||
}
|
}
|
||||||
|
18
core/client/models/base.js
Normal file
18
core/client/models/base.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
function ghostPaths() {
|
||||||
|
var path = window.location.pathname,
|
||||||
|
subdir = path.substr(0, path.search('/ghost/'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
subdir: subdir,
|
||||||
|
apiRoot: subdir + '/ghost/api/v0.1'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var BaseModel = Ember.Object.extend({
|
||||||
|
});
|
||||||
|
|
||||||
|
BaseModel.apiRoot = ghostPaths().apiRoot;
|
||||||
|
BaseModel.subdir = ghostPaths().subdir;
|
||||||
|
|
||||||
|
export default BaseModel;
|
80
core/client/models/user.js
Normal file
80
core/client/models/user.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import BaseModel from 'ghost/models/base';
|
||||||
|
|
||||||
|
var UserModel = BaseModel.extend({
|
||||||
|
url: BaseModel.apiRoot + '/users/me/',
|
||||||
|
|
||||||
|
save: function () {
|
||||||
|
return ic.ajax.request(this.url, {
|
||||||
|
type: 'POST',
|
||||||
|
data: this.getProperties(Ember.keys(this))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
validate: function () {
|
||||||
|
var validationErrors = [];
|
||||||
|
|
||||||
|
if (!validator.isLength(this.get('name'), 0, 150)) {
|
||||||
|
validationErrors.push({message: "Name is too long"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validator.isLength(this.get('bio'), 0, 200)) {
|
||||||
|
validationErrors.push({message: "Bio is too long"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validator.isEmail(this.get('email'))) {
|
||||||
|
validationErrors.push({message: "Please supply a valid email address"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validator.isLength(this.get('location'), 0, 150)) {
|
||||||
|
validationErrors.push({message: "Location is too long"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.get('website').length) {
|
||||||
|
if (!validator.isURL(this.get('website')) ||
|
||||||
|
!validator.isLength(this.get('website'), 0, 2000)) {
|
||||||
|
validationErrors.push({message: "Please use a valid url"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationErrors.length > 0) {
|
||||||
|
this.set('isValid', false);
|
||||||
|
} else {
|
||||||
|
this.set('isValid', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('errors', validationErrors);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
saveNewPassword: function (password) {
|
||||||
|
return ic.ajax.request(BaseModel.subdir + '/ghost/changepw/', {
|
||||||
|
type: 'POST',
|
||||||
|
data: password
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
validatePassword: function (password) {
|
||||||
|
var validationErrors = [];
|
||||||
|
|
||||||
|
if (!validator.equals(password.newpassword, password.ne2password)) {
|
||||||
|
validationErrors.push("Your new passwords do not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validator.isLength(password.newpassword, 8)) {
|
||||||
|
validationErrors.push("Your password is not long enough. It must be at least 8 characters long.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationErrors.length > 0) {
|
||||||
|
this.set('passwordIsValid', false);
|
||||||
|
} else {
|
||||||
|
this.set('passwordIsValid', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('passwordErrors', validationErrors);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UserModel;
|
@ -2,18 +2,16 @@
|
|||||||
<button class="button-back">Back</button>
|
<button class="button-back">Back</button>
|
||||||
<h2 class="title">Your Profile</h2>
|
<h2 class="title">Your Profile</h2>
|
||||||
<section class="page-actions">
|
<section class="page-actions">
|
||||||
<button class="button-save">Save</button>
|
<button class="button-save" {{action 'save'}}>Save</button>
|
||||||
</section>
|
</section>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section class="content no-padding">
|
<section class="content no-padding">
|
||||||
|
|
||||||
<header class="user-profile-header">
|
<header class="user-profile-header">
|
||||||
{{!-- @TODO: once we have asset helper in place we can restore this, right now it errors
|
<img id="user-cover" class="cover-image" {{bind-attr src=cover title=coverTitle}} />
|
||||||
<img id="user-cover" class="cover-image" src="{{#if cover}}{{cover}}{{else}}{{asset "shared/img/user-cover.png"}}{{/if}}" title="{{name}}'s Cover Image"/>
|
|
||||||
--}}
|
|
||||||
|
|
||||||
<a class="edit-cover-image js-modal-cover button" href="#">Change Cover</a>
|
<a class="edit-cover-image js-modal-cover button" {{action 'cover'}}>Change Cover</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<form class="user-profile" novalidate="novalidate">
|
<form class="user-profile" novalidate="novalidate">
|
||||||
@ -21,15 +19,13 @@
|
|||||||
<fieldset class="user-details-top">
|
<fieldset class="user-details-top">
|
||||||
|
|
||||||
<figure class="user-image">
|
<figure class="user-image">
|
||||||
{{!-- @TODO: once we have asset helper in place we can restore this, right now it errors
|
<div id="user-image" class="img" {{bind-attr style=image}} href="#"><span class="hidden">{{name}}'s Picture</span></div>
|
||||||
<div id="user-image" class="img" style="background-image: url({{#if image}}{{image}}{{else}}{{asset "shared/img/user-image.png"}}{{/if}});" href="#"><span class="hidden">{{name}}'s Picture</span></div>
|
<a {{action 'image'}} class="edit-user-image js-modal-image">Edit Picture</a>
|
||||||
--}}
|
|
||||||
<a href="#" class="edit-user-image js-modal-image">Edit Picture</a>
|
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-name" class="hidden">Full Name</label>
|
<label for="user-name" class="hidden">Full Name</label>
|
||||||
<input type="url" value="{{name}}" id="user-name" placeholder="Full Name" autocorrect="off" />
|
{{input value=user.name id="user-name" placeholder="Full Name" autocorrect="off"}}
|
||||||
<p>Use your real name so people can recognise you</p>
|
<p>Use your real name so people can recognise you</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -39,28 +35,28 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for"user-email">Email</label>
|
<label for"user-email">Email</label>
|
||||||
{{input type="email" value=email id="user-email" placeholder="Email Address" autocapitalize="off" autocorrect="off"}}
|
{{input type="email" value=user.email id="user-email" placeholder="Email Address" autocapitalize="off" autocorrect="off"}}
|
||||||
<p>Used for notifications</p>
|
<p>Used for notifications</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-location">Location</label>
|
<label for="user-location">Location</label>
|
||||||
{{input type="text" value=location id="user-location"}}
|
{{input type="text" value=user.location id="user-location"}}
|
||||||
<p>Where in the world do you live?</p>
|
<p>Where in the world do you live?</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-website">Website</label>
|
<label for="user-website">Website</label>
|
||||||
{{input type="text" value=website id="user-website" autocapitalize="off" autocorrect="off"}}
|
{{input type="text" value=user.website id="user-website" autocapitalize="off" autocorrect="off"}}
|
||||||
<p>Have a website or blog other than this one? Link it!</p>
|
<p>Have a website or blog other than this one? Link it!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group bio-container">
|
<div class="form-group bio-container">
|
||||||
<label for="user-bio">Bio</label>
|
<label for="user-bio">Bio</label>
|
||||||
{{textarea id="user-bio" value=bio}}
|
{{textarea id="user-bio" value=user.bio}}
|
||||||
<p>
|
<p>
|
||||||
Write about you, in 200 characters or less.
|
Write about you, in 200 characters or less.
|
||||||
<span class="word-count">0</span>
|
<span class="word-count">{{count-words user.bio}}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -72,20 +68,20 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-password-old">Old Password</label>
|
<label for="user-password-old">Old Password</label>
|
||||||
{{input type="password" id="user-password-old"}}
|
{{input value=password type="password" id="user-password-old"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-password-new">New Password</label>
|
<label for="user-password-new">New Password</label>
|
||||||
{{input type="password" id="user-password-new"}}
|
{{input value=newpassword type="password" id="user-password-new"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user-new-password-verification">Verify Password</label>
|
<label for="user-new-password-verification">Verify Password</label>
|
||||||
{{input type="password" id="user-new-password-verification"}}
|
{{input value=ne2password type="password" id="user-new-password-verification"}}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="button" class="button-delete button-change-password">Change Password</button>
|
<button type="button" class="button-delete button-change-password" {{action 'password'}}>Change Password</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
Loading…
Reference in New Issue
Block a user