mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Fix urlFor to handle secure correctly
issue #6270 - Exposed getBaseUrl on the config class. - Fix formatting config index as array was more then 140 characters long. - Updated getBaseUrl to handle secure by replacing http with https if true. - Fixed ghost_head helper to output canonical base url no https. - Fixed ghost_head helper to set secure correctly for the rss link. - Fixed navigation helper to pass secure in each nav item, so that urlFor can u$ - Fixed {{url}} to pass secure correctly to config.urlFor. - Fixed test to use urlSSL over https besides for canonical. - Add tests for {{url}} and to make sure they output https for absolute and secure. - Update twitter and og url to use the canonical url.
This commit is contained in:
parent
dfa74ffcd5
commit
e4c52a6915
7 changed files with 81 additions and 13 deletions
|
@ -33,6 +33,7 @@ function ConfigManager(config) {
|
||||||
this.urlFor = configUrl.urlFor;
|
this.urlFor = configUrl.urlFor;
|
||||||
this.urlPathForPost = configUrl.urlPathForPost;
|
this.urlPathForPost = configUrl.urlPathForPost;
|
||||||
this.apiUrl = configUrl.apiUrl;
|
this.apiUrl = configUrl.apiUrl;
|
||||||
|
this.getBaseUrl = configUrl.getBaseUrl;
|
||||||
|
|
||||||
// If we're given an initial config object then we can set it.
|
// If we're given an initial config object then we can set it.
|
||||||
if (config && _.isObject(config)) {
|
if (config && _.isObject(config)) {
|
||||||
|
@ -214,7 +215,10 @@ ConfigManager.prototype.set = function (config) {
|
||||||
// Used by generateSlug to generate slugs for posts, tags, users, ..
|
// Used by generateSlug to generate slugs for posts, tags, users, ..
|
||||||
// reserved slugs are reserved but can be extended/removed by apps
|
// reserved slugs are reserved but can be extended/removed by apps
|
||||||
// protected slugs cannot be changed or removed
|
// protected slugs cannot be changed or removed
|
||||||
reserved: ['admin', 'app', 'apps', 'archive', 'archives', 'categories', 'category', 'dashboard', 'feed', 'ghost-admin', 'login', 'logout', 'page', 'pages', 'post', 'posts', 'public', 'register', 'setup', 'signin', 'signout', 'signup', 'user', 'users', 'wp-admin', 'wp-login'],
|
reserved: ['admin', 'app', 'apps', 'archive', 'archives', 'categories',
|
||||||
|
'category', 'dashboard', 'feed', 'ghost-admin', 'login', 'logout',
|
||||||
|
'page', 'pages', 'post', 'posts', 'public', 'register', 'setup',
|
||||||
|
'signin', 'signout', 'signup', 'user', 'users', 'wp-admin', 'wp-login'],
|
||||||
protected: ['ghost', 'rss']
|
protected: ['ghost', 'rss']
|
||||||
},
|
},
|
||||||
uploads: {
|
uploads: {
|
||||||
|
|
|
@ -18,7 +18,15 @@ function setConfig(config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBaseUrl(secure) {
|
function getBaseUrl(secure) {
|
||||||
return (secure && ghostConfig.urlSSL) ? ghostConfig.urlSSL : ghostConfig.url;
|
if (secure && ghostConfig.urlSSL) {
|
||||||
|
return ghostConfig.urlSSL;
|
||||||
|
} else {
|
||||||
|
if (secure) {
|
||||||
|
return ghostConfig.url.replace('http://', 'https://');
|
||||||
|
} else {
|
||||||
|
return ghostConfig.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function urlJoin() {
|
function urlJoin() {
|
||||||
|
@ -190,6 +198,7 @@ function urlFor(context, data, absolute) {
|
||||||
return urlPath;
|
return urlPath;
|
||||||
} else if (context === 'nav' && data.nav) {
|
} else if (context === 'nav' && data.nav) {
|
||||||
urlPath = data.nav.url;
|
urlPath = data.nav.url;
|
||||||
|
secure = data.nav.secure || secure;
|
||||||
baseUrl = getBaseUrl(secure);
|
baseUrl = getBaseUrl(secure);
|
||||||
hostname = baseUrl.split('//')[1] + ghostConfig.paths.subdir;
|
hostname = baseUrl.split('//')[1] + ghostConfig.paths.subdir;
|
||||||
if (urlPath.indexOf(hostname) > -1
|
if (urlPath.indexOf(hostname) > -1
|
||||||
|
@ -242,3 +251,4 @@ module.exports.urlJoin = urlJoin;
|
||||||
module.exports.urlFor = urlFor;
|
module.exports.urlFor = urlFor;
|
||||||
module.exports.urlPathForPost = urlPathForPost;
|
module.exports.urlPathForPost = urlPathForPost;
|
||||||
module.exports.apiUrl = apiUrl;
|
module.exports.apiUrl = apiUrl;
|
||||||
|
module.exports.getBaseUrl = getBaseUrl;
|
||||||
|
|
|
@ -109,6 +109,7 @@ function addContextMetaData(context, data, metaData) {
|
||||||
function initMetaData(context, data, results) {
|
function initMetaData(context, data, results) {
|
||||||
var metaData = {
|
var metaData = {
|
||||||
url: results.url,
|
url: results.url,
|
||||||
|
canonicalUrl: results.canonicalUrl,
|
||||||
metaDescription: results.meta_description || null,
|
metaDescription: results.meta_description || null,
|
||||||
metaTitle: results.meta_title,
|
metaTitle: results.meta_title,
|
||||||
coverImage: results.image,
|
coverImage: results.image,
|
||||||
|
@ -148,7 +149,7 @@ function getStructuredData(metaData) {
|
||||||
'og:type': metaData.ogType,
|
'og:type': metaData.ogType,
|
||||||
'og:title': metaData.metaTitle,
|
'og:title': metaData.metaTitle,
|
||||||
'og:description': metaData.metaDescription,
|
'og:description': metaData.metaDescription,
|
||||||
'og:url': metaData.url,
|
'og:url': metaData.canonicalUrl,
|
||||||
'og:image': metaData.coverImage,
|
'og:image': metaData.coverImage,
|
||||||
'article:published_time': metaData.publishedDate,
|
'article:published_time': metaData.publishedDate,
|
||||||
'article:modified_time': metaData.modifiedDate,
|
'article:modified_time': metaData.modifiedDate,
|
||||||
|
@ -156,7 +157,7 @@ function getStructuredData(metaData) {
|
||||||
'twitter:card': metaData.card,
|
'twitter:card': metaData.card,
|
||||||
'twitter:title': metaData.metaTitle,
|
'twitter:title': metaData.metaTitle,
|
||||||
'twitter:description': metaData.metaDescription,
|
'twitter:description': metaData.metaDescription,
|
||||||
'twitter:url': metaData.url,
|
'twitter:url': metaData.canonicalUrl,
|
||||||
'twitter:image:src': metaData.coverImage
|
'twitter:image:src': metaData.coverImage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,6 +312,8 @@ ghost_head = function (options) {
|
||||||
|
|
||||||
// Store Async calls in an object of named promises
|
// Store Async calls in an object of named promises
|
||||||
props.url = urlHelper.call(self, {hash: {absolute: true}});
|
props.url = urlHelper.call(self, {hash: {absolute: true}});
|
||||||
|
props.canonicalUrl = config.urlJoin(config.getBaseUrl(false),
|
||||||
|
urlHelper.call(self, {hash: {absolute: false}}));
|
||||||
props.meta_description = meta_description.call(self, options);
|
props.meta_description = meta_description.call(self, options);
|
||||||
props.meta_title = meta_title.call(self, options);
|
props.meta_title = meta_title.call(self, options);
|
||||||
props.client = getClient();
|
props.client = getClient();
|
||||||
|
@ -330,7 +333,7 @@ ghost_head = function (options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// head is our main array that holds our meta data
|
// head is our main array that holds our meta data
|
||||||
head.push('<link rel="canonical" href="' + metaData.url + '" />');
|
head.push('<link rel="canonical" href="' + metaData.canonicalUrl + '" />');
|
||||||
head.push('<meta name="referrer" content="origin" />');
|
head.push('<meta name="referrer" content="origin" />');
|
||||||
|
|
||||||
// Generate context driven pagination urls
|
// Generate context driven pagination urls
|
||||||
|
@ -359,7 +362,8 @@ ghost_head = function (options) {
|
||||||
|
|
||||||
head.push('<meta name="generator" content="Ghost ' + safeVersion + '" />');
|
head.push('<meta name="generator" content="Ghost ' + safeVersion + '" />');
|
||||||
head.push('<link rel="alternate" type="application/rss+xml" title="' +
|
head.push('<link rel="alternate" type="application/rss+xml" title="' +
|
||||||
title + '" href="' + config.urlFor('rss', null, true) + '" />');
|
title + '" href="' + config.urlFor('rss', {secure: self.secure},
|
||||||
|
true) + '" />');
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
return api.settings.read({key: 'ghost_head'});
|
return api.settings.read({key: 'ghost_head'});
|
||||||
}).then(function (response) {
|
}).then(function (response) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ navigation = function (options) {
|
||||||
/*jshint unused:false*/
|
/*jshint unused:false*/
|
||||||
var navigationData = options.data.blog.navigation,
|
var navigationData = options.data.blog.navigation,
|
||||||
currentUrl = options.data.root.relativeUrl,
|
currentUrl = options.data.root.relativeUrl,
|
||||||
|
self = this,
|
||||||
output,
|
output,
|
||||||
context;
|
context;
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ navigation = function (options) {
|
||||||
out.label = e.label;
|
out.label = e.label;
|
||||||
out.slug = _slugify(e.label);
|
out.slug = _slugify(e.label);
|
||||||
out.url = hbs.handlebars.Utils.escapeExpression(e.url);
|
out.url = hbs.handlebars.Utils.escapeExpression(e.url);
|
||||||
|
out.secure = self.secure;
|
||||||
return out;
|
return out;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,22 +12,22 @@ url = function (options) {
|
||||||
var absolute = options && options.hash.absolute;
|
var absolute = options && options.hash.absolute;
|
||||||
|
|
||||||
if (schema.isPost(this)) {
|
if (schema.isPost(this)) {
|
||||||
return config.urlFor('post', {post: this}, absolute);
|
return config.urlFor('post', {post: this, secure: this.secure}, absolute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.isTag(this)) {
|
if (schema.isTag(this)) {
|
||||||
return config.urlFor('tag', {tag: this}, absolute);
|
return config.urlFor('tag', {tag: this, secure: this.secure}, absolute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.isUser(this)) {
|
if (schema.isUser(this)) {
|
||||||
return config.urlFor('author', {author: this}, absolute);
|
return config.urlFor('author', {author: this, secure: this.secure}, absolute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.isNav(this)) {
|
if (schema.isNav(this)) {
|
||||||
return config.urlFor('nav', {nav: this}, absolute);
|
return config.urlFor('nav', {nav: this, secure: this.secure}, absolute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.urlFor(this, absolute);
|
return config.urlFor(this, {}, absolute);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = url;
|
module.exports = url;
|
||||||
|
|
|
@ -985,11 +985,11 @@ describe('Frontend Routing', function () {
|
||||||
.end(doEnd(done));
|
.end(doEnd(done));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set links to urlSSL over HTTPS', function (done) {
|
it('should set links to urlSSL over HTTPS besides canonical', function (done) {
|
||||||
request.get('/')
|
request.get('/')
|
||||||
.set('X-Forwarded-Proto', 'https')
|
.set('X-Forwarded-Proto', 'https')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect(/<link rel="canonical" href="https:\/\/localhost\/" \/\>/)
|
.expect(/<link rel="canonical" href="http:\/\/127.0.0.1:2370\/" \/\>/)
|
||||||
.expect(/<a href="https:\/\/localhost">Ghost<\/a\>/)
|
.expect(/<a href="https:\/\/localhost">Ghost<\/a\>/)
|
||||||
.end(doEnd(done));
|
.end(doEnd(done));
|
||||||
});
|
});
|
||||||
|
|
|
@ -64,6 +64,28 @@ describe('{{url}} helper', function () {
|
||||||
rendered.should.equal('http://testurl.com/slug/');
|
rendered.should.equal('http://testurl.com/slug/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should output an absolute URL with https if the option is present and secure', function () {
|
||||||
|
rendered = helpers.url.call(
|
||||||
|
{html: 'content', markdown: 'ff', title: 'title', slug: 'slug',
|
||||||
|
url: '/slug/', created_at: new Date(0), secure: true},
|
||||||
|
{hash: {absolute: 'true'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
should.exist(rendered);
|
||||||
|
rendered.should.equal('https://testurl.com/slug/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should output an absolute URL with https if secure', function () {
|
||||||
|
rendered = helpers.url.call(
|
||||||
|
{html: 'content', markdown: 'ff', title: 'title', slug: 'slug',
|
||||||
|
url: '/slug/', created_at: new Date(0), secure: true},
|
||||||
|
{hash: {absolute: 'true'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
should.exist(rendered);
|
||||||
|
rendered.should.equal('https://testurl.com/slug/');
|
||||||
|
});
|
||||||
|
|
||||||
it('should return the slug with a prefixed /tag/ if the context is a tag', function () {
|
it('should return the slug with a prefixed /tag/ if the context is a tag', function () {
|
||||||
rendered = helpers.url.call({
|
rendered = helpers.url.call({
|
||||||
name: 'the tag',
|
name: 'the tag',
|
||||||
|
@ -109,6 +131,14 @@ describe('{{url}} helper', function () {
|
||||||
rendered.should.equal('http://testurl.com/bar');
|
rendered.should.equal('http://testurl.com/bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return an absolute url with https if context is secure', function () {
|
||||||
|
rendered = helpers.url.call(
|
||||||
|
{url: '/bar', label: 'Bar', slug: 'bar', current: true, secure: true},
|
||||||
|
{hash: {absolute: 'true'}});
|
||||||
|
should.exist(rendered);
|
||||||
|
rendered.should.equal('https://testurl.com/bar');
|
||||||
|
});
|
||||||
|
|
||||||
it('external urls should be retained in a nav context', function () {
|
it('external urls should be retained in a nav context', function () {
|
||||||
rendered = helpers.url.call(
|
rendered = helpers.url.call(
|
||||||
{url: 'http://casper.website/baz', label: 'Baz', slug: 'baz', current: true},
|
{url: 'http://casper.website/baz', label: 'Baz', slug: 'baz', current: true},
|
||||||
|
@ -125,6 +155,24 @@ describe('{{url}} helper', function () {
|
||||||
rendered.should.equal('http://testurl.com/qux');
|
rendered.should.equal('http://testurl.com/qux');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle hosted urls in a nav context with secure', function () {
|
||||||
|
rendered = helpers.url.call(
|
||||||
|
{url: 'http://testurl.com/qux', label: 'Qux', slug: 'qux', current: true,
|
||||||
|
secure: true},
|
||||||
|
{hash: {absolute: 'true'}});
|
||||||
|
should.exist(rendered);
|
||||||
|
rendered.should.equal('https://testurl.com/qux');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle hosted https urls in a nav context with secure', function () {
|
||||||
|
rendered = helpers.url.call(
|
||||||
|
{url: 'https://testurl.com/qux', label: 'Qux', slug: 'qux', current: true,
|
||||||
|
secure: true},
|
||||||
|
{hash: {absolute: 'true'}});
|
||||||
|
should.exist(rendered);
|
||||||
|
rendered.should.equal('https://testurl.com/qux');
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle hosted urls with the wrong protocol in a nav context', function () {
|
it('should handle hosted urls with the wrong protocol in a nav context', function () {
|
||||||
rendered = helpers.url.call(
|
rendered = helpers.url.call(
|
||||||
{url: 'https://testurl.com/quux', label: 'Quux', slug: 'quux', current: true},
|
{url: 'https://testurl.com/quux', label: 'Quux', slug: 'quux', current: true},
|
||||||
|
|
Loading…
Add table
Reference in a new issue