2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

🙇 Blog icon utils and publisher.logo for JSON-LD (#8297)

refs #8221, closes #7688, refs #7558

🙇  Improve meta data publisher logo behaviour
This is a follow-up PR for #8285.

Reasons: The code changes of #8285 caused error messages when falling back to the default `favicon.ico`, as the `image-size` tool doesn't support `ico` files.

This PR takes the logic to decide which logo needs to be listed in our schema into a new fn `blog_logo.js`. There we have now three decisions:
1. If we have a publication **logo**, we'll take that one
2. If we have no publication logo, but an **icon** we'll use this one.
3. If we have none of the above things, we fall back to our default `favicon.ico`

Additional, we're hard coding image dimensions for whenever the logo is an `.ico` file and built and extra decision to not call `image-size` when the dimension are already given.

I will create another follow-up PR, which checks the extension type for the file and offers it as a util.

🛠  Blog icon util

refs #7688

Serve functionality around the blog icon in its own util:
- getIconDimensions -> async function that takes the filepath of on ico file and returns its dimensions
- isIcoImageType -> returns true if file has `.ico` extension
- getIconType -> returns icon-type (`x-icon` or `png`)
- getIconUrl -> returns the absolut or relativ URL for the favicon: `[subdirectory or not]favicon.[ico or png]`

📖  Get .ico sizes for meta data & logo improvement

refs #7558
refs #8221

Use the new `blogIconUtil` in meta data to fetch the dimensions of `.ico` files.

Improvements for `publisher.logo`: We're now returning a hard-coded 'faked' image dimensions value to render an `imageObject` and prevent error our schema (Google structured data). As soon as an image (`.ico` or non-`.ico`) is too large, but - in case of non-`.ico` - a square format, be set the image-dimensions to 60px width and height. This reduces the chances of getting constantly error messages from Googles' webmaster tools.

- add getIconPath util
This commit is contained in:
Aileen Nowak 2017-04-11 23:32:06 +07:00 committed by Katharina Irrgang
parent 049b6d9874
commit e19e91044d
15 changed files with 637 additions and 76 deletions

View file

@ -1,5 +1,5 @@
var config = require('../../config'),
settingsCache = require('../../settings/cache'),
blogIconUtils = require('../../utils/blog-icon'),
utils = require('../../utils');
/**
@ -7,11 +7,7 @@ var config = require('../../config'),
* @return {string}
*/
function getFaviconUrl() {
if (settingsCache.get('icon')) {
return settingsCache.get('icon').match(/\.ico$/i) ? utils.url.urlJoin(utils.url.getSubdir(), '/favicon.ico') : utils.url.urlJoin(utils.url.getSubdir(), '/favicon.png');
}
return utils.url.urlJoin(utils.url.getSubdir(), '/favicon.ico');
return blogIconUtils.getIconUrl();
}
function getAssetUrl(path, hasMinFile) {

View file

@ -0,0 +1,46 @@
var utils = require('../../utils'),
settingsCache = require('../../settings/cache'),
blogIconUtils = require('../../utils/blog-icon'),
Promise = require('bluebird'),
config = require('../../config'),
path = require('path');
function getBlogLogo() {
return new Promise(function getIconSize(resolve, reject) {
var logo = {},
filePath;
if (settingsCache.get('logo')) {
logo.url = utils.url.urlFor('image', {image: settingsCache.get('logo')}, true);
} else {
// CASE: no publication logo is updated. We can try to use either an uploaded publication icon
// or use the default one to make
// Google happy with it. See https://github.com/TryGhost/Ghost/issues/7558
logo.url = blogIconUtils.getIconUrl(true);
if (blogIconUtils.isIcoImageType(logo.url)) {
filePath = blogIconUtils.getIconPath();
// getIconDimensions needs the physical path of the ico file
if (settingsCache.get('icon')) {
// CASE: custom uploaded icon
filePath = path.join(config.getContentPath('images'), filePath);
}
return blogIconUtils.getIconDimensions(filePath).then(function (response) {
logo.dimensions = {
width: response.width,
height: response.height
};
return resolve(logo);
}).catch(function (err) {
return reject(err);
});
}
}
return resolve(logo);
});
}
module.exports = getBlogLogo;

View file

@ -13,7 +13,9 @@ function getImageDimensions(metaData) {
var fetch = {
coverImage: getCachedImageSizeFromUrl(metaData.coverImage.url),
authorImage: getCachedImageSizeFromUrl(metaData.authorImage.url),
logo: getCachedImageSizeFromUrl(metaData.blog.logo.url)
// CASE: check if logo has hard coded image dimension. In that case it's an `ico` file, which
// is not supported by `image-size` and would produce an error
logo: metaData.blog.logo && metaData.blog.logo.dimensions ? metaData.blog.logo.dimensions : getCachedImageSizeFromUrl(metaData.blog.logo.url)
};
return Promise.props(fetch).then(function (resolve) {
@ -30,7 +32,7 @@ function getImageDimensions(metaData) {
// We have some restrictions for publisher.logo:
// The image needs to be <=600px wide and <=60px high (ideally exactly 600px x 60px).
// Unless we have proper image-handling (see https://github.com/TryGhost/Ghost/issues/4453),
// we will not output an ImageObject if the logo doesn't fit in the dimensions.
// we will fake it in some cases or not produce an imageObject at all.
if (value === 'logo') {
if (key.height <= 60 && key.width <= 600) {
_.assign(metaData.blog[value], {
@ -39,6 +41,17 @@ function getImageDimensions(metaData) {
height: key.height
}
});
} else if ((metaData.blog.logo && metaData.blog.logo.dimensions) || key.width === key.height) {
// CASES:
// 1. .ico files have image dimensions assigned already. If they're not
// within the requirements of Google, we fake them...
// 2. the logo (non-ico) is too large, but it is a square. We fake it as well...
_.assign(metaData.blog[value], {
dimensions: {
width: 60,
height: 60
}
});
}
} else {
_.assign(metaData[value], {

View file

@ -1,12 +1,14 @@
var Promise = require('bluebird'),
settingsCache = require('../../settings/cache'),
utils = require('../../utils'),
logging = require('../../logging'),
getUrl = require('./url'),
getImageDimensions = require('./image-dimensions'),
getCanonicalUrl = require('./canonical_url'),
getAmpUrl = require('./amp_url'),
getPaginatedUrl = require('./paginated_url'),
getAuthorUrl = require('./author_url'),
getBlogLogo = require('./blog_logo'),
getRssUrl = require('./rss_url'),
getTitle = require('./title'),
getDescription = require('./description'),
@ -61,33 +63,26 @@ function getMetaData(data, root) {
}
};
metaData.blog.logo = {};
return Promise.props(getBlogLogo()).then(function (result) {
metaData.blog.logo = result;
if (settingsCache.get('logo')) {
metaData.blog.logo.url = utils.url.urlFor('image', {image: settingsCache.get('logo')}, true);
} else {
metaData.blog.logo.url = utils.url.urlFor({relativeUrl: 'favicon.ico'}, true);
// Setting image dimensions to force the default logo to be an `ImageObject` and make
// Google happy with it. See https://github.com/TryGhost/Ghost/issues/7558
metaData.blog.logo.dimensions = {
width: 60,
height: 60
};
}
// TODO: cleanup these if statements
if (data.post && data.post.html) {
metaData.excerpt = getExcerpt(data.post.html, {words: 50});
}
// TODO: cleanup these if statements
if (data.post && data.post.html) {
metaData.excerpt = getExcerpt(data.post.html, {words: 50});
}
if (data.post && data.post.author && data.post.author.name) {
metaData.authorName = data.post.author.name;
}
if (data.post && data.post.author && data.post.author.name) {
metaData.authorName = data.post.author.name;
}
return Promise.props(getImageDimensions(metaData)).then(function () {
metaData.structuredData = getStructuredData(metaData);
metaData.schema = getSchema(metaData, data);
return Promise.props(getImageDimensions(metaData)).then(function () {
metaData.structuredData = getStructuredData(metaData);
metaData.schema = getSchema(metaData, data);
return metaData;
});
}).catch(function (err) {
logging.error(err);
return metaData;
});
}

View file

@ -4,6 +4,7 @@ var https = require('https'),
errors = require('../../errors'),
logging = require('../../logging'),
utils = require('../../utils'),
blogIconUtils = require('../../utils/blog-icon'),
events = require('../../events'),
api = require('../../api/settings'),
i18n = require('../../i18n'),
@ -73,7 +74,7 @@ function ping(post) {
slackData = {
text: message,
unfurl_links: true,
icon_url: utils.url.urlFor({relativeUrl: 'favicon.ico'}, true),
icon_url: blogIconUtils.getIconUrl(true),
username: 'Ghost'
};

View file

@ -19,7 +19,7 @@ var proxy = require('./proxy'),
api = proxy.api,
settingsCache = proxy.settingsCache,
config = proxy.config,
url = proxy.url;
blogIconUtils = proxy.blogIcon;
function getClient() {
if (labs.isSet('publicAPI') === true) {
@ -96,11 +96,8 @@ module.exports = function ghost_head(options) {
metaData: getMetaData(this, options.data.root),
client: getClient()
},
blogIcon = settingsCache.get('icon'),
// CASE: blog icon is not set in config, we serve the default
iconType = !blogIcon ? 'x-icon' : blogIcon.match(/\.ico$/i) ? 'x-icon' : 'png',
favicon = !blogIcon ? url.urlFor({relativeUrl: '/favicon.ico'}) :
blogIcon.match(/\.ico$/i) ? url.urlFor({relativeUrl: '/favicon.ico'}) : url.urlFor({relativeUrl: '/favicon.png'});
favicon = blogIconUtils.getIconUrl(),
iconType = blogIconUtils.getIconType(favicon);
return Promise.props(fetch).then(function (response) {
client = response.client;

View file

@ -62,6 +62,7 @@ module.exports = {
// Various utils, needs cleaning up / simplifying
socialUrls: require('../utils/social-urls'),
blogIcon: require('../utils/blog-icon'),
url: require('../utils').url,
utils: {
findKey: function findKey(key /* ...objects... */) {

View file

@ -1,10 +1,10 @@
var fs = require('fs'),
path = require('path'),
crypto = require('crypto'),
config = require('../config'),
storage = require('../storage'),
utils = require('../utils'),
settingsCache = require('../settings/cache'),
blogIconUtils = require('../utils/blog-icon'),
buildContentResponse,
content;
@ -36,7 +36,7 @@ function serveFavicon() {
// we are using an express route to skip /content/images and the result is a image path
// based on config.getContentPath('images') + req.path
// in this case we don't use path rewrite, that's why we have to make it manually
filePath = settingsCache.get('icon').replace(new RegExp(utils.url.STATIC_IMAGE_URL_PREFIX), '');
filePath = blogIconUtils.getIconPath();
var originalExtension = path.extname(filePath).toLowerCase(),
requestedExtension = path.extname(req.path).toLowerCase();
@ -51,7 +51,7 @@ function serveFavicon() {
storage.getStorage()
.read({path: filePath})
.then(function readFile(buf) {
iconType = settingsCache.get('icon').match(/\.ico$/i) ? 'x-icon' : 'png';
iconType = blogIconUtils.getIconType();
content = buildContentResponse(iconType, buf);
res.writeHead(200, content.headers);
@ -61,7 +61,6 @@ function serveFavicon() {
next(err);
});
} else {
filePath = path.join(config.get('paths:publicFilePath'), 'favicon.ico');
originalExtension = path.extname(filePath).toLowerCase();
// CASE: always redirect to .ico for default icon

View file

@ -1,10 +1,9 @@
var errors = require('../../errors'),
config = require('../../config'),
fs = require('fs'),
Promise = require('bluebird'),
sizeOf = require('image-size'),
i18n = require('../../i18n'),
_ = require('lodash'),
blogIconUtils = require('../../utils/blog-icon'),
validIconSize,
getIconDimensions;
@ -14,40 +13,24 @@ validIconSize = function validIconSize(size) {
getIconDimensions = function getIconDimensions(icon) {
return new Promise(function getImageSize(resolve, reject) {
var arrayBuffer,
ICO = require('icojs');
// image-size doesn't support .ico files
if (icon.name.match(/.ico$/i)) {
arrayBuffer = new Uint8Array(fs.readFileSync(icon.path)).buffer;
ICO.parse(arrayBuffer).then(function (result, error) {
if (error) {
return reject(new errors.ValidationError({message: i18n.t('errors.api.icons.couldNotGetSize', {file: icon.name, error: error.message})}));
}
// CASE: ico file contains only one size
if (result.length === 1) {
return resolve({
width: result[0].width,
height: result[0].height
});
} else {
// CASE: ico file contains multiple sizes, return only the max size
return resolve({
width: _.maxBy(result, function (w) {return w.width;}).width,
height: _.maxBy(result, function (h) {return h.height;}).height
});
}
if (blogIconUtils.isIcoImageType(icon.name)) {
blogIconUtils.getIconDimensions(icon.path).then(function (response) {
return resolve({
width: response.width,
height: response.height
});
}).catch(function (err) {
return reject(err);
});
} else {
sizeOf(icon.path, function (err, dimensions) {
sizeOf(icon.path, function (err, response) {
if (err) {
return reject(new errors.ValidationError({message: i18n.t('errors.api.icons.couldNotGetSize', {file: icon.name, error: err.message})}));
}
return resolve({
width: dimensions.width,
height: dimensions.height
width: response.width,
height: response.height
});
});
}
@ -64,9 +47,9 @@ module.exports = function blogIcon() {
return next(new errors.ValidationError({message: i18n.t('errors.api.icons.invalidFile', {extensions: iconExtensions})}));
}
return getIconDimensions(req.file).then(function (dimensions) {
return getIconDimensions(req.file).then(function (response) {
// save the image dimensions in new property for file
req.file.dimensions = dimensions;
req.file.dimensions = response;
// CASE: file needs to be a square
if (req.file.dimensions.width !== req.file.dimensions.height) {
@ -85,6 +68,8 @@ module.exports = function blogIcon() {
}
next();
}).catch(function (err) {
next(err);
});
};
};

View file

@ -115,6 +115,9 @@
"nameOrVersionMissing": "\"name\" or \"version\" is missing from theme package.json file.",
"willBeRequired": "This will be required in future. Please see {url}",
"themeFileIsMalformed": "Theme package.json file is malformed"
},
"blogIcon": {
"error": "Could not fetch icon dimensions."
}
},
"config": {

View file

@ -0,0 +1,128 @@
var ICO = require('icojs'),
errors = require('../errors'),
url = require('./url'),
Promise = require('bluebird'),
i18n = require('../i18n'),
settingsCache = require('../settings/cache'),
fs = require('fs'),
_ = require('lodash'),
path = require('path'),
config = require('../config'),
utils = require('../utils'),
getIconDimensions,
isIcoImageType,
getIconType,
getIconUrl,
getIconPath;
/**
* Get dimensions for ico file from its real file storage path
* Always returns {object} getIconDimensions
* @param {string} path
* @returns {Promise<Object>} getIconDimensions
* @description Takes a file path and returns ico width and height.
*/
getIconDimensions = function getIconDimensions(path) {
return new Promise(function getIconSize(resolve, reject) {
var arrayBuffer;
try {
arrayBuffer = new Uint8Array(fs.readFileSync(path)).buffer;
} catch (error) {
return reject(error);
}
ICO.parse(arrayBuffer).then(function (response) {
// CASE: ico file contains only one size
if (response.length === 1) {
return resolve({
width: response[0].width,
height: response[0].height
});
} else {
// CASE: ico file contains multiple sizes, return only the max size
return resolve({
width: _.maxBy(response, function (w) {return w.width;}).width,
height: _.maxBy(response, function (h) {return h.height;}).height
});
}
}).catch(function (err) {
return reject(new errors.ValidationError({message: i18n.t('errors.utils.blogIcon.error', {file: path, error: err.message})}));
});
});
};
/**
* Check if file is `.ico` extension
* Always returns {object} isIcoImageType
* @param {string} icon
* @returns {Boolean} true if submitted path is .ico file
* @description Takes a path and returns boolean value.
*/
isIcoImageType = function isIcoImageType(icon) {
var blogIcon = icon || settingsCache.get('icon');
return blogIcon.match(/.ico$/i) ? true : false;
};
/**
* Check if file is `.ico` extension
* Always returns {object} isIcoImageType
* @param {string} icon
* @returns {Boolean} true if submitted path is .ico file
* @description Takes a path and returns boolean value.
*/
getIconType = function getIconType(icon) {
var blogIcon = icon || settingsCache.get('icon');
return isIcoImageType(blogIcon) ? 'x-icon' : 'png';
};
/**
* Return URL for Blog icon: [subdirectory or not]favicon.[ico or png]
* Always returns {string} getIconUrl
* @returns {string} [subdirectory or not]favicon.[ico or png]
* @description Checks if we have a custom uploaded icon and the extension of it. If no custom uploaded icon
* exists, we're returning the default `favicon.ico`
*/
getIconUrl = function getIconUrl(absolut) {
var blogIcon = settingsCache.get('icon');
if (absolut) {
if (blogIcon) {
return isIcoImageType(blogIcon) ? url.urlFor({relativeUrl: '/favicon.ico'}, true) : url.urlFor({relativeUrl: '/favicon.png'}, true);
} else {
return url.urlFor({relativeUrl: '/favicon.ico'}, true);
}
} else {
if (blogIcon) {
return isIcoImageType(blogIcon) ? url.urlFor({relativeUrl: '/favicon.ico'}) : url.urlFor({relativeUrl: '/favicon.png'});
} else {
return url.urlFor({relativeUrl: '/favicon.ico'});
}
}
};
/**
* Return path for Blog icon without [subdirectory]/content/image prefix
* Always returns {string} getIconPath
* @returns {string} physical storage path of icon
* @description Checks if we have a custom uploaded icon. If no custom uploaded icon
* exists, we're returning the default `favicon.ico`
*/
getIconPath = function getIconPath() {
var blogIcon = settingsCache.get('icon');
if (blogIcon) {
// The '/' in urlJoin is necessary to add the '/' to `content/images`, if no subdirectory is setup
return blogIcon.replace(new RegExp('^' + utils.url.urlJoin(utils.url.getSubdir(), '/', utils.url.STATIC_IMAGE_URL_PREFIX)), '');
} else {
return path.join(config.get('paths:publicFilePath'), 'favicon.ico');
}
};
module.exports.getIconDimensions = getIconDimensions;
module.exports.isIcoImageType = isIcoImageType;
module.exports.getIconUrl = getIconUrl;
module.exports.getIconPath = getIconPath;
module.exports.getIconType = getIconType;

View file

@ -0,0 +1,98 @@
var should = require('should'),
getBlogLogo = require('../../../server/data/meta/blog_logo'),
sinon = require('sinon'),
Promise = require('bluebird'),
settingsCache = require('../../../server/settings/cache'),
blogIconUtils = require('../../../server/utils/blog-icon'),
sandbox = sinon.sandbox.create();
describe('getBlogLogo', function () {
afterEach(function () {
sandbox.restore();
});
it('should return logo if uploaded', function (done) {
sandbox.stub(settingsCache, 'get', function (key) {
return {
logo: '/content/images/logo.png',
icon: null
}[key];
});
getBlogLogo().then(function (blogLogo) {
should.exist(blogLogo);
blogLogo.should.have.property('url', 'http://127.0.0.1:2369/content/images/logo.png');
}).catch(done);
done();
});
it('should return custom uploaded png icon if no logo given', function (done) {
sandbox.stub(settingsCache, 'get', function (key) {
return {
logo: null,
icon: '/content/images/favicon.png'
}[key];
});
getBlogLogo().then(function (blogLogo) {
should.exist(blogLogo);
blogLogo.should.have.property('url', 'http://127.0.0.1:2369/favicon.png');
}).catch(done);
done();
});
it('should return custom uploaded ico icon incl. dimensions if no logo given', function (done) {
sandbox.stub(settingsCache, 'get', function (key) {
return {
logo: null,
icon: '/content/images/myicon.ico'
}[key];
});
sandbox.stub(blogIconUtils, 'getIconDimensions').returns(Promise.resolve({width: 48, height: 48}));
getBlogLogo().then(function (blogLogo) {
should.exist(blogLogo);
blogLogo.should.have.property('url', 'http://127.0.0.1:2369/favicon.ico');
blogLogo.should.have.property('dimensions');
blogLogo.dimensions.should.have.property('width', 48);
blogLogo.dimensions.should.have.property('height', 48);
}).catch(done);
done();
});
it('should return default favicon with dimensions if no logo or icon uploaded', function (done) {
getBlogLogo().then(function (blogLogo) {
should.exist(blogLogo);
blogLogo.should.have.property('url', 'http://127.0.0.1:2369/favicon.ico');
blogLogo.should.have.property('dimensions');
blogLogo.dimensions.should.have.property('width', 64);
blogLogo.dimensions.should.have.property('height', 64);
}).catch(done);
done();
});
it.skip('[failure] can handle errors', function (done) {
sandbox.stub(settingsCache, 'get', function (key) {
return {
logo: null,
icon: '/content/images/myicon.ico'
}[key];
});
sandbox.stub(blogIconUtils, 'getIconDimensions').returns(Promise.reject(new Error({message: 'could not fetch icon size'})));
getBlogLogo().then(function (blogLogo) {
should.not.exist(blogLogo);
done(new Error('should not resolve'));
}).catch(function (err) {
err.message.should.equal('could not fetch icon size');
done();
});
});
});

View file

@ -51,6 +51,14 @@ describe('getImageDimensions', function () {
result.coverImage.should.have.property('dimensions');
result.coverImage.should.have.property('url');
result.blog.logo.should.have.property('dimensions');
result.coverImage.dimensions.should.have.property('height', 50);
result.coverImage.dimensions.should.have.property('width', 50);
result.blog.logo.should.have.property('dimensions');
result.blog.logo.dimensions.should.have.property('height', 50);
result.blog.logo.dimensions.should.have.property('width', 50);
result.authorImage.should.have.property('dimensions');
result.authorImage.dimensions.should.have.property('height', 50);
result.authorImage.dimensions.should.have.property('width', 50);
result.blog.logo.should.have.property('url');
result.authorImage.should.have.property('dimensions');
result.authorImage.should.have.property('url');
@ -92,7 +100,7 @@ describe('getImageDimensions', function () {
}).catch(done);
});
it('should not return dimension for publisher.logo only if logo is too big', function (done) {
it('should not try to fetch image dimensions for logo if already set', function (done) {
var metaData = {
coverImage: {
url: 'http://mysite.com/content/image/mypostcoverimage.jpg'
@ -102,7 +110,11 @@ describe('getImageDimensions', function () {
},
blog: {
logo: {
url: 'http://mysite.com/author/image/url/logo.jpg'
url: 'http://mysite.com/author/image/url/favicon.ico',
dimensions: {
width: 60,
height: 60
}
}
}
};
@ -115,14 +127,154 @@ describe('getImageDimensions', function () {
getImageDimensions.__set__('getCachedImageSizeFromUrl', sizeOfStub);
getImageDimensions(metaData).then(function (result) {
should.exist(result);
sizeOfStub.calledWith(metaData.coverImage.url).should.be.true();
sizeOfStub.calledWith(metaData.authorImage.url).should.be.true();
sizeOfStub.calledWith(metaData.blog.logo.url).should.be.false();
result.coverImage.should.have.property('dimensions');
result.coverImage.dimensions.should.have.property('height', 80);
result.coverImage.dimensions.should.have.property('width', 480);
result.blog.logo.should.have.property('dimensions');
result.blog.logo.dimensions.should.have.property('height', 60);
result.blog.logo.dimensions.should.have.property('width', 60);
result.authorImage.should.have.property('dimensions');
result.authorImage.dimensions.should.have.property('height', 80);
result.authorImage.dimensions.should.have.property('width', 480);
result.coverImage.should.have.property('url');
result.blog.logo.should.have.property('url');
result.authorImage.should.have.property('url');
done();
}).catch(done);
});
it('should fake image dimension for publisher.logo if .ico file is too big', function (done) {
var metaData = {
coverImage: {
url: 'http://mysite.com/content/image/mypostcoverimage.jpg'
},
authorImage: {
url: 'http://mysite.com/author/image/url/me.jpg'
},
blog: {
logo: {
url: 'http://mysite.com/author/image/url/favicon.ico',
dimensions: {
width: 128,
height: 128
}
}
}
};
sizeOfStub.returns({
width: 480,
height: 480,
type: 'jpg'
});
getImageDimensions.__set__('getCachedImageSizeFromUrl', sizeOfStub);
getImageDimensions(metaData).then(function (result) {
should.exist(result);
sizeOfStub.calledWith(metaData.coverImage.url).should.be.true();
sizeOfStub.calledWith(metaData.authorImage.url).should.be.true();
sizeOfStub.calledWith(metaData.blog.logo.url).should.be.false();
result.coverImage.should.have.property('dimensions');
result.coverImage.dimensions.should.have.property('height', 480);
result.coverImage.dimensions.should.have.property('width', 480);
result.blog.logo.should.have.property('dimensions');
result.blog.logo.dimensions.should.have.property('height', 60);
result.blog.logo.dimensions.should.have.property('width', 60);
result.authorImage.should.have.property('dimensions');
result.authorImage.dimensions.should.have.property('height', 480);
result.authorImage.dimensions.should.have.property('width', 480);
result.coverImage.should.have.property('url');
result.blog.logo.should.have.property('url');
result.authorImage.should.have.property('url');
done();
}).catch(done);
});
it('should fake image dimension for publisher.logo if non-.ico file is too big and square', function (done) {
var metaData = {
coverImage: {
url: 'http://mysite.com/content/image/mypostcoverimage.jpg'
},
authorImage: {
url: 'http://mysite.com/author/image/url/me.jpg'
},
blog: {
logo: {
url: 'http://mysite.com/author/image/url/favicon.png'
}
}
};
sizeOfStub.returns({
width: 480,
height: 480,
type: 'jpg'
});
getImageDimensions.__set__('getCachedImageSizeFromUrl', sizeOfStub);
getImageDimensions(metaData).then(function (result) {
should.exist(result);
sizeOfStub.calledWith(metaData.coverImage.url).should.be.true();
sizeOfStub.calledWith(metaData.authorImage.url).should.be.true();
sizeOfStub.calledWith(metaData.blog.logo.url).should.be.true();
result.coverImage.should.have.property('dimensions');
result.coverImage.dimensions.should.have.property('height', 480);
result.coverImage.dimensions.should.have.property('width', 480);
result.blog.logo.should.have.property('dimensions');
result.blog.logo.dimensions.should.have.property('height', 60);
result.blog.logo.dimensions.should.have.property('width', 60);
result.authorImage.should.have.property('dimensions');
result.authorImage.dimensions.should.have.property('height', 480);
result.authorImage.dimensions.should.have.property('width', 480);
result.coverImage.should.have.property('url');
result.blog.logo.should.have.property('url');
result.authorImage.should.have.property('url');
done();
}).catch(done);
});
it('should not fake dimension for publisher.logo if a logo is too big but not square', function (done) {
var metaData = {
coverImage: {
url: 'http://mysite.com/content/image/mypostcoverimage.jpg'
},
authorImage: {
url: 'http://mysite.com/author/image/url/me.jpg'
},
blog: {
logo: {
url: 'http://mysite.com/author/image/url/logo.jpg'
}
}
};
sizeOfStub.returns({
width: 80,
height: 480,
type: 'jpg'
});
getImageDimensions.__set__('getCachedImageSizeFromUrl', sizeOfStub);
getImageDimensions(metaData).then(function (result) {
should.exist(result);
sizeOfStub.calledWith(metaData.coverImage.url).should.be.true();
sizeOfStub.calledWith(metaData.authorImage.url).should.be.true();
sizeOfStub.calledWith(metaData.blog.logo.url).should.be.true();
result.coverImage.should.have.property('dimensions');
result.coverImage.dimensions.should.have.property('height', 480);
result.coverImage.dimensions.should.have.property('width', 80);
result.blog.logo.should.not.have.property('dimensions');
result.authorImage.should.have.property('dimensions');
result.authorImage.dimensions.should.have.property('height', 480);
result.authorImage.dimensions.should.have.property('width', 80);
result.coverImage.should.have.property('url');
result.blog.logo.should.have.property('url');
result.authorImage.should.have.property('url');

View file

@ -166,7 +166,7 @@ describe('Serve Favicon', function () {
var middleware = serveFavicon();
req.path = '/favicon.png';
configUtils.set('paths:corePath', path.join(__dirname, '../../../test/utils/fixtures/'));
configUtils.set('paths:publicFilePath', path.join(__dirname, '../../../test/utils/fixtures/'));
localSettingsCache.icon = '';
res = {

View file

@ -0,0 +1,147 @@
// jshint unused: false
var should = require('should'),
sinon = require('sinon'),
_ = require('lodash'),
settingsCache = require('../../../server/settings/cache'),
configUtils = require('../../utils/configUtils'),
testUtils = require('../../utils'),
config = configUtils.config,
path = require('path'),
// stuff we are testing
blogIcon = require('../../../server/utils/blog-icon'),
sandbox = sinon.sandbox.create();
describe('Blog Icon', function () {
before(function () {
configUtils.restore();
});
afterEach(function () {
configUtils.restore();
sandbox.restore();
});
describe('getIconUrl', function () {
it('custom uploaded ico blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico');
blogIcon.getIconUrl().should.eql('/favicon.ico');
});
it('custom uploaded png blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png');
blogIcon.getIconUrl().should.eql('/favicon.png');
});
it('default ico blog icon', function () {
blogIcon.getIconUrl().should.eql('/favicon.ico');
});
describe('absolute URL', function () {
it('custom uploaded ico blog icon', function () {
configUtils.set({url: 'http://my-ghost-blog.com/'});
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico');
blogIcon.getIconUrl(true).should.eql('http://my-ghost-blog.com/favicon.ico');
});
it('custom uploaded png blog icon', function () {
configUtils.set({url: 'http://my-ghost-blog.com/'});
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png');
blogIcon.getIconUrl(true).should.eql('http://my-ghost-blog.com/favicon.png');
});
it('default ico blog icon', function () {
configUtils.set({url: 'http://my-ghost-blog.com/'});
blogIcon.getIconUrl(true).should.eql('http://my-ghost-blog.com/favicon.ico');
});
});
describe('with subdirectory', function () {
it('custom uploaded ico blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico');
configUtils.set({url: 'http://my-ghost-blog.com/blog'});
blogIcon.getIconUrl().should.eql('/blog/favicon.ico');
});
it('custom uploaded png blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png');
configUtils.set({url: 'http://my-ghost-blog.com/blog'});
blogIcon.getIconUrl().should.eql('/blog/favicon.png');
});
it('default ico blog icon', function () {
configUtils.set({url: 'http://my-ghost-blog.com/blog'});
blogIcon.getIconUrl().should.eql('/blog/favicon.ico');
});
});
});
describe('getIconPath', function () {
it('custom uploaded ico blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.ico');
blogIcon.getIconPath().should.eql('/2017/04/my-icon.ico');
});
it('custom uploaded png blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/content/images/2017/04/my-icon.png');
blogIcon.getIconPath().should.eql('/2017/04/my-icon.png');
});
it('default ico blog icon', function () {
blogIcon.getIconPath().should.eql(path.join(__dirname, '../../../server/public/favicon.ico'));
});
describe('with subdirectory', function () {
it('custom uploaded ico blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/blog/content/images/2017/04/my-icon.ico');
configUtils.set({url: 'http://my-ghost-blog.com/blog'});
blogIcon.getIconPath().should.eql('/2017/04/my-icon.ico');
});
it('custom uploaded png blog icon', function () {
sandbox.stub(settingsCache, 'get').withArgs('icon').returns('/blog/content/images/2017/04/my-icon.png');
configUtils.set({url: 'http://my-ghost-blog.com/blog'});
blogIcon.getIconPath().should.eql('/2017/04/my-icon.png');
});
it('default ico blog icon', function () {
configUtils.set({url: 'http://my-ghost-blog.com/blog'});
blogIcon.getIconPath().should.eql(path.join(__dirname, '../../../server/public/favicon.ico'));
});
});
});
describe('isIcoImageType', function () {
it('returns true, if icon is .ico filetype', function () {
blogIcon.isIcoImageType('icon.ico').should.be.true();
});
it('returns false, if icon is not .ico filetype', function () {
blogIcon.isIcoImageType('icon.png').should.be.false();
});
});
describe('getIconType', function () {
it('returns x-icon for ico icons', function () {
blogIcon.getIconType('favicon.ico').should.eql('x-icon');
});
it('returns png for png icon', function () {
blogIcon.getIconType('favicon.png').should.eql('png');
});
});
describe.skip('getIconDimensions', function () {
it('[success] returns icon dimensions', function (done) {
done();
});
it('[failure] return error message', function (done) {
done();
});
});
});