Merge pull request #4332 from cobbspur/escape

Escape meta title and description
This commit is contained in:
Hannah Wolfe 2014-10-27 17:03:20 +02:00
commit c0375d4087
2 changed files with 79 additions and 21 deletions

View File

@ -34,7 +34,8 @@ ghost_head = function (options) {
ops = [],
structuredData,
coverImage, authorImage, keywords,
schema;
schema,
title = hbs.handlebars.Utils.escapeExpression(blog.title);
trimmedVersion = trimmedVersion ? trimmedVersion.match(majorMinor)[0] : '?';
// Push Async calls to an array of promises
@ -49,13 +50,14 @@ ghost_head = function (options) {
metaTitle = results[2].value(),
publishedDate, modifiedDate,
tags = tagsHelper.call(self.post, {hash: {autolink: 'false'}}).string.split(','),
card = 'summary';
card = 'summary',
type, authorUrl;
if (!metaDescription) {
metaDescription = excerpt.call(self.post, {hash: {words: '40'}}).string;
}
if (tags[0] !== '') {
keywords = tagsHelper.call(self.post, {hash: {autolink: 'false', seperator: ', '}}).string;
keywords = hbs.handlebars.Utils.escapeExpression(tagsHelper.call(self.post, {hash: {autolink: 'false', seperator: ', '}}).string);
}
head.push('<link rel="canonical" href="' + url + '" />');
@ -81,25 +83,30 @@ ghost_head = function (options) {
if (self.post.image) {
coverImage = self.post.image;
// Test to see if image was linked by url or uploaded
coverImage = coverImage.substring(0, 4) === 'http' ? coverImage : _.escape(blog.url) + coverImage;
coverImage = coverImage.substring(0, 4) === 'http' ? coverImage : hbs.handlebars.Utils.escapeExpression(blog.url + coverImage);
card = 'summary_large_image';
}
if (self.post.author.image) {
authorImage = self.post.author.image;
// Test to see if image was linked by url or uploaded
authorImage = authorImage.substring(0, 4) === 'http' ? authorImage : _.escape(blog.url) + authorImage;
authorImage = authorImage.substring(0, 4) === 'http' ? authorImage : hbs.handlebars.Utils.escapeExpression(blog.url + authorImage);
}
// escaped data
metaTitle = hbs.handlebars.Utils.escapeExpression(metaTitle);
metaDescription = hbs.handlebars.Utils.escapeExpression(metaDescription + '...');
authorUrl = hbs.handlebars.Utils.escapeExpression(blog.url + '/author/' + self.post.author.slug);
schema = {
'@context': 'http://schema.org',
'@type': 'Article',
publisher: _.escape(blog.title),
publisher: title,
author: {
'@type': 'Person',
name: self.post.author.name,
image: authorImage,
url: _.escape(blog.url) + '/author/' + self.post.author.slug,
url: authorUrl,
sameAs: self.post.author.website
},
headline: metaTitle,
@ -112,10 +119,10 @@ ghost_head = function (options) {
};
structuredData = {
'og:site_name': _.escape(blog.title),
'og:site_name': title,
'og:type': 'article',
'og:title': metaTitle,
'og:description': metaDescription + '...',
'og:description': metaDescription,
'og:url': url,
'og:image': coverImage,
'article:published_time': publishedDate,
@ -123,7 +130,7 @@ ghost_head = function (options) {
'article:tag': tags,
'twitter:card': card,
'twitter:title': metaTitle,
'twitter:description': metaDescription + '...',
'twitter:description': metaDescription,
'twitter:url': url,
'twitter:image:src': coverImage
};
@ -132,16 +139,14 @@ ghost_head = function (options) {
if (property === 'article:tag') {
_.each(tags, function (tag) {
if (tag !== '') {
head.push('<meta property="' + property + '" content="' + tag.trim() + '" />');
tag = hbs.handlebars.Utils.escapeExpression(tag.trim());
head.push('<meta property="' + property + '" content="' + tag + '" />');
}
});
head.push('');
} else if (content !== null && content !== undefined) {
if (property.substring(0, 7) === 'twitter') {
head.push('<meta name="' + property + '" content="' + content + '" />');
} else {
head.push('<meta property="' + property + '" content="' + content + '" />');
}
type = property.substring(0, 7) === 'twitter' ? 'name' : 'property';
head.push('<meta ' + type + '="' + property + '" content="' + content + '" />');
}
});
head.push('');
@ -150,7 +155,7 @@ ghost_head = function (options) {
head.push('<meta name="generator" content="Ghost ' + trimmedVersion + '" />');
head.push('<link rel="alternate" type="application/rss+xml" title="' +
_.escape(blog.title) + '" href="' + config.urlFor('rss') + '" />');
title + '" href="' + config.urlFor('rss') + '" />');
return filters.doFilter('ghost_head', head);
}).then(function (head) {
var headString = _.reduce(head, function (memo, item) { return memo + '\n ' + item; }, '');

View File

@ -94,7 +94,7 @@ describe('{{ghost_head}} helper', function () {
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
' "image": "http://testurl.com/test-image.png",\n "keywords": "tag1, tag2, tag3",\n' +
' "description": "blog description"\n}\n </script>\n\n' +
' "description": "blog description..."\n}\n </script>\n\n' +
' <meta name="generator" content="Ghost 0.3" />\n' +
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/rss/" />');
@ -102,7 +102,60 @@ describe('{{ghost_head}} helper', function () {
}).catch(done);
});
it('returns structured without tags if there are no tags', function (done) {
it('returns structured data if metaTitle and metaDescription have double quotes', function (done) {
var post = {
meta_description: 'blog "test" description',
title: 'title',
meta_title: 'Welcome to Ghost "test"',
image: '/test-image.png',
published_at: moment('2008-05-31T19:18:15').toISOString(),
updated_at: moment('2014-10-06T15:23:54').toISOString(),
tags: [{name: 'tag1'}, {name: 'tag2'}, {name: 'tag3'}],
author: {
name: 'Author name',
url: 'http//:testauthorurl.com',
slug: 'Author',
image: '/test-author-image.png',
website: 'http://authorwebsite.com'
}
};
helpers.ghost_head.call({relativeUrl: '/post/', version: '0.3.0', post: post}).then(function (rendered) {
should.exist(rendered);
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/post/" />\n \n' +
' <meta property="og:site_name" content="Ghost" />\n' +
' <meta property="og:type" content="article" />\n' +
' <meta property="og:title" content="Welcome to Ghost &quot;test&quot;" />\n' +
' <meta property="og:description" content="blog &quot;test&quot; description..." />\n' +
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
' <meta property="og:image" content="http://testurl.com/test-image.png" />\n' +
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
' <meta property="article:tag" content="tag1" />\n' +
' <meta property="article:tag" content="tag2" />\n' +
' <meta property="article:tag" content="tag3" />\n \n' +
' <meta name="twitter:card" content="summary_large_image" />\n' +
' <meta name="twitter:title" content="Welcome to Ghost &quot;test&quot;" />\n' +
' <meta name="twitter:description" content="blog &quot;test&quot; description..." />\n' +
' <meta name="twitter:url" content="http://testurl.com/post/" />\n' +
' <meta name="twitter:image:src" content="http://testurl.com/test-image.png" />\n \n' +
' <script type=\"application/ld+json\">\n{\n' +
' "@context": "http://schema.org",\n "@type": "Article",\n "publisher": "Ghost",\n' +
' "author": {\n "@type": "Person",\n "name": "Author name",\n ' +
' \"image\": \"http://testurl.com/test-author-image.png\",\n ' +
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
'},\n "headline": "Welcome to Ghost &quot;test&quot;",\n "url": "http://testurl.com/post/",\n' +
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
' "image": "http://testurl.com/test-image.png",\n "keywords": "tag1, tag2, tag3",\n' +
' "description": "blog &quot;test&quot; description..."\n}\n </script>\n\n' +
' <meta name="generator" content="Ghost 0.3" />\n' +
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/rss/" />');
done();
}).catch(done);
});
it('returns structured data without tags if there are no tags', function (done) {
var post = {
meta_description: 'blog description',
title: 'Welcome to Ghost',
@ -143,7 +196,7 @@ describe('{{ghost_head}} helper', function () {
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
' "image": "http://testurl.com/test-image.png",\n' +
' "description": "blog description"\n}\n </script>\n\n' +
' "description": "blog description..."\n}\n </script>\n\n' +
' <meta name="generator" content="Ghost 0.3" />\n' +
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/rss/" />');
@ -191,7 +244,7 @@ describe('{{ghost_head}} helper', function () {
' "url": "http://testurl.com/author/Author",\n "sameAs": "http://authorwebsite.com"\n ' +
'},\n "headline": "Welcome to Ghost",\n "url": "http://testurl.com/post/",\n' +
' "datePublished": "' + post.published_at + '",\n "dateModified": "' + post.updated_at + '",\n' +
' "keywords": "tag1, tag2, tag3",\n "description": "blog description"\n}\n </script>\n\n' +
' "keywords": "tag1, tag2, tag3",\n "description": "blog description..."\n}\n </script>\n\n' +
' <meta name="generator" content="Ghost 0.3" />\n' +
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/rss/" />');