mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Ember Data with Posts
Ref #2699 - Introduce ember data dependency - Add loadInitializers and refactor most initializers into one combined - Add Post ember data model - Refactor generateSlug to use title of post and ghostPaths - Refactor post controller to not reference model.property everywhere - Use RESTAdapter for posts, users and tags - Setup author and tag relations in Post model - Fix broken API calls by adding CSRF header - Add initiaizer for csrf value - Use actual User model for current user initializer - Add action for setting featured post, test with actual api call - Fix the sending of UUID's up to the server - Refactor current-user to use ember-data store - If a user is preloaded in the application, use pushPayload to put it in the store - Do a lookup on the store to get an actual User model for injection - Fix posts/post controllerName in route/new.js - Alter signup process to push user into ember data store
This commit is contained in:
parent
97011e5eca
commit
5abeadf80d
25 changed files with 257 additions and 206 deletions
|
@ -548,8 +548,10 @@ var path = require('path'),
|
||||||
'bower_components/jquery/dist/jquery.js',
|
'bower_components/jquery/dist/jquery.js',
|
||||||
'bower_components/handlebars/handlebars.js',
|
'bower_components/handlebars/handlebars.js',
|
||||||
'bower_components/ember/ember.js',
|
'bower_components/ember/ember.js',
|
||||||
|
'bower_components/ember-data/ember-data.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/ember-load-initializers/ember-load-initializers.js',
|
||||||
'bower_components/validator-js/validator.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',
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
"codemirror": "4.0.1",
|
"codemirror": "4.0.1",
|
||||||
"Countable": "2.0.2",
|
"Countable": "2.0.2",
|
||||||
"ember": "1.5.0",
|
"ember": "1.5.0",
|
||||||
|
"ember-data": "~1.0.0-beta.7",
|
||||||
|
"ember-load-initializers": "git://github.com/stefanpenner/ember-load-initializers.git#0.0.1",
|
||||||
"ember-resolver": "git://github.com/stefanpenner/ember-jj-abrams-resolver.git#181251821cf513bb58d3e192faa13245a816f75e",
|
"ember-resolver": "git://github.com/stefanpenner/ember-jj-abrams-resolver.git#181251821cf513bb58d3e192faa13245a816f75e",
|
||||||
"fastclick": "1.0.0",
|
"fastclick": "1.0.0",
|
||||||
"ghost-ui": "0.1.3",
|
"ghost-ui": "0.1.3",
|
||||||
|
|
22
core/client/adapters/application.js
Normal file
22
core/client/adapters/application.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import ghostPaths from 'ghost/utils/ghost-paths';
|
||||||
|
|
||||||
|
// export default DS.FixtureAdapter.extend({});
|
||||||
|
|
||||||
|
export default DS.RESTAdapter.extend({
|
||||||
|
host: window.location.origin,
|
||||||
|
namespace: ghostPaths().apiRoot.slice(1),
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': $('meta[name="csrf-param"]').attr('content')
|
||||||
|
},
|
||||||
|
|
||||||
|
buildURL: function (type, id) {
|
||||||
|
// Ensure trailing slashes
|
||||||
|
var url = this._super(type, id);
|
||||||
|
|
||||||
|
if (url.slice(-1) !== '/') {
|
||||||
|
url += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,13 +1,11 @@
|
||||||
import Resolver from 'ember/resolver';
|
import Resolver from 'ember/resolver';
|
||||||
import initFixtures from 'ghost/fixtures/init';
|
import initFixtures from 'ghost/fixtures/init';
|
||||||
import injectCurrentUser from 'ghost/initializers/current-user';
|
import loadInitializers from 'ember/load-initializers';
|
||||||
import injectCsrf from 'ghost/initializers/csrf';
|
|
||||||
import {registerNotifications, injectNotifications} from 'ghost/initializers/notifications';
|
|
||||||
import registerTrailingLocationHistory from 'ghost/initializers/trailing-history';
|
|
||||||
import injectGhostPaths from 'ghost/initializers/ghost-paths';
|
|
||||||
import 'ghost/utils/link-view';
|
import 'ghost/utils/link-view';
|
||||||
import 'ghost/utils/text-field';
|
import 'ghost/utils/text-field';
|
||||||
|
|
||||||
|
Ember.MODEL_FACTORY_INJECTIONS = true;
|
||||||
|
|
||||||
var App = Ember.Application.extend({
|
var App = Ember.Application.extend({
|
||||||
/**
|
/**
|
||||||
* These are debugging flags, they are useful during development
|
* These are debugging flags, they are useful during development
|
||||||
|
@ -23,11 +21,6 @@ var App = Ember.Application.extend({
|
||||||
|
|
||||||
initFixtures();
|
initFixtures();
|
||||||
|
|
||||||
App.initializer(injectCurrentUser);
|
loadInitializers(App, 'ghost');
|
||||||
App.initializer(injectCsrf);
|
|
||||||
App.initializer(injectGhostPaths);
|
|
||||||
App.initializer(registerNotifications);
|
|
||||||
App.initializer(injectNotifications);
|
|
||||||
App.initializer(registerTrailingLocationHistory);
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
@ -13,49 +13,26 @@ var PostController = Ember.ObjectController.extend({
|
||||||
isDraft: equal('status', 'draft'),
|
isDraft: equal('status', 'draft'),
|
||||||
willPublish: Ember.computed.oneWay('isPublished'),
|
willPublish: Ember.computed.oneWay('isPublished'),
|
||||||
isStaticPage: function (key, val) {
|
isStaticPage: function (key, val) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
if (arguments.length > 1) {
|
if (arguments.length > 1) {
|
||||||
this.set('model.page', val ? 1 : 0);
|
this.set('page', val ? 1 : 0);
|
||||||
this.get('model').save('page').then(function () {
|
|
||||||
this.notifications.showSuccess('Succesfully converted ' + (val ? 'to static page' : 'to post'));
|
return this.get('model').save().then(function () {
|
||||||
|
self.notifications.showSuccess('Succesfully converted to ' + (val ? 'static page' : 'post'));
|
||||||
|
|
||||||
|
return !!self.get('page');
|
||||||
}, this.notifications.showErrors);
|
}, this.notifications.showErrors);
|
||||||
}
|
}
|
||||||
return !!this.get('model.page');
|
|
||||||
}.property('model.page'),
|
|
||||||
|
|
||||||
isOnServer: function () {
|
return !!this.get('page');
|
||||||
return this.get('model.id') !== undefined;
|
}.property('page'),
|
||||||
}.property('model.id'),
|
|
||||||
|
|
||||||
newSlugBinding: Ember.Binding.oneWay('model.slug'),
|
newSlugBinding: Ember.computed.oneWay('slug'),
|
||||||
slugPlaceholder: null,
|
|
||||||
// Requests a new slug when the title was changed
|
|
||||||
updateSlugPlaceholder: function () {
|
|
||||||
var model,
|
|
||||||
self = this,
|
|
||||||
title = this.get('title');
|
|
||||||
|
|
||||||
// If there's a title present we want to
|
slugPlaceholder: function () {
|
||||||
// validate it against existing slugs in the db
|
return this.get('model').generateSlug();
|
||||||
// and then update the placeholder value.
|
}.property('title'),
|
||||||
if (title) {
|
|
||||||
model = self.get('model');
|
|
||||||
model.generateSlug().then(function (slug) {
|
|
||||||
self.set('slugPlaceholder', slug);
|
|
||||||
}, function () {
|
|
||||||
self.notifications.showWarn('Unable to generate a slug for "' + title + '"');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// If there's no title set placeholder to blank
|
|
||||||
// and don't make an ajax request to server
|
|
||||||
// for a proper slug (as there won't be any).
|
|
||||||
self.set('slugPlaceholder', '');
|
|
||||||
}
|
|
||||||
}.observes('model.title'),
|
|
||||||
|
|
||||||
publishedAt: null,
|
|
||||||
publishedAtChanged: function () {
|
|
||||||
this.set('publishedAt', formatDate(this.get('model.published_at')));
|
|
||||||
}.observes('model.published_at'),
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save: function () {
|
save: function () {
|
||||||
|
@ -78,6 +55,11 @@ var PostController = Ember.ObjectController.extend({
|
||||||
console.warn('Received invalid save type; ignoring.');
|
console.warn('Received invalid save type; ignoring.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
toggleFeatured: function () {
|
||||||
|
this.set('featured', !this.get('featured'));
|
||||||
|
|
||||||
|
this.get('model').save();
|
||||||
|
},
|
||||||
editSettings: function () {
|
editSettings: function () {
|
||||||
var isEditing = this.toggleProperty('isEditingSettings');
|
var isEditing = this.toggleProperty('isEditingSettings');
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
|
@ -91,9 +73,10 @@ var PostController = Ember.ObjectController.extend({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSlug: function () {
|
updateSlug: function () {
|
||||||
var newSlug = this.get('newSlug'),
|
var newSlug = this.get('newSlug'),
|
||||||
slug = this.get('model.slug'),
|
slug = this.get('slug'),
|
||||||
placeholder = this.get('slugPlaceholder'),
|
placeholder = this.get('slugPlaceholder'),
|
||||||
self = this;
|
self = this;
|
||||||
|
|
||||||
|
@ -110,17 +93,17 @@ var PostController = Ember.ObjectController.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
//Validation complete
|
//Validation complete
|
||||||
this.set('model.slug', newSlug);
|
this.set('slug', newSlug);
|
||||||
|
|
||||||
// If the model doesn't currently
|
// If the model doesn't currently
|
||||||
// exist on the server
|
// exist on the server
|
||||||
// then just update the model's value
|
// then just update the model's value
|
||||||
if (!this.get('isOnServer')) {
|
if (!this.get('isNew')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get('model').save('slug').then(function () {
|
this.get('model').save().then(function () {
|
||||||
self.notifications.showSuccess('Permalink successfully changed to <strong>' + this.get('model.slug') + '</strong>.');
|
self.notifications.showSuccess('Permalink successfully changed to <strong>' + this.get('slug') + '</strong>.');
|
||||||
}, this.notifications.showErrors);
|
}, this.notifications.showErrors);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -182,20 +165,20 @@ var PostController = Ember.ObjectController.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
//Validation complete
|
//Validation complete
|
||||||
this.set('model.published_at', newPubDateMoment.toDate());
|
this.set('published_at', newPubDateMoment.toDate());
|
||||||
|
|
||||||
// If the model doesn't currently
|
// If the model doesn't currently
|
||||||
// exist on the server
|
// exist on the server
|
||||||
// then just update the model's value
|
// then just update the model's value
|
||||||
if (!this.get('isOnServer')) {
|
if (!this.get('isNew')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get('model').save('published_at').then(function () {
|
this.get('model').save().then(function () {
|
||||||
this.notifications.showSuccess('Publish date successfully changed to <strong>' + this.get('publishedAt') + '</strong>.');
|
this.notifications.showSuccess('Publish date successfully changed to <strong>' + this.get('publishedAt') + '</strong>.');
|
||||||
}, this.notifications.showErrors);
|
}, this.notifications.showErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PostController;
|
export default PostController;
|
||||||
|
|
11
core/client/initializers/csrf-token.js
Normal file
11
core/client/initializers/csrf-token.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export default {
|
||||||
|
name: 'csrf-token',
|
||||||
|
|
||||||
|
initialize: function (container) {
|
||||||
|
container.register('csrf:token', $('meta[name="csrf-param"]').attr('content'), { instantiate: false });
|
||||||
|
|
||||||
|
container.injection('route', 'csrf', 'csrf:token');
|
||||||
|
container.injection('model', 'csrf', 'csrf:token');
|
||||||
|
container.injection('controller', 'csrf', 'csrf:token');
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,14 +1,34 @@
|
||||||
import User from 'ghost/models/user';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'currentUser',
|
name: 'currentUser',
|
||||||
|
after: 'store',
|
||||||
|
|
||||||
initialize: function (container, application) {
|
initialize: function (container, application) {
|
||||||
var user = User.create(application.get('user') || {});
|
var store = container.lookup('store:main'),
|
||||||
|
preloadedUser = application.get('user');
|
||||||
|
|
||||||
container.register('user:current', user, { instantiate: false });
|
// If we don't have a user, don't do the injection
|
||||||
|
if (!preloadedUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
container.injection('route', 'user', 'user:current');
|
// Push the preloaded user into the data store
|
||||||
container.injection('controller', 'user', 'user:current');
|
store.pushPayload({
|
||||||
|
users: [preloadedUser]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signal to wait until the user is loaded before continuing.
|
||||||
|
application.deferReadiness();
|
||||||
|
|
||||||
|
// Find the user (which should be fast since we just preloaded it in the store)
|
||||||
|
store.find('user', preloadedUser.id).then(function (user) {
|
||||||
|
// Register the value for injection
|
||||||
|
container.register('user:current', user, { instantiate: false });
|
||||||
|
|
||||||
|
// Inject into the routes and controllers as the user property.
|
||||||
|
container.injection('route', 'user', 'user:current');
|
||||||
|
container.injection('controller', 'user', 'user:current');
|
||||||
|
|
||||||
|
application.advanceReadiness();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -2,6 +2,7 @@ import ghostPaths from 'ghost/utils/ghost-paths';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ghost-paths',
|
name: 'ghost-paths',
|
||||||
|
after: 'store',
|
||||||
|
|
||||||
initialize: function (container) {
|
initialize: function (container) {
|
||||||
container.register('ghost:paths', ghostPaths(), {instantiate: false});
|
container.register('ghost:paths', ghostPaths(), {instantiate: false});
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
import Notifications from 'ghost/utils/notifications';
|
import Notifications from 'ghost/utils/notifications';
|
||||||
|
|
||||||
var registerNotifications = {
|
export default {
|
||||||
name: 'registerNotifications',
|
|
||||||
|
|
||||||
initialize: function (container, application) {
|
|
||||||
application.register('notifications:main', Notifications);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var injectNotifications = {
|
|
||||||
name: 'injectNotifications',
|
name: 'injectNotifications',
|
||||||
|
|
||||||
initialize: function (container, application) {
|
initialize: function (container, application) {
|
||||||
|
application.register('notifications:main', Notifications);
|
||||||
|
|
||||||
application.inject('controller', 'notifications', 'notifications:main');
|
application.inject('controller', 'notifications', 'notifications:main');
|
||||||
application.inject('component', 'notifications', 'notifications:main');
|
application.inject('component', 'notifications', 'notifications:main');
|
||||||
application.inject('route', 'notifications', 'notifications:main');
|
application.inject('route', 'notifications', 'notifications:main');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export {registerNotifications, injectNotifications};
|
|
|
@ -1,56 +1,45 @@
|
||||||
import BaseModel from 'ghost/models/base';
|
var Post = DS.Model.extend({
|
||||||
|
uuid: DS.attr('string'),
|
||||||
var PostModel = BaseModel.extend({
|
title: DS.attr('string'),
|
||||||
url: BaseModel.apiRoot + '/posts/',
|
slug: DS.attr('string'),
|
||||||
|
markdown: DS.attr('string'),
|
||||||
|
html: DS.attr('string'),
|
||||||
|
image: DS.attr('string'),
|
||||||
|
featured: DS.attr('boolean'),
|
||||||
|
page: DS.attr('boolean'),
|
||||||
|
status: DS.attr('string'),
|
||||||
|
language: DS.attr('string'),
|
||||||
|
meta_title: DS.attr('string'),
|
||||||
|
meta_description: DS.attr('string'),
|
||||||
|
author: DS.belongsTo('user', { async: true }),
|
||||||
|
created_at: DS.attr('date'),
|
||||||
|
created_by: DS.belongsTo('user', { async: true }),
|
||||||
|
updated_at: DS.attr('date'),
|
||||||
|
updated_by: DS.belongsTo('user', { async: true }),
|
||||||
|
published_at: DS.attr('date'),
|
||||||
|
published_by: DS.belongsTo('user', { async: true }),
|
||||||
|
tags: DS.hasMany('tag', { async: true }),
|
||||||
|
|
||||||
generateSlug: function () {
|
generateSlug: function () {
|
||||||
// @TODO Make this request use this.get('title') once we're an actual user
|
var title = this.get('title'),
|
||||||
var url = this.get('url') + 'slug/' + encodeURIComponent('test title') + '/';
|
url = this.get('ghostPaths').apiUrl('posts', 'slug', encodeURIComponent(title));
|
||||||
|
|
||||||
return ic.ajax.request(url, {
|
return ic.ajax.request(url, {
|
||||||
type: 'GET'
|
type: 'GET'
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
save: function (properties) {
|
|
||||||
var url = this.url,
|
|
||||||
self = this,
|
|
||||||
type,
|
|
||||||
validationErrors = this.validate();
|
|
||||||
|
|
||||||
if (validationErrors.length) {
|
|
||||||
return Ember.RSVP.Promise(function (resolve, reject) {
|
|
||||||
return reject(validationErrors);
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
//If specific properties are being saved,
|
|
||||||
//this is an edit. Otherwise, it's an add.
|
|
||||||
if (properties && properties.length > 0) {
|
|
||||||
type = 'PUT';
|
|
||||||
url += this.get('id');
|
|
||||||
} else {
|
|
||||||
type = 'POST';
|
|
||||||
properties = Ember.keys(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ic.ajax.request(url, {
|
|
||||||
type: type,
|
|
||||||
data: this.getProperties(properties)
|
|
||||||
}).then(function (model) {
|
|
||||||
return self.setProperties(model);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
validate: function () {
|
|
||||||
|
validationErrors: function () {
|
||||||
var validationErrors = [];
|
var validationErrors = [];
|
||||||
|
|
||||||
if (!(this.get('title') && this.get('title').length)) {
|
if (!this.get('title.length')) {
|
||||||
validationErrors.push({
|
validationErrors.push({
|
||||||
message: "You must specify a title for the post."
|
message: "You must specify a title for the post."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return validationErrors;
|
return validationErrors;
|
||||||
}
|
}.property('title')
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PostModel;
|
export default Post;
|
13
core/client/models/tag.js
Normal file
13
core/client/models/tag.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export default DS.Model.extend({
|
||||||
|
uuid: DS.attr('string'),
|
||||||
|
name: DS.attr('string'),
|
||||||
|
slug: DS.attr('string'),
|
||||||
|
description: DS.attr('string'),
|
||||||
|
parent_id: DS.attr('number'),
|
||||||
|
meta_title: DS.attr('string'),
|
||||||
|
meta_description: DS.attr('string'),
|
||||||
|
created_at: DS.attr('date'),
|
||||||
|
created_by: DS.attr('number'),
|
||||||
|
updated_at: DS.attr('date'),
|
||||||
|
updated_by: DS.attr('number'),
|
||||||
|
});
|
|
@ -1,24 +1,28 @@
|
||||||
import BaseModel from 'ghost/models/base';
|
var User = DS.Model.extend({
|
||||||
|
uuid: DS.attr('string'),
|
||||||
var UserModel = BaseModel.extend({
|
name: DS.attr('string'),
|
||||||
id: null,
|
slug: DS.attr('string'),
|
||||||
name: null,
|
password: DS.attr('string'),
|
||||||
image: null,
|
email: DS.attr('string'),
|
||||||
|
image: DS.attr('string'),
|
||||||
|
cover: DS.attr('string'),
|
||||||
|
bio: DS.attr('string'),
|
||||||
|
website: DS.attr('string'),
|
||||||
|
location: DS.attr('string'),
|
||||||
|
accessibility: DS.attr('string'),
|
||||||
|
status: DS.attr('string'),
|
||||||
|
language: DS.attr('string'),
|
||||||
|
meta_title: DS.attr('string'),
|
||||||
|
meta_description: DS.attr('string'),
|
||||||
|
last_login: DS.attr('date'),
|
||||||
|
created_at: DS.attr('date'),
|
||||||
|
created_by: DS.attr('number'),
|
||||||
|
updated_at: DS.attr('date'),
|
||||||
|
updated_by: DS.attr('number'),
|
||||||
|
|
||||||
isSignedIn: Ember.computed.bool('id'),
|
isSignedIn: Ember.computed.bool('id'),
|
||||||
|
|
||||||
url: BaseModel.apiRoot + '/users/me/',
|
validationErrors: function () {
|
||||||
forgottenUrl: BaseModel.apiRoot + '/forgotten/',
|
|
||||||
resetUrl: BaseModel.apiRoot + '/reset/',
|
|
||||||
|
|
||||||
save: function () {
|
|
||||||
return ic.ajax.request(this.url, {
|
|
||||||
type: 'POST',
|
|
||||||
data: this.getProperties(Ember.keys(this))
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
validate: function () {
|
|
||||||
var validationErrors = [];
|
var validationErrors = [];
|
||||||
|
|
||||||
if (!validator.isLength(this.get('name'), 0, 150)) {
|
if (!validator.isLength(this.get('name'), 0, 150)) {
|
||||||
|
@ -44,25 +48,20 @@ var UserModel = BaseModel.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validationErrors.length > 0) {
|
return validationErrors;
|
||||||
this.set('isValid', false);
|
}.property('name', 'bio', 'email', 'location', 'website'),
|
||||||
} else {
|
|
||||||
this.set('isValid', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('errors', validationErrors);
|
isValid: Ember.computed.empty('validationErrors.[]'),
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
saveNewPassword: function (password) {
|
saveNewPassword: function (password) {
|
||||||
return ic.ajax.request(BaseModel.subdir + '/ghost/changepw/', {
|
var url = this.get('ghostPaths').adminUrl('changepw');
|
||||||
|
return ic.ajax.request(url, {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: password
|
data: password
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
validatePassword: function (password) {
|
passwordValidationErrors: function (password) {
|
||||||
var validationErrors = [];
|
var validationErrors = [];
|
||||||
|
|
||||||
if (!validator.equals(password.newPassword, password.ne2Password)) {
|
if (!validator.equals(password.newPassword, password.ne2Password)) {
|
||||||
|
@ -73,24 +72,17 @@ var UserModel = BaseModel.extend({
|
||||||
validationErrors.push("Your password is not long enough. It must be at least 8 characters long.");
|
validationErrors.push("Your password is not long enough. It must be at least 8 characters long.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validationErrors.length > 0) {
|
return validationErrors;
|
||||||
this.set('passwordIsValid', false);
|
|
||||||
} else {
|
|
||||||
this.set('passwordIsValid', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('passwordErrors', validationErrors);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchForgottenPasswordFor: function (email) {
|
fetchForgottenPasswordFor: function (email) {
|
||||||
var self = this;
|
var forgottenUrl = this.get('ghostPaths').apiUrl('forgotten');
|
||||||
|
|
||||||
return new Ember.RSVP.Promise(function (resolve, reject) {
|
return new Ember.RSVP.Promise(function (resolve, reject) {
|
||||||
if (!validator.isEmail(email)) {
|
if (!validator.isEmail(email)) {
|
||||||
reject(new Error('Please enter a correct email address.'));
|
reject(new Error('Please enter a correct email address.'));
|
||||||
} else {
|
} else {
|
||||||
resolve(ic.ajax.request(self.forgottenUrl, {
|
resolve(ic.ajax.request(forgottenUrl, {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
// @TODO Find a more proper way to do this.
|
// @TODO Find a more proper way to do this.
|
||||||
|
@ -105,12 +97,14 @@ var UserModel = BaseModel.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
resetPassword: function (passwords, token) {
|
resetPassword: function (passwords, token) {
|
||||||
var self = this;
|
var self = this,
|
||||||
|
resetUrl = this.get('ghostPaths').apiUrl('reset');
|
||||||
|
|
||||||
return new Ember.RSVP.Promise(function (resolve, reject) {
|
return new Ember.RSVP.Promise(function (resolve, reject) {
|
||||||
if (!self.validatePassword(passwords).get('passwordIsValid')) {
|
if (!self.validatePassword(passwords).get('passwordIsValid')) {
|
||||||
reject(new Error('Errors found! ' + JSON.stringify(self.get('passwordErrors'))));
|
reject(new Error('Errors found! ' + JSON.stringify(self.get('passwordErrors'))));
|
||||||
} else {
|
} else {
|
||||||
resolve(ic.ajax.request(self.resetUrl, {
|
resolve(ic.ajax.request(resetUrl, {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
// @TODO: find a more proper way to do this.
|
// @TODO: find a more proper way to do this.
|
||||||
|
@ -127,4 +121,4 @@ var UserModel = BaseModel.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default UserModel;
|
export default User;
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
var ApplicationRoute = Ember.Route.extend({
|
var ApplicationRoute = Ember.Route.extend({
|
||||||
actions: {
|
actions: {
|
||||||
signedIn: function (user) {
|
signedIn: function (user) {
|
||||||
this.container.lookup('user:current').setProperties(user);
|
// Update the user on all routes and controllers
|
||||||
|
this.container.unregister('user:current');
|
||||||
|
this.container.register('user:current', user, { instantiate: false });
|
||||||
|
|
||||||
|
this.container.injection('route', 'user', 'user:current');
|
||||||
|
this.container.injection('controller', 'user', 'user:current');
|
||||||
|
|
||||||
|
this.set('user', user);
|
||||||
|
this.set('controller.user', user);
|
||||||
},
|
},
|
||||||
|
|
||||||
signedOut: function () {
|
signedOut: function () {
|
||||||
this.container.lookup('user:current').setProperties({
|
// Nullify the user on all routes and controllers
|
||||||
id: null,
|
this.container.unregister('user:current');
|
||||||
name: null,
|
this.container.register('user:current', null, { instantiate: false });
|
||||||
image: null
|
|
||||||
});
|
this.container.injection('route', 'user', 'user:current');
|
||||||
|
this.container.injection('controller', 'user', 'user:current');
|
||||||
|
|
||||||
|
this.set('user', null);
|
||||||
|
this.set('controller.user', null);
|
||||||
},
|
},
|
||||||
|
|
||||||
openModal: function (modalName, model) {
|
openModal: function (modalName, model) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
var AuthenticatedRoute = Ember.Route.extend({
|
var AuthenticatedRoute = Ember.Route.extend({
|
||||||
beforeModel: function () {
|
beforeModel: function () {
|
||||||
if (!this.get('user.isSignedIn')) {
|
var user = this.container.lookup('user:current');
|
||||||
|
|
||||||
|
if (!user || !user.get('isSignedIn')) {
|
||||||
this.notifications.showError('Please sign in');
|
this.notifications.showError('Please sign in');
|
||||||
|
|
||||||
this.transitionTo('signin');
|
this.transitionTo('signin');
|
||||||
|
@ -9,7 +11,7 @@ var AuthenticatedRoute = Ember.Route.extend({
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
error: function (error) {
|
error: function (error) {
|
||||||
if (error.jqXHR.status === 401) {
|
if (error.jqXHR && error.jqXHR.status === 401) {
|
||||||
this.transitionTo('signin');
|
this.transitionTo('signin');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import ajax from 'ghost/utils/ajax';
|
|
||||||
import styleBody from 'ghost/mixins/style-body';
|
import styleBody from 'ghost/mixins/style-body';
|
||||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||||
import Post from 'ghost/models/post';
|
|
||||||
var EditorRoute = AuthenticatedRoute.extend(styleBody, {
|
var EditorRoute = AuthenticatedRoute.extend(styleBody, {
|
||||||
classNames: ['editor'],
|
classNames: ['editor'],
|
||||||
controllerName: 'posts.post',
|
controllerName: 'posts.post',
|
||||||
model: function (params) {
|
model: function (params) {
|
||||||
return ajax('/ghost/api/v0.1/posts/' + params.post_id).then(function (post) {
|
return this.store.find('post', params.post_id);
|
||||||
return Post.create(post);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||||
import styleBody from 'ghost/mixins/style-body';
|
import styleBody from 'ghost/mixins/style-body';
|
||||||
import Post from 'ghost/models/post';
|
|
||||||
|
|
||||||
var NewRoute = AuthenticatedRoute.extend(styleBody, {
|
var NewRoute = AuthenticatedRoute.extend(styleBody, {
|
||||||
controllerName: 'posts/post',
|
controllerName: 'posts.post',
|
||||||
classNames: ['editor'],
|
classNames: ['editor'],
|
||||||
|
|
||||||
renderTemplate: function () {
|
renderTemplate: function () {
|
||||||
|
@ -11,7 +10,9 @@ var NewRoute = AuthenticatedRoute.extend(styleBody, {
|
||||||
},
|
},
|
||||||
|
|
||||||
model: function () {
|
model: function () {
|
||||||
return Post.create();
|
return this.store.createRecord('post', {
|
||||||
|
title: 'New Post'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
import ajax from 'ghost/utils/ajax';
|
|
||||||
import styleBody from 'ghost/mixins/style-body';
|
import styleBody from 'ghost/mixins/style-body';
|
||||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||||
import Post from 'ghost/models/post';
|
|
||||||
|
|
||||||
var PostsRoute = AuthenticatedRoute.extend(styleBody, {
|
var PostsRoute = AuthenticatedRoute.extend(styleBody, {
|
||||||
classNames: ['manage'],
|
classNames: ['manage'],
|
||||||
|
|
||||||
model: function () {
|
model: function () {
|
||||||
return ajax('/ghost/api/v0.1/posts').then(function (response) {
|
return this.store.find('post', { status: 'all', staticPages: 'all' });
|
||||||
return response.posts.map(function (post) {
|
|
||||||
return Post.create(post);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
/*global ajax */
|
export default Ember.Route.extend({
|
||||||
import Post from 'ghost/models/post';
|
|
||||||
var PostsPostRoute = Ember.Route.extend({
|
|
||||||
model: function (params) {
|
model: function (params) {
|
||||||
return ajax('/ghost/api/v0.1/posts/' + params.post_id).then(function (post) {
|
return this.store.find('post', params.post_id);
|
||||||
return Post.create(post);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PostsPostRoute;
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ var SigninRoute = Ember.Route.extend(styleBody, {
|
||||||
if (!isEmpty(data.email) && !isEmpty(data.password)) {
|
if (!isEmpty(data.email) && !isEmpty(data.password)) {
|
||||||
|
|
||||||
ajax({
|
ajax({
|
||||||
url: '/ghost/signin/',
|
url: this.get('ghostPaths').adminUrl('signin'),
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"X-CSRF-Token": this.get('csrf')
|
"X-CSRF-Token": this.get('csrf')
|
||||||
|
@ -23,11 +23,12 @@ var SigninRoute = Ember.Route.extend(styleBody, {
|
||||||
data: data
|
data: data
|
||||||
}).then(
|
}).then(
|
||||||
function (response) {
|
function (response) {
|
||||||
self.send('signedIn', response.userData);
|
self.store.pushPayload({ users: [response.userData]});
|
||||||
|
self.store.find('user', response.userData.id).then(function (user) {
|
||||||
self.notifications.clear();
|
self.send('signedIn', user);
|
||||||
|
self.notifications.clear();
|
||||||
self.transitionTo('posts');
|
self.transitionTo('posts');
|
||||||
|
});
|
||||||
}, function (resp) {
|
}, function (resp) {
|
||||||
// This path is ridiculous, should be a helper in notifications; e.g. notifications.showAPIError
|
// This path is ridiculous, should be a helper in notifications; e.g. notifications.showAPIError
|
||||||
self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
|
self.notifications.showAPIError(resp, 'There was a problem logging in, please try again.');
|
||||||
|
|
|
@ -26,11 +26,12 @@ var SignupRoute = Ember.Route.extend(styleBody, {
|
||||||
data: data
|
data: data
|
||||||
}).then(function (resp) {
|
}).then(function (resp) {
|
||||||
if (resp && resp.userData) {
|
if (resp && resp.userData) {
|
||||||
self.send('signedIn', resp.userData);
|
self.store.pushPayload({ users: [resp.userData]});
|
||||||
|
self.store.find('user', resp.userData.id).then(function (user) {
|
||||||
self.notifications.clear();
|
self.send('signedIn', user);
|
||||||
|
self.notifications.clear();
|
||||||
self.transitionTo('posts');
|
self.transitionTo('posts');
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
self.transitionTo('signin');
|
self.transitionTo('signin');
|
||||||
}
|
}
|
||||||
|
|
16
core/client/serializers/application.js
Normal file
16
core/client/serializers/application.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export default DS.RESTSerializer.extend({
|
||||||
|
serializeIntoHash: function (hash, type, record, options) {
|
||||||
|
// Our API expects an id on the posted object
|
||||||
|
options = options || {};
|
||||||
|
options.includeId = true;
|
||||||
|
|
||||||
|
// We have a plural root in the API
|
||||||
|
var root = Ember.String.pluralize(type.typeKey),
|
||||||
|
data = this.serialize(record, options);
|
||||||
|
|
||||||
|
// Don't ever pass uuid's
|
||||||
|
delete data.uuid;
|
||||||
|
|
||||||
|
hash[root] = [data];
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
<header class="floatingheader">
|
<header class="floatingheader">
|
||||||
<button class="button-back" href="#">Back</button>
|
<button class="button-back" href="#">Back</button>
|
||||||
{{!-- @TODO: add back title updates depending on featured state --}}
|
{{!-- @TODO: add back title updates depending on featured state --}}
|
||||||
<a {{bind-attr class="featured:featured:unfeatured"}} href="#" title="Feature this post">
|
<a {{bind-attr class="featured:featured:unfeatured"}} href="#" title="Feature this post" {{action "toggleFeatured"}}>
|
||||||
<span class="hidden">Star</span>
|
<span class="hidden">Star</span>
|
||||||
</a>
|
</a>
|
||||||
<small>
|
<small>
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
<nav>
|
<nav>
|
||||||
<section id="entry-tags" href="#" class="left">
|
<section id="entry-tags" href="#" class="left">
|
||||||
<label class="tag-label" for="tags" title="Tags"><span class="hidden">Tags</span></label>
|
<label class="tag-label" for="tags" title="Tags"><span class="hidden">Tags</span></label>
|
||||||
<div class="tags"></div>
|
<div class="tags">
|
||||||
|
{{#each tags}}
|
||||||
|
<span class="tag">{{name}}</span>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
<input type="hidden" class="tags-holder" id="tags-holder">
|
<input type="hidden" class="tags-holder" id="tags-holder">
|
||||||
<input class="tag-input" id="tags" type="text" data-input-behaviour="tag" />
|
<input class="tag-input" id="tags" type="text" data-input-behaviour="tag" />
|
||||||
<ul class="suggestions overlay"></ul>
|
<ul class="suggestions overlay"></ul>
|
||||||
|
|
|
@ -43,8 +43,14 @@ function setSelected(list, name) {
|
||||||
adminControllers = {
|
adminControllers = {
|
||||||
'index': function (req, res) {
|
'index': function (req, res) {
|
||||||
/*jslint unparam:true*/
|
/*jslint unparam:true*/
|
||||||
|
var userData;
|
||||||
|
|
||||||
|
if (req.session && req.session.userData) {
|
||||||
|
userData = JSON.stringify(req.session.userData);
|
||||||
|
}
|
||||||
|
|
||||||
res.render('default-ember', {
|
res.render('default-ember', {
|
||||||
user: JSON.stringify(req.session.userData)
|
user: userData
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Route: index
|
// Route: index
|
||||||
|
|
|
@ -32,7 +32,8 @@ var middleware = {
|
||||||
authenticate: function (req, res, next) {
|
authenticate: function (req, res, next) {
|
||||||
var noAuthNeeded = [
|
var noAuthNeeded = [
|
||||||
'/ghost/signin/', '/ghost/signout/', '/ghost/signup/',
|
'/ghost/signin/', '/ghost/signout/', '/ghost/signup/',
|
||||||
'/ghost/forgotten/', '/ghost/reset/', '/ghost/ember/'
|
'/ghost/forgotten/', '/ghost/reset/', '/ghost/ember/',
|
||||||
|
'/ghost/ember/signin'
|
||||||
],
|
],
|
||||||
subPath;
|
subPath;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue