mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Add meta tags for client_id & client_secret
refs #5942 - refactor ghost_head to use Promise.props (settle is going away and this is easier) - add a new call to fetch the frontend client, if it exists - add meta tags for the client_id and client_secret on all pages - don't include the meta tags if the client is not enabled, or if the labs flag is not set
This commit is contained in:
parent
eb3cce0235
commit
e70898a842
3 changed files with 121 additions and 73 deletions
|
@ -21,21 +21,47 @@ var hbs = require('express-hbs'),
|
|||
excerpt = require('./excerpt'),
|
||||
tagsHelper = require('./tags'),
|
||||
imageHelper = require('./image'),
|
||||
|
||||
labs = require('../utils/labs'),
|
||||
|
||||
blog,
|
||||
ghost_head;
|
||||
|
||||
function getImage(ops, context, contextObject) {
|
||||
function getClient() {
|
||||
return labs.isSet('publicAPI').then(function (publicAPI) {
|
||||
if (publicAPI === true) {
|
||||
return api.clients.read({slug: 'ghost-frontend'}).then(function (client) {
|
||||
client = client.clients[0];
|
||||
if (client.status === 'enabled') {
|
||||
return {
|
||||
id: client.slug,
|
||||
secret: client.secret
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
function writeMetaTag(property, content, type) {
|
||||
type = type || property.substring(0, 7) === 'twitter' ? 'name' : 'property';
|
||||
return '<meta ' + type + '="' + property + '" content="' + content + '" />';
|
||||
}
|
||||
|
||||
function getImage(props, context, contextObject) {
|
||||
if (context === 'home' || context === 'author') {
|
||||
contextObject.image = contextObject.cover;
|
||||
}
|
||||
|
||||
ops.push(imageHelper.call(contextObject, {hash: {absolute: true}}));
|
||||
props.image = imageHelper.call(contextObject, {hash: {absolute: true}});
|
||||
|
||||
if (context === 'post' && contextObject.author) {
|
||||
ops.push(imageHelper.call(contextObject.author, {hash: {absolute: true}}));
|
||||
props.author_image = imageHelper.call(contextObject.author, {hash: {absolute: true}});
|
||||
}
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
function getPaginationUrls(pagination, relativeUrl, secure, head) {
|
||||
|
@ -84,11 +110,11 @@ function addContextMetaData(context, data, metaData) {
|
|||
|
||||
function initMetaData(context, data, results) {
|
||||
var metaData = {
|
||||
url: results[0].value(),
|
||||
metaDescription: results[1].value() || null,
|
||||
metaTitle: results[2].value(),
|
||||
coverImage: results.length > 3 ? results[3].value() : null,
|
||||
authorImage: results.length > 4 ? results[4].value() : null,
|
||||
url: results.url,
|
||||
metaDescription: results.meta_description || null,
|
||||
metaTitle: results.meta_title,
|
||||
coverImage: results.image,
|
||||
authorImage: results.author_image,
|
||||
publishedDate: null,
|
||||
modifiedDate: null,
|
||||
tags: null,
|
||||
|
@ -97,7 +123,9 @@ function initMetaData(context, data, results) {
|
|||
ogType: 'website',
|
||||
keywords: null,
|
||||
blog: blog,
|
||||
title: blog.title
|
||||
title: blog.title,
|
||||
clientId: results.client.id,
|
||||
clientSecret: results.client.secret
|
||||
};
|
||||
|
||||
if (!metaData.metaDescription) {
|
||||
|
@ -228,19 +256,17 @@ function chooseSchema(metaData, context, data) {
|
|||
}
|
||||
|
||||
function finaliseStructuredData(structuredData, tags, head) {
|
||||
var type;
|
||||
_.each(structuredData, function (content, property) {
|
||||
if (property === 'article:tag') {
|
||||
_.each(tags, function (tag) {
|
||||
if (tag !== '') {
|
||||
tag = hbs.handlebars.Utils.escapeExpression(tag.trim());
|
||||
head.push('<meta property="' + property + '" content="' + tag + '" />');
|
||||
head.push(writeMetaTag(property, tag));
|
||||
}
|
||||
});
|
||||
head.push('');
|
||||
} else if (content !== null && content !== undefined) {
|
||||
type = property.substring(0, 7) === 'twitter' ? 'name' : 'property';
|
||||
head.push('<meta ' + type + '="' + property + '" content="' + content + '" />');
|
||||
head.push(writeMetaTag(property, content));
|
||||
}
|
||||
});
|
||||
return head;
|
||||
|
@ -262,21 +288,22 @@ ghost_head = function (options) {
|
|||
useStructuredData = !config.isPrivacyDisabled('useStructuredData'),
|
||||
head = [],
|
||||
safeVersion = this.safeVersion,
|
||||
ops = [],
|
||||
props = {},
|
||||
structuredData,
|
||||
schema,
|
||||
title = hbs.handlebars.Utils.escapeExpression(blog.title),
|
||||
context = self.context ? self.context[0] : null,
|
||||
contextObject = self[context] || blog;
|
||||
|
||||
// Push Async calls to an array of promises
|
||||
ops.push(urlHelper.call(self, {hash: {absolute: true}}));
|
||||
ops.push(meta_description.call(self, options));
|
||||
ops.push(meta_title.call(self, options));
|
||||
ops = getImage(ops, context, contextObject);
|
||||
// Store Async calls in an object of named promises
|
||||
props.url = urlHelper.call(self, {hash: {absolute: true}});
|
||||
props.meta_description = meta_description.call(self, options);
|
||||
props.meta_title = meta_title.call(self, options);
|
||||
props.client = getClient();
|
||||
getImage(props, context, contextObject);
|
||||
|
||||
// Resolves promises then push pushes meta data into ghost_head
|
||||
return Promise.settle(ops).then(function (results) {
|
||||
return Promise.props(props).then(function (results) {
|
||||
if (context) {
|
||||
var metaData = initMetaData(context, self, results),
|
||||
tags = tagsHelper.call(self.post, {hash: {autolink: 'false'}}).string.split(',');
|
||||
|
@ -310,7 +337,13 @@ ghost_head = function (options) {
|
|||
// Formats schema script/JSONLD data and pushes to head array
|
||||
finaliseSchema(schema, head);
|
||||
}
|
||||
|
||||
if (metaData.clientId && metaData.clientSecret) {
|
||||
head.push(writeMetaTag('ghost:client_id', metaData.clientId));
|
||||
head.push(writeMetaTag('ghost:client_secret', metaData.clientSecret));
|
||||
}
|
||||
}
|
||||
|
||||
head.push('<meta name="generator" content="Ghost ' + safeVersion + '" />');
|
||||
head.push('<link rel="alternate" type="application/rss+xml" title="' +
|
||||
title + '" href="' + config.urlFor('rss', null, true) + '" />');
|
||||
|
|
|
@ -11,7 +11,15 @@ flagIsSet = function flagIsSet(flag) {
|
|||
return setting.key === 'labs';
|
||||
});
|
||||
|
||||
labsValue = JSON.parse(labs.value);
|
||||
if (!labs || !labs.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
labsValue = JSON.parse(labs.value);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!labsValue[flag] && labsValue[flag] === true;
|
||||
});
|
||||
|
|
|
@ -10,14 +10,47 @@ var should = require('should'),
|
|||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers'),
|
||||
api = require('../../../server/api');
|
||||
api = require('../../../server/api'),
|
||||
|
||||
labs = require('../../../server/utils/labs'),
|
||||
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('{{ghost_head}} helper', function () {
|
||||
var sandbox;
|
||||
var settingsReadStub;
|
||||
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
settingsReadStub = sandbox.stub(api.settings, 'read').returns(new Promise.resolve({
|
||||
settings: [
|
||||
{value: ''}
|
||||
]
|
||||
}));
|
||||
|
||||
sandbox.stub(api.clients, 'read').returns(new Promise.resolve({
|
||||
clients: [
|
||||
{slug: 'ghost-frontend', secret: 'a1bcde23cfe5', status: 'enabled'}
|
||||
]
|
||||
}));
|
||||
|
||||
sandbox.stub(labs, 'isSet').returns(new Promise.resolve(true));
|
||||
});
|
||||
|
||||
function expectGhostClientMeta(rendered) {
|
||||
rendered.string.should.match(/<meta property="ghost:client_id" content="ghost-frontend" \/>/);
|
||||
rendered.string.should.match(/<meta property="ghost:client_secret" content="[a-f0-9]{12}" \/>/);
|
||||
}
|
||||
|
||||
describe('without Code Injection', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({
|
||||
|
@ -30,25 +63,6 @@ describe('{{ghost_head}} helper', function () {
|
|||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [
|
||||
{value: ''}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('has loaded ghost_head helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_head);
|
||||
});
|
||||
|
@ -59,6 +73,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['paged', 'index']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
@ -75,6 +90,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['home', 'index']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="referrer" content="origin" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
|
@ -115,6 +131,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/tag\/tagtitle\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="website" \/>/);
|
||||
|
@ -156,6 +173,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/tag\/tagtitle\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="website" \/>/);
|
||||
|
@ -196,6 +214,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.not.match(/<meta property="og:description" \/>/);
|
||||
rendered.string.should.not.match(/<meta name="twitter:description"\/>/);
|
||||
rendered.string.should.not.match(/"description":/);
|
||||
|
@ -217,6 +236,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['tag']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/tag\/tagtitle\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
@ -242,6 +262,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['author']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/author\/AuthorName\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="profile" \/>/);
|
||||
|
@ -284,6 +305,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['paged', 'author']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/author\/AuthorName\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
@ -335,7 +357,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="article" \/>/);
|
||||
|
@ -407,7 +429,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="article" \/>/);
|
||||
|
@ -477,7 +499,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="article" \/>/);
|
||||
|
@ -546,7 +568,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
re4 = new RegExp('"dateModified": "' + post.updated_at);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
|
||||
rendered.string.should.match(/<meta property="og:type" content="article" \/>/);
|
||||
|
@ -593,6 +615,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['page']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/about\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
@ -609,6 +632,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['index', 'paged'], pagination: {total: 4, page: 3, next: 4, prev: 2}}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/page\/3\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="prev" href="http:\/\/testurl.com\/page\/2\/" \/>/);
|
||||
|
@ -627,6 +651,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['index', 'paged'], pagination: {total: 3, page: 2, next: 3, prev: 1}}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/page\/2\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="prev" href="http:\/\/testurl.com\/" \/>/);
|
||||
|
@ -661,6 +686,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/blog\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/blog\/rss\/" \/>/);
|
||||
|
@ -684,19 +710,6 @@ describe('{{ghost_head}} helper', function () {
|
|||
useStructuredData: false
|
||||
}
|
||||
});
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [
|
||||
{value: ''}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('does not return structured data', function (done) {
|
||||
|
@ -721,6 +734,7 @@ describe('{{ghost_head}} helper', function () {
|
|||
{data: {root: {context: ['post']}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/post\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
@ -733,13 +747,10 @@ describe('{{ghost_head}} helper', function () {
|
|||
});
|
||||
|
||||
describe('with Code Injection', function () {
|
||||
before(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [{value: '<style>body {background: red;}</style>'}]
|
||||
});
|
||||
});
|
||||
beforeEach(function () {
|
||||
settingsReadStub.returns(new Promise.resolve({
|
||||
settings: [{value: '<style>body {background: red;}</style>'}]
|
||||
}));
|
||||
|
||||
utils.overrideConfig({
|
||||
url: 'http://testurl.com/',
|
||||
|
@ -751,17 +762,13 @@ describe('{{ghost_head}} helper', function () {
|
|||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
sandbox.restore();
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('returns meta tag plus injected code', function (done) {
|
||||
helpers.ghost_head.call(
|
||||
{safeVersion: '0.3', context: ['paged', 'index'], post: false},
|
||||
{data: {root: {context: []}}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
expectGhostClientMeta(rendered);
|
||||
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/" \/>/);
|
||||
rendered.string.should.match(/<meta name="generator" content="Ghost 0.3" \/>/);
|
||||
rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
|
||||
|
|
Loading…
Add table
Reference in a new issue