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:
Harry Wolff 2014-03-22 22:31:45 -04:00
parent 79a333b480
commit 81eb705a37
8 changed files with 201 additions and 37 deletions

View File

@ -170,7 +170,9 @@ var path = require('path'),
"Ember": true,
"Em": true,
"DS": true,
"$": true
"$": true,
"validator": true,
"ic": true
},
// node environment
node: false,
@ -577,17 +579,17 @@ var path = require('path'),
'bower_components/ember/ember.js',
'bower_components/ember-resolver/dist/ember-resolver.js',
'bower_components/ic-ajax/dist/globals/main.js',
'bower_components/validator-js/validator.js',
'bower_components/codemirror/lib/codemirror.js',
'bower_components/codemirror/addon/mode/overlay.js',
'bower_components/codemirror/mode/markdown/markdown.js',
'bower_components/codemirror/mode/gfm/gfm.js',
'bower_components/showdown/src/showdown.js',
'bower_components/moment/moment.js',
'core/clientold/assets/lib/showdown/extensions/ghostdown.js',
'core/shared/lib/showdown/extensions/typography.js',
'core/shared/lib/showdown/extensions/github.js',
'bower_components/moment/moment.js'
'core/shared/lib/showdown/extensions/github.js'
]
}
},

View 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;

View File

@ -1,4 +1,3 @@
/*global ic */
import postFixtures from 'ghost/fixtures/posts';
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/2', post(2, 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;

View File

@ -5,16 +5,16 @@ var users = [
"name": "some-user",
"slug": "some-user",
"email": "some@email.com",
"image": null,
"cover": null,
"bio": "",
"image": undefined,
"cover": undefined,
"bio": "Example bio",
"website": "",
"location": "",
"accessibility": null,
"location": "Imaginationland",
"accessibility": undefined,
"status": "active",
"language": "en_US",
"meta_title": null,
"meta_description": null,
"meta_title": undefined,
"meta_description": undefined,
"created_at": "2014-02-15T20:02:25.000Z",
"updated_at": "2014-03-11T14:06:43.000Z"
}

View File

@ -1,14 +1,11 @@
import User from 'ghost/models/user';
import userFixtures from 'ghost/fixtures/users';
var currentUser = {
name: 'currentUser',
initialize: function (container) {
var userFixture = userFixtures.findBy("id", 1);
container.register('user:current', Ember.Object.extend(userFixture));
// Todo: remove userFixture
// Todo: use model User instead of Ember.Object once model layer exists
container.register('user:current', User);
}
};
@ -17,6 +14,9 @@ var injectCurrentUser = {
initialize: function (container) {
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('controller', 'user', 'user:current');
}

View 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;

View 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;

View File

@ -2,18 +2,16 @@
<button class="button-back">Back</button>
<h2 class="title">Your Profile</h2>
<section class="page-actions">
<button class="button-save">Save</button>
<button class="button-save" {{action 'save'}}>Save</button>
</section>
</header>
<section class="content no-padding">
<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" src="{{#if cover}}{{cover}}{{else}}{{asset "shared/img/user-cover.png"}}{{/if}}" title="{{name}}'s Cover Image"/>
--}}
<img id="user-cover" class="cover-image" {{bind-attr src=cover title=coverTitle}} />
<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>
<form class="user-profile" novalidate="novalidate">
@ -21,15 +19,13 @@
<fieldset class="user-details-top">
<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" 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 href="#" class="edit-user-image js-modal-image">Edit Picture</a>
<div id="user-image" class="img" {{bind-attr style=image}} href="#"><span class="hidden">{{name}}'s Picture</span></div>
<a {{action 'image'}} class="edit-user-image js-modal-image">Edit Picture</a>
</figure>
<div class="form-group">
<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>
</div>
@ -39,28 +35,28 @@
<div class="form-group">
<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>
</div>
<div class="form-group">
<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>
</div>
<div class="form-group">
<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>
</div>
<div class="form-group bio-container">
<label for="user-bio">Bio</label>
{{textarea id="user-bio" value=bio}}
{{textarea id="user-bio" value=user.bio}}
<p>
Write about you, in 200 characters or less.
<span class="word-count">0</span>
<span class="word-count">{{count-words user.bio}}</span>
</p>
</div>
@ -72,20 +68,20 @@
<div class="form-group">
<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 class="form-group">
<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 class="form-group">
<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 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>
</fieldset>