diff --git a/core/client/models/post.js b/core/client/models/post.js index 26f5c792fac5..f39a1b3280c2 100644 --- a/core/client/models/post.js +++ b/core/client/models/post.js @@ -67,12 +67,12 @@ parse: function (resp) { if (_.isArray(resp.posts)) { - this.limit = resp.limit; - this.currentPage = resp.page; - this.totalPages = resp.pages; - this.totalPosts = resp.total; - this.nextPage = resp.next; - this.prevPage = resp.prev; + this.limit = resp.meta.pagination.limit; + this.currentPage = resp.meta.pagination.page; + this.totalPages = resp.meta.pagination.pages; + this.totalPosts = resp.meta.pagination.total; + this.nextPage = resp.meta.pagination.next; + this.prevPage = resp.meta.pagination.prev; return resp.posts; } return resp; diff --git a/core/server/api/posts.js b/core/server/api/posts.js index 26753b967ddc..e3cd2ec86a35 100644 --- a/core/server/api/posts.js +++ b/core/server/api/posts.js @@ -19,7 +19,8 @@ posts = { // **takes:** filter / pagination parameters browse: function browse(options) { options = options || {}; - + + // only published posts if no user is present if (!this.user) { options.status = 'published'; } @@ -39,8 +40,9 @@ posts = { // **takes:** an identifier (id or slug?) read: function read(options) { options = options || {}; + + // only published posts if no user is present if (!this.user) { - // only published posts for options.status = 'published'; } diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index f58a89cd9505..e020ee4f55ed 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -33,8 +33,8 @@ function getPostPage(options) { }).then(function (page) { // A bit of a hack for situations with no content. - if (page.pages === 0) { - page.pages = 1; + if (page.meta.pagination.pages === 0) { + page.meta.pagination.pages = 1; } return page; @@ -44,14 +44,7 @@ function getPostPage(options) { 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 - } + pagination: page.meta.pagination }; } @@ -79,8 +72,8 @@ frontendControllers = { 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(page.pages === 1 ? config().paths.subdir + '/' : (config().paths.subdir + '/page/' + page.pages + '/')); + if (pageParam > page.meta.pagination.pages) { + return res.redirect(page.meta.pagination.pages === 1 ? config().paths.subdir + '/' : (config().paths.subdir + '/page/' + page.meta.pagination.pages + '/')); } // Render the page of posts @@ -116,8 +109,8 @@ frontendControllers = { 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)); + if (pageParam > page.meta.pagination.pages) { + return res.redirect(tagUrl(options.tag, page.meta.pagination.pages)); } // Render the page of posts @@ -264,9 +257,7 @@ frontendControllers = { } } - // TODO: needs refactor for multi user to not use first user as default return when.settle([ - api.users.read.call({user : 'internal'}, {id : 1}), api.settings.read('title'), api.settings.read('description'), api.settings.read('permalinks') @@ -278,13 +269,12 @@ frontendControllers = { return api.posts.browse(options).then(function (page) { - var user = result[0].value, - title = result[1].value.value, - description = result[2].value.value, - permalinks = result[3].value, + var title = result[0].value.value, + description = result[1].value.value, + permalinks = result[2].value, siteUrl = config.urlFor('home', null, true), feedUrl = config.urlFor('rss', null, true), - maxPage = page.pages, + maxPage = page.meta.pagination.pages, feedItems = [], feed; @@ -306,7 +296,7 @@ frontendControllers = { // A bit of a hack for situations with no content. if (maxPage === 0) { maxPage = 1; - page.pages = 1; + page.meta.pagination.pages = 1; } // If page is greater than number of pages we have, redirect to last page @@ -327,7 +317,7 @@ frontendControllers = { url: config.urlFor('post', {post: post, permalinks: permalinks}, true), date: post.published_at, categories: _.pluck(post.tags, 'name'), - author: user ? user.name : null + author: post.author ? post.author.name : null }, content = post.html; diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js index 8e6c3855c385..3b32f01a4c79 100644 --- a/core/server/helpers/index.js +++ b/core/server/helpers/index.js @@ -656,8 +656,8 @@ coreHelpers.pagination = function (options) { errors.logAndThrowError('All values must be defined for page, pages, limit and total'); return; } - if ((!_.isUndefined(this.pagination.next) && !_.isNumber(this.pagination.next)) - || (!_.isUndefined(this.pagination.prev) && !_.isNumber(this.pagination.prev))) { + if ((!_.isNull(this.pagination.next) && !_.isNumber(this.pagination.next)) + || (!_.isNull(this.pagination.prev) && !_.isNumber(this.pagination.prev))) { errors.logAndThrowError('Invalid value, Next/Prev must be a number'); return; } diff --git a/core/server/models/post.js b/core/server/models/post.js index 7c03a66325ed..ca9b864f331c 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -364,22 +364,29 @@ Post = ghostBookshelf.Model.extend({ // 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; + pagination = {}, + meta = {}, + data = {}; + + pagination['page'] = parseInt(opts.page, 10); + pagination['limit'] = opts.limit; + pagination['pages'] = Math.ceil(totalPosts / opts.limit); + pagination['total'] = totalPosts; + pagination['next'] = null; + pagination['prev'] = null; + + data['posts'] = postCollection.toJSON(); + data['meta'] = meta; + meta['pagination'] = pagination; + + if (pagination.pages > 1) { + if (pagination.page === 1) { + pagination.next = pagination.page + 1; + } else if (pagination.page === pagination.pages) { + pagination.prev = pagination.page - 1; } else { - data.next = data.page + 1; - data.prev = data.page - 1; + pagination.next = pagination.page + 1; + pagination.prev = pagination.page - 1; } } diff --git a/core/test/functional/routes/api/posts_test.js b/core/test/functional/routes/api/posts_test.js index 13ee9b0eff13..683a88617e21 100644 --- a/core/test/functional/routes/api/posts_test.js +++ b/core/test/functional/routes/api/posts_test.js @@ -99,6 +99,7 @@ describe('Post API', function () { testUtils.API.checkResponse(jsonResponse, 'posts'); jsonResponse.posts.should.have.length(5); testUtils.API.checkResponse(jsonResponse.posts[0], 'post'); + testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); done(); }); }); @@ -118,6 +119,7 @@ describe('Post API', function () { testUtils.API.checkResponse(jsonResponse, 'posts'); jsonResponse.posts.should.have.length(6); testUtils.API.checkResponse(jsonResponse.posts[0], 'post'); + testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); done(); }); @@ -140,6 +142,7 @@ describe('Post API', function () { testUtils.API.checkResponse(jsonResponse, 'posts'); jsonResponse.posts.should.have.length(8); testUtils.API.checkResponse(jsonResponse.posts[0], 'post'); + testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); done(); }); }); @@ -159,6 +162,7 @@ describe('Post API', function () { testUtils.API.checkResponse(jsonResponse, 'posts'); jsonResponse.posts.should.have.length(1); testUtils.API.checkResponse(jsonResponse.posts[0], 'post'); + testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); done(); }); }); @@ -178,6 +182,7 @@ describe('Post API', function () { testUtils.API.checkResponse(jsonResponse, 'posts'); jsonResponse.posts.should.have.length(1); testUtils.API.checkResponse(jsonResponse.posts[0], 'post'); + testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination'); done(); }); }); diff --git a/core/test/integration/model/model_posts_spec.js b/core/test/integration/model/model_posts_spec.js index 0ed011d37459..c52ca6b9bf74 100644 --- a/core/test/integration/model/model_posts_spec.js +++ b/core/test/integration/model/model_posts_spec.js @@ -356,72 +356,72 @@ describe('Post Model', function () { }).then(function () { return PostModel.findPage({page: 2}); }).then(function (paginationResult) { - paginationResult.page.should.equal(2); - paginationResult.limit.should.equal(15); + paginationResult.meta.pagination.page.should.equal(2); + paginationResult.meta.pagination.limit.should.equal(15); + paginationResult.meta.pagination.pages.should.equal(4); paginationResult.posts.length.should.equal(15); - paginationResult.pages.should.equal(4); return PostModel.findPage({page: 5}); }).then(function (paginationResult) { - paginationResult.page.should.equal(5); - paginationResult.limit.should.equal(15); + paginationResult.meta.pagination.page.should.equal(5); + paginationResult.meta.pagination.limit.should.equal(15); + paginationResult.meta.pagination.pages.should.equal(4); paginationResult.posts.length.should.equal(0); - paginationResult.pages.should.equal(4); return PostModel.findPage({limit: 30}); }).then(function (paginationResult) { - paginationResult.page.should.equal(1); - paginationResult.limit.should.equal(30); + paginationResult.meta.pagination.page.should.equal(1); + paginationResult.meta.pagination.limit.should.equal(30); + paginationResult.meta.pagination.pages.should.equal(2); paginationResult.posts.length.should.equal(30); - paginationResult.pages.should.equal(2); return PostModel.findPage({limit: 10, staticPages: true}); }).then(function (paginationResult) { - paginationResult.page.should.equal(1); - paginationResult.limit.should.equal(10); + paginationResult.meta.pagination.page.should.equal(1); + paginationResult.meta.pagination.limit.should.equal(10); + paginationResult.meta.pagination.pages.should.equal(1); paginationResult.posts.length.should.equal(1); - paginationResult.pages.should.equal(1); return PostModel.findPage({limit: 10, page: 2, status: 'all'}); }).then(function (paginationResult) { - paginationResult.pages.should.equal(11); + paginationResult.meta.pagination.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.meta.pagination.page.should.equal(1); + paginationResult.meta.pagination.limit.should.equal(15); + paginationResult.meta.pagination.pages.should.equal(1); paginationResult.aspect.tag.name.should.equal('bacon'); paginationResult.aspect.tag.slug.should.equal('bacon'); + paginationResult.posts.length.should.equal(2); 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.meta.pagination.page.should.equal(1); + paginationResult.meta.pagination.limit.should.equal(15); + paginationResult.meta.pagination.pages.should.equal(1); paginationResult.aspect.tag.name.should.equal('kitchen sink'); paginationResult.aspect.tag.slug.should.equal('kitchen-sink'); + paginationResult.posts.length.should.equal(2); 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.meta.pagination.page.should.equal(1); + paginationResult.meta.pagination.limit.should.equal(15); + paginationResult.meta.pagination.pages.should.equal(2); paginationResult.aspect.tag.name.should.equal('injection'); paginationResult.aspect.tag.slug.should.equal('injection'); + paginationResult.posts.length.should.equal(15); 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(11); - paginationResult.pages.should.equal(2); + paginationResult.meta.pagination.page.should.equal(2); + paginationResult.meta.pagination.limit.should.equal(15); + paginationResult.meta.pagination.pages.should.equal(2); paginationResult.aspect.tag.name.should.equal('injection'); paginationResult.aspect.tag.slug.should.equal('injection'); + paginationResult.posts.length.should.equal(11); done(); }).then(null, done); diff --git a/core/test/unit/frontend_spec.js b/core/test/unit/frontend_spec.js index 6ec1928f4f71..1cdcacf09d83 100644 --- a/core/test/unit/frontend_spec.js +++ b/core/test/unit/frontend_spec.js @@ -40,7 +40,7 @@ describe('Frontend Controller', function () { }; sandbox.stub(api.posts, 'browse', function () { - return when({posts: {}, pages: 3}); + return when({posts: {}, meta: {pagination: { pages: 3}}}); }); apiSettingsStub = sandbox.stub(api.settings, 'read'); @@ -183,10 +183,14 @@ describe('Frontend Controller', function () { beforeEach(function () { sandbox.stub(api.posts, 'browse', function (args) { return when({ - posts: mockPosts, - page: 1, - pages: 1, - aspect: {tag: mockTags[0]} + posts: mockPosts, + meta: { + pagination: { + page: 1, + pages: 1, + }, + }, + aspect: {tag: mockTags[0]} }); }); @@ -254,7 +258,7 @@ describe('Frontend Controller', function () { }; sandbox.stub(api.posts, 'browse', function () { - return when({posts: {}, pages: 3}); + return when({posts: {}, meta: {pagination: { pages: 3}}}); }); apiSettingsStub = sandbox.stub(api.settings, 'read'); @@ -878,7 +882,7 @@ describe('Frontend Controller', function () { }; sandbox.stub(api.posts, 'browse', function () { - return when({posts: {}, pages: 3}); + return when({posts: {}, meta: {pagination: { pages: 3}}}); }); apiUsersStub = sandbox.stub(api.users, 'read').returns(when({})); diff --git a/core/test/unit/server_helpers_index_spec.js b/core/test/unit/server_helpers_index_spec.js index ea6318b0446c..e7727a4dd5f6 100644 --- a/core/test/unit/server_helpers_index_spec.js +++ b/core/test/unit/server_helpers_index_spec.js @@ -682,7 +682,7 @@ describe('Core Helpers', function () { }); it('can render single page with no pagination necessary', function () { - var rendered = helpers.pagination.call({pagination: {page: 1, prev: undefined, next: undefined, limit: 15, total: 8, pages: 1}}); + var rendered = helpers.pagination.call({pagination: {page: 1, prev: null, next: null, limit: 15, total: 8, pages: 1}}); should.exist(rendered); // strip out carriage returns and compare. rendered.string.should.match(paginationRegex); @@ -693,7 +693,7 @@ describe('Core Helpers', function () { }); it('can render first page of many with older posts link', function () { - var rendered = helpers.pagination.call({pagination: {page: 1, prev: undefined, next: 2, limit: 15, total: 8, pages: 3}}); + var rendered = helpers.pagination.call({pagination: {page: 1, prev: null, next: 2, limit: 15, total: 8, pages: 3}}); should.exist(rendered); rendered.string.should.match(paginationRegex); @@ -715,7 +715,7 @@ describe('Core Helpers', function () { }); it('can render last page of many with newer posts link', function () { - var rendered = helpers.pagination.call({pagination: {page: 3, prev: 2, next: undefined, limit: 15, total: 8, pages: 3}}); + var rendered = helpers.pagination.call({pagination: {page: 3, prev: 2, next: null, limit: 15, total: 8, pages: 3}}); should.exist(rendered); rendered.string.should.match(paginationRegex); @@ -733,7 +733,7 @@ describe('Core Helpers', function () { }; }; - runErrorTest({pagination: {page: 3, prev: true, next: undefined, limit: 15, total: 8, pages: 3}}) + runErrorTest({pagination: {page: 3, prev: true, next: null, limit: 15, total: 8, pages: 3}}) .should.throwError('Invalid value, Next/Prev must be a number'); runErrorTest({pagination: {page: 3, prev: 2, next: true, limit: 15, total: 8, pages: 3}}) .should.throwError('Invalid value, Next/Prev must be a number'); @@ -747,13 +747,13 @@ describe('Core Helpers', function () { runErrorTest({pagination: {page: 3, limit: 15, total: 8}}) .should.throwError('All values must be defined for page, pages, limit and total'); - runErrorTest({pagination: {page: null, limit: 15, total: 8, pages: 3}}) + runErrorTest({pagination: {page: null, prev: null, next: null, limit: 15, total: 8, pages: 3}}) .should.throwError('Invalid value, check page, pages, limit and total are numbers'); - runErrorTest({pagination: {page: 1, limit: null, total: 8, pages: 3}}) + runErrorTest({pagination: {page: 1, prev: null, next: null, limit: null, total: 8, pages: 3}}) .should.throwError('Invalid value, check page, pages, limit and total are numbers'); - runErrorTest({pagination: {page: 1, limit: 15, total: null, pages: 3}}) + runErrorTest({pagination: {page: 1, prev: null, next: null, limit: 15, total: null, pages: 3}}) .should.throwError('Invalid value, check page, pages, limit and total are numbers'); - runErrorTest({pagination: {page: 1, limit: 15, total: 8, pages: null}}) + runErrorTest({pagination: {page: 1, prev: null, next: null, limit: 15, total: 8, pages: null}}) .should.throwError('Invalid value, check page, pages, limit and total are numbers'); }); }); diff --git a/core/test/utils/api.js b/core/test/utils/api.js index 597ebb766411..817bb8b1265e 100644 --- a/core/test/utils/api.js +++ b/core/test/utils/api.js @@ -5,7 +5,8 @@ var _ = require('lodash'), port = '2369'; schema = "http://", expectedProperties = { - posts: ['posts', 'page', 'limit', 'pages', 'total'], + posts: ['posts', 'meta'], + pagination: ['page', 'limit', 'pages', 'total', 'next', 'prev'], post: ['id', 'uuid', 'title', 'slug', 'markdown', 'html', 'meta_title', 'meta_description', 'featured', 'image', 'status', 'language', 'author_id', 'created_at', 'created_by', 'updated_at', 'updated_by', 'published_at', 'published_by', 'page', 'author', 'tags', 'fields'],