0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

timezones: support permalinks based on current timezone

issue #6406
- redirect date permalink if timezone changed
- create permalinks based on blog TZ
- post-lookup fn is now more generic
This commit is contained in:
kirrg001 2016-05-18 16:27:54 +02:00
parent 38a261daac
commit ff132fd934
7 changed files with 78 additions and 51 deletions

1
core/client Submodule

@ -0,0 +1 @@
Subproject commit 39622c4e284554d0ac1f6dbe3de90a6e6943a6ed

View file

@ -1,7 +1,7 @@
// Contains all path information to be used throughout
// the codebase.
var moment = require('moment'),
var moment = require('moment-timezone'),
_ = require('lodash'),
ghostConfig = '',
// @TODO: unify this with routes.apiBaseUrl
@ -95,18 +95,20 @@ function createUrl(urlPath, absolute, secure) {
return urlJoin(base, urlPath);
}
// ## urlPathForPost
// Always sync
// Creates the url path for a post, given a post and a permalink
// Parameters:
// - post - a json object representing a post
/**
* creates the url path for a post based on blog timezone and permalink pattern
*
* @param {JSON} post
* @returns {string}
*/
function urlPathForPost(post) {
var output = '',
permalinks = ghostConfig.theme.permalinks,
publishedAtMoment = moment.tz(post.published_at, ghostConfig.theme.timezone),
tags = {
year: function () { return moment(post.published_at).format('YYYY'); },
month: function () { return moment(post.published_at).format('MM'); },
day: function () { return moment(post.published_at).format('DD'); },
year: function () { return publishedAtMoment.format('YYYY'); },
month: function () { return publishedAtMoment.format('MM'); },
day: function () { return publishedAtMoment.format('DD'); },
author: function () { return post.author.slug; },
slug: function () { return post.slug; },
id: function () { return post.id; }

View file

@ -66,11 +66,21 @@ frontendControllers = {
return next();
}
// If we're ready to render the page but the last param is 'edit' then we'll send you to the edit page.
// CASE: we only support /:slug format for pages
if (post.page && post.url !== req.path) {
return next();
}
// CASE: last param is of url is /edit, redirect to admin
if (lookup.isEditURL) {
return res.redirect(config.paths.subdir + '/ghost/editor/' + post.id + '/');
}
// CASE: permalink is not valid anymore, we redirect him permanently to the correct one
if (post.url !== req.path) {
return res.redirect(301, post.url);
}
setRequestIsSecure(req, post);
filters.doFilter('prePostsRender', post, res.locals)

View file

@ -16,28 +16,32 @@ function postLookup(postUrl) {
postPermalink = config.theme.permalinks,
pagePermalink = '/:slug/',
isEditURL = false,
matchFunc,
matchFuncPost,
matchFuncPage,
postParams,
pageParams,
params;
// Convert saved permalink into a path-match function
matchFunc = routeMatch(getEditFormat(postPermalink));
params = matchFunc(postPath);
matchFuncPost = routeMatch(getEditFormat(postPermalink));
postParams = matchFuncPost(postPath);
// Check if the path matches the permalink structure.
// If there are no matches found, test to see if this is a page instead
if (params === false) {
matchFunc = routeMatch(getEditFormat(pagePermalink));
params = matchFunc(postPath);
if (postParams === false) {
matchFuncPage = routeMatch(getEditFormat(pagePermalink));
pageParams = matchFuncPage(postPath);
}
// If there are still no matches then return empty.
if (params === false) {
if (pageParams === false) {
return Promise.resolve();
}
params = postParams || pageParams;
// If params contains edit, and it is equal to 'edit' this is an edit URL
if (params.edit && params.edit.toLowerCase() === 'edit') {
postPath = postPath.replace(params.edit + '/', '');
isEditURL = true;
} else if (params.edit !== undefined) {
// Unknown string in URL, return empty
@ -53,9 +57,12 @@ function postLookup(postUrl) {
return api.posts.read(params).then(function then(result) {
var post = result.posts[0];
// If there is no post, or the post has no URL, or it isn't a match for our original lookup, return empty
// This also catches the case where we use the pagePermalink but the post is not a page
if (!post || !post.url || post.url !== postPath) {
if (!post) {
return Promise.resolve();
}
// CASE: we originally couldn't match the post based on date permalink and we tried to check if its a page
if (!post.page && !postParams) {
return Promise.resolve();
}

View file

@ -516,7 +516,6 @@ describe('Frontend Routing', function () {
});
it('should load a post with date permalink', function (done) {
// get today's date
var date = moment().format('YYYY/MM/DD');
request.get('/' + date + '/welcome-to-ghost/')
@ -525,6 +524,20 @@ describe('Frontend Routing', function () {
.end(doEnd(done));
});
it('expect redirect because of wrong/old permalink prefix', function (done) {
var date = moment().format('YYYY/MM/DD');
request.get('/2016/04/01/welcome-to-ghost/')
.expect('Content-Type', /html/)
.end(function (err, res) {
res.status.should.eql(301);
request.get('/' + date + '/welcome-to-ghost/')
.expect(200)
.expect('Content-Type', /html/)
.end(doEnd(done));
});
});
it('should serve RSS with date permalink', function (done) {
request.get('/rss/')
.expect('Content-Type', 'text/xml; charset=utf-8')

View file

@ -384,8 +384,8 @@ describe('Config', function () {
});
describe('urlPathForPost', function () {
it('should output correct url for post', function () {
configUtils.set({theme: {permalinks: '/:slug/'}});
it('permalink is /:slug/', function () {
configUtils.set({theme: {permalinks: '/:slug/', timezone: 'Europe/Dublin'}});
var testData = testUtils.DataGenerator.Content.posts[2],
postLink = '/short-and-sweet/';
@ -393,20 +393,17 @@ describe('Config', function () {
config.urlPathForPost(testData).should.equal(postLink);
});
it('should output correct url for post with date permalink', function () {
configUtils.set({theme: {permalinks: '/:year/:month/:day/:slug/'}});
it('permalink is /:year/:month/:day/:slug, blog timezone is Los Angeles', function () {
configUtils.set({theme: {permalinks: '/:year/:month/:day/:slug/', timezone: 'America/Los_Angeles'}});
var testData = testUtils.DataGenerator.Content.posts[2],
today = testData.published_at,
dd = ('0' + today.getDate()).slice(-2),
mm = ('0' + (today.getMonth() + 1)).slice(-2),
yyyy = today.getFullYear(),
postLink = '/' + yyyy + '/' + mm + '/' + dd + '/short-and-sweet/';
postLink = '/2016/05/17/short-and-sweet/';
testData.published_at = new Date('2016-05-18T06:30:00.000Z');
config.urlPathForPost(testData).should.equal(postLink);
});
it('should output correct url for page with date permalink', function () {
configUtils.set({theme: {permalinks: '/:year/:month/:day/:slug/'}});
it('post is page, no permalink usage allowed at all', function () {
configUtils.set({theme: {permalinks: '/:year/:month/:day/:slug/', timezone: 'America/Los_Angeles'}});
var testData = testUtils.DataGenerator.Content.posts[5],
postLink = '/static-page-test/';
@ -414,16 +411,23 @@ describe('Config', function () {
config.urlPathForPost(testData).should.equal(postLink);
});
it('should output correct url for post with complex permalink', function () {
configUtils.set({theme: {permalinks: '/:year/:id/:author/'}});
it('permalink is /:year/:id:/:author', function () {
configUtils.set({theme: {permalinks: '/:year/:id/:author/', timezone: 'America/Los_Angeles'}});
var testData = _.extend(
{}, testUtils.DataGenerator.Content.posts[2], {id: 3}, {author: {slug: 'joe-bloggs'}}
),
today = testData.published_at,
yyyy = today.getFullYear(),
postLink = '/' + yyyy + '/3/joe-bloggs/';
var testData = _.merge(testUtils.DataGenerator.Content.posts[2], {id: 3}, {author: {slug: 'joe-blog'}}),
postLink = '/2015/3/joe-blog/';
testData.published_at = new Date('2016-01-01T00:00:00.000Z');
config.urlPathForPost(testData).should.equal(postLink);
});
it('permalink is /:year/:id:/:author', function () {
configUtils.set({theme: {permalinks: '/:year/:id/:author/', timezone: 'Europe/Berlin'}});
var testData = _.merge(testUtils.DataGenerator.Content.posts[2], {id: 3}, {author: {slug: 'joe-blog'}}),
postLink = '/2016/3/joe-blog/';
testData.published_at = new Date('2016-01-01T00:00:00.000Z');
config.urlPathForPost(testData).should.equal(postLink);
});
});

View file

@ -426,16 +426,6 @@ describe('Frontend Controller', function () {
frontend.single(req, res, failTest(done));
});
it('will NOT render post via /YYYY/MM/DD/:slug/ with non-matching date in url', function (done) {
var date = moment(mockPosts[1].published_at).subtract(1, 'days').format('YYYY/MM/DD');
req.path = '/' + [date, mockPosts[1].posts[0].slug].join('/') + '/';
frontend.single(req, res, function () {
res.render.called.should.be.false();
done();
});
});
it('will NOT render post via /:slug/', function (done) {
req.path = '/' + mockPosts[1].posts[0].slug + '/';