mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-25 02:31:59 -05:00
Merge pull request #1793 from hswolff/fix-post-and-page-routing
Fix routing of posts and static pages
This commit is contained in:
commit
8281d5a21c
8 changed files with 232 additions and 117 deletions
|
@ -3,11 +3,11 @@
|
||||||
|
|
||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
when = require('when'),
|
when = require('when'),
|
||||||
|
config = require('../config'),
|
||||||
errors = require('../errorHandling'),
|
errors = require('../errorHandling'),
|
||||||
db = require('./db'),
|
db = require('./db'),
|
||||||
settings = require('./settings'),
|
settings = require('./settings'),
|
||||||
notifications = require('./notifications'),
|
notifications = require('./notifications'),
|
||||||
config = require('../config'),
|
|
||||||
posts = require('./posts'),
|
posts = require('./posts'),
|
||||||
users = require('./users'),
|
users = require('./users'),
|
||||||
tags = require('./tags'),
|
tags = require('./tags'),
|
||||||
|
@ -49,34 +49,15 @@ requestHandler = function (apiMethod) {
|
||||||
var options = _.extend(req.body, req.query, req.params),
|
var options = _.extend(req.body, req.query, req.params),
|
||||||
apiContext = {
|
apiContext = {
|
||||||
user: req.session && req.session.user
|
user: req.session && req.session.user
|
||||||
},
|
};
|
||||||
postRouteIndex,
|
|
||||||
i;
|
|
||||||
|
|
||||||
settings.read('permalinks').then(function (permalinks) {
|
return apiMethod.call(apiContext, options).then(function (result) {
|
||||||
// If permalinks have changed, find old post route
|
invalidateCache(req, res, result);
|
||||||
if (req.body.permalinks && req.body.permalinks !== permalinks) {
|
res.json(result || {});
|
||||||
for (i = 0; i < req.app.routes.get.length; i += 1) {
|
}, function (error) {
|
||||||
if (req.app.routes.get[i].path === config.paths().subdir + permalinks) {
|
var errorCode = error.errorCode || 500,
|
||||||
postRouteIndex = i;
|
errorMsg = {error: _.isString(error) ? error : (_.isObject(error) ? error.message : 'Unknown API Error')};
|
||||||
break;
|
res.json(errorCode, errorMsg);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiMethod.call(apiContext, options).then(function (result) {
|
|
||||||
// Reload post route
|
|
||||||
if (postRouteIndex) {
|
|
||||||
req.app.get(permalinks, req.app.routes.get.splice(postRouteIndex, 1)[0].callbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidateCache(req, res, result);
|
|
||||||
res.json(result || {});
|
|
||||||
}, function (error) {
|
|
||||||
var errorCode = error.errorCode || 500,
|
|
||||||
errorMsg = {error: _.isString(error) ? error : (_.isObject(error) ? error.message : 'Unknown API Error')};
|
|
||||||
res.json(errorCode, errorMsg);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,36 +69,55 @@ frontendControllers = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'single': function (req, res, next) {
|
'single': function (req, res, next) {
|
||||||
api.posts.read(_.pick(req.params, ['id', 'slug', 'page'])).then(function (post) {
|
// From route check if a date was parsed
|
||||||
if (post) {
|
// from the regex
|
||||||
|
var dateInSlug = req.params[0] !== '';
|
||||||
|
when.join(
|
||||||
|
api.settings.read('permalinks'),
|
||||||
|
api.posts.read({slug: req.params[1]})
|
||||||
|
).then(function (promises) {
|
||||||
|
var permalink = promises[0].value,
|
||||||
|
post = promises[1];
|
||||||
|
|
||||||
|
function render() {
|
||||||
filters.doFilter('prePostsRender', post).then(function (post) {
|
filters.doFilter('prePostsRender', post).then(function (post) {
|
||||||
api.settings.read('activeTheme').then(function (activeTheme) {
|
api.settings.read('activeTheme').then(function (activeTheme) {
|
||||||
var paths = config.paths().availableThemes[activeTheme.value];
|
var paths = config.paths().availableThemes[activeTheme.value],
|
||||||
if (post.page && paths.hasOwnProperty('page')) {
|
view = post.page && paths.hasOwnProperty('page') ? 'page' : 'post';
|
||||||
res.render('page', {post: post});
|
res.render(view, {post: post});
|
||||||
} else {
|
|
||||||
res.render('post', {post: post});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A page can only be rendered when there is no date in the url.
|
||||||
|
// A post can either be rendered with a date in the url
|
||||||
|
// depending on the permalink setting.
|
||||||
|
// For all other conditions return 404.
|
||||||
|
if (post.page === 1 && dateInSlug === false) {
|
||||||
|
return render();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.page === 0) {
|
||||||
|
// Handle post rendering
|
||||||
|
if ((permalink === '/:slug/' && dateInSlug === false) ||
|
||||||
|
(permalink !== '/:slug/' && dateInSlug === true)) {
|
||||||
|
return render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
|
||||||
|
|
||||||
}).otherwise(function (err) {
|
}).otherwise(function (err) {
|
||||||
var e = new Error(err.message);
|
var e = new Error(err.message);
|
||||||
e.status = err.errorCode;
|
e.status = err.errorCode;
|
||||||
return next(e);
|
return next(e);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'post': function (req, res, next) {
|
|
||||||
req.params.page = 0;
|
|
||||||
return frontendControllers.single(req, res, next);
|
|
||||||
},
|
|
||||||
'page': function (req, res, next) {
|
|
||||||
req.params.page = 1;
|
|
||||||
return frontendControllers.single(req, res, next);
|
|
||||||
},
|
|
||||||
'rss': function (req, res, next) {
|
'rss': function (req, res, next) {
|
||||||
// Initialize RSS
|
// Initialize RSS
|
||||||
var siteUrl = config().url,
|
var siteUrl = config().url,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
var admin = require('../controllers/admin'),
|
var admin = require('../controllers/admin'),
|
||||||
api = require('../api'),
|
|
||||||
config = require('../config'),
|
config = require('../config'),
|
||||||
middleware = require('../middleware').middleware,
|
middleware = require('../middleware').middleware;
|
||||||
url = require('url');
|
|
||||||
|
|
||||||
module.exports = function (server) {
|
module.exports = function (server) {
|
||||||
var subdir = config.paths().subdir;
|
var subdir = config.paths().subdir;
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
var frontend = require('../controllers/frontend'),
|
var frontend = require('../controllers/frontend');
|
||||||
api = require('../api');
|
|
||||||
module.exports = function (server) {
|
module.exports = function (server) {
|
||||||
|
/*jslint regexp: true */
|
||||||
|
|
||||||
// ### Frontend routes
|
// ### Frontend routes
|
||||||
/* TODO: dynamic routing, homepage generator, filters ETC ETC */
|
/* TODO: dynamic routing, homepage generator, filters ETC ETC */
|
||||||
server.get('/rss/', frontend.rss);
|
server.get('/rss/', frontend.rss);
|
||||||
server.get('/rss/:page/', frontend.rss);
|
server.get('/rss/:page/', frontend.rss);
|
||||||
server.get('/page/:page/', frontend.homepage);
|
server.get('/page/:page/', frontend.homepage);
|
||||||
|
// Only capture the :slug part of the URL
|
||||||
|
// This regex will always have two capturing groups,
|
||||||
|
// one for date, and one for the slug.
|
||||||
|
// Examples:
|
||||||
|
// Given `/plain-slug/` the req.params would be ['', 'plain-slug']
|
||||||
|
// Given `/2012/12/24/plain-slug/` the req.params would be ['2012/12/24', 'plain-slug']
|
||||||
|
server.get(/^\/([0-9\/]*)([^\/.]*)\/$/, frontend.single);
|
||||||
server.get('/', frontend.homepage);
|
server.get('/', frontend.homepage);
|
||||||
|
};
|
||||||
api.settings.read('permalinks').then(function (permalinks) {
|
|
||||||
if (permalinks.value !== '/:slug/') {
|
|
||||||
server.get('/:slug/', frontend.page);
|
|
||||||
server.get(permalinks.value, frontend.post);
|
|
||||||
} else {
|
|
||||||
server.get(permalinks.value, frontend.single);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -209,6 +209,17 @@ CasperTest.Routines = (function () {
|
||||||
}, id);
|
}, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function togglePermalinks(test) {
|
||||||
|
casper.thenOpen(url + "ghost/settings/");
|
||||||
|
casper.thenClick('#permalinks');
|
||||||
|
casper.thenClick('.button-save');
|
||||||
|
casper.waitFor(function successNotification() {
|
||||||
|
return this.evaluate(function () {
|
||||||
|
return document.querySelectorAll('.js-bb-notification section').length > 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function _createRunner(fn) {
|
function _createRunner(fn) {
|
||||||
fn.run = function run(test) {
|
fn.run = function run(test) {
|
||||||
var routine = this;
|
var routine = this;
|
||||||
|
@ -225,7 +236,8 @@ CasperTest.Routines = (function () {
|
||||||
register: _createRunner(register),
|
register: _createRunner(register),
|
||||||
login: _createRunner(login),
|
login: _createRunner(login),
|
||||||
logout: _createRunner(logout),
|
logout: _createRunner(logout),
|
||||||
deletePost: _createRunner(deletePost)
|
deletePost: _createRunner(deletePost),
|
||||||
|
togglePermalinks: _createRunner(togglePermalinks)
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
|
@ -28,18 +28,8 @@ CasperTest.begin('Ensure that RSS is available', 11, function suite(test) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
CasperTest.begin('Ensures dated permalinks works with RSS', 4, function suite(test) {
|
CasperTest.begin('Ensures dated permalinks works with RSS', 2, function suite(test) {
|
||||||
casper.thenOpen(url + "ghost/settings/", function testTitleAndUrl() {
|
CasperTest.Routines.togglePermalinks.run(test);
|
||||||
test.assertTitle("Ghost Admin", "Ghost admin has no title");
|
|
||||||
test.assertUrlMatch(/ghost\/settings\/general\/$/, "Ghost doesn't require login this time");
|
|
||||||
});
|
|
||||||
casper.thenClick('#permalinks');
|
|
||||||
casper.thenClick('.button-save');
|
|
||||||
casper.waitFor(function successNotification() {
|
|
||||||
return this.evaluate(function () {
|
|
||||||
return document.querySelectorAll('.js-bb-notification section').length > 0;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
casper.thenOpen(url + 'rss/', function (response) {
|
casper.thenOpen(url + 'rss/', function (response) {
|
||||||
var content = this.getPageContent(),
|
var content = this.getPageContent(),
|
||||||
today = new Date(),
|
today = new Date(),
|
||||||
|
|
|
@ -3,7 +3,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*globals CasperTest, casper, __utils__, url, testPost, falseUser, email */
|
/*globals CasperTest, casper, __utils__, url, testPost, falseUser, email */
|
||||||
|
|
||||||
|
// Tests when permalinks is set to date (changed in feed_test)
|
||||||
|
CasperTest.begin('Post page does not load as slug', 2, function suite(test) {
|
||||||
|
casper.start(url + 'welcome-to-ghost', function then(response) {
|
||||||
|
test.assertTitle('404 — Page Not Found', 'The post should return 404 page');
|
||||||
|
test.assertElementCount('.content .post', 0, 'There is no post on this page');
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
CasperTest.begin('Toggle permalinks', 0, function suite(test) {
|
||||||
|
CasperTest.Routines.togglePermalinks.run(test);
|
||||||
|
});
|
||||||
|
|
||||||
CasperTest.begin('Post page loads', 3, function suite(test) {
|
CasperTest.begin('Post page loads', 3, function suite(test) {
|
||||||
|
CasperTest.Routines.togglePermalinks.run(test);
|
||||||
casper.start(url + 'welcome-to-ghost', function then(response) {
|
casper.start(url + 'welcome-to-ghost', function then(response) {
|
||||||
test.assertTitle('Welcome to Ghost', 'The post should have a title and it should be "Welcome to Ghost"');
|
test.assertTitle('Welcome to Ghost', 'The post should have a title and it should be "Welcome to Ghost"');
|
||||||
test.assertElementCount('.content .post', 1, 'There is exactly one post on this page');
|
test.assertElementCount('.content .post', 1, 'There is exactly one post on this page');
|
||||||
|
|
|
@ -13,7 +13,7 @@ describe('Frontend Controller', function () {
|
||||||
|
|
||||||
var ghost,
|
var ghost,
|
||||||
sandbox,
|
sandbox,
|
||||||
apiStub;
|
apiSettingsStub;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
|
@ -48,16 +48,16 @@ describe('Frontend Controller', function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
apiStub = sandbox.stub(api.posts , 'read', function (args) {
|
sandbox.stub(api.posts , 'read', function (args) {
|
||||||
return when(args.id === 1 ? mockStaticPost : mockPost);
|
return when(args.slug === mockStaticPost.slug ? mockStaticPost : mockPost);
|
||||||
});
|
});
|
||||||
|
|
||||||
sandbox.stub(api.settings , 'read', function () {
|
apiSettingsStub = sandbox.stub(api.settings , 'read');
|
||||||
return when({
|
|
||||||
'key': 'activeTheme',
|
apiSettingsStub.withArgs('activeTheme').returns(when({
|
||||||
'value': 'casper'
|
'key': 'activeTheme',
|
||||||
});
|
'value': 'casper'
|
||||||
});
|
}));
|
||||||
|
|
||||||
sandbox.stub(config , 'paths', function () {
|
sandbox.stub(config , 'paths', function () {
|
||||||
return {
|
return {
|
||||||
|
@ -74,47 +74,148 @@ describe('Frontend Controller', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can render a static page', function(done) {
|
describe('permalink set to slug', function() {
|
||||||
var req = {
|
beforeEach(function() {
|
||||||
params: {
|
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||||
'id': 1,
|
value: '/:slug/'
|
||||||
'slug': 'test-static-page'
|
}));
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
var res = {
|
it('can render a static page', function(done) {
|
||||||
render: function(view, context) {
|
var req = {
|
||||||
assert.equal(view, 'page');
|
params: ['', 'test-static-page']
|
||||||
assert(context.post, 'Context object has post attribute');
|
};
|
||||||
assert.equal(context.post, mockStaticPost);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
frontend.single(req, res, null);
|
var res = {
|
||||||
|
render: function(view, context) {
|
||||||
|
assert.equal(view, 'page');
|
||||||
|
assert.equal(context.post, mockStaticPost);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
});
|
frontend.single(req, res, null);
|
||||||
|
});
|
||||||
|
|
||||||
it('can render a normal post', function(done) {
|
it('won\'t render a static page accessed as a date url', function(done) {
|
||||||
var req = {
|
var req = {
|
||||||
params: {
|
params: ['2012/12/30', 'test-static-page']
|
||||||
'id': 2,
|
};
|
||||||
'slug': 'test-normal-post'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var res = {
|
var res = {
|
||||||
render: function(view, context) {
|
render: sinon.spy()
|
||||||
assert.equal(view, 'post');
|
};
|
||||||
assert(context.post, 'Context object has post attribute');
|
|
||||||
assert.equal(context.post, mockPost);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
frontend.single(req, res, null);
|
frontend.single(req, res, function() {
|
||||||
|
res.render.called.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can render a normal post', function(done) {
|
||||||
|
var req = {
|
||||||
|
params: ['', 'test-normal-post']
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
render: function(view, context) {
|
||||||
|
assert.equal(view, 'post');
|
||||||
|
assert(context.post, 'Context object has post attribute');
|
||||||
|
assert.equal(context.post, mockPost);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend.single(req, res, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('won\'t render a normal post accessed as a date url', function(done) {
|
||||||
|
var req = {
|
||||||
|
params: ['2012/12/30', 'test-normal-post']
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
render: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend.single(req, res, function() {
|
||||||
|
res.render.called.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('permalink set to date', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||||
|
value: '/:year/:month/:day/:slug/'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can render a static page', function(done) {
|
||||||
|
var req = {
|
||||||
|
params: ['', 'test-static-page']
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
render: function(view, context) {
|
||||||
|
assert.equal(view, 'page');
|
||||||
|
assert.equal(context.post, mockStaticPost);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend.single(req, res, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('won\'t render a static page accessed as a date url', function(done) {
|
||||||
|
var req = {
|
||||||
|
params: ['2012/12/30', 'test-static-page']
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
render: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend.single(req, res, function() {
|
||||||
|
res.render.called.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can render a normal post', function(done) {
|
||||||
|
var req = {
|
||||||
|
params: ['2012/12/30', 'test-normal-post']
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
render: function(view, context) {
|
||||||
|
assert.equal(view, 'post');
|
||||||
|
assert(context.post, 'Context object has post attribute');
|
||||||
|
assert.equal(context.post, mockPost);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend.single(req, res, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('won\'t render a normal post accessed as a slug url', function(done) {
|
||||||
|
var req = {
|
||||||
|
params: ['', 'test-normal-post']
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
render: sinon.spy()
|
||||||
|
};
|
||||||
|
|
||||||
|
frontend.single(req, res, function() {
|
||||||
|
res.render.called.should.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
Loading…
Add table
Reference in a new issue