mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Adds tag pages
fixes #2111 - modified Post model to support a tag query param that will filter the desired post collection to only include posts that contain the requested tag - in the updated Post model it includes the Tag model under a nested object called 'aspects' - added tests for updated Post model, updating test utils to add more posts_tags relations - adds two new routes to frontend, one for initial tag page, another to page that tag page - for tag pages the array of posts is exposed to the view similarly to the homepeage - on the tag view page the information for the tag is also accessible for further theme usage - the tag view page supports a hierarchy of views, it'll first attempt to use a tag.hbs file if it exists, otherwise fall back to the default index.hbs file - modified pageUrl and pagination helper to have it be compatible with tag paging - added unit tests for frontend controller - added unit tests for handlebar helper modifications - add functional tests for new tag routes
This commit is contained in:
parent
679f65c50a
commit
9ab4b7d4d5
11 changed files with 493 additions and 96 deletions
|
@ -20,53 +20,121 @@ var moment = require('moment'),
|
|||
// Cache static post permalink regex
|
||||
staticPostPermalink = new Route(null, '/:slug/:edit?');
|
||||
|
||||
function getPostPage(options) {
|
||||
return api.settings.read('postsPerPage').then(function (postPP) {
|
||||
var postsPerPage = parseInt(postPP.value, 10);
|
||||
|
||||
// No negative posts per page, must be number
|
||||
if (!isNaN(postsPerPage) && postsPerPage > 0) {
|
||||
options.limit = postsPerPage;
|
||||
}
|
||||
|
||||
return api.posts.browse(options);
|
||||
}).then(function (page) {
|
||||
|
||||
// A bit of a hack for situations with no content.
|
||||
if (page.pages === 0) {
|
||||
page.pages = 1;
|
||||
}
|
||||
|
||||
return page;
|
||||
});
|
||||
}
|
||||
|
||||
function formatPageResponse(posts, page) {
|
||||
return {
|
||||
posts: posts,
|
||||
pagination: {
|
||||
page: page.page,
|
||||
prev: page.prev,
|
||||
next: page.next,
|
||||
limit: page.limit,
|
||||
total: page.total,
|
||||
pages: page.pages
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleError(next) {
|
||||
return function (err) {
|
||||
var e = new Error(err.message);
|
||||
e.status = err.errorCode;
|
||||
return next(e);
|
||||
};
|
||||
}
|
||||
|
||||
frontendControllers = {
|
||||
'homepage': function (req, res, next) {
|
||||
// Parse the page number
|
||||
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
|
||||
postsPerPage,
|
||||
options = {};
|
||||
options = {
|
||||
page: pageParam
|
||||
};
|
||||
|
||||
// No negative pages, or page 1
|
||||
if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && req.route.path === '/page/:page/')) {
|
||||
return res.redirect(config().paths.subdir + '/');
|
||||
}
|
||||
|
||||
return api.settings.read('postsPerPage').then(function (postPP) {
|
||||
postsPerPage = parseInt(postPP.value, 10);
|
||||
options.page = pageParam;
|
||||
|
||||
// No negative posts per page, must be number
|
||||
if (!isNaN(postsPerPage) && postsPerPage > 0) {
|
||||
options.limit = postsPerPage;
|
||||
}
|
||||
return;
|
||||
}).then(function () {
|
||||
return api.posts.browse(options);
|
||||
}).then(function (page) {
|
||||
var maxPage = page.pages;
|
||||
|
||||
// A bit of a hack for situations with no content.
|
||||
if (maxPage === 0) {
|
||||
maxPage = 1;
|
||||
page.pages = 1;
|
||||
}
|
||||
return getPostPage(options).then(function (page) {
|
||||
|
||||
// If page is greater than number of pages we have, redirect to last page
|
||||
if (pageParam > maxPage) {
|
||||
return res.redirect(maxPage === 1 ? config().paths.subdir + '/' : (config().paths.subdir + '/page/' + maxPage + '/'));
|
||||
if (pageParam > page.pages) {
|
||||
return res.redirect(page.pages === 1 ? config().paths.subdir + '/' : (config().paths.subdir + '/page/' + page.pages + '/'));
|
||||
}
|
||||
|
||||
// Render the page of posts
|
||||
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
|
||||
res.render('index', {posts: posts, pagination: {page: page.page, prev: page.prev, next: page.next, limit: page.limit, total: page.total, pages: page.pages}});
|
||||
res.render('index', formatPageResponse(posts, page));
|
||||
});
|
||||
}).otherwise(function (err) {
|
||||
var e = new Error(err.message);
|
||||
e.status = err.errorCode;
|
||||
return next(e);
|
||||
});
|
||||
}).otherwise(handleError(next));
|
||||
},
|
||||
'tag': function (req, res, next) {
|
||||
// Parse the page number
|
||||
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
|
||||
options = {
|
||||
page: pageParam,
|
||||
tag: req.params.slug
|
||||
};
|
||||
|
||||
// Get url for tag page
|
||||
function tagUrl(tag, page) {
|
||||
var url = config().paths.subdir + '/tag/' + tag + '/';
|
||||
|
||||
if (page && page > 1) {
|
||||
url += 'page/' + page + '/';
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
// No negative pages, or page 1
|
||||
if (isNaN(pageParam) || pageParam < 1 || (req.params.page !== undefined && pageParam === 1)) {
|
||||
return res.redirect(tagUrl(options.tag));
|
||||
}
|
||||
|
||||
return getPostPage(options).then(function (page) {
|
||||
|
||||
// If page is greater than number of pages we have, redirect to last page
|
||||
if (pageParam > page.pages) {
|
||||
return res.redirect(tagUrl(options.tag, page.pages));
|
||||
}
|
||||
|
||||
// Render the page of posts
|
||||
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
|
||||
api.settings.read('activeTheme').then(function (activeTheme) {
|
||||
var paths = config().paths.availableThemes[activeTheme.value],
|
||||
view = paths.hasOwnProperty('tag') ? 'tag' : 'index',
|
||||
|
||||
// Format data for template
|
||||
response = _.extend(formatPageResponse(posts, page), {
|
||||
tag: page.aspect.tag
|
||||
});
|
||||
|
||||
res.render(view, response);
|
||||
});
|
||||
});
|
||||
}).otherwise(handleError(next));
|
||||
},
|
||||
'single': function (req, res, next) {
|
||||
var path = req.path,
|
||||
|
@ -177,15 +245,9 @@ frontendControllers = {
|
|||
return next();
|
||||
}
|
||||
|
||||
var e = new Error(err.message);
|
||||
e.status = err.errorCode;
|
||||
return next(e);
|
||||
return handleError(next)(err);
|
||||
});
|
||||
},
|
||||
'edit': function (req, res, next) {
|
||||
req.params[2] = 'edit';
|
||||
return frontendControllers.single(req, res, next);
|
||||
},
|
||||
'rss': function (req, res, next) {
|
||||
// Initialize RSS
|
||||
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
|
||||
|
@ -271,11 +333,7 @@ frontendControllers = {
|
|||
res.send(feed.xml());
|
||||
});
|
||||
});
|
||||
}).otherwise(function (err) {
|
||||
var e = new Error(err.message);
|
||||
e.status = err.errorCode;
|
||||
return next(e);
|
||||
});
|
||||
}).otherwise(handleError(next));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -92,7 +92,19 @@ coreHelpers.encode = function (context, str) {
|
|||
//
|
||||
coreHelpers.pageUrl = function (context, block) {
|
||||
/*jslint unparam:true*/
|
||||
return config().paths.subdir + (context === 1 ? '/' : ('/page/' + context + '/'));
|
||||
var url = config().paths.subdir;
|
||||
|
||||
if (this.tagSlug !== undefined) {
|
||||
url += '/tag/' + this.tagSlug;
|
||||
}
|
||||
|
||||
if (context > 1) {
|
||||
url += '/page/' + context;
|
||||
}
|
||||
|
||||
url += '/';
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
// ### URL helper
|
||||
|
@ -296,7 +308,7 @@ coreHelpers.body_class = function (options) {
|
|||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false;
|
||||
|
||||
if (_.isString(this.relativeUrl) && this.relativeUrl.match(/\/page/)) {
|
||||
if (_.isString(this.relativeUrl) && this.relativeUrl.match(/\/(page|tag)/)) {
|
||||
classes.push('archive-template');
|
||||
} else if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
classes.push('home-template');
|
||||
|
@ -542,7 +554,13 @@ coreHelpers.pagination = function (options) {
|
|||
errors.logAndThrowError('Invalid value, check page, pages, limit and total are numbers');
|
||||
return;
|
||||
}
|
||||
return template.execute('pagination', this.pagination);
|
||||
var context = _.merge({}, this.pagination);
|
||||
|
||||
if (this.tag !== undefined) {
|
||||
context.tagSlug = this.tag.slug;
|
||||
}
|
||||
|
||||
return template.execute('pagination', context);
|
||||
};
|
||||
|
||||
coreHelpers.helperMissing = function (arg) {
|
||||
|
|
|
@ -254,31 +254,24 @@ Post = ghostBookshelf.Model.extend({
|
|||
* @params opts
|
||||
*/
|
||||
findPage: function (opts) {
|
||||
var postCollection,
|
||||
var postCollection = Posts.forge(),
|
||||
tagInstance = opts.tag !== undefined ? Tag.forge({slug: opts.tag}) : false,
|
||||
permittedOptions = ['page', 'limit', 'status', 'staticPages'];
|
||||
|
||||
// sanitize opts
|
||||
// sanitize opts so we are not automatically passing through any and all
|
||||
// query strings to Bookshelf / Knex. Although the API requires auth, we
|
||||
// should prevent this until such time as we can design the API properly and safely.
|
||||
opts = _.pick(opts, permittedOptions);
|
||||
|
||||
// Allow findPage(n)
|
||||
if (_.isString(opts) || _.isNumber(opts)) {
|
||||
opts = {page: opts};
|
||||
}
|
||||
|
||||
// Without this we are automatically passing through any and all query strings
|
||||
// to Bookshelf / Knex. Although the API requires auth, we should prevent this
|
||||
// until such time as we can design the API properly and safely.
|
||||
opts.where = {};
|
||||
|
||||
// Set default settings for options
|
||||
opts = _.extend({
|
||||
page: 1, // pagination page
|
||||
limit: 15,
|
||||
staticPages: false, // include static pages
|
||||
status: 'published'
|
||||
status: 'published',
|
||||
where: {}
|
||||
}, opts);
|
||||
|
||||
postCollection = Posts.forge();
|
||||
|
||||
if (opts.staticPages !== 'all') {
|
||||
// convert string true/false to boolean
|
||||
if (!_.isBoolean(opts.staticPages)) {
|
||||
|
@ -301,53 +294,96 @@ Post = ghostBookshelf.Model.extend({
|
|||
postCollection.query('where', opts.where);
|
||||
}
|
||||
|
||||
// Fetch related models
|
||||
opts.withRelated = [ 'author', 'user', 'tags' ];
|
||||
|
||||
// Set the limit & offset for the query, fetching
|
||||
// with the opts (to specify any eager relations, etc.)
|
||||
// Omitting the `page`, `limit`, `where` just to be sure
|
||||
// aren't used for other purposes.
|
||||
return postCollection
|
||||
.query('limit', opts.limit)
|
||||
.query('offset', opts.limit * (opts.page - 1))
|
||||
.query('orderBy', 'status', 'ASC')
|
||||
.query('orderBy', 'published_at', 'DESC')
|
||||
.query('orderBy', 'updated_at', 'DESC')
|
||||
.fetch(_.omit(opts, 'page', 'limit'))
|
||||
.then(function (collection) {
|
||||
var qb;
|
||||
// If a query param for a tag is attached
|
||||
// we need to fetch the tag model to find its id
|
||||
function fetchTagQuery() {
|
||||
if (tagInstance) {
|
||||
return tagInstance.fetch();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return when(fetchTagQuery())
|
||||
|
||||
// Set the limit & offset for the query, fetching
|
||||
// with the opts (to specify any eager relations, etc.)
|
||||
// Omitting the `page`, `limit`, `where` just to be sure
|
||||
// aren't used for other purposes.
|
||||
.then(function () {
|
||||
// If we have a tag instance we need to modify our query.
|
||||
// We need to ensure we only select posts that contain
|
||||
// the tag given in the query param.
|
||||
if (tagInstance) {
|
||||
postCollection
|
||||
.query('join', 'posts_tags', 'posts_tags.post_id', '=', 'posts.id')
|
||||
.query('where', 'posts_tags.tag_id', '=', tagInstance.id);
|
||||
}
|
||||
|
||||
return postCollection
|
||||
.query('limit', opts.limit)
|
||||
.query('offset', opts.limit * (opts.page - 1))
|
||||
.query('orderBy', 'status', 'ASC')
|
||||
.query('orderBy', 'published_at', 'DESC')
|
||||
.query('orderBy', 'updated_at', 'DESC')
|
||||
.fetch(_.omit(opts, 'page', 'limit'));
|
||||
})
|
||||
|
||||
// Fetch pagination information
|
||||
.then(function () {
|
||||
var qb,
|
||||
tableName = _.result(postCollection, 'tableName'),
|
||||
idAttribute = _.result(postCollection, 'idAttribute');
|
||||
|
||||
// After we're done, we need to figure out what
|
||||
// the limits are for the pagination values.
|
||||
qb = ghostBookshelf.knex(_.result(collection, 'tableName'));
|
||||
qb = ghostBookshelf.knex(tableName);
|
||||
|
||||
if (opts.where) {
|
||||
qb.where(opts.where);
|
||||
}
|
||||
|
||||
return qb.count(_.result(collection, 'idAttribute') + ' as aggregate').then(function (resp) {
|
||||
var totalPosts = parseInt(resp[0].aggregate, 10),
|
||||
data = {
|
||||
posts: collection.toJSON(),
|
||||
page: parseInt(opts.page, 10),
|
||||
limit: opts.limit,
|
||||
pages: Math.ceil(totalPosts / opts.limit),
|
||||
total: totalPosts
|
||||
};
|
||||
if (tagInstance) {
|
||||
qb.join('posts_tags', 'posts_tags.post_id', '=', 'posts.id');
|
||||
qb.where('posts_tags.tag_id', '=', tagInstance.id);
|
||||
}
|
||||
|
||||
if (data.pages > 1) {
|
||||
if (data.page === 1) {
|
||||
data.next = data.page + 1;
|
||||
} else if (data.page === data.pages) {
|
||||
data.prev = data.page - 1;
|
||||
} else {
|
||||
data.next = data.page + 1;
|
||||
data.prev = data.page - 1;
|
||||
}
|
||||
return qb.count(tableName + '.' + idAttribute + ' as aggregate');
|
||||
})
|
||||
|
||||
// Format response of data
|
||||
.then(function (resp) {
|
||||
var totalPosts = parseInt(resp[0].aggregate, 10),
|
||||
data = {
|
||||
posts: postCollection.toJSON(),
|
||||
page: parseInt(opts.page, 10),
|
||||
limit: opts.limit,
|
||||
pages: Math.ceil(totalPosts / opts.limit),
|
||||
total: totalPosts
|
||||
};
|
||||
|
||||
if (data.pages > 1) {
|
||||
if (data.page === 1) {
|
||||
data.next = data.page + 1;
|
||||
} else if (data.page === data.pages) {
|
||||
data.prev = data.page - 1;
|
||||
} else {
|
||||
data.next = data.page + 1;
|
||||
data.prev = data.page - 1;
|
||||
}
|
||||
return data;
|
||||
}, errors.logAndThrowError);
|
||||
}, errors.logAndThrowError);
|
||||
}
|
||||
|
||||
if (tagInstance) {
|
||||
data.aspect = {
|
||||
tag: tagInstance.toJSON()
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
})
|
||||
.catch(errors.logAndThrowError);
|
||||
},
|
||||
|
||||
permissable: function (postModelOrId, userId, action_type, userPermissions) {
|
||||
|
|
|
@ -6,6 +6,8 @@ module.exports = function (server) {
|
|||
// ### Frontend routes
|
||||
server.get('/rss/', frontend.rss);
|
||||
server.get('/rss/:page/', frontend.rss);
|
||||
server.get('/tag/:slug/page/:page/', frontend.tag);
|
||||
server.get('/tag/:slug/', frontend.tag);
|
||||
server.get('/page/:page/', frontend.homepage);
|
||||
server.get('/', frontend.homepage);
|
||||
server.get('*', frontend.single);
|
||||
|
|
|
@ -47,7 +47,7 @@ describe('Tag API', function () {
|
|||
response.should.be.json;
|
||||
var jsonResponse = JSON.parse(body);
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.should.have.length(5);
|
||||
jsonResponse.should.have.length(6);
|
||||
testUtils.API.checkResponse(jsonResponse[0], 'tag');
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -290,6 +290,68 @@ describe('Frontend Routing', function () {
|
|||
// });
|
||||
});
|
||||
|
||||
describe('Tag pages', function () {
|
||||
|
||||
// Add enough posts to trigger tag pages
|
||||
before(function (done) {
|
||||
testUtils.clearData().then(function () {
|
||||
// we initialise data, but not a user. No user should be required for navigating the frontend
|
||||
return testUtils.initData();
|
||||
}).then(function () {
|
||||
|
||||
return testUtils.insertPosts();
|
||||
}).then(function () {
|
||||
return testUtils.insertMorePosts(22);
|
||||
}).then(function() {
|
||||
return testUtils.insertMorePostsTags(22);
|
||||
}).then(function () {
|
||||
done();
|
||||
}).then(null, done);
|
||||
|
||||
});
|
||||
|
||||
it('should redirect without slash', function (done) {
|
||||
request.get('/tag/injection/page/2')
|
||||
.expect('Location', '/tag/injection/page/2/')
|
||||
.expect('Cache-Control', cacheRules.year)
|
||||
.expect(301)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should respond with html', function (done) {
|
||||
request.get('/tag/injection/page/2/')
|
||||
.expect('Content-Type', /html/)
|
||||
.expect('Cache-Control', cacheRules['public'])
|
||||
.expect(200)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should redirect page 1', function (done) {
|
||||
request.get('/tag/injection/page/1/')
|
||||
.expect('Location', '/tag/injection/')
|
||||
.expect('Cache-Control', cacheRules['public'])
|
||||
// TODO: This should probably be a 301?
|
||||
.expect(302)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should redirect to last page is page too high', function (done) {
|
||||
request.get('/tag/injection/page/4/')
|
||||
.expect('Location', '/tag/injection/page/2/')
|
||||
.expect('Cache-Control', cacheRules['public'])
|
||||
.expect(302)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
|
||||
it('should redirect to first page is page too low', function (done) {
|
||||
request.get('/tag/injection/page/0/')
|
||||
.expect('Location', '/tag/injection/')
|
||||
.expect('Cache-Control', cacheRules['public'])
|
||||
.expect(302)
|
||||
.end(doEnd(done));
|
||||
});
|
||||
});
|
||||
|
||||
// ### The rest of the tests switch to date permalinks
|
||||
|
||||
// describe('Date permalinks', function () {
|
||||
|
|
|
@ -353,6 +353,8 @@ describe('Post Model', function () {
|
|||
it('can fetch a paginated set, with various options', function (done) {
|
||||
testUtils.insertMorePosts().then(function () {
|
||||
|
||||
return testUtils.insertMorePostsTags();
|
||||
}).then(function () {
|
||||
return PostModel.findPage({page: 2});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.page.should.equal(2);
|
||||
|
@ -385,6 +387,43 @@ describe('Post Model', function () {
|
|||
}).then(function (paginationResult) {
|
||||
paginationResult.pages.should.equal(11);
|
||||
|
||||
// Test tag filter
|
||||
return PostModel.findPage({page: 1, tag: 'bacon'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.page.should.equal(1);
|
||||
paginationResult.limit.should.equal(15);
|
||||
paginationResult.posts.length.should.equal(2);
|
||||
paginationResult.pages.should.equal(1);
|
||||
paginationResult.aspect.tag.name.should.equal('bacon');
|
||||
paginationResult.aspect.tag.slug.should.equal('bacon');
|
||||
|
||||
return PostModel.findPage({page: 1, tag: 'kitchen-sink'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.page.should.equal(1);
|
||||
paginationResult.limit.should.equal(15);
|
||||
paginationResult.posts.length.should.equal(2);
|
||||
paginationResult.pages.should.equal(1);
|
||||
paginationResult.aspect.tag.name.should.equal('kitchen sink');
|
||||
paginationResult.aspect.tag.slug.should.equal('kitchen-sink');
|
||||
|
||||
return PostModel.findPage({page: 1, tag: 'injection'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.page.should.equal(1);
|
||||
paginationResult.limit.should.equal(15);
|
||||
paginationResult.posts.length.should.equal(15);
|
||||
paginationResult.pages.should.equal(2);
|
||||
paginationResult.aspect.tag.name.should.equal('injection');
|
||||
paginationResult.aspect.tag.slug.should.equal('injection');
|
||||
|
||||
return PostModel.findPage({page: 2, tag: 'injection'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.page.should.equal(2);
|
||||
paginationResult.limit.should.equal(15);
|
||||
paginationResult.posts.length.should.equal(9);
|
||||
paginationResult.pages.should.equal(2);
|
||||
paginationResult.aspect.tag.name.should.equal('injection');
|
||||
paginationResult.aspect.tag.slug.should.equal('injection');
|
||||
|
||||
done();
|
||||
}).then(null, done);
|
||||
});
|
||||
|
|
|
@ -143,6 +143,120 @@ describe('Frontend Controller', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('tag redirects', function () {
|
||||
var res;
|
||||
|
||||
beforeEach(function () {
|
||||
res = {
|
||||
redirect: sandbox.spy(),
|
||||
render: sandbox.spy()
|
||||
};
|
||||
|
||||
sandbox.stub(api.posts, 'browse', function () {
|
||||
return when({posts: {}, pages: 3});
|
||||
});
|
||||
|
||||
apiSettingsStub = sandbox.stub(api.settings, 'read');
|
||||
apiSettingsStub.withArgs('postsPerPage').returns(when({
|
||||
'key': 'postsPerPage',
|
||||
'value': 6
|
||||
}));
|
||||
});
|
||||
|
||||
it('Redirects to base tag page if page number is -1', function () {
|
||||
var req = {params: {page: -1, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, null);
|
||||
|
||||
res.redirect.called.should.be.true;
|
||||
res.redirect.calledWith('/tag/pumpkin/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
|
||||
});
|
||||
|
||||
it('Redirects to base tag page if page number is 0', function () {
|
||||
var req = {params: {page: 0, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, null);
|
||||
|
||||
res.redirect.called.should.be.true;
|
||||
res.redirect.calledWith('/tag/pumpkin/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
|
||||
});
|
||||
|
||||
it('Redirects to base tag page if page number is 1', function () {
|
||||
var req = {params: {page: 1, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, null);
|
||||
|
||||
res.redirect.called.should.be.true;
|
||||
res.redirect.calledWith('/tag/pumpkin/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
});
|
||||
|
||||
it('Redirects to base tag page if page number is 0 with subdirectory', function () {
|
||||
frontend.__set__('config', function() {
|
||||
return {
|
||||
paths: {subdir: '/blog'}
|
||||
};
|
||||
});
|
||||
|
||||
var req = {params: {page: 0, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, null);
|
||||
|
||||
res.redirect.called.should.be.true;
|
||||
res.redirect.calledWith('/blog/tag/pumpkin/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
});
|
||||
|
||||
it('Redirects to base tag page if page number is 1 with subdirectory', function () {
|
||||
frontend.__set__('config', function() {
|
||||
return {
|
||||
paths: {subdir: '/blog'}
|
||||
};
|
||||
});
|
||||
|
||||
var req = {params: {page: 1, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, null);
|
||||
|
||||
res.redirect.called.should.be.true;
|
||||
res.redirect.calledWith('/blog/tag/pumpkin/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
});
|
||||
|
||||
it('Redirects to last page if page number too big', function (done) {
|
||||
var req = {params: {page: 4, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, done).then(function () {
|
||||
res.redirect.called.should.be.true;
|
||||
res.redirect.calledWith('/tag/pumpkin/page/3/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Redirects to last page if page number too big with subdirectory', function (done) {
|
||||
frontend.__set__('config', function() {
|
||||
return {
|
||||
paths: {subdir: '/blog'}
|
||||
};
|
||||
});
|
||||
|
||||
var req = {params: {page: 4, slug: 'pumpkin'}};
|
||||
|
||||
frontend.tag(req, res, done).then(function () {
|
||||
res.redirect.calledOnce.should.be.true;
|
||||
res.redirect.calledWith('/blog/tag/pumpkin/page/3/').should.be.true;
|
||||
res.render.called.should.be.false;
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('single', function () {
|
||||
var mockStaticPost = {
|
||||
'status': 'published',
|
||||
|
|
|
@ -486,6 +486,29 @@ describe('Core Helpers', function () {
|
|||
helpers.pageUrl(2).should.equal('/blog/page/2/');
|
||||
helpers.pageUrl(50).should.equal('/blog/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages', function () {
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.pageUrl.call(tagContext, 1).should.equal('/tag/pumpkin/');
|
||||
helpers.pageUrl.call(tagContext, 2).should.equal('/tag/pumpkin/page/2/');
|
||||
helpers.pageUrl.call(tagContext, 50).should.equal('/tag/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages with subdirectory', function () {
|
||||
helpers.__set__('config', function() {
|
||||
return {
|
||||
paths: {'subdir': '/blog'}
|
||||
};
|
||||
});
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.pageUrl.call(tagContext, 1).should.equal('/blog/tag/pumpkin/');
|
||||
helpers.pageUrl.call(tagContext, 2).should.equal('/blog/tag/pumpkin/page/2/');
|
||||
helpers.pageUrl.call(tagContext, 50).should.equal('/blog/tag/pumpkin/page/50/');
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pagination helper", function () {
|
||||
|
|
|
@ -62,6 +62,10 @@ DataGenerator.Content = {
|
|||
{
|
||||
name: "pollo",
|
||||
slug: "pollo"
|
||||
},
|
||||
{
|
||||
name: "injection",
|
||||
slug: "injection"
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -174,6 +178,13 @@ DataGenerator.forKnex = (function () {
|
|||
};
|
||||
}
|
||||
|
||||
function createPostsTags(postId, tagId) {
|
||||
return {
|
||||
post_id: postId,
|
||||
tag_id: tagId
|
||||
};
|
||||
}
|
||||
|
||||
posts = [
|
||||
createPost(DataGenerator.Content.posts[0]),
|
||||
createPost(DataGenerator.Content.posts[1]),
|
||||
|
@ -188,12 +199,14 @@ DataGenerator.forKnex = (function () {
|
|||
createTag(DataGenerator.Content.tags[0]),
|
||||
createTag(DataGenerator.Content.tags[1]),
|
||||
createTag(DataGenerator.Content.tags[2]),
|
||||
createTag(DataGenerator.Content.tags[3])
|
||||
createTag(DataGenerator.Content.tags[3]),
|
||||
createTag(DataGenerator.Content.tags[4])
|
||||
];
|
||||
|
||||
posts_tags = [
|
||||
{ post_id: 2, tag_id: 2 },
|
||||
{ post_id: 2, tag_id: 3 },
|
||||
{ post_id: 3, tag_id: 2 },
|
||||
{ post_id: 3, tag_id: 3 },
|
||||
{ post_id: 4, tag_id: 4 },
|
||||
{ post_id: 5, tag_id: 5 }
|
||||
|
@ -206,6 +219,7 @@ DataGenerator.forKnex = (function () {
|
|||
createUser: createUser,
|
||||
createGenericUser: createGenericUser,
|
||||
createUserRole: createUserRole,
|
||||
createPostsTags: createPostsTags,
|
||||
|
||||
posts: posts,
|
||||
tags: tags,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
var knex = require('../../server/models/base').knex,
|
||||
when = require('when'),
|
||||
nodefn = require('when/node/function'),
|
||||
_ = require('lodash'),
|
||||
fs = require('fs-extra'),
|
||||
path = require('path'),
|
||||
migration = require("../../server/data/migration/"),
|
||||
|
@ -50,6 +51,35 @@ function insertMorePosts(max) {
|
|||
return when.all(promises);
|
||||
}
|
||||
|
||||
function insertMorePostsTags(max) {
|
||||
max = max || 50;
|
||||
|
||||
return when.all([
|
||||
knex('posts').select('id'),
|
||||
knex('tags').select('id', 'name')
|
||||
]).then(function (results) {
|
||||
var posts = _.pluck(results[0], 'id'),
|
||||
injectionTagId = _.chain(results[1])
|
||||
.where({name: 'injection'})
|
||||
.pluck('id')
|
||||
.value()[0],
|
||||
promises = [],
|
||||
i;
|
||||
|
||||
if (max > posts.length) {
|
||||
throw new Error('Trying to add more posts_tags than the number of posts.');
|
||||
}
|
||||
|
||||
for (i = 0; i < max; i += 1) {
|
||||
promises.push(DataGenerator.forKnex.createPostsTags(posts[i], injectionTagId));
|
||||
}
|
||||
|
||||
promises.push(knex('posts_tags').insert(promises));
|
||||
|
||||
return when.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
function insertDefaultUser() {
|
||||
var users = [],
|
||||
userRoles = [];
|
||||
|
@ -90,6 +120,7 @@ module.exports = {
|
|||
insertDefaultFixtures: insertDefaultFixtures,
|
||||
insertPosts: insertPosts,
|
||||
insertMorePosts: insertMorePosts,
|
||||
insertMorePostsTags: insertMorePostsTags,
|
||||
insertDefaultUser: insertDefaultUser,
|
||||
|
||||
loadExportFixture: loadExportFixture,
|
||||
|
|
Loading…
Reference in a new issue