Add structured data to static pages

refs #6534

- adds structured data on static pages
- selects post context object for static pages
- updates tests
This commit is contained in:
cobbspur 2016-03-30 10:41:41 +01:00
parent 48b846b13d
commit 5102637b8e
7 changed files with 132 additions and 13 deletions

View File

@ -1,11 +1,11 @@
var config = require('../../config'); var config = require('../../config'),
getContextObject = require('./context_object.js');
function getAuthorImage(data, absolute) { function getAuthorImage(data, absolute) {
var context = data.context ? data.context[0] : null, var context = data.context ? data.context[0] : null,
blog = config.theme, contextObject = getContextObject(data, context);
contextObject = data[context] || blog;
if (context === 'post' && contextObject.author && contextObject.author.image) { if ((context === 'post' || context === 'page') && contextObject.author && contextObject.author.image) {
return config.urlFor('image', {image: contextObject.author.image}, absolute); return config.urlFor('image', {image: contextObject.author.image}, absolute);
} }
return null; return null;

View File

@ -0,0 +1,13 @@
var config = require('../../config');
function getContextObject(data, context) {
var blog = config.theme,
contextObject;
context = context === 'page' ? 'post' : context;
contextObject = data[context] || blog;
return contextObject;
}
module.exports = getContextObject;

View File

@ -1,9 +1,9 @@
var config = require('../../config'); var config = require('../../config'),
getContextObject = require('./context_object.js');
function getCoverImage(data) { function getCoverImage(data) {
var context = data.context ? data.context[0] : null, var context = data.context ? data.context[0] : null,
blog = config.theme, contextObject = getContextObject(data, context);
contextObject = data[context] || blog;
if (context === 'home' || context === 'author') { if (context === 'home' || context === 'author') {
if (contextObject.cover) { if (contextObject.cover) {

View File

@ -97,7 +97,7 @@ function getAuthorSchema(metaData, data) {
function getSchema(metaData, data) { function getSchema(metaData, data) {
if (!config.isPrivacyDisabled('useStructuredData')) { if (!config.isPrivacyDisabled('useStructuredData')) {
var context = data.context ? data.context[0] : null; var context = data.context ? data.context[0] : null;
if (context === 'post') { if (context === 'post' || context === 'page') {
return getPostSchema(metaData, data); return getPostSchema(metaData, data);
} else if (context === 'home') { } else if (context === 'home') {
return getHomeSchema(metaData); return getHomeSchema(metaData);

View File

@ -98,7 +98,7 @@ function ghost_head(options) {
escapeExpression(metaData.nextUrl) + '" />'); escapeExpression(metaData.nextUrl) + '" />');
} }
if (context !== 'paged' && context !== 'page' && useStructuredData) { if (context !== 'paged' && useStructuredData) {
head.push(''); head.push('');
head.push.apply(head, finaliseStructuredData(metaData)); head.push.apply(head, finaliseStructuredData(metaData));
head.push(''); head.push('');

View File

@ -0,0 +1,49 @@
/*globals describe, it, before, after */
var should = require('should'),
getContextObject = require('../../../server/data/meta/context_object.js'),
configUtils = require('../../utils/configUtils');
describe('getContextObject', function () {
var data, context, contextObject;
it('should be a function', function () {
should.exist(getContextObject);
});
it('should return post context object for a post', function () {
data = {post: {id: 2}};
context = 'post';
contextObject = getContextObject(data, context);
should.exist(contextObject);
contextObject.should.eql(data.post);
});
it('should return post context object for a static page', function () {
data = {post: {id: 2}};
context = 'page';
contextObject = getContextObject(data, context);
should.exist(contextObject);
contextObject.should.eql(data.post);
});
describe('override blog', function () {
before(function () {
configUtils.set({theme: {foo: 'bar'}});
});
after(function () {
configUtils.restore();
});
it('should return blog context object for unknown context', function () {
data = {post: {id: 2}};
context = 'unknown';
contextObject = getContextObject(data, context);
should.exist(contextObject);
contextObject.should.have.property('foo', 'bar');
});
});
});

View File

@ -110,6 +110,57 @@ describe('{{ghost_head}} helper', function () {
}).catch(done); }).catch(done);
}); });
it('returns structured data on static page', function (done) {
var post = {
meta_description: 'all about our blog',
title: 'About',
image: '/content/images/test-image-about.png',
published_at: moment('2008-05-31T19:18:15').toISOString(),
updated_at: moment('2014-10-06T15:23:54').toISOString(),
page: true,
author: {
name: 'Author name',
url: 'http://testauthorurl.com',
slug: 'Author',
image: '/content/images/test-author-image.png',
website: 'http://authorwebsite.com',
bio: 'Author bio'
}
};
helpers.ghost_head.call(
{safeVersion: '0.3', relativeUrl: '/about/', context: ['page'], post: post},
{data: {root: {context: ['page']}}}
).then(function (rendered) {
should.exist(rendered);
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/about\/" \/>/);
rendered.string.should.match(/<meta name="referrer" content="origin" \/>/);
rendered.string.should.match(/<meta property="og:site_name" content="Ghost" \/>/);
rendered.string.should.match(/<meta property="og:type" content="website" \/>/);
rendered.string.should.match(/<meta property="og:title" content="About" \/>/);
rendered.string.should.match(/<meta property="og:description" content="all about our blog" \/>/);
rendered.string.should.match(/<meta property="og:url" content="http:\/\/testurl.com\/about\/" \/>/);
rendered.string.should.match(/<meta property="og:image" content="http:\/\/testurl.com\/content\/images\/test-image-about.png" \/>/);
rendered.string.should.match(/<meta name="twitter:card" content="summary_large_image" \/>/);
rendered.string.should.match(/<meta name="twitter:title" content="About" \/>/);
rendered.string.should.match(/<meta name="twitter:description" content="all about our blog" \/>/);
rendered.string.should.match(/<meta name="twitter:url" content="http:\/\/testurl.com\/about\/" \/>/);
rendered.string.should.match(/<meta name="twitter:image:src" content="http:\/\/testurl.com\/content\/images\/test-image-about.png" \/>/);
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\/" \/>/);
rendered.string.should.match(/<script type=\"application\/ld\+json\">/);
rendered.string.should.match(/"@context": "http:\/\/schema.org"/);
rendered.string.should.match(/"@type": "Article"/);
rendered.string.should.match(/"publisher": "Ghost"/);
rendered.string.should.match(/"url": "http:\/\/testurl.com\/about\/"/);
rendered.string.should.match(/"image": "http:\/\/testurl.com\/content\/images\/test-image-about.png"/);
rendered.string.should.match(/"image\": \"http:\/\/testurl.com\/content\/images\/test-author-image.png\"/);
rendered.string.should.match(/"description": "all about our blog"/);
done();
}).catch(done);
});
it('returns structured data and schema first tag page with meta description and meta title', function (done) { it('returns structured data and schema first tag page with meta description and meta title', function (done) {
var tag = { var tag = {
meta_description: 'tag meta description', meta_description: 'tag meta description',
@ -327,7 +378,7 @@ describe('{{ghost_head}} helper', function () {
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}], tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
author: { author: {
name: 'Author name', name: 'Author name',
url: 'http//:testauthorurl.com', url: 'http://testauthorurl.com',
slug: 'Author', slug: 'Author',
image: '/content/images/test-author-image.png', image: '/content/images/test-author-image.png',
website: 'http://authorwebsite.com', website: 'http://authorwebsite.com',
@ -637,16 +688,22 @@ describe('{{ghost_head}} helper', function () {
}); });
it('returns canonical URL', function (done) { it('returns canonical URL', function (done) {
var post = {
title: 'Welcome to Ghost',
html: '<p>This is a short post</p>',
author: {
name: 'Author name'
}
};
helpers.ghost_head.call( helpers.ghost_head.call(
{safeVersion: '0.3', relativeUrl: '/about/', context: ['page']}, {safeVersion: '0.3', relativeUrl: '/about/', context: ['page'], post: post},
{data: {root: {context: ['page']}}} {data: {root: {context: ['page']}}}
).then(function (rendered) { ).then(function (rendered) {
should.exist(rendered); should.exist(rendered);
rendered.string.should.match(/<link rel="canonical" href="http:\/\/testurl.com\/about\/" \/>/); 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(/<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\/" \/>/); rendered.string.should.match(/<link rel="alternate" type="application\/rss\+xml" title="Ghost" href="http:\/\/testurl.com\/rss\/" \/>/);
rendered.string.should.not.match(/<meta property="og/);
rendered.string.should.not.match(/<script type=\"application\/ld\+json\">/);
done(); done();
}).catch(done); }).catch(done);