Merge pull request #5646 from ErisDS/issue-5150-nextprev

Next/Prev Post helpers include author & tags
This commit is contained in:
Sebastian Gierlinger 2015-08-11 15:46:23 +02:00
commit 9376bdf699
6 changed files with 118 additions and 83 deletions

View File

@ -9,7 +9,10 @@ var Promise = require('bluebird'),
pipeline = require('../utils/pipeline'),
docName = 'posts',
allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields', 'next', 'previous'],
allowedIncludes = [
'created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields',
'next', 'previous', 'next.author', 'next.tags', 'previous.author', 'previous.tags'
],
posts;
/**

View File

@ -29,7 +29,7 @@ prevNext = function (options) {
options = options || {};
var apiOptions = {
include: options.name === 'prev_post' ? 'previous' : 'next'
include: options.name === 'prev_post' ? 'previous,previous.author,previous.tags' : 'next,next.author,next.tags'
};
if (schema.isPost(this) && this.status === 'published') {

View File

@ -399,7 +399,21 @@ Post = ghostBookshelf.Model.extend({
options = options || {};
var withNext = _.contains(options.include, 'next'),
withPrev = _.contains(options.include, 'previous');
withPrev = _.contains(options.include, 'previous'),
nextRelations = _.transform(options.include, function (relations, include) {
if (include === 'next.tags') {
relations.push('tags');
} else if (include === 'next.author') {
relations.push('author');
}
}, []),
prevRelations = _.transform(options.include, function (relations, include) {
if (include === 'previous.tags') {
relations.push('tags');
} else if (include === 'previous.author') {
relations.push('author');
}
}, []);
data = _.defaults(data || {}, {
status: 'published'
@ -410,7 +424,10 @@ Post = ghostBookshelf.Model.extend({
}
// Add related objects, excluding next and previous as they are not real db objects
options.withRelated = _.union(options.withRelated, _.pull([].concat(options.include), 'next', 'previous'));
options.withRelated = _.union(options.withRelated, _.pull(
[].concat(options.include),
'next', 'next.author', 'next.tags', 'previous', 'previous.author', 'previous.tags')
);
return ghostBookshelf.Model.findOne.call(this, data, options).then(function then(post) {
if ((withNext || withPrev) && post && !post.page) {
@ -425,7 +442,7 @@ Post = ghostBookshelf.Model.extend({
.andWhere('published_at', '>', publishedAt)
.orderBy('published_at', 'asc')
.limit(1);
}).fetch();
}).fetch({withRelated: nextRelations});
}
if (withPrev) {
@ -435,7 +452,7 @@ Post = ghostBookshelf.Model.extend({
.andWhere('published_at', '<', publishedAt)
.orderBy('published_at', 'desc')
.limit(1);
}).fetch();
}).fetch({withRelated: prevRelations});
}
return Promise.join(next, prev)

View File

@ -320,6 +320,27 @@ describe('Post API', function () {
}).catch(done);
});
it('can include next post with author and tags', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'next,next.tags,next.author'}).then(function (results) {
should.exist(results.posts[0].next.slug);
results.posts[0].next.slug.should.eql('not-so-short-bit-complex');
results.posts[0].next.author.should.be.an.Object;
results.posts[0].next.tags.should.be.an.Array;
done();
}).catch(done);
});
it('can include next post with just tags', function (done) {
PostAPI.read({context: {user: 1}, id: 2, include: 'next,next.tags'}).then(function (results) {
should.exist(results.posts[0].next.slug);
results.posts[0].next.slug.should.eql('short-and-sweet');
results.posts[0].next.author.should.eql(1);
results.posts[0].next.tags.should.be.an.Array;
results.posts[0].next.tags[0].name.should.eql('chorizo');
done();
}).catch(done);
});
it('can include previous post', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'previous'}).then(function (results) {
should.exist(results.posts[0].previous.slug);
@ -327,5 +348,29 @@ describe('Post API', function () {
done();
}).catch(done);
});
it('can include previous post with author and tags', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'previous,previous.author,previous.tags'}).then(function (results) {
should.exist(results.posts[0].previous.slug);
results.posts[0].previous.slug.should.eql('ghostly-kitchen-sink');
results.posts[0].previous.author.should.be.an.Object;
results.posts[0].previous.author.name.should.eql('Joe Bloggs');
results.posts[0].previous.tags.should.be.an.Array;
results.posts[0].previous.tags.should.have.lengthOf(2);
results.posts[0].previous.tags[0].slug.should.eql('kitchen-sink');
done();
}).catch(done);
});
it('can include previous post with just author', function (done) {
PostAPI.read({context: {user: 1}, id: 3, include: 'previous,previous.author'}).then(function (results) {
should.exist(results.posts[0].previous.slug);
should.not.exist(results.posts[0].previous.tags);
results.posts[0].previous.slug.should.eql('ghostly-kitchen-sink');
results.posts[0].previous.author.should.be.an.Object;
results.posts[0].previous.author.name.should.eql('Joe Bloggs');
done();
}).catch(done);
});
});
});

View File

@ -9,16 +9,22 @@ var should = require('should'),
// Stuff we are testing
handlebars = hbs.handlebars,
helpers = require('../../../server/helpers'),
api = require('../../../server/api');
api = require('../../../server/api'),
sandbox = sinon.sandbox.create();
describe('{{next_post}} helper', function () {
var readPostStub;
afterEach(function () {
sandbox.restore();
});
describe('with valid post data - ', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'next') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('next') === 0) {
return Promise.resolve({
posts: [{slug: '/current/', title: 'post 2', next: {slug: '/next/', title: 'post 3'}}]
});
@ -26,10 +32,6 @@ describe('{{next_post}} helper', function () {
});
});
afterEach(function () {
sandbox.restore();
});
it('has loaded next_post helper', function () {
should.exist(handlebars.helpers.prev_post);
});
@ -46,8 +48,11 @@ describe('{{next_post}} helper', function () {
slug: 'current',
created_at: new Date(0),
url: '/current/'}, optionsData).then(function () {
fn.called.should.be.true;
inverse.called.should.be.false;
fn.calledOnce.should.be.true;
inverse.calledOnce.should.be.false;
readPostStub.calledOnce.should.be.true;
readPostStub.firstCall.args[0].include.should.eql('next,next.author,next.tags');
done();
}).catch(function (err) {
console.log('err ', err);
@ -57,22 +62,15 @@ describe('{{next_post}} helper', function () {
});
describe('for valid post with no next post', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'next') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('next') === 0) {
return Promise.resolve({posts: [{slug: '/current/', title: 'post 2'}]});
}
});
});
afterEach(function () {
sandbox.restore();
});
it('shows \'else\' template', function (done) {
var fn = sinon.spy(),
inverse = sinon.spy(),
@ -94,22 +92,15 @@ describe('{{next_post}} helper', function () {
});
describe('for invalid post data', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'previous') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('next') === 0) {
return Promise.resolve({});
}
});
});
afterEach(function () {
sandbox.restore();
});
it('shows \'else\' template', function (done) {
var fn = sinon.spy(),
inverse = sinon.spy(),
@ -118,6 +109,7 @@ describe('{{next_post}} helper', function () {
helpers.prev_post.call({}, optionsData).then(function () {
fn.called.should.be.false;
inverse.called.should.be.true;
readPostStub.called.should.be.false;
done();
}).catch(function (err) {
done(err);
@ -126,13 +118,10 @@ describe('{{next_post}} helper', function () {
});
describe('for unpublished post', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'next') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('next') === 0) {
return Promise.resolve({
posts: [{slug: '/current/', title: 'post 2', next: {slug: '/next/', title: 'post 3'}}]
});
@ -140,10 +129,6 @@ describe('{{next_post}} helper', function () {
});
});
afterEach(function () {
sandbox.restore();
});
it('shows \'else\' template', function (done) {
var fn = sinon.spy(),
inverse = sinon.spy(),

View File

@ -9,17 +9,22 @@ var should = require('should'),
// Stuff we are testing
handlebars = hbs.handlebars,
helpers = require('../../../server/helpers'),
api = require('../../../server/api');
api = require('../../../server/api'),
sandbox = sinon.sandbox.create();
describe('{{prev_post}} helper', function () {
describe('with valid post data - ', function () {
var sandbox;
var readPostStub;
afterEach(function () {
sandbox.restore();
});
describe('with valid post data - ', function () {
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'previous') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('previous') === 0) {
return Promise.resolve({
posts: [{slug: '/current/', title: 'post 2', previous: {slug: '/previous/', title: 'post 1'}}]
});
@ -27,10 +32,6 @@ describe('{{prev_post}} helper', function () {
});
});
afterEach(function () {
sandbox.restore();
});
it('has loaded prev_post helper', function () {
should.exist(handlebars.helpers.prev_post);
});
@ -47,8 +48,12 @@ describe('{{prev_post}} helper', function () {
slug: 'current',
created_at: new Date(0),
url: '/current/'}, optionsData).then(function () {
fn.called.should.be.true;
inverse.called.should.be.false;
fn.calledOnce.should.be.true;
inverse.calledOnce.should.be.false;
readPostStub.calledOnce.should.be.true;
readPostStub.firstCall.args[0].include.should.eql('previous,previous.author,previous.tags');
done();
}).catch(function (err) {
console.log('err ', err);
@ -58,22 +63,15 @@ describe('{{prev_post}} helper', function () {
});
describe('for valid post with no previous post', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'previous') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('previous') === 0) {
return Promise.resolve({posts: [{slug: '/current/', title: 'post 2'}]});
}
});
});
afterEach(function () {
sandbox.restore();
});
it('shows \'else\' template', function (done) {
var fn = sinon.spy(),
inverse = sinon.spy(),
@ -96,22 +94,15 @@ describe('{{prev_post}} helper', function () {
});
describe('for invalid post data', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'previous') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('previous') === 0) {
return Promise.resolve({});
}
});
});
afterEach(function () {
sandbox.restore();
});
it('shows \'else\' template', function (done) {
var fn = sinon.spy(),
inverse = sinon.spy(),
@ -120,6 +111,7 @@ describe('{{prev_post}} helper', function () {
helpers.prev_post.call({}, optionsData).then(function () {
fn.called.should.be.false;
inverse.called.should.be.true;
readPostStub.called.should.be.false;
done();
}).catch(function (err) {
done(err);
@ -128,22 +120,15 @@ describe('{{prev_post}} helper', function () {
});
describe('for unpublished post', function () {
var sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
utils.loadHelpers();
sandbox.stub(api.posts, 'read', function (options) {
if (options.include === 'previous') {
readPostStub = sandbox.stub(api.posts, 'read', function (options) {
if (options.include.indexOf('previous') === 0) {
return Promise.resolve({posts: [{slug: '/current/', title: 'post 2', previous: {slug: '/previous/', title: 'post 1'}}]});
}
});
});
afterEach(function () {
sandbox.restore();
});
it('shows \'else\' template', function (done) {
var fn = sinon.spy(),
inverse = sinon.spy(),