From 5abeadf80da93baf06a93834c831196a3474d9e6 Mon Sep 17 00:00:00 2001 From: Jacob Gable Date: Fri, 9 May 2014 00:00:10 -0500 Subject: [PATCH] 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 --- Gruntfile.js | 2 + bower.json | 2 + core/client/adapters/application.js | 22 ++++++ core/client/app.js | 15 ++-- core/client/controllers/posts/post.js | 81 +++++++++------------- core/client/initializers/csrf-token.js | 11 +++ core/client/initializers/current-user.js | 32 +++++++-- core/client/initializers/ghost-paths.js | 1 + core/client/initializers/notifications.js | 14 +--- core/client/models/post.js | 71 ++++++++----------- core/client/models/tag.js | 13 ++++ core/client/models/user.js | 80 ++++++++++----------- core/client/routes/application.js | 24 +++++-- core/client/routes/authenticated.js | 6 +- core/client/routes/editor.js | 7 +- core/client/routes/new.js | 7 +- core/client/routes/posts.js | 8 +-- core/client/routes/posts/post.js | 10 +-- core/client/routes/signin.js | 13 ++-- core/client/routes/signup.js | 11 +-- core/client/serializers/application.js | 16 +++++ core/client/templates/-floating-header.hbs | 2 +- core/client/templates/-publish-bar.hbs | 6 +- core/server/controllers/admin.js | 8 ++- core/server/middleware/middleware.js | 3 +- 25 files changed, 258 insertions(+), 207 deletions(-) create mode 100644 core/client/adapters/application.js create mode 100644 core/client/initializers/csrf-token.js create mode 100644 core/client/models/tag.js create mode 100644 core/client/serializers/application.js diff --git a/Gruntfile.js b/Gruntfile.js index 090b0477c53b..2191dc03b88a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -548,8 +548,10 @@ var path = require('path'), 'bower_components/jquery/dist/jquery.js', 'bower_components/handlebars/handlebars.js', 'bower_components/ember/ember.js', + 'bower_components/ember-data/ember-data.js', 'bower_components/ember-resolver/dist/ember-resolver.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/codemirror/lib/codemirror.js', 'bower_components/codemirror/addon/mode/overlay.js', diff --git a/bower.json b/bower.json index b53e95fd32a1..4c161878eeee 100644 --- a/bower.json +++ b/bower.json @@ -5,6 +5,8 @@ "codemirror": "4.0.1", "Countable": "2.0.2", "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", "fastclick": "1.0.0", "ghost-ui": "0.1.3", diff --git a/core/client/adapters/application.js b/core/client/adapters/application.js new file mode 100644 index 000000000000..ac2435c5fdc2 --- /dev/null +++ b/core/client/adapters/application.js @@ -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; + } +}); \ No newline at end of file diff --git a/core/client/app.js b/core/client/app.js index a7ed76051592..73bb940b4b07 100755 --- a/core/client/app.js +++ b/core/client/app.js @@ -1,13 +1,11 @@ import Resolver from 'ember/resolver'; import initFixtures from 'ghost/fixtures/init'; -import injectCurrentUser from 'ghost/initializers/current-user'; -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 loadInitializers from 'ember/load-initializers'; import 'ghost/utils/link-view'; import 'ghost/utils/text-field'; +Ember.MODEL_FACTORY_INJECTIONS = true; + var App = Ember.Application.extend({ /** * These are debugging flags, they are useful during development @@ -23,11 +21,6 @@ var App = Ember.Application.extend({ initFixtures(); -App.initializer(injectCurrentUser); -App.initializer(injectCsrf); -App.initializer(injectGhostPaths); -App.initializer(registerNotifications); -App.initializer(injectNotifications); -App.initializer(registerTrailingLocationHistory); +loadInitializers(App, 'ghost'); export default App; diff --git a/core/client/controllers/posts/post.js b/core/client/controllers/posts/post.js index 9070748c6521..394010f1a50c 100644 --- a/core/client/controllers/posts/post.js +++ b/core/client/controllers/posts/post.js @@ -13,49 +13,26 @@ var PostController = Ember.ObjectController.extend({ isDraft: equal('status', 'draft'), willPublish: Ember.computed.oneWay('isPublished'), isStaticPage: function (key, val) { + var self = this; + if (arguments.length > 1) { - this.set('model.page', val ? 1 : 0); - this.get('model').save('page').then(function () { - this.notifications.showSuccess('Succesfully converted ' + (val ? 'to static page' : 'to post')); + this.set('page', val ? 1 : 0); + + return this.get('model').save().then(function () { + self.notifications.showSuccess('Succesfully converted to ' + (val ? 'static page' : 'post')); + + return !!self.get('page'); }, this.notifications.showErrors); } - return !!this.get('model.page'); - }.property('model.page'), - - isOnServer: function () { - return this.get('model.id') !== undefined; - }.property('model.id'), - - newSlugBinding: Ember.Binding.oneWay('model.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 - // validate it against existing slugs in the db - // and then update the placeholder value. - 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'), + return !!this.get('page'); + }.property('page'), + + newSlugBinding: Ember.computed.oneWay('slug'), + + slugPlaceholder: function () { + return this.get('model').generateSlug(); + }.property('title'), actions: { save: function () { @@ -78,6 +55,11 @@ var PostController = Ember.ObjectController.extend({ console.warn('Received invalid save type; ignoring.'); } }, + toggleFeatured: function () { + this.set('featured', !this.get('featured')); + + this.get('model').save(); + }, editSettings: function () { var isEditing = this.toggleProperty('isEditingSettings'); if (isEditing) { @@ -91,9 +73,10 @@ var PostController = Ember.ObjectController.extend({ }); } }, + updateSlug: function () { var newSlug = this.get('newSlug'), - slug = this.get('model.slug'), + slug = this.get('slug'), placeholder = this.get('slugPlaceholder'), self = this; @@ -110,17 +93,17 @@ var PostController = Ember.ObjectController.extend({ } //Validation complete - this.set('model.slug', newSlug); + this.set('slug', newSlug); // If the model doesn't currently // exist on the server // then just update the model's value - if (!this.get('isOnServer')) { + if (!this.get('isNew')) { return; } - - this.get('model').save('slug').then(function () { - self.notifications.showSuccess('Permalink successfully changed to ' + this.get('model.slug') + '.'); + + this.get('model').save().then(function () { + self.notifications.showSuccess('Permalink successfully changed to ' + this.get('slug') + '.'); }, this.notifications.showErrors); }, @@ -182,20 +165,20 @@ var PostController = Ember.ObjectController.extend({ } //Validation complete - this.set('model.published_at', newPubDateMoment.toDate()); + this.set('published_at', newPubDateMoment.toDate()); // If the model doesn't currently // exist on the server // then just update the model's value - if (!this.get('isOnServer')) { + if (!this.get('isNew')) { return; } - this.get('model').save('published_at').then(function () { + this.get('model').save().then(function () { this.notifications.showSuccess('Publish date successfully changed to ' + this.get('publishedAt') + '.'); }, this.notifications.showErrors); } } }); -export default PostController; \ No newline at end of file +export default PostController; diff --git a/core/client/initializers/csrf-token.js b/core/client/initializers/csrf-token.js new file mode 100644 index 000000000000..12671c7277e7 --- /dev/null +++ b/core/client/initializers/csrf-token.js @@ -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'); + } +}; \ No newline at end of file diff --git a/core/client/initializers/current-user.js b/core/client/initializers/current-user.js index cc42283ce79d..892d6f5ad6c6 100644 --- a/core/client/initializers/current-user.js +++ b/core/client/initializers/current-user.js @@ -1,14 +1,34 @@ -import User from 'ghost/models/user'; - export default { name: 'currentUser', + after: 'store', initialize: function (container, application) { - var user = User.create(application.get('user') || {}); + var store = container.lookup('store:main'), + preloadedUser = application.get('user'); + + // If we don't have a user, don't do the injection + if (!preloadedUser) { + return; + } + + // Push the preloaded user into the data store + 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 }); - 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'); - container.injection('route', 'user', 'user:current'); - container.injection('controller', 'user', 'user:current'); + application.advanceReadiness(); + }); } }; \ No newline at end of file diff --git a/core/client/initializers/ghost-paths.js b/core/client/initializers/ghost-paths.js index 0c1703b6922d..82cd54e462b2 100644 --- a/core/client/initializers/ghost-paths.js +++ b/core/client/initializers/ghost-paths.js @@ -2,6 +2,7 @@ import ghostPaths from 'ghost/utils/ghost-paths'; export default { name: 'ghost-paths', + after: 'store', initialize: function (container) { container.register('ghost:paths', ghostPaths(), {instantiate: false}); diff --git a/core/client/initializers/notifications.js b/core/client/initializers/notifications.js index 554818fda4a8..a73abc3b358d 100644 --- a/core/client/initializers/notifications.js +++ b/core/client/initializers/notifications.js @@ -1,21 +1,13 @@ import Notifications from 'ghost/utils/notifications'; -var registerNotifications = { - name: 'registerNotifications', +export default { + name: 'injectNotifications', initialize: function (container, application) { application.register('notifications:main', Notifications); - } -}; - -var injectNotifications = { - name: 'injectNotifications', - initialize: function (container, application) { application.inject('controller', 'notifications', 'notifications:main'); application.inject('component', 'notifications', 'notifications:main'); application.inject('route', 'notifications', 'notifications:main'); } -}; - -export {registerNotifications, injectNotifications}; \ No newline at end of file +}; \ No newline at end of file diff --git a/core/client/models/post.js b/core/client/models/post.js index 6cbb44449496..87c8a4e7cdc6 100644 --- a/core/client/models/post.js +++ b/core/client/models/post.js @@ -1,56 +1,45 @@ -import BaseModel from 'ghost/models/base'; - -var PostModel = BaseModel.extend({ - url: BaseModel.apiRoot + '/posts/', +var Post = DS.Model.extend({ + uuid: DS.attr('string'), + title: DS.attr('string'), + 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 () { - // @TODO Make this request use this.get('title') once we're an actual user - var url = this.get('url') + 'slug/' + encodeURIComponent('test title') + '/'; - return ic.ajax.request(url, { - 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); - } + var title = this.get('title'), + url = this.get('ghostPaths').apiUrl('posts', 'slug', encodeURIComponent(title)); return ic.ajax.request(url, { - type: type, - data: this.getProperties(properties) - }).then(function (model) { - return self.setProperties(model); - }); + type: 'GET' + }); }, - validate: function () { + + validationErrors: function () { var validationErrors = []; - if (!(this.get('title') && this.get('title').length)) { + if (!this.get('title.length')) { validationErrors.push({ message: "You must specify a title for the post." }); } return validationErrors; - } + }.property('title') }); -export default PostModel; \ No newline at end of file +export default Post; \ No newline at end of file diff --git a/core/client/models/tag.js b/core/client/models/tag.js new file mode 100644 index 000000000000..6000f2777c07 --- /dev/null +++ b/core/client/models/tag.js @@ -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'), +}); \ No newline at end of file diff --git a/core/client/models/user.js b/core/client/models/user.js index 0dab328e8036..530be37026cc 100644 --- a/core/client/models/user.js +++ b/core/client/models/user.js @@ -1,24 +1,28 @@ -import BaseModel from 'ghost/models/base'; - -var UserModel = BaseModel.extend({ - id: null, - name: null, - image: null, +var User = DS.Model.extend({ + uuid: DS.attr('string'), + name: DS.attr('string'), + slug: DS.attr('string'), + password: DS.attr('string'), + 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'), - url: BaseModel.apiRoot + '/users/me/', - 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 () { + validationErrors: function () { var validationErrors = []; if (!validator.isLength(this.get('name'), 0, 150)) { @@ -44,25 +48,20 @@ var UserModel = BaseModel.extend({ } } - if (validationErrors.length > 0) { - this.set('isValid', false); - } else { - this.set('isValid', true); - } - - this.set('errors', validationErrors); + return validationErrors; + }.property('name', 'bio', 'email', 'location', 'website'), - return this; - }, + isValid: Ember.computed.empty('validationErrors.[]'), 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', data: password }); }, - validatePassword: function (password) { + passwordValidationErrors: function (password) { var validationErrors = []; 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."); } - if (validationErrors.length > 0) { - this.set('passwordIsValid', false); - } else { - this.set('passwordIsValid', true); - } - - this.set('passwordErrors', validationErrors); - - return this; + return validationErrors; }, fetchForgottenPasswordFor: function (email) { - var self = this; + var forgottenUrl = this.get('ghostPaths').apiUrl('forgotten'); + return new Ember.RSVP.Promise(function (resolve, reject) { if (!validator.isEmail(email)) { reject(new Error('Please enter a correct email address.')); } else { - resolve(ic.ajax.request(self.forgottenUrl, { + resolve(ic.ajax.request(forgottenUrl, { type: 'POST', headers: { // @TODO Find a more proper way to do this. @@ -105,12 +97,14 @@ var UserModel = BaseModel.extend({ }, resetPassword: function (passwords, token) { - var self = this; + var self = this, + resetUrl = this.get('ghostPaths').apiUrl('reset'); + return new Ember.RSVP.Promise(function (resolve, reject) { if (!self.validatePassword(passwords).get('passwordIsValid')) { reject(new Error('Errors found! ' + JSON.stringify(self.get('passwordErrors')))); } else { - resolve(ic.ajax.request(self.resetUrl, { + resolve(ic.ajax.request(resetUrl, { type: 'POST', headers: { // @TODO: find a more proper way to do this. @@ -127,4 +121,4 @@ var UserModel = BaseModel.extend({ } }); -export default UserModel; +export default User; diff --git a/core/client/routes/application.js b/core/client/routes/application.js index d1c905b7113b..3cb172953cfb 100644 --- a/core/client/routes/application.js +++ b/core/client/routes/application.js @@ -1,15 +1,27 @@ var ApplicationRoute = Ember.Route.extend({ actions: { 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 () { - this.container.lookup('user:current').setProperties({ - id: null, - name: null, - image: null - }); + // Nullify the user on all routes and controllers + this.container.unregister('user:current'); + this.container.register('user:current', null, { instantiate: false }); + + 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) { diff --git a/core/client/routes/authenticated.js b/core/client/routes/authenticated.js index 93487eb4f03b..fa1a0971ed4c 100644 --- a/core/client/routes/authenticated.js +++ b/core/client/routes/authenticated.js @@ -1,6 +1,8 @@ var AuthenticatedRoute = Ember.Route.extend({ 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.transitionTo('signin'); @@ -9,7 +11,7 @@ var AuthenticatedRoute = Ember.Route.extend({ actions: { error: function (error) { - if (error.jqXHR.status === 401) { + if (error.jqXHR && error.jqXHR.status === 401) { this.transitionTo('signin'); } } diff --git a/core/client/routes/editor.js b/core/client/routes/editor.js index baf4b4e1c46a..fa33e62dcddb 100644 --- a/core/client/routes/editor.js +++ b/core/client/routes/editor.js @@ -1,14 +1,11 @@ -import ajax from 'ghost/utils/ajax'; import styleBody from 'ghost/mixins/style-body'; import AuthenticatedRoute from 'ghost/routes/authenticated'; -import Post from 'ghost/models/post'; + var EditorRoute = AuthenticatedRoute.extend(styleBody, { classNames: ['editor'], controllerName: 'posts.post', model: function (params) { - return ajax('/ghost/api/v0.1/posts/' + params.post_id).then(function (post) { - return Post.create(post); - }); + return this.store.find('post', params.post_id); } }); diff --git a/core/client/routes/new.js b/core/client/routes/new.js index ea320e8fecf5..d84d0f646e33 100644 --- a/core/client/routes/new.js +++ b/core/client/routes/new.js @@ -1,9 +1,8 @@ import AuthenticatedRoute from 'ghost/routes/authenticated'; import styleBody from 'ghost/mixins/style-body'; -import Post from 'ghost/models/post'; var NewRoute = AuthenticatedRoute.extend(styleBody, { - controllerName: 'posts/post', + controllerName: 'posts.post', classNames: ['editor'], renderTemplate: function () { @@ -11,7 +10,9 @@ var NewRoute = AuthenticatedRoute.extend(styleBody, { }, model: function () { - return Post.create(); + return this.store.createRecord('post', { + title: 'New Post' + }); } }); diff --git a/core/client/routes/posts.js b/core/client/routes/posts.js index 278b15a98939..5c3af9741a50 100644 --- a/core/client/routes/posts.js +++ b/core/client/routes/posts.js @@ -1,17 +1,11 @@ -import ajax from 'ghost/utils/ajax'; import styleBody from 'ghost/mixins/style-body'; import AuthenticatedRoute from 'ghost/routes/authenticated'; -import Post from 'ghost/models/post'; var PostsRoute = AuthenticatedRoute.extend(styleBody, { classNames: ['manage'], model: function () { - return ajax('/ghost/api/v0.1/posts').then(function (response) { - return response.posts.map(function (post) { - return Post.create(post); - }); - }); + return this.store.find('post', { status: 'all', staticPages: 'all' }); }, actions: { diff --git a/core/client/routes/posts/post.js b/core/client/routes/posts/post.js index d857d854fa1e..f5e0f1d13d4c 100644 --- a/core/client/routes/posts/post.js +++ b/core/client/routes/posts/post.js @@ -1,11 +1,5 @@ -/*global ajax */ -import Post from 'ghost/models/post'; -var PostsPostRoute = Ember.Route.extend({ +export default Ember.Route.extend({ model: function (params) { - return ajax('/ghost/api/v0.1/posts/' + params.post_id).then(function (post) { - return Post.create(post); - }); + return this.store.find('post', params.post_id); } }); - -export default PostsPostRoute; diff --git a/core/client/routes/signin.js b/core/client/routes/signin.js index da2327309ba7..1c129aa47e95 100644 --- a/core/client/routes/signin.js +++ b/core/client/routes/signin.js @@ -15,7 +15,7 @@ var SigninRoute = Ember.Route.extend(styleBody, { if (!isEmpty(data.email) && !isEmpty(data.password)) { ajax({ - url: '/ghost/signin/', + url: this.get('ghostPaths').adminUrl('signin'), type: 'POST', headers: { "X-CSRF-Token": this.get('csrf') @@ -23,11 +23,12 @@ var SigninRoute = Ember.Route.extend(styleBody, { data: data }).then( function (response) { - self.send('signedIn', response.userData); - - self.notifications.clear(); - - self.transitionTo('posts'); + self.store.pushPayload({ users: [response.userData]}); + self.store.find('user', response.userData.id).then(function (user) { + self.send('signedIn', user); + self.notifications.clear(); + self.transitionTo('posts'); + }); }, function (resp) { // 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.'); diff --git a/core/client/routes/signup.js b/core/client/routes/signup.js index ffe8d2c41497..acf7d64cb143 100644 --- a/core/client/routes/signup.js +++ b/core/client/routes/signup.js @@ -26,11 +26,12 @@ var SignupRoute = Ember.Route.extend(styleBody, { data: data }).then(function (resp) { if (resp && resp.userData) { - self.send('signedIn', resp.userData); - - self.notifications.clear(); - - self.transitionTo('posts'); + self.store.pushPayload({ users: [resp.userData]}); + self.store.find('user', resp.userData.id).then(function (user) { + self.send('signedIn', user); + self.notifications.clear(); + self.transitionTo('posts'); + }); } else { self.transitionTo('signin'); } diff --git a/core/client/serializers/application.js b/core/client/serializers/application.js new file mode 100644 index 000000000000..d35bf879d879 --- /dev/null +++ b/core/client/serializers/application.js @@ -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]; + } +}); \ No newline at end of file diff --git a/core/client/templates/-floating-header.hbs b/core/client/templates/-floating-header.hbs index d450468c74a6..561b66f74dd4 100644 --- a/core/client/templates/-floating-header.hbs +++ b/core/client/templates/-floating-header.hbs @@ -1,7 +1,7 @@
{{!-- @TODO: add back title updates depending on featured state --}} - + diff --git a/core/client/templates/-publish-bar.hbs b/core/client/templates/-publish-bar.hbs index 8a79a404a5c1..5b9aee00ad82 100644 --- a/core/client/templates/-publish-bar.hbs +++ b/core/client/templates/-publish-bar.hbs @@ -2,7 +2,11 @@