Skip to content

Commit

Permalink
Updated meta/structured data sources & fallbacks (#11068)
Browse files Browse the repository at this point in the history
refs #10921, closes #11357, closes #11403

- updates the sources and fallbacks for the output of `{{ghost_head}}` meta/structured data
- re-works related tests to better show the fallback chains for different scenarios
- fixes `{{ghost_head}}` tests to use `before/afterEach` so that tests are not interdependent
  • Loading branch information
kevinansfield committed Nov 21, 2019
1 parent 193c179 commit be4a5a8
Show file tree
Hide file tree
Showing 15 changed files with 1,383 additions and 668 deletions.
4 changes: 4 additions & 0 deletions core/frontend/meta/context_object.js
Expand Up @@ -22,6 +22,10 @@ function getContextObject(data, context) {
chosenContext = data.post;
} else if (_.includes(context, 'page') && data.page) {
chosenContext = data.page;
} else if (_.includes(context, 'tag') && data.tag) {
chosenContext = data.tag;
} else if (_.includes(context, 'author') && data.author) {
chosenContext = data.author;
} else if (data[context]) {
// @NOTE: This is confusing as hell. It tries to get data[['author']], which works, but coincidence?
chosenContext = data[context];
Expand Down
76 changes: 50 additions & 26 deletions core/frontend/meta/description.js
@@ -1,59 +1,83 @@
const _ = require('lodash');
const settingsCache = require('../../server/services/settings/cache');
const getExcerpt = require('./excerpt');

function getDescription(data, root, options) {
function getDescription(data, root, options = {}) {
const context = root ? root.context : null;
const siteDescription = settingsCache.get('meta_description') || settingsCache.get('description');

let description = '';
let postSdDescription;

options = options ? options : {};

// We only return meta_description if provided
if (data.meta_description) {
description = data.meta_description;
} else if (_.includes(context, 'paged')) {
description = '';
} else if (_.includes(context, 'home')) {
if (options && options.property) {
const siteSdDescription = options.property + '_description';
description = settingsCache.get(siteSdDescription) || '';
const siteDescription = settingsCache.get('meta_description') || settingsCache.get('description');

if (options.property) {
// options.property = null/'og'/'twitter'
const optionsPropertyName = `${options.property || 'meta'}_description`;
description = settingsCache.get(optionsPropertyName) || siteDescription || '';
} else {
description = siteDescription;
}
} else if (_.includes(context, 'author') && data.author) {
// The usage of meta data fields for author is currently not implemented.
// We do have meta_description and meta_title fields
// in the users table, but there's no UI to populate those.
description = data.author.meta_description || '';
if (!options.property && _.includes(context, 'paged')) {
description = '';
} else {
// The usage of meta data fields for author is currently not implemented.
// We do have meta_description and meta_title fields
// in the users table, but there's no UI to populate those.
description = data.author.meta_description
|| data.author.bio
|| (options.property ? settingsCache.get('meta_description') : '')
|| '';
}
} else if (_.includes(context, 'tag') && data.tag) {
description = data.tag.meta_description || '';
if (!options.property && _.includes(context, 'paged')) {
description = '';
} else {
description = data.tag.meta_description
|| data.tag.description
|| (options.property ? settingsCache.get('meta_description') : '')
|| '';
}
} else if (_.includes(context, 'post') && data.post) {
if (options && options.property) {
postSdDescription = options.property + '_description';
description = data.post[postSdDescription] || '';
if (options.property) {
description = data.post[`${options.property}_description`]
|| data.post.custom_excerpt
|| data.post.meta_description
|| getExcerpt(data.post.html || '', {words: 50})
|| settingsCache.get('description')
|| '';
} else {
description = data.post.meta_description || '';
}
} else if (_.includes(context, 'page') && data.post) {
// Page title dependent on legacy object formatting (https://github.com/TryGhost/Ghost/issues/10042)
if (options && options.property) {
postSdDescription = options.property + '_description';
description = data.post[postSdDescription] || '';
// Page description dependent on legacy object formatting (https://github.com/TryGhost/Ghost/issues/10042)
if (options.property) {
description = data.post[`${options.property}_description`]
|| data.post.custom_excerpt
|| data.post.meta_description
|| getExcerpt(data.post.html || '', {words: 50})
|| settingsCache.get('description')
|| '';
} else {
description = data.post.meta_description || '';
}
} else if (_.includes(context, 'page') && data.page) {
if (options && options.property) {
postSdDescription = options.property + '_description';
description = data.page[postSdDescription] || '';
if (options.property) {
description = data.page[`${options.property}_description`]
|| data.page.custom_excerpt
|| data.page.meta_description
|| getExcerpt(data.page.html || '', {words: 50})
|| settingsCache.get('description')
|| '';
} else {
description = data.page.meta_description || '';
}
}

return (description || '').trim();
return (description || '').trim() || null;
}

module.exports = getDescription;
2 changes: 1 addition & 1 deletion core/frontend/meta/index.js
Expand Up @@ -44,7 +44,7 @@ function getMetaData(data, root) {
url: getAuthorImage(data, true)
},
ogImage: {
url: getOgImage(data, true)
url: getOgImage(data)
},
ogTitle: getTitle(data, root, {property: 'og'}),
ogDescription: getDescription(data, root, {property: 'og'}),
Expand Down
24 changes: 18 additions & 6 deletions core/frontend/meta/og_image.js
Expand Up @@ -5,19 +5,31 @@ const settingsCache = require('../../server/services/settings/cache');

function getOgImage(data) {
const context = data.context ? data.context : null;
const contextObject = getContextObject(data, context);
const siteOgImage = settingsCache.get('og_image');
const contextObject = getContextObject(data, context, false);

if (_.includes(context, 'home')) {
const imgUrl = settingsCache.get('og_image') || settingsCache.get('cover_image');
return (imgUrl && urlUtils.relativeToAbsolute(imgUrl)) || null;
}

if (_.includes(context, 'post') || _.includes(context, 'page') || _.includes(context, 'amp')) {
if (contextObject.og_image) {
return urlUtils.urlFor('image', {image: contextObject.og_image}, true);
return urlUtils.relativeToAbsolute(contextObject.og_image);
} else if (contextObject.feature_image) {
return urlUtils.urlFor('image', {image: contextObject.feature_image}, true);
return urlUtils.relativeToAbsolute(contextObject.feature_image);
}
}

if (_.includes(context, 'home') && siteOgImage) {
return urlUtils.urlFor('image', {image: siteOgImage}, true);
if (_.includes(context, 'author') && contextObject.cover_image) {
return urlUtils.relativeToAbsolute(contextObject.cover_image);
}

if (_.includes(context, 'tag')) {
if (contextObject.feature_image) {
return urlUtils.relativeToAbsolute(contextObject.feature_image);
} else if (settingsCache.get('cover_image')) {
return urlUtils.relativeToAbsolute(settingsCache.get('cover_image'));
}
}

return null;
Expand Down
8 changes: 4 additions & 4 deletions core/frontend/meta/structured_data.js
Expand Up @@ -11,11 +11,11 @@ function getStructuredData(metaData) {
structuredData = {
'og:site_name': metaData.site.title,
'og:type': metaData.ogType,
'og:title': metaData.ogTitle || metaData.metaTitle,
'og:title': metaData.ogTitle,
// CASE: metaData.excerpt for post context is populated by either the custom excerpt,
// the meta description, or the automated excerpt of 50 words. It is empty for any
// other context and *always* uses the provided meta description fields.
'og:description': metaData.ogDescription || metaData.excerpt || metaData.metaDescription,
'og:description': metaData.ogDescription,
'og:url': metaData.canonicalUrl,
'og:image': metaData.ogImage.url || metaData.coverImage.url,
'article:published_time': metaData.publishedDate,
Expand All @@ -24,8 +24,8 @@ function getStructuredData(metaData) {
'article:publisher': metaData.site.facebook ? socialUrls.facebook(metaData.site.facebook) : undefined,
'article:author': metaData.authorFacebook ? socialUrls.facebook(metaData.authorFacebook) : undefined,
'twitter:card': card,
'twitter:title': metaData.twitterTitle || metaData.metaTitle,
'twitter:description': metaData.twitterDescription || metaData.excerpt || metaData.metaDescription,
'twitter:title': metaData.twitterTitle,
'twitter:description': metaData.twitterDescription,
'twitter:url': metaData.canonicalUrl,
'twitter:image': metaData.twitterImage || metaData.coverImage.url,
'twitter:label1': metaData.authorName ? 'Written by' : undefined,
Expand Down
38 changes: 11 additions & 27 deletions core/frontend/meta/title.js
@@ -1,17 +1,17 @@
const _ = require('lodash');
const settingsCache = require('../../server/services/settings/cache');

function getTitle(data, root, options) {
function getTitle(data, root, options = {}) {
const context = root ? root.context : null;
const siteTitle = settingsCache.get('meta_title') || settingsCache.get('title');
const siteTitle = settingsCache.get('title');
const pagination = root ? root.pagination : null;

// options.property = null/'og'/'twitter'
const optionsPropertyName = `${options.property || 'meta'}_title`;

let title = '';
let postSdTitle;
let pageString = '';

options = options ? options : {};

if (pagination && pagination.total > 1) {
pageString = _.has(options.hash, 'page') ? options.hash.page.replace('%', pagination.page) : ' (Page ' + pagination.page + ')';
}
Expand All @@ -21,11 +21,10 @@ function getTitle(data, root, options) {
title = data.meta_title;
// Home title
} else if (_.includes(context, 'home')) {
if (options && options.property) {
const siteSdTitle = options.property + '_title';
title = settingsCache.get(siteSdTitle) || '';
if (options.property) {
title = settingsCache.get(optionsPropertyName) || siteTitle;
} else {
title = siteTitle;
title = settingsCache.get('meta_title') || siteTitle;
}
// Author title, paged
} else if (_.includes(context, 'author') && data.author && _.includes(context, 'paged')) {
Expand All @@ -41,28 +40,13 @@ function getTitle(data, root, options) {
title = data.tag.meta_title || data.tag.name + ' - ' + siteTitle;
// Post title
} else if (_.includes(context, 'post') && data.post) {
if (options && options.property) {
postSdTitle = options.property + '_title';
title = data.post[postSdTitle] || '';
} else {
title = data.post.meta_title || data.post.title;
}
title = data.post[optionsPropertyName] || data.post.meta_title || data.post.title;
// Page title dependent on legacy object formatting (https://github.com/TryGhost/Ghost/issues/10042)
} else if (_.includes(context, 'page') && data.post) {
if (options && options.property) {
postSdTitle = options.property + '_title';
title = data.post[postSdTitle] || '';
} else {
title = data.post.meta_title || data.post.title;
}
title = data.post[optionsPropertyName] || data.post.meta_title || data.post.title;
// Page title v2
} else if (_.includes(context, 'page') && data.page) {
if (options && options.property) {
postSdTitle = options.property + '_title';
title = data.page[postSdTitle] || '';
} else {
title = data.page.meta_title || data.page.title;
}
title = data.page[optionsPropertyName] || data.page.meta_title || data.page.title;
// Fallback
} else {
title = siteTitle + pageString;
Expand Down
24 changes: 18 additions & 6 deletions core/frontend/meta/twitter_image.js
Expand Up @@ -5,19 +5,31 @@ const settingsCache = require('../../server/services/settings/cache');

function getTwitterImage(data) {
const context = data.context ? data.context : null;
const contextObject = getContextObject(data, context);
const siteTwitterImage = settingsCache.get('twitter_image');
const contextObject = getContextObject(data, context, false);

if (_.includes(context, 'home')) {
const imgUrl = settingsCache.get('twitter_image') || settingsCache.get('cover_image');
return (imgUrl && urlUtils.relativeToAbsolute(imgUrl)) || null;
}

if (_.includes(context, 'post') || _.includes(context, 'page') || _.includes(context, 'amp')) {
if (contextObject.twitter_image) {
return urlUtils.urlFor('image', {image: contextObject.twitter_image}, true);
return urlUtils.relativeToAbsolute(contextObject.twitter_image);
} else if (contextObject.feature_image) {
return urlUtils.urlFor('image', {image: contextObject.feature_image}, true);
return urlUtils.relativeToAbsolute(contextObject.feature_image);
}
}

if (_.includes(context, 'home') && siteTwitterImage) {
return urlUtils.urlFor('image', {image: siteTwitterImage}, true);
if (_.includes(context, 'author') && contextObject.cover_image) {
return urlUtils.relativeToAbsolute(contextObject.cover_image);
}

if (_.includes(context, 'tag')) {
if (contextObject.feature_image) {
return urlUtils.relativeToAbsolute(contextObject.feature_image);
} else if (settingsCache.get('cover_image')) {
return urlUtils.relativeToAbsolute(settingsCache.get('cover_image'));
}
}

return null;
Expand Down

0 comments on commit be4a5a8

Please sign in to comment.