Ghost/core/server/api/posts.js
Sebastian Gierlinger 39e654e9c3 Change error message response
closes #2643
- added error type
- added error property for validations
- wrapped errors in an array
- returns multiple errors for validation
- updated tests and admin
2014-05-05 15:51:21 +02:00

202 lines
7.1 KiB
JavaScript

var when = require('when'),
_ = require('lodash'),
dataProvider = require('../models'),
canThis = require('../permissions').canThis,
filteredUserAttributes = require('./users').filteredAttributes,
posts,
allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'];
function checkPostData(postData) {
if (_.isEmpty(postData) || _.isEmpty(postData.posts) || _.isEmpty(postData.posts[0])) {
return when.reject({type: 'BadRequest', message: 'No root key (\'posts\') provided.'});
}
return when.resolve(postData);
}
function prepareInclude(include) {
var index;
include = _.intersection(include.split(","), allowedIncludes);
index = include.indexOf('author');
if (index !== -1) {
include[index] = 'author_id';
}
return include;
}
// ## Posts
posts = {
// #### Browse
// **takes:** filter / pagination parameters
browse: function browse(options) {
options = options || {};
// only published posts if no user is present
if (!this.user) {
options.status = 'published';
}
if (options.include) {
options.include = prepareInclude(options.include);
}
// **returns:** a promise for a page of posts in a json object
return dataProvider.Post.findPage(options).then(function (result) {
var i = 0,
omitted = result;
for (i = 0; i < omitted.posts.length; i = i + 1) {
if (!_.isNumber(omitted.posts[i].author)) {
omitted.posts[i].author = _.omit(omitted.posts[i].author, filteredUserAttributes);
}
}
return omitted;
});
},
// #### Read
// **takes:** an identifier (id or slug?)
read: function read(options) {
var include;
options = options || {};
// only published posts if no user is present
if (!this.user) {
options.status = 'published';
}
if (options.include) {
include = prepareInclude(options.include);
delete options.include;
}
// **returns:** a promise for a single post in a json object
return dataProvider.Post.findOne(options, {include: include}).then(function (result) {
var omitted;
if (result) {
omitted = result.toJSON();
if (!_.isNumber(omitted.author)) {
omitted.author = _.omit(omitted.author, filteredUserAttributes);
}
return { posts: [ omitted ]};
}
return when.reject({type: 'NotFound', message: 'Post not found.'});
});
},
// #### Edit
// **takes:** a json object with all the properties which should be updated
edit: function edit(postData) {
// **returns:** a promise for the resulting post in a json object
var self = this,
include;
return canThis(self.user).edit.post(postData.id).then(function () {
return checkPostData(postData).then(function (checkedPostData) {
if (postData.include) {
include = prepareInclude(postData.include);
}
return dataProvider.Post.edit(checkedPostData.posts[0], {user: self.user, include: include});
}).then(function (result) {
if (result) {
var omitted = result.toJSON();
if (!_.isNumber(omitted.author)) {
omitted.author = _.omit(omitted.author, filteredUserAttributes);
}
// If previously was not published and now is, signal the change
if (result.updated('status') !== result.get('status')) {
omitted.statusChanged = true;
}
return { posts: [ omitted ]};
}
return when.reject({type: 'NotFound', message: 'Post not found.'});
});
}, function () {
return when.reject({type: 'NoPermission', message: 'You do not have permission to edit this post.'});
});
},
// #### Add
// **takes:** a json object representing a post,
add: function add(postData) {
var self = this,
include;
// **returns:** a promise for the resulting post in a json object
return canThis(this.user).create.post().then(function () {
return checkPostData(postData).then(function (checkedPostData) {
if (postData.include) {
include = prepareInclude(postData.include);
}
return dataProvider.Post.add(checkedPostData.posts[0], {user: self.user, include: include});
}).then(function (result) {
var omitted = result.toJSON();
if (!_.isNumber(omitted.author)) {
omitted.author = _.omit(omitted.author, filteredUserAttributes);
}
if (omitted.status === 'published') {
// When creating a new post that is published right now, signal the change
omitted.statusChanged = true;
}
return { posts: [ omitted ]};
});
}, function () {
return when.reject({type: 'NoPermission', message: 'You do not have permission to add posts.'});
});
},
// #### Destroy
// **takes:** an identifier (id or slug?)
destroy: function destroy(args) {
var self = this;
// **returns:** a promise for a json response with the id of the deleted post
return canThis(this.user).remove.post(args.id).then(function () {
// TODO: Would it be good to get rid of .call()?
return posts.read.call({user: self.user}, {id : args.id, status: 'all'}).then(function (result) {
return dataProvider.Post.destroy(args.id).then(function () {
var deletedObj = result;
if (deletedObj.posts) {
_.each(deletedObj.posts, function (post) {
post.statusChanged = true;
});
}
return deletedObj;
});
});
}, function () {
return when.reject({type: 'NoPermission', message: 'You do not have permission to remove posts.'});
});
},
// #### Generate slug
// **takes:** a string to generate the slug from
generateSlug: function generateSlug(args) {
return canThis(this.user).slug.post().then(function () {
return dataProvider.Base.Model.generateSlug(dataProvider.Post, args.title, {status: 'all'}).then(function (slug) {
if (slug) {
return slug;
}
return when.reject({type: 'InternalServerError', message: 'Could not generate slug'});
});
}, function () {
return when.reject({type: 'NoPermission', message: 'You do not have permission.'});
});
}
};
module.exports = posts;