mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-18 02:21:47 -05:00
Merge pull request #4283 from ErisDS/split-helpers
Refactor helpers & tests into individual files
This commit is contained in:
commit
dc426e94b3
51 changed files with 3238 additions and 2757 deletions
|
@ -231,6 +231,10 @@ var _ = require('lodash'),
|
|||
src: ['core/test/unit/**/server*_spec.js']
|
||||
},
|
||||
|
||||
helpers: {
|
||||
src: ['core/test/unit/server_helpers/*_spec.js']
|
||||
},
|
||||
|
||||
showdown: {
|
||||
src: ['core/test/unit/**/showdown*_spec.js']
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
var path = require('path'),
|
||||
Promise = require('bluebird'),
|
||||
crypto = require('crypto'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
_ = require('lodash'),
|
||||
|
@ -13,6 +14,7 @@ var path = require('path'),
|
|||
requireTree = require('../require-tree').readAll,
|
||||
errors = require('../errors'),
|
||||
configUrl = require('./url'),
|
||||
packageInfo = require('../../../package.json'),
|
||||
appRoot = path.resolve(__dirname, '../../../'),
|
||||
corePath = path.resolve(appRoot, 'core/'),
|
||||
testingEnvs = ['testing', 'testing-mysql', 'testing-pg'],
|
||||
|
@ -70,7 +72,8 @@ ConfigManager.prototype.init = function (rawConfig) {
|
|||
ConfigManager.prototype.set = function (config) {
|
||||
var localPath = '',
|
||||
contentPath,
|
||||
subdir;
|
||||
subdir,
|
||||
assetHash;
|
||||
|
||||
// Merge passed in config object onto our existing config object.
|
||||
// We're using merge here as it doesn't assign `undefined` properties
|
||||
|
@ -98,6 +101,9 @@ ConfigManager.prototype.set = function (config) {
|
|||
// Otherwise default to default content path location
|
||||
contentPath = this._config.paths.contentPath || path.resolve(appRoot, 'content');
|
||||
|
||||
assetHash = this._config.assetHash ||
|
||||
(crypto.createHash('md5').update(packageInfo.version + Date.now()).digest('hex')).substring(0, 10);
|
||||
|
||||
if (!knexInstance && this._config.database && this._config.database.client) {
|
||||
knexInstance = knex(this._config.database);
|
||||
}
|
||||
|
@ -145,12 +151,14 @@ ConfigManager.prototype.set = function (config) {
|
|||
extensions: ['.jpg', '.jpeg', '.gif', '.png', '.svg', '.svgz'],
|
||||
contentTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml']
|
||||
},
|
||||
deprecatedItems: ['updateCheck', 'mail.fromaddress']
|
||||
deprecatedItems: ['updateCheck', 'mail.fromaddress'],
|
||||
// create a hash for cache busting assets
|
||||
assetHash: assetHash
|
||||
});
|
||||
|
||||
// Also pass config object to
|
||||
// configUrl object to maintain
|
||||
// clean depedency tree
|
||||
// clean dependency tree
|
||||
configUrl.setConfig(this._config);
|
||||
|
||||
// For now we're going to copy the current state of this._config
|
||||
|
|
39
core/server/helpers/asset.js
Normal file
39
core/server/helpers/asset.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
// # Asset helper
|
||||
// Usage: `{{asset "css/screen.css"}}`, `{{asset "css/screen.css" ghost="true"}}`
|
||||
//
|
||||
// Returns the path to the specified asset. The ghost flag outputs the asset path for the Ghost admin
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
config = require('../config'),
|
||||
utils = require('./utils'),
|
||||
asset;
|
||||
|
||||
asset = function (context, options) {
|
||||
var output = '',
|
||||
isAdmin = options && options.hash && options.hash.ghost;
|
||||
|
||||
output += config.paths.subdir + '/';
|
||||
|
||||
if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) {
|
||||
if (isAdmin) {
|
||||
output += 'ghost/';
|
||||
} else {
|
||||
output += 'assets/';
|
||||
}
|
||||
}
|
||||
|
||||
// Get rid of any leading slash on the context
|
||||
context = context.replace(/^\//, '');
|
||||
output += context;
|
||||
|
||||
if (!context.match(/^favicon\.ico$/)) {
|
||||
output = utils.assetTemplate({
|
||||
source: output,
|
||||
version: config.assetHash
|
||||
});
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
};
|
||||
|
||||
module.exports = asset;
|
45
core/server/helpers/author.js
Normal file
45
core/server/helpers/author.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
// # Author Helper
|
||||
// Usage: `{{author}}` OR `{{#author}}{{/author}}`
|
||||
//
|
||||
// Can be used as either an output or a block helper
|
||||
//
|
||||
// Output helper: `{{author}}`
|
||||
// Returns the full name of the author of a given post, or a blank string
|
||||
// if the author could not be determined.
|
||||
//
|
||||
// Block helper: `{{#author}}{{/author}}`
|
||||
// This is the default handlebars behaviour of dropping into the author object scope
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
config = require('../config'),
|
||||
utils = require('./utils'),
|
||||
author;
|
||||
|
||||
author = function (context, options) {
|
||||
if (_.isUndefined(options)) {
|
||||
options = context;
|
||||
}
|
||||
|
||||
if (options.fn) {
|
||||
return hbs.handlebars.helpers['with'].call(this, this.author, options);
|
||||
}
|
||||
|
||||
var autolink = _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||
output = '';
|
||||
|
||||
if (this.author && this.author.name) {
|
||||
if (autolink) {
|
||||
output = utils.linkTemplate({
|
||||
url: config.urlFor('author', {author: this.author}),
|
||||
text: _.escape(this.author.name)
|
||||
});
|
||||
} else {
|
||||
output = _.escape(this.author.name);
|
||||
}
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
};
|
||||
|
||||
module.exports = author;
|
78
core/server/helpers/body_class.js
Normal file
78
core/server/helpers/body_class.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
// # Body Class Helper
|
||||
// Usage: `{{body_class}}`
|
||||
//
|
||||
// Output classes for the body element
|
||||
//
|
||||
// We use the name body_class to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
api = require('../api'),
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
template = require('./template'),
|
||||
body_class;
|
||||
|
||||
body_class = function () {
|
||||
var classes = [],
|
||||
post = this.post,
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false;
|
||||
|
||||
if (this.tag !== undefined) {
|
||||
classes.push('tag-template');
|
||||
classes.push('tag-' + this.tag.slug);
|
||||
}
|
||||
|
||||
if (this.author !== undefined) {
|
||||
classes.push('author-template');
|
||||
classes.push('author-' + this.author.slug);
|
||||
}
|
||||
|
||||
if (_.isString(this.relativeUrl) && this.relativeUrl.match(/\/(page\/\d)/)) {
|
||||
classes.push('paged');
|
||||
// To be removed from pages by #2597 when we're ready to deprecate this
|
||||
classes.push('archive-template');
|
||||
} else if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
classes.push('home-template');
|
||||
} else if (post) {
|
||||
// To be removed from pages by #2597 when we're ready to deprecate this
|
||||
// i.e. this should be if (post && !page) { ... }
|
||||
classes.push('post-template');
|
||||
}
|
||||
|
||||
if (page) {
|
||||
classes.push('page-template');
|
||||
// To be removed by #2597 when we're ready to deprecate this
|
||||
classes.push('page');
|
||||
}
|
||||
|
||||
if (tags) {
|
||||
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
||||
}
|
||||
|
||||
return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
|
||||
var activeTheme = response.settings[0],
|
||||
paths = config.paths.availableThemes[activeTheme.value],
|
||||
view;
|
||||
|
||||
if (post && page) {
|
||||
view = template.getThemeViewForPost(paths, post).split('-');
|
||||
|
||||
if (view[0] === 'page' && view.length > 1) {
|
||||
classes.push(view.join('-'));
|
||||
// To be removed by #2597 when we're ready to deprecate this
|
||||
view.splice(1, 0, 'template');
|
||||
classes.push(view.join('-'));
|
||||
}
|
||||
}
|
||||
|
||||
return filters.doFilter('body_class', classes).then(function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = body_class;
|
38
core/server/helpers/content.js
Normal file
38
core/server/helpers/content.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
// # Content Helper
|
||||
// Usage: `{{content}}`, `{{content words="20"}}`, `{{content characters="256"}}`
|
||||
//
|
||||
// Turns content html into a safestring so that the user doesn't have to
|
||||
// escape it or tell handlebars to leave it alone with a triple-brace.
|
||||
//
|
||||
// Enables tag-safe truncation of content by characters or words.
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
downsize = require('downsize'),
|
||||
downzero = require('../utils/downzero'),
|
||||
content;
|
||||
|
||||
content = function (options) {
|
||||
var truncateOptions = (options || {}).hash || {};
|
||||
truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
|
||||
_.keys(truncateOptions).map(function (key) {
|
||||
truncateOptions[key] = parseInt(truncateOptions[key], 10);
|
||||
});
|
||||
|
||||
if (truncateOptions.hasOwnProperty('words') || truncateOptions.hasOwnProperty('characters')) {
|
||||
// Legacy function: {{content words="0"}} should return leading tags.
|
||||
if (truncateOptions.hasOwnProperty('words') && truncateOptions.words === 0) {
|
||||
return new hbs.handlebars.SafeString(
|
||||
downzero(this.html)
|
||||
);
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(
|
||||
downsize(this.html, truncateOptions)
|
||||
);
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(this.html);
|
||||
};
|
||||
|
||||
module.exports = content;
|
36
core/server/helpers/date.js
Normal file
36
core/server/helpers/date.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
// # Date Helper
|
||||
// Usage: `{{date format="DD MM, YYYY"}}`, `{{date updated_at format="DD MM, YYYY"}}`
|
||||
//
|
||||
// Formats a date using moment.js. Formats published_at by default but will also take a date as a parameter
|
||||
|
||||
var moment = require('moment'),
|
||||
date;
|
||||
|
||||
date = function (context, options) {
|
||||
if (!options && context.hasOwnProperty('hash')) {
|
||||
options = context;
|
||||
context = undefined;
|
||||
|
||||
// set to published_at by default, if it's available
|
||||
// otherwise, this will print the current date
|
||||
if (this.published_at) {
|
||||
context = this.published_at;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that context is undefined, not null, as that can cause errors
|
||||
context = context === null ? undefined : context;
|
||||
|
||||
var f = options.hash.format || 'MMM Do, YYYY',
|
||||
timeago = options.hash.timeago,
|
||||
date;
|
||||
|
||||
if (timeago) {
|
||||
date = moment(context).fromNow();
|
||||
} else {
|
||||
date = moment(context).format(f);
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
module.exports = date;
|
15
core/server/helpers/encode.js
Normal file
15
core/server/helpers/encode.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
// # Encode Helper
|
||||
//
|
||||
// Usage: `{{encode uri}}`
|
||||
//
|
||||
// Returns URI encoded string
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
encode;
|
||||
|
||||
encode = function (context, str) {
|
||||
var uri = context || str;
|
||||
return new hbs.handlebars.SafeString(encodeURIComponent(uri));
|
||||
};
|
||||
|
||||
module.exports = encode;
|
36
core/server/helpers/excerpt.js
Normal file
36
core/server/helpers/excerpt.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
// # Excerpt Helper
|
||||
// Usage: `{{excerpt}}`, `{{excerpt words="50"}}`, `{{excerpt characters="256"}}`
|
||||
//
|
||||
// Attempts to remove all HTML from the string, and then shortens the result according to the provided option.
|
||||
//
|
||||
// Defaults to words="50"
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
downsize = require('downsize'),
|
||||
excerpt;
|
||||
|
||||
excerpt = function (options) {
|
||||
var truncateOptions = (options || {}).hash || {},
|
||||
excerpt;
|
||||
|
||||
truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
|
||||
_.keys(truncateOptions).map(function (key) {
|
||||
truncateOptions[key] = parseInt(truncateOptions[key], 10);
|
||||
});
|
||||
|
||||
/*jslint regexp:true */
|
||||
excerpt = String(this.html).replace(/<\/?[^>]+>/gi, '');
|
||||
excerpt = excerpt.replace(/(\r\n|\n|\r)+/gm, ' ');
|
||||
/*jslint regexp:false */
|
||||
|
||||
if (!truncateOptions.words && !truncateOptions.characters) {
|
||||
truncateOptions.words = 50;
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(
|
||||
downsize(excerpt, truncateOptions)
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = excerpt;
|
80
core/server/helpers/foreach.js
Normal file
80
core/server/helpers/foreach.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
// # Foreach Helper
|
||||
// Usage: `{{#foreach data}}{{/foreach}}`
|
||||
//
|
||||
// Block helper designed for looping through posts
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
foreach;
|
||||
|
||||
foreach = function (context, options) {
|
||||
var fn = options.fn,
|
||||
inverse = options.inverse,
|
||||
i = 0,
|
||||
j = 0,
|
||||
columns = options.hash.columns,
|
||||
key,
|
||||
ret = '',
|
||||
data;
|
||||
|
||||
if (options.data) {
|
||||
data = hbs.handlebars.createFrame(options.data);
|
||||
}
|
||||
|
||||
function setKeys(_data, _i, _j, _columns) {
|
||||
if (_i === 0) {
|
||||
_data.first = true;
|
||||
}
|
||||
if (_i === _j - 1) {
|
||||
_data.last = true;
|
||||
}
|
||||
// first post is index zero but still needs to be odd
|
||||
if (_i % 2 === 1) {
|
||||
_data.even = true;
|
||||
} else {
|
||||
_data.odd = true;
|
||||
}
|
||||
if (_i % _columns === 0) {
|
||||
_data.rowStart = true;
|
||||
} else if (_i % _columns === (_columns - 1)) {
|
||||
_data.rowEnd = true;
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
if (context && typeof context === 'object') {
|
||||
if (context instanceof Array) {
|
||||
for (j = context.length; i < j; i += 1) {
|
||||
if (data) {
|
||||
data.index = i;
|
||||
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
|
||||
data = setKeys(data, i, j, columns);
|
||||
}
|
||||
ret = ret + fn(context[i], {data: data});
|
||||
}
|
||||
} else {
|
||||
for (key in context) {
|
||||
if (context.hasOwnProperty(key)) {
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
for (key in context) {
|
||||
if (context.hasOwnProperty(key)) {
|
||||
if (data) {
|
||||
data.key = key;
|
||||
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
|
||||
data = setKeys(data, i, j, columns);
|
||||
}
|
||||
ret = ret + fn(context[key], {data: data});
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
ret = inverse(this);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
module.exports = foreach;
|
32
core/server/helpers/ghost_foot.js
Normal file
32
core/server/helpers/ghost_foot.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// # Ghost Foot Helper
|
||||
// Usage: `{{ghost_foot}}`
|
||||
//
|
||||
// Outputs scripts and other assets at the bottom of a Ghost theme
|
||||
//
|
||||
// We use the name ghost_foot to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
utils = require('./utils'),
|
||||
ghost_foot;
|
||||
|
||||
ghost_foot = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var jquery = utils.isProduction ? 'jquery.min.js' : 'jquery.js',
|
||||
foot = [];
|
||||
|
||||
foot.push(utils.scriptTemplate({
|
||||
source: config.paths.subdir + '/public/' + jquery,
|
||||
version: config.assetHash
|
||||
}));
|
||||
|
||||
return filters.doFilter('ghost_foot', foot).then(function (foot) {
|
||||
var footString = _.reduce(foot, function (memo, item) { return memo + '\n' + item; }, '\n');
|
||||
return new hbs.handlebars.SafeString(footString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = ghost_foot;
|
113
core/server/helpers/ghost_head.js
Normal file
113
core/server/helpers/ghost_head.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
// # Ghost Head Helper
|
||||
// Usage: `{{ghost_head}}`
|
||||
//
|
||||
// Outputs scripts and other assets at the top of a Ghost theme
|
||||
//
|
||||
// We use the name ghost_head to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
moment = require('moment'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
|
||||
urlHelper = require('./url'),
|
||||
meta_description = require('./meta_description'),
|
||||
meta_title = require('./meta_title'),
|
||||
excerpt = require('./excerpt'),
|
||||
tagsHelper = require('./tags'),
|
||||
ghost_head;
|
||||
|
||||
ghost_head = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var self = this,
|
||||
blog = config.theme,
|
||||
useStructuredData = !config.isPrivacyDisabled('useStructuredData'),
|
||||
head = [],
|
||||
majorMinor = /^(\d+\.)?(\d+)/,
|
||||
trimmedVersion = this.version,
|
||||
trimmedUrlpattern = /.+(?=\/page\/\d*\/)/,
|
||||
trimmedUrl, next, prev, tags,
|
||||
ops = [],
|
||||
structuredData;
|
||||
|
||||
trimmedVersion = trimmedVersion ? trimmedVersion.match(majorMinor)[0] : '?';
|
||||
|
||||
// Push Async calls to an array of promises
|
||||
ops.push(urlHelper.call(self, {hash: {absolute: true}}));
|
||||
ops.push(meta_description.call(self));
|
||||
ops.push(meta_title.call(self));
|
||||
|
||||
// Resolves promises then push pushes meta data into ghost_head
|
||||
return Promise.settle(ops).then(function (results) {
|
||||
var url = results[0].value(),
|
||||
metaDescription = results[1].value(),
|
||||
metaTitle = results[2].value(),
|
||||
publishedDate, modifiedDate;
|
||||
|
||||
if (!metaDescription) {
|
||||
metaDescription = excerpt.call(self.post, {hash: {words: '40'}});
|
||||
}
|
||||
|
||||
head.push('<link rel="canonical" href="' + url + '" />');
|
||||
|
||||
if (self.pagination) {
|
||||
trimmedUrl = self.relativeUrl.match(trimmedUrlpattern);
|
||||
if (self.pagination.prev) {
|
||||
prev = (self.pagination.prev > 1 ? prev = '/page/' + self.pagination.prev + '/' : prev = '/');
|
||||
prev = (trimmedUrl) ? '/' + trimmedUrl + prev : prev;
|
||||
head.push('<link rel="prev" href="' + config.urlFor({relativeUrl: prev}, true) + '" />');
|
||||
}
|
||||
if (self.pagination.next) {
|
||||
next = '/page/' + self.pagination.next + '/';
|
||||
next = (trimmedUrl) ? '/' + trimmedUrl + next : next;
|
||||
head.push('<link rel="next" href="' + config.urlFor({relativeUrl: next}, true) + '" />');
|
||||
}
|
||||
}
|
||||
|
||||
// Test to see if we are on a post page and that Structured data has not been disabled in config.js
|
||||
if (self.post && useStructuredData) {
|
||||
publishedDate = moment(self.post.published_at).toISOString();
|
||||
modifiedDate = moment(self.post.updated_at).toISOString();
|
||||
|
||||
structuredData = {
|
||||
'og:site_name': _.escape(blog.title),
|
||||
'og:type': 'article',
|
||||
'og:title': metaTitle,
|
||||
'og:description': metaDescription + '...',
|
||||
'og:url': url,
|
||||
'article:published_time': publishedDate,
|
||||
'article:modified_time': modifiedDate
|
||||
};
|
||||
|
||||
if (self.post.image) {
|
||||
structuredData['og:image'] = _.escape(blog.url) + self.post.image;
|
||||
}
|
||||
|
||||
_.each(structuredData, function (content, property) {
|
||||
head.push('<meta property="' + property + '" content="' + content + '" />');
|
||||
});
|
||||
|
||||
// Calls tag helper and assigns an array of tag names for a post
|
||||
tags = tagsHelper.call(self.post, {hash: {autolink: 'false'}}).string.split(',');
|
||||
|
||||
_.each(tags, function (tag) {
|
||||
if (tag !== '') {
|
||||
head.push('<meta property="article:tag" content="' + tag.trim() + '" />');
|
||||
}
|
||||
});
|
||||
}
|
||||
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') + '" />');
|
||||
return filters.doFilter('ghost_head', head);
|
||||
}).then(function (head) {
|
||||
var headString = _.reduce(head, function (memo, item) { return memo + '\n ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(headString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = ghost_head;
|
25
core/server/helpers/ghost_script_tags.js
Normal file
25
core/server/helpers/ghost_script_tags.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
// # Ghost Script Tags Helpers
|
||||
// Used in the ghost admin only
|
||||
//
|
||||
// We use the name ghost_script_tags to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var _ = require('lodash'),
|
||||
utils = require('./utils'),
|
||||
config = require('../config'),
|
||||
ghost_script_tags;
|
||||
|
||||
ghost_script_tags = function () {
|
||||
var scriptList = utils.isProduction ? utils.scriptFiles.production : utils.scriptFiles.development;
|
||||
|
||||
scriptList = _.map(scriptList, function (fileName) {
|
||||
return utils.scriptTemplate({
|
||||
source: config.paths.subdir + '/ghost/scripts/' + fileName,
|
||||
version: config.assetHash
|
||||
});
|
||||
});
|
||||
|
||||
return scriptList.join('');
|
||||
};
|
||||
|
||||
module.exports = ghost_script_tags;
|
56
core/server/helpers/has.js
Normal file
56
core/server/helpers/has.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
// # Has Helper
|
||||
// Usage: `{{#has tag="video, music"}}`, `{{#has author="sam, pat"}}`
|
||||
//
|
||||
// Checks if a post has a particular property
|
||||
|
||||
var _ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
has;
|
||||
|
||||
has = function (options) {
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
|
||||
var tags = _.pluck(this.tags, 'name'),
|
||||
author = this.author ? this.author.name : null,
|
||||
tagList = options.hash.tag || false,
|
||||
authorList = options.hash.author || false,
|
||||
tagsOk,
|
||||
authorOk;
|
||||
|
||||
function evaluateTagList(expr, tags) {
|
||||
return expr.split(',').map(function (v) {
|
||||
return v.trim();
|
||||
}).reduce(function (p, c) {
|
||||
return p || (_.findIndex(tags, function (item) {
|
||||
// Escape regex special characters
|
||||
item = item.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&');
|
||||
item = new RegExp(item, 'i');
|
||||
return item.test(c);
|
||||
}) !== -1);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function evaluateAuthorList(expr, author) {
|
||||
var authorList = expr.split(',').map(function (v) {
|
||||
return v.trim().toLocaleLowerCase();
|
||||
});
|
||||
|
||||
return _.contains(authorList, author.toLocaleLowerCase());
|
||||
}
|
||||
|
||||
if (!tagList && !authorList) {
|
||||
errors.logWarn('Invalid or no attribute given to has helper');
|
||||
return;
|
||||
}
|
||||
|
||||
tagsOk = tagList && evaluateTagList(tagList, tags) || false;
|
||||
authorOk = authorList && evaluateAuthorList(authorList, author) || false;
|
||||
|
||||
if (tagsOk || authorOk) {
|
||||
return options.fn(this);
|
||||
}
|
||||
return options.inverse(this);
|
||||
};
|
||||
|
||||
module.exports = has;
|
|
@ -1,348 +1,47 @@
|
|||
var downsize = require('downsize'),
|
||||
hbs = require('express-hbs'),
|
||||
moment = require('moment'),
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
Promise = require('bluebird'),
|
||||
api = require('../api'),
|
||||
|
||||
config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
filters = require('../filters'),
|
||||
template = require('./template'),
|
||||
schema = require('../data/schema').checks,
|
||||
downzero = require('../utils/downzero'),
|
||||
|
||||
assetTemplate = _.template('<%= source %>?v=<%= version %>'),
|
||||
linkTemplate = _.template('<a href="<%= url %>"><%= text %></a>'),
|
||||
scriptTemplate = _.template('<script src="<%= source %>?v=<%= version %>"></script>'),
|
||||
isProduction = process.env.NODE_ENV === 'production',
|
||||
utils = require('./utils'),
|
||||
|
||||
coreHelpers = {},
|
||||
registerHelpers,
|
||||
registerHelpers;
|
||||
|
||||
scriptFiles = {
|
||||
production: [
|
||||
'vendor.min.js',
|
||||
'ghost.min.js'
|
||||
],
|
||||
development: [
|
||||
'vendor-dev.js',
|
||||
'templates-dev.js',
|
||||
'ghost-dev.js'
|
||||
]
|
||||
};
|
||||
// Pre-load settings data:
|
||||
// - activeTheme
|
||||
// - permalinks
|
||||
|
||||
if (!isProduction) {
|
||||
if (!utils.isProduction) {
|
||||
hbs.handlebars.logger.level = 0;
|
||||
}
|
||||
|
||||
// [ description]
|
||||
//
|
||||
// @param {Object} context date object
|
||||
// @param {*} options
|
||||
// @return {Object} A Moment time / date object
|
||||
coreHelpers.asset = require('./asset');
|
||||
coreHelpers.author = require('./author');
|
||||
coreHelpers.body_class = require('./body_class');
|
||||
coreHelpers.content = require('./content');
|
||||
coreHelpers.date = require('./date');
|
||||
coreHelpers.encode = require('./encode');
|
||||
coreHelpers.excerpt = require('./excerpt');
|
||||
coreHelpers.foreach = require('./foreach');
|
||||
coreHelpers.ghost_foot = require('./ghost_foot');
|
||||
coreHelpers.ghost_head = require('./ghost_head');
|
||||
coreHelpers.is = require('./is');
|
||||
coreHelpers.has = require('./has');
|
||||
coreHelpers.meta_description = require('./meta_description');
|
||||
coreHelpers.meta_title = require('./meta_title');
|
||||
coreHelpers.page_url = require('./page_url');
|
||||
coreHelpers.pageUrl = require('./page_url').deprecated;
|
||||
coreHelpers.pagination = require('./pagination');
|
||||
coreHelpers.plural = require('./plural');
|
||||
coreHelpers.post_class = require('./post_class');
|
||||
coreHelpers.tags = require('./tags');
|
||||
coreHelpers.title = require('./title');
|
||||
coreHelpers.url = require('./url');
|
||||
|
||||
coreHelpers.date = function (context, options) {
|
||||
if (!options && context.hasOwnProperty('hash')) {
|
||||
options = context;
|
||||
context = undefined;
|
||||
|
||||
// set to published_at by default, if it's available
|
||||
// otherwise, this will print the current date
|
||||
if (this.published_at) {
|
||||
context = this.published_at;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that context is undefined, not null, as that can cause errors
|
||||
context = context === null ? undefined : context;
|
||||
|
||||
var f = options.hash.format || 'MMM Do, YYYY',
|
||||
timeago = options.hash.timeago,
|
||||
date;
|
||||
|
||||
if (timeago) {
|
||||
date = moment(context).fromNow();
|
||||
} else {
|
||||
date = moment(context).format(f);
|
||||
}
|
||||
return date;
|
||||
};
|
||||
|
||||
//
|
||||
// ### URI Encoding helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{encode uri}}`
|
||||
//
|
||||
// Returns URI encoded string
|
||||
//
|
||||
coreHelpers.encode = function (context, str) {
|
||||
var uri = context || str;
|
||||
return new hbs.handlebars.SafeString(encodeURIComponent(uri));
|
||||
};
|
||||
|
||||
// ### Page URL Helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{page_url 2}}`
|
||||
//
|
||||
// Returns the URL for the page specified in the current object
|
||||
// context.
|
||||
//
|
||||
coreHelpers.page_url = function (context, block) {
|
||||
/*jshint unused:false*/
|
||||
var url = config.paths.subdir;
|
||||
|
||||
if (this.tagSlug !== undefined) {
|
||||
url += '/tag/' + this.tagSlug;
|
||||
}
|
||||
|
||||
if (this.authorSlug !== undefined) {
|
||||
url += '/author/' + this.authorSlug;
|
||||
}
|
||||
|
||||
if (context > 1) {
|
||||
url += '/page/' + context;
|
||||
}
|
||||
|
||||
url += '/';
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
// ### Page URL Helper: DEPRECATED
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{pageUrl 2}}`
|
||||
//
|
||||
// Returns the URL for the page specified in the current object
|
||||
// context. This helper is deprecated and will be removed in future versions.
|
||||
//
|
||||
coreHelpers.pageUrl = function (context, block) {
|
||||
errors.logWarn('Warning: pageUrl is deprecated, please use page_url instead\n' +
|
||||
'The helper pageUrl has been replaced with page_url in Ghost 0.4.2, and will be removed entirely in Ghost 0.6\n' +
|
||||
'In your theme\'s pagination.hbs file, pageUrl should be renamed to page_url');
|
||||
|
||||
/*jshint unused:false*/
|
||||
var self = this;
|
||||
|
||||
return coreHelpers.page_url.call(self, context, block);
|
||||
};
|
||||
|
||||
// ### URL helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{url}}`
|
||||
// `{{url absolute="true"}}`
|
||||
//
|
||||
// Returns the URL for the current object context
|
||||
// i.e. If inside a post context will return post permalink
|
||||
// absolute flag outputs absolute URL, else URL is relative
|
||||
coreHelpers.url = function (options) {
|
||||
var absolute = options && options.hash.absolute;
|
||||
|
||||
if (schema.isPost(this)) {
|
||||
return config.urlForPost(api.settings, this, absolute);
|
||||
}
|
||||
|
||||
if (schema.isTag(this)) {
|
||||
return Promise.resolve(config.urlFor('tag', {tag: this}, absolute));
|
||||
}
|
||||
|
||||
if (schema.isUser(this)) {
|
||||
return Promise.resolve(config.urlFor('author', {author: this}, absolute));
|
||||
}
|
||||
|
||||
return Promise.resolve(config.urlFor(this, absolute));
|
||||
};
|
||||
|
||||
// ### Asset helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{asset "css/screen.css"}}`
|
||||
// `{{asset "css/screen.css" ghost="true"}}`
|
||||
// Returns the path to the specified asset. The ghost
|
||||
// flag outputs the asset path for the Ghost admin
|
||||
coreHelpers.asset = function (context, options) {
|
||||
var output = '',
|
||||
isAdmin = options && options.hash && options.hash.ghost;
|
||||
|
||||
output += config.paths.subdir + '/';
|
||||
|
||||
if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) {
|
||||
if (isAdmin) {
|
||||
output += 'ghost/';
|
||||
} else {
|
||||
output += 'assets/';
|
||||
}
|
||||
}
|
||||
|
||||
// Get rid of any leading slash on the context
|
||||
context = context.replace(/^\//, '');
|
||||
output += context;
|
||||
|
||||
if (!context.match(/^favicon\.ico$/)) {
|
||||
output = assetTemplate({
|
||||
source: output,
|
||||
version: coreHelpers.assetHash
|
||||
});
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
};
|
||||
|
||||
// ### Author Helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{author}}`
|
||||
//
|
||||
// Returns the full name of the author of a given post, or a blank string
|
||||
// if the author could not be determined.
|
||||
//
|
||||
coreHelpers.author = function (context, options) {
|
||||
if (_.isUndefined(options)) {
|
||||
options = context;
|
||||
}
|
||||
|
||||
if (options.fn) {
|
||||
return hbs.handlebars.helpers['with'].call(this, this.author, options);
|
||||
}
|
||||
|
||||
var autolink = _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||
output = '';
|
||||
|
||||
if (this.author && this.author.name) {
|
||||
if (autolink) {
|
||||
output = linkTemplate({
|
||||
url: config.urlFor('author', {author: this.author}),
|
||||
text: _.escape(this.author.name)
|
||||
});
|
||||
} else {
|
||||
output = _.escape(this.author.name);
|
||||
}
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
};
|
||||
|
||||
// ### Tags Helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{tags}}`
|
||||
// `{{tags separator=' - '}}`
|
||||
//
|
||||
// Returns a string of the tags on the post.
|
||||
// By default, tags are separated by commas.
|
||||
//
|
||||
// Note that the standard {{#each tags}} implementation is unaffected by this helper
|
||||
// and can be used for more complex templates.
|
||||
coreHelpers.tags = function (options) {
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
|
||||
var autolink = options.hash && _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||
separator = options.hash && _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
||||
prefix = options.hash && _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
||||
suffix = options.hash && _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
||||
output = '';
|
||||
|
||||
function createTagList(tags) {
|
||||
var tagNames = _.pluck(tags, 'name');
|
||||
|
||||
if (autolink) {
|
||||
return _.map(tags, function (tag) {
|
||||
return linkTemplate({
|
||||
url: config.urlFor('tag', {tag: tag}),
|
||||
text: _.escape(tag.name)
|
||||
});
|
||||
}).join(separator);
|
||||
}
|
||||
return _.escape(tagNames.join(separator));
|
||||
}
|
||||
|
||||
if (this.tags && this.tags.length) {
|
||||
output = prefix + createTagList(this.tags) + suffix;
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
};
|
||||
|
||||
// ### Content Helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{content}}`
|
||||
// `{{content words="20"}}`
|
||||
// `{{content characters="256"}}`
|
||||
//
|
||||
// Turns content html into a safestring so that the user doesn't have to
|
||||
// escape it or tell handlebars to leave it alone with a triple-brace.
|
||||
//
|
||||
// Enables tag-safe truncation of content by characters or words.
|
||||
//
|
||||
// **returns** SafeString content html, complete or truncated.
|
||||
//
|
||||
coreHelpers.content = function (options) {
|
||||
var truncateOptions = (options || {}).hash || {};
|
||||
truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
|
||||
_.keys(truncateOptions).map(function (key) {
|
||||
truncateOptions[key] = parseInt(truncateOptions[key], 10);
|
||||
});
|
||||
|
||||
if (truncateOptions.hasOwnProperty('words') || truncateOptions.hasOwnProperty('characters')) {
|
||||
// Legacy function: {{content words="0"}} should return leading tags.
|
||||
if (truncateOptions.hasOwnProperty('words') && truncateOptions.words === 0) {
|
||||
return new hbs.handlebars.SafeString(
|
||||
downzero(this.html)
|
||||
);
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(
|
||||
downsize(this.html, truncateOptions)
|
||||
);
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(this.html);
|
||||
};
|
||||
|
||||
coreHelpers.title = function () {
|
||||
return new hbs.handlebars.SafeString(hbs.handlebars.Utils.escapeExpression(this.title || ''));
|
||||
};
|
||||
|
||||
// ### Excerpt Helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{excerpt}}`
|
||||
// `{{excerpt words="50"}}`
|
||||
// `{{excerpt characters="256"}}`
|
||||
//
|
||||
// Attempts to remove all HTML from the string, and then shortens the result according to the provided option.
|
||||
//
|
||||
// Defaults to words="50"
|
||||
//
|
||||
// **returns** SafeString truncated, HTML-free content.
|
||||
//
|
||||
coreHelpers.excerpt = function (options) {
|
||||
var truncateOptions = (options || {}).hash || {},
|
||||
excerpt;
|
||||
|
||||
truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
|
||||
_.keys(truncateOptions).map(function (key) {
|
||||
truncateOptions[key] = parseInt(truncateOptions[key], 10);
|
||||
});
|
||||
|
||||
/*jslint regexp:true */
|
||||
excerpt = String(this.html).replace(/<\/?[^>]+>/gi, '');
|
||||
excerpt = excerpt.replace(/(\r\n|\n|\r)+/gm, ' ');
|
||||
/*jslint regexp:false */
|
||||
|
||||
if (!truncateOptions.words && !truncateOptions.characters) {
|
||||
truncateOptions.words = 50;
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(
|
||||
downsize(excerpt, truncateOptions)
|
||||
);
|
||||
};
|
||||
coreHelpers.ghost_script_tags = require('./ghost_script_tags');
|
||||
|
||||
// ### Filestorage helper
|
||||
//
|
||||
|
@ -383,481 +82,6 @@ coreHelpers.blog_url = function (context, options) {
|
|||
return config.theme.url.toString();
|
||||
};
|
||||
|
||||
coreHelpers.ghost_script_tags = function () {
|
||||
var scriptList = isProduction ? scriptFiles.production : scriptFiles.development;
|
||||
|
||||
scriptList = _.map(scriptList, function (fileName) {
|
||||
return scriptTemplate({
|
||||
source: config.paths.subdir + '/ghost/scripts/' + fileName,
|
||||
version: coreHelpers.assetHash
|
||||
});
|
||||
});
|
||||
|
||||
return scriptList.join('');
|
||||
};
|
||||
|
||||
/*
|
||||
* Asynchronous Theme Helpers (Registered with registerAsyncThemeHelper)
|
||||
*/
|
||||
|
||||
coreHelpers.body_class = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var classes = [],
|
||||
post = this.post,
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false;
|
||||
|
||||
if (this.tag !== undefined) {
|
||||
classes.push('tag-template');
|
||||
classes.push('tag-' + this.tag.slug);
|
||||
}
|
||||
|
||||
if (this.author !== undefined) {
|
||||
classes.push('author-template');
|
||||
classes.push('author-' + this.author.slug);
|
||||
}
|
||||
|
||||
if (_.isString(this.relativeUrl) && this.relativeUrl.match(/\/(page\/\d)/)) {
|
||||
classes.push('paged');
|
||||
// To be removed from pages by #2597 when we're ready to deprecate this
|
||||
classes.push('archive-template');
|
||||
} else if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
classes.push('home-template');
|
||||
} else if (post) {
|
||||
// To be removed from pages by #2597 when we're ready to deprecate this
|
||||
// i.e. this should be if (post && !page) { ... }
|
||||
classes.push('post-template');
|
||||
}
|
||||
|
||||
if (page) {
|
||||
classes.push('page-template');
|
||||
// To be removed by #2597 when we're ready to deprecate this
|
||||
classes.push('page');
|
||||
}
|
||||
|
||||
if (tags) {
|
||||
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
||||
}
|
||||
|
||||
return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
|
||||
var activeTheme = response.settings[0],
|
||||
paths = config.paths.availableThemes[activeTheme.value],
|
||||
view;
|
||||
|
||||
if (post && page) {
|
||||
view = template.getThemeViewForPost(paths, post).split('-');
|
||||
|
||||
if (view[0] === 'page' && view.length > 1) {
|
||||
classes.push(view.join('-'));
|
||||
// To be removed by #2597 when we're ready to deprecate this
|
||||
view.splice(1, 0, 'template');
|
||||
classes.push(view.join('-'));
|
||||
}
|
||||
}
|
||||
|
||||
return filters.doFilter('body_class', classes).then(function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
coreHelpers.post_class = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var classes = ['post'],
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
||||
featured = this.post && this.post.featured ? this.post.featured : this.featured || false,
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false;
|
||||
|
||||
if (tags) {
|
||||
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
||||
}
|
||||
|
||||
if (featured) {
|
||||
classes.push('featured');
|
||||
}
|
||||
|
||||
if (page) {
|
||||
classes.push('page');
|
||||
}
|
||||
|
||||
return filters.doFilter('post_class', classes).then(function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
coreHelpers.ghost_head = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var self = this,
|
||||
blog = config.theme,
|
||||
useStructuredData = !config.isPrivacyDisabled('useStructuredData'),
|
||||
head = [],
|
||||
majorMinor = /^(\d+\.)?(\d+)/,
|
||||
trimmedVersion = this.version,
|
||||
trimmedUrlpattern = /.+(?=\/page\/\d*\/)/,
|
||||
trimmedUrl, next, prev, tags,
|
||||
ops = [],
|
||||
structuredData;
|
||||
|
||||
trimmedVersion = trimmedVersion ? trimmedVersion.match(majorMinor)[0] : '?';
|
||||
|
||||
// Push Async calls to an array of promises
|
||||
ops.push(coreHelpers.url.call(self, {hash: {absolute: true}}));
|
||||
ops.push(coreHelpers.meta_description.call(self));
|
||||
ops.push(coreHelpers.meta_title.call(self));
|
||||
|
||||
// Resolves promises then push pushes meta data into ghost_head
|
||||
return Promise.settle(ops).then(function (results) {
|
||||
var url = results[0].value(),
|
||||
metaDescription = results[1].value(),
|
||||
metaTitle = results[2].value(),
|
||||
publishedDate, modifiedDate;
|
||||
|
||||
if (!metaDescription) {
|
||||
metaDescription = coreHelpers.excerpt.call(self.post, {hash: {words: '40'}});
|
||||
}
|
||||
|
||||
head.push('<link rel="canonical" href="' + url + '" />');
|
||||
|
||||
if (self.pagination) {
|
||||
trimmedUrl = self.relativeUrl.match(trimmedUrlpattern);
|
||||
if (self.pagination.prev) {
|
||||
prev = (self.pagination.prev > 1 ? prev = '/page/' + self.pagination.prev + '/' : prev = '/');
|
||||
prev = (trimmedUrl) ? '/' + trimmedUrl + prev : prev;
|
||||
head.push('<link rel="prev" href="' + config.urlFor({relativeUrl: prev}, true) + '" />');
|
||||
}
|
||||
if (self.pagination.next) {
|
||||
next = '/page/' + self.pagination.next + '/';
|
||||
next = (trimmedUrl) ? '/' + trimmedUrl + next : next;
|
||||
head.push('<link rel="next" href="' + config.urlFor({relativeUrl: next}, true) + '" />');
|
||||
}
|
||||
}
|
||||
|
||||
// Test to see if we are on a post page and that Structured data has not been disabled in config.js
|
||||
if (self.post && useStructuredData) {
|
||||
publishedDate = moment(self.post.published_at).toISOString();
|
||||
modifiedDate = moment(self.post.updated_at).toISOString();
|
||||
|
||||
structuredData = {
|
||||
'og:site_name': _.escape(blog.title),
|
||||
'og:type': 'article',
|
||||
'og:title': metaTitle,
|
||||
'og:description': metaDescription + '...',
|
||||
'og:url': url,
|
||||
'article:published_time': publishedDate,
|
||||
'article:modified_time': modifiedDate
|
||||
};
|
||||
|
||||
if (self.post.image) {
|
||||
structuredData['og:image'] = _.escape(blog.url) + self.post.image;
|
||||
}
|
||||
|
||||
_.each(structuredData, function (content, property) {
|
||||
head.push('<meta property="' + property + '" content="' + content + '" />');
|
||||
});
|
||||
|
||||
// Calls tag helper and assigns an array of tag names for a post
|
||||
tags = coreHelpers.tags.call(self.post, {hash: {autolink: 'false'}}).string.split(',');
|
||||
|
||||
_.each(tags, function (tag) {
|
||||
if (tag !== '') {
|
||||
head.push('<meta property="article:tag" content="' + tag.trim() + '" />');
|
||||
}
|
||||
});
|
||||
}
|
||||
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') + '" />');
|
||||
return filters.doFilter('ghost_head', head);
|
||||
}).then(function (head) {
|
||||
var headString = _.reduce(head, function (memo, item) { return memo + '\n ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(headString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
coreHelpers.ghost_foot = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var jquery = isProduction ? 'jquery.min.js' : 'jquery.js',
|
||||
foot = [];
|
||||
|
||||
foot.push(scriptTemplate({
|
||||
source: config.paths.subdir + '/public/' + jquery,
|
||||
version: coreHelpers.assetHash
|
||||
}));
|
||||
|
||||
return filters.doFilter('ghost_foot', foot).then(function (foot) {
|
||||
var footString = _.reduce(foot, function (memo, item) { return memo + '\n' + item; }, '\n');
|
||||
return new hbs.handlebars.SafeString(footString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
coreHelpers.meta_title = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var title = '',
|
||||
blog,
|
||||
page,
|
||||
pageString = '';
|
||||
|
||||
if (_.isString(this.relativeUrl)) {
|
||||
blog = config.theme;
|
||||
|
||||
page = this.relativeUrl.match(/\/page\/(\d+)/);
|
||||
|
||||
if (page) {
|
||||
pageString = ' - Page ' + page[1];
|
||||
}
|
||||
|
||||
if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
title = blog.title;
|
||||
} else if (this.author) {
|
||||
title = this.author.name + pageString + ' - ' + blog.title;
|
||||
} else if (this.tag) {
|
||||
title = this.tag.name + pageString + ' - ' + blog.title;
|
||||
} else if (this.post) {
|
||||
title = _.isEmpty(this.post.meta_title) ? this.post.title : this.post.meta_title;
|
||||
} else {
|
||||
title = blog.title + pageString;
|
||||
}
|
||||
}
|
||||
return filters.doFilter('meta_title', title).then(function (title) {
|
||||
title = title || '';
|
||||
return title.trim();
|
||||
});
|
||||
};
|
||||
|
||||
coreHelpers.meta_description = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var description,
|
||||
blog;
|
||||
|
||||
if (_.isString(this.relativeUrl)) {
|
||||
blog = config.theme;
|
||||
if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
description = blog.description;
|
||||
} else if (this.author) {
|
||||
description = /\/page\//.test(this.relativeUrl) ? '' : this.author.bio;
|
||||
} else if (this.tag || /\/page\//.test(this.relativeUrl)) {
|
||||
description = '';
|
||||
} else if (this.post) {
|
||||
description = _.isEmpty(this.post.meta_description) ? '' : this.post.meta_description;
|
||||
}
|
||||
}
|
||||
|
||||
return filters.doFilter('meta_description', description).then(function (description) {
|
||||
description = description || '';
|
||||
return description.trim();
|
||||
});
|
||||
};
|
||||
|
||||
coreHelpers.foreach = function (context, options) {
|
||||
var fn = options.fn,
|
||||
inverse = options.inverse,
|
||||
i = 0,
|
||||
j = 0,
|
||||
columns = options.hash.columns,
|
||||
key,
|
||||
ret = '',
|
||||
data;
|
||||
|
||||
if (options.data) {
|
||||
data = hbs.handlebars.createFrame(options.data);
|
||||
}
|
||||
|
||||
function setKeys(_data, _i, _j, _columns) {
|
||||
if (_i === 0) {
|
||||
_data.first = true;
|
||||
}
|
||||
if (_i === _j - 1) {
|
||||
_data.last = true;
|
||||
}
|
||||
// first post is index zero but still needs to be odd
|
||||
if (_i % 2 === 1) {
|
||||
_data.even = true;
|
||||
} else {
|
||||
_data.odd = true;
|
||||
}
|
||||
if (_i % _columns === 0) {
|
||||
_data.rowStart = true;
|
||||
} else if (_i % _columns === (_columns - 1)) {
|
||||
_data.rowEnd = true;
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
if (context && typeof context === 'object') {
|
||||
if (context instanceof Array) {
|
||||
for (j = context.length; i < j; i += 1) {
|
||||
if (data) {
|
||||
data.index = i;
|
||||
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
|
||||
data = setKeys(data, i, j, columns);
|
||||
}
|
||||
ret = ret + fn(context[i], {data: data});
|
||||
}
|
||||
} else {
|
||||
for (key in context) {
|
||||
if (context.hasOwnProperty(key)) {
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
for (key in context) {
|
||||
if (context.hasOwnProperty(key)) {
|
||||
if (data) {
|
||||
data.key = key;
|
||||
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
|
||||
data = setKeys(data, i, j, columns);
|
||||
}
|
||||
ret = ret + fn(context[key], {data: data});
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
ret = inverse(this);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
// ### Is Helper
|
||||
// `{{#is "paged"}}`
|
||||
// `{{#is "index, paged"}}`
|
||||
// Checks whether we're in a given context.
|
||||
coreHelpers.is = function (context, options) {
|
||||
options = options || {};
|
||||
|
||||
var currentContext = options.data.root.context;
|
||||
|
||||
if (!_.isString(context)) {
|
||||
errors.logWarn('Invalid or no attribute given to is helper');
|
||||
return;
|
||||
}
|
||||
|
||||
function evaluateContext(expr) {
|
||||
return expr.split(',').map(function (v) {
|
||||
return v.trim();
|
||||
}).reduce(function (p, c) {
|
||||
return p || _.contains(currentContext, c);
|
||||
}, false);
|
||||
}
|
||||
|
||||
if (evaluateContext(context)) {
|
||||
return options.fn(this);
|
||||
}
|
||||
return options.inverse(this);
|
||||
};
|
||||
|
||||
// ### Has Helper
|
||||
// `{{#has tag="video, music"}}`
|
||||
// `{{#has author="sam, pat"}}`
|
||||
// Checks whether a post has at least one of the tags
|
||||
coreHelpers.has = function (options) {
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
|
||||
var tags = _.pluck(this.tags, 'name'),
|
||||
author = this.author ? this.author.name : null,
|
||||
tagList = options.hash.tag || false,
|
||||
authorList = options.hash.author || false,
|
||||
tagsOk,
|
||||
authorOk;
|
||||
|
||||
function evaluateTagList(expr, tags) {
|
||||
return expr.split(',').map(function (v) {
|
||||
return v.trim();
|
||||
}).reduce(function (p, c) {
|
||||
return p || (_.findIndex(tags, function (item) {
|
||||
// Escape regex special characters
|
||||
item = item.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&');
|
||||
item = new RegExp(item, 'i');
|
||||
return item.test(c);
|
||||
}) !== -1);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function evaluateAuthorList(expr, author) {
|
||||
var authorList = expr.split(',').map(function (v) {
|
||||
return v.trim().toLocaleLowerCase();
|
||||
});
|
||||
|
||||
return _.contains(authorList, author.toLocaleLowerCase());
|
||||
}
|
||||
|
||||
if (!tagList && !authorList) {
|
||||
errors.logWarn('Invalid or no attribute given to has helper');
|
||||
return;
|
||||
}
|
||||
|
||||
tagsOk = tagList && evaluateTagList(tagList, tags) || false;
|
||||
authorOk = authorList && evaluateAuthorList(authorList, author) || false;
|
||||
|
||||
if (tagsOk || authorOk) {
|
||||
return options.fn(this);
|
||||
}
|
||||
return options.inverse(this);
|
||||
};
|
||||
|
||||
// ### Pagination Helper
|
||||
// `{{pagination}}`
|
||||
// Outputs previous and next buttons, along with info about the current page
|
||||
coreHelpers.pagination = function (options) {
|
||||
/*jshint unused:false*/
|
||||
if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
|
||||
return errors.logAndThrowError('pagination data is not an object or is a function');
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages) ||
|
||||
_.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
|
||||
return errors.logAndThrowError('All values must be defined for page, pages, limit and total');
|
||||
}
|
||||
|
||||
if ((!_.isNull(this.pagination.next) && !_.isNumber(this.pagination.next)) ||
|
||||
(!_.isNull(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
|
||||
return errors.logAndThrowError('Invalid value, Next/Prev must be a number');
|
||||
}
|
||||
|
||||
if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages) ||
|
||||
!_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
|
||||
return errors.logAndThrowError('Invalid value, check page, pages, limit and total are numbers');
|
||||
}
|
||||
|
||||
var context = _.merge({}, this.pagination);
|
||||
|
||||
if (this.tag !== undefined) {
|
||||
context.tagSlug = this.tag.slug;
|
||||
}
|
||||
|
||||
if (this.author !== undefined) {
|
||||
context.authorSlug = this.author.slug;
|
||||
}
|
||||
|
||||
return template.execute('pagination', context);
|
||||
};
|
||||
|
||||
// ## Pluralize strings depending on item count
|
||||
// {{plural 0 empty='No posts' singular='% post' plural='% posts'}}
|
||||
// The 1st argument is the numeric variable which the helper operates on
|
||||
// The 2nd argument is the string that will be output if the variable's value is 0
|
||||
// The 3rd argument is the string that will be output if the variable's value is 1
|
||||
// The 4th argument is the string that will be output if the variable's value is 2+
|
||||
// coreHelpers.plural = function (number, empty, singular, plural) {
|
||||
coreHelpers.plural = function (context, options) {
|
||||
if (_.isUndefined(options.hash) || _.isUndefined(options.hash.empty) ||
|
||||
_.isUndefined(options.hash.singular) || _.isUndefined(options.hash.plural)) {
|
||||
return errors.logAndThrowError('All values must be defined for empty, singular and plural');
|
||||
}
|
||||
|
||||
if (context === 0) {
|
||||
return new hbs.handlebars.SafeString(options.hash.empty);
|
||||
} else if (context === 1) {
|
||||
return new hbs.handlebars.SafeString(options.hash.singular.replace('%', context));
|
||||
} else if (context >= 2) {
|
||||
return new hbs.handlebars.SafeString(options.hash.plural.replace('%', context));
|
||||
}
|
||||
};
|
||||
|
||||
coreHelpers.helperMissing = function (arg) {
|
||||
if (arguments.length === 2) {
|
||||
return undefined;
|
||||
|
@ -865,16 +89,6 @@ coreHelpers.helperMissing = function (arg) {
|
|||
errors.logError('Missing helper: "' + arg + '"');
|
||||
};
|
||||
|
||||
// ## Admin URL helper
|
||||
// uses urlFor to generate a URL for either the admin or the frontend.
|
||||
coreHelpers.admin_url = function (options) {
|
||||
var absolute = options && options.hash && options.hash.absolute,
|
||||
// Ghost isn't a named route as currently it violates the must start-and-end with slash rule
|
||||
context = !options || !options.hash || !options.hash.frontend ? {relativeUrl: '/ghost'} : 'home';
|
||||
|
||||
return config.urlFor(context, absolute);
|
||||
};
|
||||
|
||||
// Register an async handlebars helper for a given handlebars instance
|
||||
function registerAsyncHelper(hbs, name, fn) {
|
||||
hbs.registerAsyncHelper(name, function (options, cb) {
|
||||
|
@ -903,69 +117,41 @@ function registerAdminHelper(name, fn) {
|
|||
coreHelpers.adminHbs.registerHelper(name, fn);
|
||||
}
|
||||
|
||||
registerHelpers = function (adminHbs, assetHash) {
|
||||
registerHelpers = function (adminHbs) {
|
||||
// Expose hbs instance for admin
|
||||
coreHelpers.adminHbs = adminHbs;
|
||||
|
||||
// Store hash for assets
|
||||
coreHelpers.assetHash = assetHash;
|
||||
|
||||
// Register theme helpers
|
||||
registerThemeHelper('asset', coreHelpers.asset);
|
||||
|
||||
registerThemeHelper('author', coreHelpers.author);
|
||||
|
||||
registerThemeHelper('content', coreHelpers.content);
|
||||
|
||||
registerThemeHelper('title', coreHelpers.title);
|
||||
|
||||
registerThemeHelper('date', coreHelpers.date);
|
||||
|
||||
registerThemeHelper('encode', coreHelpers.encode);
|
||||
|
||||
registerThemeHelper('excerpt', coreHelpers.excerpt);
|
||||
|
||||
registerThemeHelper('foreach', coreHelpers.foreach);
|
||||
|
||||
registerThemeHelper('is', coreHelpers.is);
|
||||
|
||||
registerThemeHelper('has', coreHelpers.has);
|
||||
|
||||
registerThemeHelper('page_url', coreHelpers.page_url);
|
||||
|
||||
registerThemeHelper('pageUrl', coreHelpers.pageUrl);
|
||||
|
||||
registerThemeHelper('pagination', coreHelpers.pagination);
|
||||
|
||||
registerThemeHelper('tags', coreHelpers.tags);
|
||||
|
||||
registerThemeHelper('plural', coreHelpers.plural);
|
||||
|
||||
// Async theme helpers
|
||||
registerAsyncThemeHelper('body_class', coreHelpers.body_class);
|
||||
|
||||
registerAsyncThemeHelper('e', coreHelpers.e);
|
||||
|
||||
registerAsyncThemeHelper('ghost_foot', coreHelpers.ghost_foot);
|
||||
|
||||
registerAsyncThemeHelper('ghost_head', coreHelpers.ghost_head);
|
||||
|
||||
registerAsyncThemeHelper('meta_description', coreHelpers.meta_description);
|
||||
|
||||
registerAsyncThemeHelper('meta_title', coreHelpers.meta_title);
|
||||
|
||||
registerAsyncThemeHelper('post_class', coreHelpers.post_class);
|
||||
|
||||
registerAsyncThemeHelper('url', coreHelpers.url);
|
||||
|
||||
// Register admin helpers
|
||||
registerAdminHelper('ghost_script_tags', coreHelpers.ghost_script_tags);
|
||||
|
||||
registerAdminHelper('asset', coreHelpers.asset);
|
||||
|
||||
registerAdminHelper('apps', coreHelpers.apps);
|
||||
|
||||
registerAdminHelper('file_storage', coreHelpers.file_storage);
|
||||
|
||||
registerAdminHelper('blog_url', coreHelpers.blog_url);
|
||||
};
|
||||
|
||||
|
@ -973,4 +159,4 @@ module.exports = coreHelpers;
|
|||
module.exports.loadCoreHelpers = registerHelpers;
|
||||
module.exports.registerThemeHelper = registerThemeHelper;
|
||||
module.exports.registerAsyncThemeHelper = registerAsyncThemeHelper;
|
||||
module.exports.scriptFiles = scriptFiles;
|
||||
module.exports.scriptFiles = utils.scriptFiles;
|
||||
|
|
32
core/server/helpers/is.js
Normal file
32
core/server/helpers/is.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// # Is Helper
|
||||
// Usage: `{{#is "paged"}}`, `{{#is "index, paged"}}`
|
||||
// Checks whether we're in a given context.
|
||||
var _ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
is;
|
||||
|
||||
is = function (context, options) {
|
||||
options = options || {};
|
||||
|
||||
var currentContext = options.data.root.context;
|
||||
|
||||
if (!_.isString(context)) {
|
||||
errors.logWarn('Invalid or no attribute given to is helper');
|
||||
return;
|
||||
}
|
||||
|
||||
function evaluateContext(expr) {
|
||||
return expr.split(',').map(function (v) {
|
||||
return v.trim();
|
||||
}).reduce(function (p, c) {
|
||||
return p || _.contains(currentContext, c);
|
||||
}, false);
|
||||
}
|
||||
|
||||
if (evaluateContext(context)) {
|
||||
return options.fn(this);
|
||||
}
|
||||
return options.inverse(this);
|
||||
};
|
||||
|
||||
module.exports = is;
|
37
core/server/helpers/meta_description.js
Normal file
37
core/server/helpers/meta_description.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
// # Meta Description Helper
|
||||
// Usage: `{{meta_description}}`
|
||||
//
|
||||
// Page description used for sharing and SEO
|
||||
//
|
||||
// We use the name meta_description to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var _ = require('lodash'),
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
meta_description;
|
||||
|
||||
meta_description = function () {
|
||||
var description,
|
||||
blog;
|
||||
|
||||
if (_.isString(this.relativeUrl)) {
|
||||
blog = config.theme;
|
||||
if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
description = blog.description;
|
||||
} else if (this.author) {
|
||||
description = /\/page\//.test(this.relativeUrl) ? '' : this.author.bio;
|
||||
} else if (this.tag || /\/page\//.test(this.relativeUrl)) {
|
||||
description = '';
|
||||
} else if (this.post) {
|
||||
description = _.isEmpty(this.post.meta_description) ? '' : this.post.meta_description;
|
||||
}
|
||||
}
|
||||
|
||||
return filters.doFilter('meta_description', description).then(function (description) {
|
||||
description = description || '';
|
||||
return description.trim();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = meta_description;
|
48
core/server/helpers/meta_title.js
Normal file
48
core/server/helpers/meta_title.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
// # Meta Title Helper
|
||||
// Usage: `{{meta_title}}`
|
||||
//
|
||||
// Page title used for sharing and SEO
|
||||
//
|
||||
// We use the name meta_title to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var _ = require('lodash'),
|
||||
config = require('../config'),
|
||||
filters = require('../filters'),
|
||||
meta_title;
|
||||
|
||||
meta_title = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var title = '',
|
||||
blog,
|
||||
page,
|
||||
pageString = '';
|
||||
|
||||
if (_.isString(this.relativeUrl)) {
|
||||
blog = config.theme;
|
||||
|
||||
page = this.relativeUrl.match(/\/page\/(\d+)/);
|
||||
|
||||
if (page) {
|
||||
pageString = ' - Page ' + page[1];
|
||||
}
|
||||
|
||||
if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
||||
title = blog.title;
|
||||
} else if (this.author) {
|
||||
title = this.author.name + pageString + ' - ' + blog.title;
|
||||
} else if (this.tag) {
|
||||
title = this.tag.name + pageString + ' - ' + blog.title;
|
||||
} else if (this.post) {
|
||||
title = _.isEmpty(this.post.meta_title) ? this.post.title : this.post.meta_title;
|
||||
} else {
|
||||
title = blog.title + pageString;
|
||||
}
|
||||
}
|
||||
return filters.doFilter('meta_title', title).then(function (title) {
|
||||
title = title || '';
|
||||
return title.trim();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = meta_title;
|
58
core/server/helpers/page_url.js
Normal file
58
core/server/helpers/page_url.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
// ### Page URL Helper
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{page_url 2}}`
|
||||
//
|
||||
// Returns the URL for the page specified in the current object
|
||||
// context.
|
||||
//
|
||||
// We use the name page_url to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var config = require('../config'),
|
||||
errors = require('../errors'),
|
||||
page_url,
|
||||
pageUrl;
|
||||
|
||||
page_url = function (context, block) {
|
||||
/*jshint unused:false*/
|
||||
var url = config.paths.subdir;
|
||||
|
||||
if (this.tagSlug !== undefined) {
|
||||
url += '/tag/' + this.tagSlug;
|
||||
}
|
||||
|
||||
if (this.authorSlug !== undefined) {
|
||||
url += '/author/' + this.authorSlug;
|
||||
}
|
||||
|
||||
if (context > 1) {
|
||||
url += '/page/' + context;
|
||||
}
|
||||
|
||||
url += '/';
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
// ### Page URL Helper: DEPRECATED
|
||||
//
|
||||
// *Usage example:*
|
||||
// `{{pageUrl 2}}`
|
||||
//
|
||||
// Returns the URL for the page specified in the current object
|
||||
// context. This helper is deprecated and will be removed in future versions.
|
||||
//
|
||||
pageUrl = function (context, block) {
|
||||
errors.logWarn('Warning: pageUrl is deprecated, please use page_url instead\n' +
|
||||
'The helper pageUrl has been replaced with page_url in Ghost 0.4.2, and will be removed entirely in Ghost 0.6\n' +
|
||||
'In your theme\'s pagination.hbs file, pageUrl should be renamed to page_url');
|
||||
|
||||
/*jshint unused:false*/
|
||||
var self = this;
|
||||
|
||||
return page_url.call(self, context, block);
|
||||
};
|
||||
|
||||
module.exports = page_url;
|
||||
module.exports.deprecated = pageUrl;
|
44
core/server/helpers/pagination.js
Normal file
44
core/server/helpers/pagination.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
// ### Pagination Helper
|
||||
// `{{pagination}}`
|
||||
// Outputs previous and next buttons, along with info about the current page
|
||||
|
||||
var _ = require('lodash'),
|
||||
errors = require('../errors'),
|
||||
template = require('./template'),
|
||||
pagination;
|
||||
|
||||
pagination = function (options) {
|
||||
/*jshint unused:false*/
|
||||
if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
|
||||
return errors.logAndThrowError('pagination data is not an object or is a function');
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages) ||
|
||||
_.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
|
||||
return errors.logAndThrowError('All values must be defined for page, pages, limit and total');
|
||||
}
|
||||
|
||||
if ((!_.isNull(this.pagination.next) && !_.isNumber(this.pagination.next)) ||
|
||||
(!_.isNull(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
|
||||
return errors.logAndThrowError('Invalid value, Next/Prev must be a number');
|
||||
}
|
||||
|
||||
if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages) ||
|
||||
!_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
|
||||
return errors.logAndThrowError('Invalid value, check page, pages, limit and total are numbers');
|
||||
}
|
||||
|
||||
var context = _.merge({}, this.pagination);
|
||||
|
||||
if (this.tag !== undefined) {
|
||||
context.tagSlug = this.tag.slug;
|
||||
}
|
||||
|
||||
if (this.author !== undefined) {
|
||||
context.authorSlug = this.author.slug;
|
||||
}
|
||||
|
||||
return template.execute('pagination', context);
|
||||
};
|
||||
|
||||
module.exports = pagination;
|
31
core/server/helpers/plural.js
Normal file
31
core/server/helpers/plural.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
// # Plural Helper
|
||||
// Usage: `{{plural 0 empty='No posts' singular='% post' plural='% posts'}}`
|
||||
//
|
||||
// pluralises strings depending on item count
|
||||
//
|
||||
// The 1st argument is the numeric variable which the helper operates on
|
||||
// The 2nd argument is the string that will be output if the variable's value is 0
|
||||
// The 3rd argument is the string that will be output if the variable's value is 1
|
||||
// The 4th argument is the string that will be output if the variable's value is 2+
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
errors = require('../errors'),
|
||||
_ = require('lodash'),
|
||||
plural;
|
||||
|
||||
plural = function (context, options) {
|
||||
if (_.isUndefined(options.hash) || _.isUndefined(options.hash.empty) ||
|
||||
_.isUndefined(options.hash.singular) || _.isUndefined(options.hash.plural)) {
|
||||
return errors.logAndThrowError('All values must be defined for empty, singular and plural');
|
||||
}
|
||||
|
||||
if (context === 0) {
|
||||
return new hbs.handlebars.SafeString(options.hash.empty);
|
||||
} else if (context === 1) {
|
||||
return new hbs.handlebars.SafeString(options.hash.singular.replace('%', context));
|
||||
} else if (context >= 2) {
|
||||
return new hbs.handlebars.SafeString(options.hash.plural.replace('%', context));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = plural;
|
39
core/server/helpers/post_class.js
Normal file
39
core/server/helpers/post_class.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
// # Post Class Helper
|
||||
// Usage: `{{post_class}}`
|
||||
//
|
||||
// Output classes for the body element
|
||||
//
|
||||
// We use the name body_class to match the helper for consistency:
|
||||
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
filters = require('../filters'),
|
||||
post_class;
|
||||
|
||||
post_class = function (options) {
|
||||
/*jshint unused:false*/
|
||||
var classes = ['post'],
|
||||
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
||||
featured = this.post && this.post.featured ? this.post.featured : this.featured || false,
|
||||
page = this.post && this.post.page ? this.post.page : this.page || false;
|
||||
|
||||
if (tags) {
|
||||
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
||||
}
|
||||
|
||||
if (featured) {
|
||||
classes.push('featured');
|
||||
}
|
||||
|
||||
if (page) {
|
||||
classes.push('page');
|
||||
}
|
||||
|
||||
return filters.doFilter('post_class', classes).then(function (classes) {
|
||||
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
||||
return new hbs.handlebars.SafeString(classString.trim());
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = post_class;
|
46
core/server/helpers/tags.js
Normal file
46
core/server/helpers/tags.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
// # Tags Helper
|
||||
// Usage: `{{tags}}`, `{{tags separator=' - '}}`
|
||||
//
|
||||
// Returns a string of the tags on the post.
|
||||
// By default, tags are separated by commas.
|
||||
//
|
||||
// Note that the standard {{#each tags}} implementation is unaffected by this helper
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
config = require('../config'),
|
||||
utils = require('./utils'),
|
||||
tags;
|
||||
|
||||
tags = function (options) {
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
|
||||
var autolink = options.hash && _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||
separator = options.hash && _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
||||
prefix = options.hash && _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
||||
suffix = options.hash && _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
||||
output = '';
|
||||
|
||||
function createTagList(tags) {
|
||||
var tagNames = _.pluck(tags, 'name');
|
||||
|
||||
if (autolink) {
|
||||
return _.map(tags, function (tag) {
|
||||
return utils.linkTemplate({
|
||||
url: config.urlFor('tag', {tag: tag}),
|
||||
text: _.escape(tag.name)
|
||||
});
|
||||
}).join(separator);
|
||||
}
|
||||
return _.escape(tagNames.join(separator));
|
||||
}
|
||||
|
||||
if (this.tags && this.tags.length) {
|
||||
output = prefix + createTagList(this.tags) + suffix;
|
||||
}
|
||||
|
||||
return new hbs.handlebars.SafeString(output);
|
||||
};
|
||||
|
||||
module.exports = tags;
|
13
core/server/helpers/title.js
Normal file
13
core/server/helpers/title.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
// # Title Helper
|
||||
// Usage: `{{title}}`
|
||||
//
|
||||
// Overrides the standard behaviour of `{[title}}` to ensure the content is correctly escaped
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
title;
|
||||
|
||||
title = function () {
|
||||
return new hbs.handlebars.SafeString(hbs.handlebars.Utils.escapeExpression(this.title || ''));
|
||||
};
|
||||
|
||||
module.exports = title;
|
31
core/server/helpers/url.js
Normal file
31
core/server/helpers/url.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
// # URL helper
|
||||
// Usage: `{{url}}`, `{{url absolute="true"}}`
|
||||
//
|
||||
// Returns the URL for the current object scope i.e. If inside a post scope will return post permalink
|
||||
// `absolute` flag outputs absolute URL, else URL is relative
|
||||
|
||||
var Promise = require('bluebird'),
|
||||
config = require('../config'),
|
||||
api = require('../api'),
|
||||
schema = require('../data/schema').checks,
|
||||
url;
|
||||
|
||||
url = function (options) {
|
||||
var absolute = options && options.hash.absolute;
|
||||
|
||||
if (schema.isPost(this)) {
|
||||
return config.urlForPost(api.settings, this, absolute);
|
||||
}
|
||||
|
||||
if (schema.isTag(this)) {
|
||||
return Promise.resolve(config.urlFor('tag', {tag: this}, absolute));
|
||||
}
|
||||
|
||||
if (schema.isUser(this)) {
|
||||
return Promise.resolve(config.urlFor('author', {author: this}, absolute));
|
||||
}
|
||||
|
||||
return Promise.resolve(config.urlFor(this, absolute));
|
||||
};
|
||||
|
||||
module.exports = url;
|
22
core/server/helpers/utils.js
Normal file
22
core/server/helpers/utils.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
var _ = require('lodash'),
|
||||
utils;
|
||||
|
||||
utils = {
|
||||
assetTemplate: _.template('<%= source %>?v=<%= version %>'),
|
||||
linkTemplate: _.template('<a href="<%= url %>"><%= text %></a>'),
|
||||
scriptTemplate: _.template('<script src="<%= source %>?v=<%= version %>"></script>'),
|
||||
isProduction: process.env.NODE_ENV === 'production',
|
||||
scriptFiles: {
|
||||
production: [
|
||||
'vendor.min.js',
|
||||
'ghost.min.js'
|
||||
],
|
||||
development: [
|
||||
'vendor-dev.js',
|
||||
'templates-dev.js',
|
||||
'ghost-dev.js'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = utils;
|
|
@ -1,6 +1,5 @@
|
|||
// Module dependencies
|
||||
var crypto = require('crypto'),
|
||||
express = require('express'),
|
||||
var express = require('express'),
|
||||
hbs = require('express-hbs'),
|
||||
compress = require('compression'),
|
||||
fs = require('fs'),
|
||||
|
@ -18,7 +17,6 @@ var crypto = require('crypto'),
|
|||
models = require('./models'),
|
||||
permissions = require('./permissions'),
|
||||
apps = require('./apps'),
|
||||
packageInfo = require('../../package.json'),
|
||||
GhostServer = require('./ghost-server'),
|
||||
|
||||
// Variables
|
||||
|
@ -132,9 +130,7 @@ function initNotifications() {
|
|||
function init(options) {
|
||||
// Get reference to an express app instance.
|
||||
var blogApp = express(),
|
||||
adminApp = express(),
|
||||
// create a hash for cache busting assets
|
||||
assetHash = (crypto.createHash('md5').update(packageInfo.version + Date.now()).digest('hex')).substring(0, 10);
|
||||
adminApp = express();
|
||||
|
||||
// ### Initialisation
|
||||
// The server and its dependencies require a populated config
|
||||
|
@ -196,7 +192,7 @@ function init(options) {
|
|||
adminApp.engine('hbs', adminHbs.express3({}));
|
||||
|
||||
// Load helpers
|
||||
helpers.loadCoreHelpers(adminHbs, assetHash);
|
||||
helpers.loadCoreHelpers(adminHbs);
|
||||
|
||||
// ## Middleware and Routing
|
||||
middleware(blogApp, adminApp);
|
||||
|
|
110
core/test/unit/server_helpers/asset_spec.js
Normal file
110
core/test/unit/server_helpers/asset_spec.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*globals describe, before, after, it */
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{asset}} helper', function () {
|
||||
var rendered;
|
||||
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('has loaded asset helper', function () {
|
||||
should.exist(handlebars.helpers.asset);
|
||||
});
|
||||
|
||||
describe('no subdirectory', function () {
|
||||
it('handles favicon correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('favicon.ico', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/favicon.ico');
|
||||
|
||||
// without ghost set
|
||||
rendered = helpers.asset('favicon.ico');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/favicon.ico');
|
||||
});
|
||||
|
||||
it('handles shared assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('shared/asset.js', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/shared/asset.js?v=abc');
|
||||
|
||||
// without ghost set
|
||||
rendered = helpers.asset('shared/asset.js');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/shared/asset.js?v=abc');
|
||||
});
|
||||
|
||||
it('handles admin assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('js/asset.js', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/ghost/js/asset.js?v=abc');
|
||||
});
|
||||
|
||||
it('handles theme assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('js/asset.js');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/assets/js/asset.js?v=abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({url: 'http://testurl.com/blog'});
|
||||
});
|
||||
|
||||
it('handles favicon correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('favicon.ico', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/favicon.ico');
|
||||
|
||||
// without ghost set
|
||||
rendered = helpers.asset('favicon.ico');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/favicon.ico');
|
||||
});
|
||||
|
||||
it('handles shared assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('shared/asset.js', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/shared/asset.js?v=abc');
|
||||
|
||||
// without ghost set
|
||||
rendered = helpers.asset('shared/asset.js');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/shared/asset.js?v=abc');
|
||||
});
|
||||
|
||||
it('handles admin assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('js/asset.js', {hash: {ghost: 'true'}});
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/ghost/js/asset.js?v=abc');
|
||||
});
|
||||
|
||||
it('handles theme assets correctly', function () {
|
||||
// with ghost set
|
||||
rendered = helpers.asset('js/asset.js');
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('/blog/assets/js/asset.js?v=abc');
|
||||
});
|
||||
});
|
||||
});
|
49
core/test/unit/server_helpers/author_spec.js
Normal file
49
core/test/unit/server_helpers/author_spec.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{author}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded author helper', function () {
|
||||
should.exist(handlebars.helpers.author);
|
||||
});
|
||||
|
||||
it('Returns the link to the author from the context', function () {
|
||||
var data = {author: {name: 'abc 123', slug: 'abc123', bio: '', website: '', status: '', location: ''}},
|
||||
result = helpers.author.call(data, {hash: {}});
|
||||
|
||||
String(result).should.equal('<a href="/author/abc123/">abc 123</a>');
|
||||
});
|
||||
|
||||
it('Returns the full name of the author from the context if no autolink', function () {
|
||||
var data = {author: {name: 'abc 123', slug: 'abc123'}},
|
||||
result = helpers.author.call(data, {hash: {autolink: 'false'}});
|
||||
|
||||
String(result).should.equal('abc 123');
|
||||
});
|
||||
|
||||
it('Returns a blank string where author data is missing', function () {
|
||||
var data = {author: null},
|
||||
result = helpers.author.call(data, {hash: {}});
|
||||
|
||||
String(result).should.equal('');
|
||||
});
|
||||
|
||||
it('Functions as block helper if called with #', function () {
|
||||
var data = {author: {name: 'abc 123', slug: 'abc123'}},
|
||||
// including fn emulates the #
|
||||
result = helpers.author.call(data, {hash: {}, fn: function () { return 'FN'; }});
|
||||
|
||||
// It outputs the result of fn
|
||||
String(result).should.equal('FN');
|
||||
});
|
||||
});
|
119
core/test/unit/server_helpers/body_class_spec.js
Normal file
119
core/test/unit/server_helpers/body_class_spec.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*globals describe, before, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers'),
|
||||
api = require('../../../server/api');
|
||||
|
||||
describe('{{body_class}} helper', function () {
|
||||
var sandbox;
|
||||
|
||||
before(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({
|
||||
settings: [{value: 'casper'}]
|
||||
});
|
||||
});
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({paths: {
|
||||
availableThemes: {
|
||||
casper: {
|
||||
assets: null,
|
||||
'default.hbs': '/content/themes/casper/default.hbs',
|
||||
'index.hbs': '/content/themes/casper/index.hbs',
|
||||
'page.hbs': '/content/themes/casper/page.hbs',
|
||||
'page-about.hbs': '/content/themes/casper/page-about.hbs',
|
||||
'post.hbs': '/content/themes/casper/post.hbs'
|
||||
}
|
||||
}
|
||||
}});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('has loaded body_class helper', function () {
|
||||
should.exist(handlebars.helpers.body_class);
|
||||
});
|
||||
|
||||
it('can render class string', function (done) {
|
||||
helpers.body_class.call({}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
|
||||
rendered.string.should.equal('home-template');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render class string for context', function (done) {
|
||||
Promise.all([
|
||||
helpers.body_class.call({relativeUrl: '/'}),
|
||||
helpers.body_class.call({relativeUrl: '/a-post-title', post: {}}),
|
||||
helpers.body_class.call({relativeUrl: '/page/4'}),
|
||||
helpers.body_class.call({relativeUrl: '/tag/foo', tag: {slug: 'foo'}}),
|
||||
helpers.body_class.call({relativeUrl: '/tag/foo/page/2', tag: {slug: 'foo'}}),
|
||||
helpers.body_class.call({relativeUrl: '/author/bar', author: {slug: 'bar'}}),
|
||||
helpers.body_class.call({relativeUrl: '/author/bar/page/2', author: {slug: 'bar'}})
|
||||
]).then(function (rendered) {
|
||||
rendered.length.should.equal(7);
|
||||
|
||||
should.exist(rendered[0]);
|
||||
should.exist(rendered[1]);
|
||||
should.exist(rendered[2]);
|
||||
should.exist(rendered[3]);
|
||||
should.exist(rendered[4]);
|
||||
should.exist(rendered[5]);
|
||||
should.exist(rendered[6]);
|
||||
|
||||
rendered[0].string.should.equal('home-template');
|
||||
rendered[1].string.should.equal('post-template');
|
||||
rendered[2].string.should.equal('paged archive-template');
|
||||
rendered[3].string.should.equal('tag-template tag-foo');
|
||||
rendered[4].string.should.equal('tag-template tag-foo paged archive-template');
|
||||
rendered[5].string.should.equal('author-template author-bar');
|
||||
rendered[6].string.should.equal('author-template author-bar paged archive-template');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render class for static page', function (done) {
|
||||
helpers.body_class.call({
|
||||
relativeUrl: '/',
|
||||
post: {
|
||||
page: true
|
||||
}
|
||||
}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('home-template page-template page');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render class for static page with custom template', function (done) {
|
||||
helpers.body_class.call({
|
||||
relativeUrl: '/about',
|
||||
post: {
|
||||
page: true,
|
||||
slug: 'about'
|
||||
|
||||
}
|
||||
}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('post-template page-template page page-about page-template-about');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
182
core/test/unit/server_helpers/content_spec.js
Normal file
182
core/test/unit/server_helpers/content_spec.js
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{content}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded content helper', function () {
|
||||
should.exist(handlebars.helpers.content);
|
||||
});
|
||||
|
||||
it('can render content', function () {
|
||||
var html = 'Hello World',
|
||||
rendered = helpers.content.call({html: html});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(html);
|
||||
});
|
||||
|
||||
it('can truncate html by word', function () {
|
||||
var html = '<p>Hello <strong>World! It\'s me!</strong></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: 2}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p>Hello <strong>World</strong></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words', function () {
|
||||
var html = '<p>Hello <strong>World! It\'s me!</strong></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving image tag if it is first', function () {
|
||||
var html = '<p><img src="example.jpg" />Hello <strong>World! It\'s me!</strong></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><img src="example.jpg" /></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving image tag with attributes', function () {
|
||||
var html = '<p><img src="example.png" alt="Alternative" title="Title"></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><img src="example.png" alt="Alternative" title="Title"></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving first image tag & if alt text has a single quote', function () {
|
||||
var html = '<p><img src="example.jpg" alt="It\'s me!" />Hello <strong>World! It\'s me!</strong></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><img src="example.jpg" alt="It\'s me!" /></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving first image tag & if alt text has a double quote', function () {
|
||||
var html = '<p><img src="example.jpg" alt="A double quote is \'" />' +
|
||||
'Hello <strong>World! It\'s me!</strong></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><img src="example.jpg" alt="A double quote is \'" /></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving first image tag if it contains > & <', function () {
|
||||
var html = '<p><img src="examp>><><>le.png"></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><img src="examp>><><>le.png"></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving first two image tags', function () {
|
||||
var html = '<p><img src="example.png"><img src="example.png">Hi<img src="example.png"></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><img src="example.png"><img src="example.png"></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, removing image if text comes first', function () {
|
||||
var html = '<p><a>Bli<a><a><img src="example.png"></a></a>Blob</a></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><a></a></p>');
|
||||
});
|
||||
|
||||
it('can truncate html to 0 words, leaving video tag', function () {
|
||||
var html = '<p><video><source src="movie.mp4"><source src="movie.ogg"></video></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {words: '0'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p><video><source src="movie.mp4"><source src="movie.ogg"></video></p>');
|
||||
});
|
||||
|
||||
it('can truncate html by character', function () {
|
||||
var html = '<p>Hello <strong>World! It\'s me!</strong></p>',
|
||||
rendered = (
|
||||
helpers.content
|
||||
.call(
|
||||
{html: html},
|
||||
{hash: {characters: 8}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<p>Hello <strong>Wo</strong></p>');
|
||||
});
|
||||
});
|
68
core/test/unit/server_helpers/date_spec.js
Normal file
68
core/test/unit/server_helpers/date_spec.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers'),
|
||||
moment = require('moment');
|
||||
|
||||
describe('{{date}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('is loaded', function () {
|
||||
should.exist(handlebars.helpers.date);
|
||||
});
|
||||
|
||||
// TODO: When timezone support is added these tests should be updated
|
||||
// to test the output of the helper against static strings instead
|
||||
// of calling moment(). Without timezone support the output of this
|
||||
// helper may differ depending on what timezone the tests are run in.
|
||||
|
||||
it('creates properly formatted date strings', function () {
|
||||
var testDates = [
|
||||
'2013-12-31T11:28:58.593Z',
|
||||
'2014-01-01T01:28:58.593Z',
|
||||
'2014-02-20T01:28:58.593Z',
|
||||
'2014-03-01T01:28:58.593Z'
|
||||
],
|
||||
format = 'MMM Do, YYYY',
|
||||
context = {
|
||||
hash: {
|
||||
format: format
|
||||
}
|
||||
};
|
||||
|
||||
testDates.forEach(function (d) {
|
||||
var rendered = helpers.date.call({published_at: d}, context);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.should.equal(moment(d).format(format));
|
||||
});
|
||||
});
|
||||
|
||||
it('creates properly formatted time ago date strings', function () {
|
||||
var testDates = [
|
||||
'2013-12-31T23:58:58.593Z',
|
||||
'2014-01-01T00:28:58.593Z',
|
||||
'2014-11-20T01:28:58.593Z',
|
||||
'2014-03-01T01:28:58.593Z'
|
||||
],
|
||||
context = {
|
||||
hash: {
|
||||
timeago: true
|
||||
}
|
||||
};
|
||||
|
||||
testDates.forEach(function (d) {
|
||||
var rendered = helpers.date.call({published_at: d}, context);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.should.equal(moment(d).fromNow());
|
||||
});
|
||||
});
|
||||
});
|
28
core/test/unit/server_helpers/encode_spec.js
Normal file
28
core/test/unit/server_helpers/encode_spec.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{encode}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded encode helper', function () {
|
||||
should.exist(handlebars.helpers.encode);
|
||||
});
|
||||
|
||||
it('can escape URI', function () {
|
||||
var uri = '$pecial!Charact3r(De[iver]y)Foo #Bar',
|
||||
expected = '%24pecial!Charact3r(De%5Biver%5Dy)Foo%20%23Bar',
|
||||
escaped = helpers.encode(uri);
|
||||
|
||||
should.exist(escaped);
|
||||
String(escaped).should.equal(expected);
|
||||
});
|
||||
});
|
82
core/test/unit/server_helpers/excerpt_spec.js
Normal file
82
core/test/unit/server_helpers/excerpt_spec.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{excerpt}} Helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded excerpt helper', function () {
|
||||
should.exist(handlebars.helpers.excerpt);
|
||||
});
|
||||
|
||||
it('can render excerpt', function () {
|
||||
var html = 'Hello World',
|
||||
rendered = helpers.excerpt.call({html: html});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(html);
|
||||
});
|
||||
|
||||
it('does not output HTML', function () {
|
||||
var html = '<p>There are <br />10<br> types<br/> of people in <img src="a">the world:' +
|
||||
'<img src=b alt="c"> those who <img src="@" onclick="javascript:alert(\'hello\');">' +
|
||||
'understand trinary</p>, those who don\'t <div style="" class=~/\'-,._?!|#>and' +
|
||||
'< test > those<<< test >>> who mistake it <for> binary.',
|
||||
expected = 'There are 10 types of people in the world: those who understand trinary, those who ' +
|
||||
'don\'t and those>> who mistake it <for> binary.',
|
||||
rendered = helpers.excerpt.call({html: html});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
|
||||
it('can truncate html by word', function () {
|
||||
var html = '<p>Hello <strong>World! It\'s me!</strong></p>',
|
||||
expected = 'Hello World',
|
||||
rendered = (
|
||||
helpers.excerpt.call(
|
||||
{html: html},
|
||||
{hash: {words: '2'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
|
||||
it('can truncate html with non-ascii characters by word', function () {
|
||||
var html = '<p>Едквюэ опортэат <strong>праэчынт ючю но, квуй эю</strong></p>',
|
||||
expected = 'Едквюэ опортэат',
|
||||
rendered = (
|
||||
helpers.excerpt.call(
|
||||
{html: html},
|
||||
{hash: {words: '2'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
|
||||
it('can truncate html by character', function () {
|
||||
var html = '<p>Hello <strong>World! It\'s me!</strong></p>',
|
||||
expected = 'Hello Wo',
|
||||
rendered = (
|
||||
helpers.excerpt.call(
|
||||
{html: html},
|
||||
{hash: {characters: '8'}}
|
||||
)
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
});
|
165
core/test/unit/server_helpers/foreach_spec.js
Normal file
165
core/test/unit/server_helpers/foreach_spec.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{#foreach}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
// passed into the foreach helper. takes the input string along with the metadata about
|
||||
// the current row and builds a csv output string that can be used to check the results.
|
||||
function fn(input, data) {
|
||||
data = data.data;
|
||||
|
||||
// if there was no private data passed into the helper, no metadata
|
||||
// was created, so just return the input
|
||||
if (!data) {
|
||||
return input + '\n';
|
||||
}
|
||||
|
||||
return input + ',' + data.first + ',' + data.rowEnd + ',' + data.rowStart + ',' +
|
||||
data.last + ',' + data.even + ',' + data.odd + '\n';
|
||||
}
|
||||
|
||||
function inverse(input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
it('is loaded', function () {
|
||||
should.exist(handlebars.helpers.foreach);
|
||||
});
|
||||
|
||||
it('should return the correct result when no private data is supplied', function () {
|
||||
var options = {},
|
||||
context = [],
|
||||
_this = {},
|
||||
rendered;
|
||||
|
||||
options.fn = fn;
|
||||
options.inverse = inverse;
|
||||
options.hash = {
|
||||
columns: 0
|
||||
};
|
||||
|
||||
// test with context as an array
|
||||
|
||||
context = 'hello world this is ghost'.split(' ');
|
||||
|
||||
rendered = helpers.foreach.call(_this, context, options);
|
||||
rendered.should.equal('hello\nworld\nthis\nis\nghost\n');
|
||||
|
||||
// test with context as an object
|
||||
|
||||
context = {
|
||||
one: 'hello',
|
||||
two: 'world',
|
||||
three: 'this',
|
||||
four: 'is',
|
||||
five: 'ghost'
|
||||
};
|
||||
|
||||
rendered = helpers.foreach.call(_this, context, options);
|
||||
rendered.should.equal('hello\nworld\nthis\nis\nghost\n');
|
||||
});
|
||||
|
||||
it('should return the correct result when private data is supplied', function () {
|
||||
var options = {},
|
||||
context = [],
|
||||
_this = {},
|
||||
rendered,
|
||||
result;
|
||||
|
||||
options.fn = fn;
|
||||
options.inverse = inverse;
|
||||
|
||||
options.hash = {
|
||||
columns: 0
|
||||
};
|
||||
|
||||
options.data = {};
|
||||
|
||||
context = 'hello world this is ghost'.split(' ');
|
||||
|
||||
rendered = helpers.foreach.call(_this, context, options);
|
||||
|
||||
result = rendered.split('\n');
|
||||
result[0].should.equal('hello,true,false,false,false,false,true');
|
||||
result[1].should.equal('world,false,false,false,false,true,false');
|
||||
result[2].should.equal('this,false,false,false,false,false,true');
|
||||
result[3].should.equal('is,false,false,false,false,true,false');
|
||||
result[4].should.equal('ghost,false,false,false,true,false,true');
|
||||
});
|
||||
|
||||
it('should return the correct result when private data is supplied & there are multiple columns', function () {
|
||||
var options = {},
|
||||
context = [],
|
||||
_this = {},
|
||||
rendered,
|
||||
result;
|
||||
|
||||
options.fn = fn;
|
||||
options.inverse = inverse;
|
||||
|
||||
options.hash = {
|
||||
columns: 2
|
||||
};
|
||||
|
||||
options.data = {};
|
||||
|
||||
// test with context as an array
|
||||
|
||||
context = 'hello world this is ghost'.split(' ');
|
||||
|
||||
rendered = helpers.foreach.call(_this, context, options);
|
||||
|
||||
result = rendered.split('\n');
|
||||
result[0].should.equal('hello,true,false,true,false,false,true');
|
||||
result[1].should.equal('world,false,true,false,false,true,false');
|
||||
result[2].should.equal('this,false,false,true,false,false,true');
|
||||
result[3].should.equal('is,false,true,false,false,true,false');
|
||||
result[4].should.equal('ghost,false,false,true,true,false,true');
|
||||
|
||||
// test with context as an object
|
||||
|
||||
context = {
|
||||
one: 'hello',
|
||||
two: 'world',
|
||||
three: 'this',
|
||||
four: 'is',
|
||||
five: 'ghost'
|
||||
};
|
||||
|
||||
rendered = helpers.foreach.call(_this, context, options);
|
||||
|
||||
result = rendered.split('\n');
|
||||
result[0].should.equal('hello,true,false,true,false,false,true');
|
||||
result[1].should.equal('world,false,true,false,false,true,false');
|
||||
result[2].should.equal('this,false,false,true,false,false,true');
|
||||
result[3].should.equal('is,false,true,false,false,true,false');
|
||||
result[4].should.equal('ghost,false,false,true,true,false,true');
|
||||
});
|
||||
|
||||
it('should return the correct inverse result if no context is provided', function () {
|
||||
var options = {},
|
||||
context = [],
|
||||
_this = 'the inverse data',
|
||||
rendered;
|
||||
|
||||
options.fn = function () {};
|
||||
options.inverse = inverse;
|
||||
options.hash = {
|
||||
columns: 0
|
||||
};
|
||||
options.data = {};
|
||||
|
||||
rendered = helpers.foreach.call(_this, context, options);
|
||||
rendered.should.equal(_this);
|
||||
});
|
||||
});
|
48
core/test/unit/server_helpers/ghost_foot_spec.js
Normal file
48
core/test/unit/server_helpers/ghost_foot_spec.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*globals describe, before, afterEach, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
rewire = require('rewire'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = rewire('../../../server/helpers');
|
||||
|
||||
describe('{{ghost_foot}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
utils.restoreConfig();
|
||||
helpers.__set__('utils.isProduction', false);
|
||||
});
|
||||
|
||||
it('has loaded ghost_foot helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_foot);
|
||||
});
|
||||
|
||||
it('outputs correct jquery for development mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.js\?v=abc"><\/script>/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('outputs correct jquery for production mode', function (done) {
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
helpers.__set__('utils.isProduction', true);
|
||||
|
||||
helpers.ghost_foot.call().then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.match(/<script src=".*\/public\/jquery.min.js\?v=abc"><\/script>/);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
144
core/test/unit/server_helpers/ghost_head_spec.js
Normal file
144
core/test/unit/server_helpers/ghost_head_spec.js
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*globals describe, before, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
moment = require('moment'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{ghost_head}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({
|
||||
url: 'http://testurl.com/',
|
||||
theme: {
|
||||
title: 'Ghost'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('has loaded ghost_head helper', function () {
|
||||
should.exist(handlebars.helpers.ghost_head);
|
||||
});
|
||||
|
||||
it('returns meta tag string', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', post: false}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\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 meta tag string even if version is invalid', function (done) {
|
||||
helpers.ghost_head.call({version: '0.9'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.9" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns open graph data on post page', function (done) {
|
||||
var post = {
|
||||
meta_description: 'blog description',
|
||||
title: 'Welcome to Ghost',
|
||||
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'}]
|
||||
};
|
||||
|
||||
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' +
|
||||
' <meta property="og:site_name" content="Ghost" />\n' +
|
||||
' <meta property="og:type" content="article" />\n' +
|
||||
' <meta property="og:title" content="Welcome to Ghost" />\n' +
|
||||
' <meta property="og:description" content="blog description..." />\n' +
|
||||
' <meta property="og:url" content="http://testurl.com/post/" />\n' +
|
||||
' <meta property="article:published_time" content="' + post.published_at + '" />\n' +
|
||||
' <meta property="article:modified_time" content="' + post.updated_at + '" />\n' +
|
||||
' <meta property="og:image" content="http://testurl.com/test-image.png" />\n' +
|
||||
' <meta property="article:tag" content="tag1" />\n' +
|
||||
' <meta property="article:tag" content="tag2" />\n' +
|
||||
' <meta property="article:tag" content="tag3" />\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 canonical URL', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/about/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/about/" />\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 next & prev URL correctly for middle page', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/page/3/', pagination: {next: '4', prev: '2'}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/page/3/" />\n' +
|
||||
' <link rel="prev" href="http://testurl.com/page/2/" />\n' +
|
||||
' <link rel="next" href="http://testurl.com/page/4/" />\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 next & prev URL correctly for second page', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0', relativeUrl: '/page/2/', pagination: {next: '3', prev: '1'}}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/page/2/" />\n' +
|
||||
' <link rel="prev" href="http://testurl.com/" />\n' +
|
||||
' <link rel="next" href="http://testurl.com/page/3/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/rss/" />');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({
|
||||
url: 'http://testurl.com/blog/',
|
||||
theme: {
|
||||
title: 'Ghost'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('returns correct rss url with subdirectory', function (done) {
|
||||
helpers.ghost_head.call({version: '0.3.0'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('<link rel="canonical" href="http://testurl.com/blog/" />\n' +
|
||||
' <meta name="generator" content="Ghost 0.3" />\n' +
|
||||
' <link rel="alternate" type="application/rss+xml" title="Ghost" href="/blog/rss/" />');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
79
core/test/unit/server_helpers/ghost_script_tags_spec.js
Normal file
79
core/test/unit/server_helpers/ghost_script_tags_spec.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*globals describe, before, after, afterEach, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
rewire = require('rewire'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
helpers = rewire('../../../server/helpers');
|
||||
|
||||
// ## Admin only helpers
|
||||
describe('ghost_script_tags helper', function () {
|
||||
var rendered;
|
||||
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({assetHash: 'abc'});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
helpers.__set__('utils.isProduction', false);
|
||||
});
|
||||
|
||||
it('has loaded ghostScriptTags helper', function () {
|
||||
should.exist(helpers.ghost_script_tags);
|
||||
});
|
||||
|
||||
it('outputs correct scripts for development mode', function () {
|
||||
rendered = helpers.ghost_script_tags();
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal(
|
||||
'<script src="/ghost/scripts/vendor-dev.js?v=abc"></script>' +
|
||||
'<script src="/ghost/scripts/templates-dev.js?v=abc"></script>' +
|
||||
'<script src="/ghost/scripts/ghost-dev.js?v=abc"></script>'
|
||||
);
|
||||
});
|
||||
|
||||
it('outputs correct scripts for production mode', function () {
|
||||
helpers.__set__('utils.isProduction', true);
|
||||
|
||||
rendered = helpers.ghost_script_tags();
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal(
|
||||
'<script src="/ghost/scripts/vendor.min.js?v=abc"></script>' +
|
||||
'<script src="/ghost/scripts/ghost.min.js?v=abc"></script>'
|
||||
);
|
||||
});
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({url: 'http://testurl.com/blog'});
|
||||
});
|
||||
|
||||
it('outputs correct scripts for development mode', function () {
|
||||
helpers.__set__('utils.isProduction', false);
|
||||
rendered = helpers.ghost_script_tags();
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal(
|
||||
'<script src="/blog/ghost/scripts/vendor-dev.js?v=abc"></script>' +
|
||||
'<script src="/blog/ghost/scripts/templates-dev.js?v=abc"></script>' +
|
||||
'<script src="/blog/ghost/scripts/ghost-dev.js?v=abc"></script>'
|
||||
);
|
||||
});
|
||||
|
||||
it('outputs correct scripts for production mode', function () {
|
||||
helpers.__set__('utils.isProduction', true);
|
||||
|
||||
rendered = helpers.ghost_script_tags();
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal(
|
||||
'<script src="/blog/ghost/scripts/vendor.min.js?v=abc"></script>' +
|
||||
'<script src="/blog/ghost/scripts/ghost.min.js?v=abc"></script>'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
163
core/test/unit/server_helpers/has_spec.js
Normal file
163
core/test/unit/server_helpers/has_spec.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{#has}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded has block helper', function () {
|
||||
should.exist(handlebars.helpers.has);
|
||||
});
|
||||
|
||||
it('should handle tag list that validates true', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{tags: [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{hash: {tag: 'invalid, bar, wat'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle tags with case-insensitivity', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{tags: [{name: 'ghost'}]},
|
||||
{hash: {tag: 'GhoSt'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle tag list that validates false', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{tags: [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{hash: {tag: 'much, such, wow'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.false;
|
||||
inverse.called.should.be.true;
|
||||
});
|
||||
|
||||
it('should not do anything if there are no attributes', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{tags: [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.false;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should not do anything when an invalid attribute is given', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{tags: [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{hash: {invalid: 'nonsense'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.false;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle author list that evaluates to true', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{author: {name: 'sam'}},
|
||||
{hash: {author: 'joe, sam, pat'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle author list that evaluates to false', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{author: {name: 'jamie'}},
|
||||
{hash: {author: 'joe, sam, pat'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.false;
|
||||
inverse.called.should.be.true;
|
||||
});
|
||||
|
||||
it('should handle authors with case-insensitivity', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{author: {name: 'Sam'}},
|
||||
{hash: {author: 'joe, sAm, pat'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle tags and authors like an OR query (pass)', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{author: {name: 'sam'}, tags: [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{hash: {author: 'joe, sam, pat', tag: 'much, such, wow'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle tags and authors like an OR query (pass)', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{author: {name: 'sam'}, tags: [{name: 'much'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{hash: {author: 'joe, sam, pat', tag: 'much, such, wow'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should handle tags and authors like an OR query (fail)', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.has.call(
|
||||
{author: {name: 'fred'}, tags: [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}]},
|
||||
{hash: {author: 'joe, sam, pat', tag: 'much, such, wow'}, fn: fn, inverse: inverse}
|
||||
);
|
||||
|
||||
fn.called.should.be.false;
|
||||
inverse.called.should.be.true;
|
||||
});
|
||||
});
|
63
core/test/unit/server_helpers/is_spec.js
Normal file
63
core/test/unit/server_helpers/is_spec.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{#is}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded is block helper', function () {
|
||||
should.exist(handlebars.helpers.is);
|
||||
});
|
||||
|
||||
// All positive tests
|
||||
it('should match single context "index"', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.is.call(
|
||||
{},
|
||||
'index',
|
||||
{fn: fn, inverse: inverse, data: {root: {context: ['home', 'index']}}}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should match OR context "index, paged"', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.is.call(
|
||||
{},
|
||||
'index, paged',
|
||||
{fn: fn, inverse: inverse, data: {root: {context: ['tag', 'paged']}}}
|
||||
);
|
||||
|
||||
fn.called.should.be.true;
|
||||
inverse.called.should.be.false;
|
||||
});
|
||||
|
||||
it('should not match "paged"', function () {
|
||||
var fn = sinon.spy(),
|
||||
inverse = sinon.spy();
|
||||
|
||||
helpers.is.call(
|
||||
{},
|
||||
'paged',
|
||||
{fn: fn, inverse: inverse, data: {root: {context: ['index', 'home']}}}
|
||||
);
|
||||
|
||||
fn.called.should.be.false;
|
||||
inverse.called.should.be.true;
|
||||
});
|
||||
});
|
106
core/test/unit/server_helpers/meta_description_spec.js
Normal file
106
core/test/unit/server_helpers/meta_description_spec.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*globals describe, before, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{meta_description}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({
|
||||
theme: {
|
||||
description: 'Just a blogging platform.'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('has loaded meta_description helper', function () {
|
||||
should.exist(handlebars.helpers.meta_description);
|
||||
});
|
||||
|
||||
it('returns correct blog description', function (done) {
|
||||
helpers.meta_description.call({relativeUrl: '/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Just a blogging platform.');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns empty description on paginated page', function (done) {
|
||||
helpers.meta_description.call({relativeUrl: '/page/2/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns empty description for a tag page', function (done) {
|
||||
var tag = {relativeUrl: '/tag/rasper-red', tag: {name: 'Rasper Red'}};
|
||||
helpers.meta_description.call(tag).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns empty description for a paginated tag page', function (done) {
|
||||
var tag = {relativeUrl: '/tag/rasper-red/page/2/', tag: {name: 'Rasper Red'}};
|
||||
helpers.meta_description.call(tag).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct description for an author page', function (done) {
|
||||
var author = {relativeUrl: '/author/donald', author: {bio: 'I am a Duck.'}};
|
||||
helpers.meta_description.call(author).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('I am a Duck.');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns empty description for a paginated author page', function (done) {
|
||||
var author = {relativeUrl: '/author/donald/page/2/', author: {name: 'Donald Duck'}};
|
||||
helpers.meta_description.call(author).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns empty description when meta_description is not set', function (done) {
|
||||
var post = {relativeUrl: '/nice-post', post: {title: 'Post Title', html: 'Very nice post indeed.'}};
|
||||
helpers.meta_description.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns meta_description on post with meta_description set', function (done) {
|
||||
var post = {relativeUrl: '/nice-post', post: {title: 'Post Title', meta_description: 'Nice post about stuff.'}};
|
||||
helpers.meta_description.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Nice post about stuff.');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
116
core/test/unit/server_helpers/meta_title_spec.js
Normal file
116
core/test/unit/server_helpers/meta_title_spec.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*globals describe, before, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{meta_title}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
utils.overrideConfig({
|
||||
theme: {
|
||||
title: 'Ghost'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('has loaded meta_title helper', function () {
|
||||
should.exist(handlebars.helpers.meta_title);
|
||||
});
|
||||
|
||||
it('returns correct title for homepage', function (done) {
|
||||
helpers.meta_title.call({relativeUrl: '/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Ghost');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for paginated page', function (done) {
|
||||
helpers.meta_title.call({relativeUrl: '/page/2/'}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Ghost - Page 2');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for a post', function (done) {
|
||||
var post = {relativeUrl: '/nice-post', post: {title: 'Post Title'}};
|
||||
helpers.meta_title.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Post Title');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for a post with meta_title set', function (done) {
|
||||
var post = {relativeUrl: '/nice-post', post: {title: 'Post Title', meta_title: 'Awesome Post'}};
|
||||
helpers.meta_title.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Awesome Post');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for a tag page', function (done) {
|
||||
var tag = {relativeUrl: '/tag/rasper-red', tag: {name: 'Rasper Red'}};
|
||||
helpers.meta_title.call(tag).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Rasper Red - Ghost');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for a paginated tag page', function (done) {
|
||||
var tag = {relativeUrl: '/tag/rasper-red/page/2/', tag: {name: 'Rasper Red'}};
|
||||
helpers.meta_title.call(tag).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Rasper Red - Page 2 - Ghost');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for an author page', function (done) {
|
||||
var author = {relativeUrl: '/author/donald', author: {name: 'Donald Duck'}};
|
||||
helpers.meta_title.call(author).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Donald Duck - Ghost');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correct title for a paginated author page', function (done) {
|
||||
var author = {relativeUrl: '/author/donald/page/2/', author: {name: 'Donald Duck'}};
|
||||
helpers.meta_title.call(author).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Donald Duck - Page 2 - Ghost');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('returns correctly escaped title of a post', function (done) {
|
||||
var post = {relativeUrl: '/nice-escaped-post', post: {title: 'Post Title "</>'}};
|
||||
helpers.meta_title.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
String(rendered).should.equal('Post Title "</>');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
145
core/test/unit/server_helpers/page_url_spec.js
Normal file
145
core/test/unit/server_helpers/page_url_spec.js
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*globals describe, before, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{page_url}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded page_url helper', function () {
|
||||
should.exist(handlebars.helpers.page_url);
|
||||
});
|
||||
|
||||
it('can return a valid url', function () {
|
||||
helpers.page_url(1).should.equal('/');
|
||||
helpers.page_url(2).should.equal('/page/2/');
|
||||
helpers.page_url(50).should.equal('/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages', function () {
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.page_url.call(tagContext, 1).should.equal('/tag/pumpkin/');
|
||||
helpers.page_url.call(tagContext, 2).should.equal('/tag/pumpkin/page/2/');
|
||||
helpers.page_url.call(tagContext, 50).should.equal('/tag/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for author pages', function () {
|
||||
var authorContext = {
|
||||
authorSlug: 'pumpkin'
|
||||
};
|
||||
helpers.page_url.call(authorContext, 1).should.equal('/author/pumpkin/');
|
||||
helpers.page_url.call(authorContext, 2).should.equal('/author/pumpkin/page/2/');
|
||||
helpers.page_url.call(authorContext, 50).should.equal('/author/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({url: 'http://testurl.com/blog'});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('can return a valid url with subdirectory', function () {
|
||||
helpers.page_url(1).should.equal('/blog/');
|
||||
helpers.page_url(2).should.equal('/blog/page/2/');
|
||||
helpers.page_url(50).should.equal('/blog/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages with subdirectory', function () {
|
||||
var authorContext = {
|
||||
authorSlug: 'pumpkin'
|
||||
};
|
||||
helpers.page_url.call(authorContext, 1).should.equal('/blog/author/pumpkin/');
|
||||
helpers.page_url.call(authorContext, 2).should.equal('/blog/author/pumpkin/page/2/');
|
||||
helpers.page_url.call(authorContext, 50).should.equal('/blog/author/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages with subdirectory', function () {
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.page_url.call(tagContext, 1).should.equal('/blog/tag/pumpkin/');
|
||||
helpers.page_url.call(tagContext, 2).should.equal('/blog/tag/pumpkin/page/2/');
|
||||
helpers.page_url.call(tagContext, 50).should.equal('/blog/tag/pumpkin/page/50/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('{{pageUrl}} helper [DEPRECATED]', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded pageUrl helper', function () {
|
||||
should.exist(handlebars.helpers.pageUrl);
|
||||
});
|
||||
|
||||
it('can return a valid url', function () {
|
||||
helpers.pageUrl(1).should.equal('/');
|
||||
helpers.pageUrl(2).should.equal('/page/2/');
|
||||
helpers.pageUrl(50).should.equal('/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for author pages', function () {
|
||||
var authorContext = {
|
||||
authorSlug: 'pumpkin'
|
||||
};
|
||||
helpers.pageUrl.call(authorContext, 1).should.equal('/author/pumpkin/');
|
||||
helpers.pageUrl.call(authorContext, 2).should.equal('/author/pumpkin/page/2/');
|
||||
helpers.pageUrl.call(authorContext, 50).should.equal('/author/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages', function () {
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.pageUrl.call(tagContext, 1).should.equal('/tag/pumpkin/');
|
||||
helpers.pageUrl.call(tagContext, 2).should.equal('/tag/pumpkin/page/2/');
|
||||
helpers.pageUrl.call(tagContext, 50).should.equal('/tag/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
describe('with /blog subdirectory', function () {
|
||||
before(function () {
|
||||
utils.overrideConfig({url: 'http://testurl.com/blog'});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('can return a valid url with subdirectory', function () {
|
||||
helpers.pageUrl(1).should.equal('/blog/');
|
||||
helpers.pageUrl(2).should.equal('/blog/page/2/');
|
||||
helpers.pageUrl(50).should.equal('/blog/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages with subdirectory', function () {
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.pageUrl.call(tagContext, 1).should.equal('/blog/tag/pumpkin/');
|
||||
helpers.pageUrl.call(tagContext, 2).should.equal('/blog/tag/pumpkin/page/2/');
|
||||
helpers.pageUrl.call(tagContext, 50).should.equal('/blog/tag/pumpkin/page/50/');
|
||||
});
|
||||
|
||||
it('can return a valid url for tag pages with subdirectory', function () {
|
||||
var tagContext = {
|
||||
tagSlug: 'pumpkin'
|
||||
};
|
||||
helpers.pageUrl.call(tagContext, 1).should.equal('/blog/tag/pumpkin/');
|
||||
helpers.pageUrl.call(tagContext, 2).should.equal('/blog/tag/pumpkin/page/2/');
|
||||
helpers.pageUrl.call(tagContext, 50).should.equal('/blog/tag/pumpkin/page/50/');
|
||||
});
|
||||
});
|
||||
});
|
123
core/test/unit/server_helpers/pagination_spec.js
Normal file
123
core/test/unit/server_helpers/pagination_spec.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{pagination}} helper', function () {
|
||||
before(function (done) {
|
||||
utils.loadHelpers();
|
||||
hbs.express3({partialsDir: [utils.config.paths.helperTemplates]});
|
||||
hbs.cachePartials(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
var paginationRegex = /class="pagination"/,
|
||||
newerRegex = /class="newer-posts"/,
|
||||
olderRegex = /class="older-posts"/,
|
||||
pageRegex = /class="page-number"/;
|
||||
|
||||
it('has loaded pagination helper', function () {
|
||||
should.exist(handlebars.helpers.pagination);
|
||||
});
|
||||
|
||||
it('should throw if pagination data is incorrect', function () {
|
||||
var runHelper = function (data) {
|
||||
return function () {
|
||||
helpers.pagination.call(data);
|
||||
};
|
||||
};
|
||||
|
||||
runHelper('not an object').should.throwError('pagination data is not an object or is a function');
|
||||
runHelper(function () {}).should.throwError('pagination data is not an object or is a function');
|
||||
});
|
||||
|
||||
it('can render single page with no pagination necessary', function () {
|
||||
var rendered = helpers.pagination.call({
|
||||
pagination: {page: 1, prev: null, next: null, limit: 15, total: 8, pages: 1},
|
||||
tag: {slug: 'slug'}
|
||||
});
|
||||
should.exist(rendered);
|
||||
// strip out carriage returns and compare.
|
||||
rendered.string.should.match(paginationRegex);
|
||||
rendered.string.should.match(pageRegex);
|
||||
rendered.string.should.match(/Page 1 of 1/);
|
||||
rendered.string.should.not.match(newerRegex);
|
||||
rendered.string.should.not.match(olderRegex);
|
||||
});
|
||||
|
||||
it('can render first page of many with older posts link', function () {
|
||||
var rendered = helpers.pagination.call({
|
||||
pagination: {page: 1, prev: null, next: 2, limit: 15, total: 8, pages: 3}
|
||||
});
|
||||
should.exist(rendered);
|
||||
|
||||
rendered.string.should.match(paginationRegex);
|
||||
rendered.string.should.match(pageRegex);
|
||||
rendered.string.should.match(olderRegex);
|
||||
rendered.string.should.match(/Page 1 of 3/);
|
||||
rendered.string.should.not.match(newerRegex);
|
||||
});
|
||||
|
||||
it('can render middle pages of many with older and newer posts link', function () {
|
||||
var rendered = helpers.pagination.call({
|
||||
pagination: {page: 2, prev: 1, next: 3, limit: 15, total: 8, pages: 3}
|
||||
});
|
||||
should.exist(rendered);
|
||||
|
||||
rendered.string.should.match(paginationRegex);
|
||||
rendered.string.should.match(pageRegex);
|
||||
rendered.string.should.match(olderRegex);
|
||||
rendered.string.should.match(newerRegex);
|
||||
rendered.string.should.match(/Page 2 of 3/);
|
||||
});
|
||||
|
||||
it('can render last page of many with newer posts link', function () {
|
||||
var rendered = helpers.pagination.call({
|
||||
pagination: {page: 3, prev: 2, next: null, limit: 15, total: 8, pages: 3}
|
||||
});
|
||||
should.exist(rendered);
|
||||
|
||||
rendered.string.should.match(paginationRegex);
|
||||
rendered.string.should.match(pageRegex);
|
||||
rendered.string.should.match(newerRegex);
|
||||
rendered.string.should.match(/Page 3 of 3/);
|
||||
rendered.string.should.not.match(olderRegex);
|
||||
});
|
||||
|
||||
it('validates values', function () {
|
||||
var runErrorTest = function (data) {
|
||||
return function () {
|
||||
helpers.pagination.call(data);
|
||||
};
|
||||
};
|
||||
|
||||
runErrorTest({pagination: {page: 3, prev: true, next: null, limit: 15, total: 8, pages: 3}})
|
||||
.should.throwError('Invalid value, Next/Prev must be a number');
|
||||
runErrorTest({pagination: {page: 3, prev: 2, next: true, limit: 15, total: 8, pages: 3}})
|
||||
.should.throwError('Invalid value, Next/Prev must be a number');
|
||||
|
||||
runErrorTest({pagination: {limit: 15, total: 8, pages: 3}})
|
||||
.should.throwError('All values must be defined for page, pages, limit and total');
|
||||
runErrorTest({pagination: {page: 3, total: 8, pages: 3}})
|
||||
.should.throwError('All values must be defined for page, pages, limit and total');
|
||||
runErrorTest({pagination: {page: 3, limit: 15, pages: 3}})
|
||||
.should.throwError('All values must be defined for page, pages, limit and total');
|
||||
runErrorTest({pagination: {page: 3, limit: 15, total: 8}})
|
||||
.should.throwError('All values must be defined for page, pages, limit and total');
|
||||
|
||||
runErrorTest({pagination: {page: null, prev: null, next: null, limit: 15, total: 8, pages: 3}})
|
||||
.should.throwError('Invalid value, check page, pages, limit and total are numbers');
|
||||
runErrorTest({pagination: {page: 1, prev: null, next: null, limit: null, total: 8, pages: 3}})
|
||||
.should.throwError('Invalid value, check page, pages, limit and total are numbers');
|
||||
runErrorTest({pagination: {page: 1, prev: null, next: null, limit: 15, total: null, pages: 3}})
|
||||
.should.throwError('Invalid value, check page, pages, limit and total are numbers');
|
||||
runErrorTest({pagination: {page: 1, prev: null, next: null, limit: 15, total: 8, pages: null}})
|
||||
.should.throwError('Invalid value, check page, pages, limit and total are numbers');
|
||||
});
|
||||
});
|
61
core/test/unit/server_helpers/plural_spec.js
Normal file
61
core/test/unit/server_helpers/plural_spec.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{plural}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded plural helper', function () {
|
||||
should.exist(handlebars.helpers.plural);
|
||||
});
|
||||
|
||||
it('will show no-value string', function () {
|
||||
var expected = 'No Posts',
|
||||
rendered = helpers.plural.call({}, 0, {
|
||||
hash: {
|
||||
empty: 'No Posts',
|
||||
singular: '% Post',
|
||||
plural: '% Posts'
|
||||
}
|
||||
});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
|
||||
it('will show singular string', function () {
|
||||
var expected = '1 Post',
|
||||
rendered = helpers.plural.call({}, 1, {
|
||||
hash: {
|
||||
empty: 'No Posts',
|
||||
singular: '% Post',
|
||||
plural: '% Posts'
|
||||
}
|
||||
});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
|
||||
it('will show plural string', function () {
|
||||
var expected = '2 Posts',
|
||||
rendered = helpers.plural.call({}, 2, {
|
||||
hash: {
|
||||
empty: 'No Posts',
|
||||
singular: '% Post',
|
||||
plural: '% Posts'
|
||||
}
|
||||
});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(expected);
|
||||
});
|
||||
});
|
49
core/test/unit/server_helpers/post_class_spec.js
Normal file
49
core/test/unit/server_helpers/post_class_spec.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{post_class}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded postclass helper', function () {
|
||||
should.exist(handlebars.helpers.post_class);
|
||||
});
|
||||
|
||||
it('can render class string', function (done) {
|
||||
helpers.post_class.call({}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('post');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render featured class', function (done) {
|
||||
var post = {featured: true};
|
||||
|
||||
helpers.post_class.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('post featured');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can render page class', function (done) {
|
||||
var post = {page: true};
|
||||
|
||||
helpers.post_class.call(post).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal('post page');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
112
core/test/unit/server_helpers/tags_spec.js
Normal file
112
core/test/unit/server_helpers/tags_spec.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
rewire = require('rewire'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = rewire('../../../server/helpers');
|
||||
|
||||
describe('{{tags}} helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded tags helper', function () {
|
||||
should.exist(handlebars.helpers.tags);
|
||||
});
|
||||
|
||||
it('can return string with tags', function () {
|
||||
var tags = [{name: 'foo'}, {name: 'bar'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {autolink: 'false'}}
|
||||
);
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('foo, bar');
|
||||
});
|
||||
|
||||
it('can use a different separator', function () {
|
||||
var tags = [{name: 'haunted'}, {name: 'ghost'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {separator: '|', autolink: 'false'}}
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('haunted|ghost');
|
||||
});
|
||||
|
||||
it('can add a single prefix to multiple tags', function () {
|
||||
var tags = [{name: 'haunted'}, {name: 'ghost'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {prefix: 'on ', autolink: 'false'}}
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('on haunted, ghost');
|
||||
});
|
||||
|
||||
it('can add a single suffix to multiple tags', function () {
|
||||
var tags = [{name: 'haunted'}, {name: 'ghost'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {suffix: ' forever', autolink: 'false'}}
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('haunted, ghost forever');
|
||||
});
|
||||
|
||||
it('can add a prefix and suffix to multiple tags', function () {
|
||||
var tags = [{name: 'haunted'}, {name: 'ghost'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {suffix: ' forever', prefix: 'on ', autolink: 'false'}}
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('on haunted, ghost forever');
|
||||
});
|
||||
|
||||
it('can add a prefix and suffix with HTML', function () {
|
||||
var tags = [{name: 'haunted'}, {name: 'ghost'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags},
|
||||
{hash: {suffix: ' •', prefix: '… ', autolink: 'false'}}
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('… haunted, ghost •');
|
||||
});
|
||||
|
||||
it('does not add prefix or suffix if no tags exist', function () {
|
||||
var rendered = helpers.tags.call(
|
||||
{},
|
||||
{hash: {prefix: 'on ', suffix: ' forever', autolink: 'false'}}
|
||||
);
|
||||
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('');
|
||||
});
|
||||
|
||||
it('can autolink tags to tag pages', function () {
|
||||
var tags = [{name: 'foo', slug: 'foo-bar'}, {name: 'bar', slug: 'bar'}],
|
||||
rendered = helpers.tags.call(
|
||||
{tags: tags}
|
||||
);
|
||||
should.exist(rendered);
|
||||
|
||||
String(rendered).should.equal('<a href="/tag/foo-bar/">foo</a>, <a href="/tag/bar/">bar</a>');
|
||||
});
|
||||
});
|
45
core/test/unit/server_helpers/title_spec.js
Normal file
45
core/test/unit/server_helpers/title_spec.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*globals describe, before, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers');
|
||||
|
||||
describe('{{title}} Helper', function () {
|
||||
before(function () {
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
it('has loaded title helper', function () {
|
||||
should.exist(handlebars.helpers.title);
|
||||
});
|
||||
|
||||
it('can render title', function () {
|
||||
var title = 'Hello World',
|
||||
rendered = helpers.title.call({title: title});
|
||||
|
||||
should.exist(rendered);
|
||||
rendered.string.should.equal(title);
|
||||
});
|
||||
|
||||
it('escapes correctly', function () {
|
||||
var rendered = helpers.title.call({title: '<h1>I am a title</h1>'});
|
||||
|
||||
rendered.string.should.equal('<h1>I am a title</h1>');
|
||||
});
|
||||
|
||||
it('returns a blank string where title is missing', function () {
|
||||
var rendered = helpers.title.call({title: null});
|
||||
|
||||
rendered.string.should.equal('');
|
||||
});
|
||||
|
||||
it('returns a blank string where data missing', function () {
|
||||
var rendered = helpers.title.call({});
|
||||
|
||||
rendered.string.should.equal('');
|
||||
});
|
||||
});
|
98
core/test/unit/server_helpers/url_spec.js
Normal file
98
core/test/unit/server_helpers/url_spec.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*globals describe, before, beforeEach, afterEach, after, it*/
|
||||
/*jshint expr:true*/
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
Promise = require('bluebird'),
|
||||
hbs = require('express-hbs'),
|
||||
utils = require('./utils'),
|
||||
|
||||
// Stuff we are testing
|
||||
handlebars = hbs.handlebars,
|
||||
helpers = require('../../../server/helpers'),
|
||||
api = require('../../../server/api');
|
||||
|
||||
describe('{{url}} helper', function () {
|
||||
var sandbox;
|
||||
|
||||
before(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
utils.overrideConfig({url: 'http://testurl.com/'});
|
||||
utils.loadHelpers();
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(api.settings, 'read', function () {
|
||||
return Promise.resolve({settings: [{value: '/:slug/'}]});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
utils.restoreConfig();
|
||||
});
|
||||
|
||||
it('has loaded url helper', function () {
|
||||
should.exist(handlebars.helpers.url);
|
||||
});
|
||||
|
||||
it('should return the slug with a prefix slash if the context is a post', function (done) {
|
||||
helpers.url.call({
|
||||
html: 'content',
|
||||
markdown: 'ff',
|
||||
title: 'title',
|
||||
slug: 'slug',
|
||||
created_at: new Date(0)
|
||||
}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.should.equal('/slug/');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should output an absolute URL if the option is present', function (done) {
|
||||
helpers.url.call(
|
||||
{html: 'content', markdown: 'ff', title: 'title', slug: 'slug', created_at: new Date(0)},
|
||||
{hash: {absolute: 'true'}}
|
||||
).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.should.equal('http://testurl.com/slug/');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should return the slug with a prefixed /tag/ if the context is a tag', function (done) {
|
||||
helpers.url.call({
|
||||
name: 'the tag',
|
||||
slug: 'the-tag',
|
||||
description: null,
|
||||
parent: null
|
||||
}).then(function (rendered) {
|
||||
should.exist(rendered);
|
||||
rendered.should.equal('/tag/the-tag/');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should return / if not a post or tag', function (done) {
|
||||
helpers.url.call({markdown: 'ff', title: 'title', slug: 'slug'}).then(function (rendered) {
|
||||
rendered.should.equal('/');
|
||||
}).then(function () {
|
||||
return helpers.url.call({html: 'content', title: 'title', slug: 'slug'}).then(function (rendered) {
|
||||
rendered.should.equal('/');
|
||||
});
|
||||
}).then(function () {
|
||||
return helpers.url.call({html: 'content', markdown: 'ff', slug: 'slug'}).then(function (rendered) {
|
||||
rendered.should.equal('/');
|
||||
});
|
||||
}).then(function () {
|
||||
helpers.url.call({html: 'content', markdown: 'ff', title: 'title'}).then(function (rendered) {
|
||||
rendered.should.equal('/');
|
||||
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
32
core/test/unit/server_helpers/utils.js
Normal file
32
core/test/unit/server_helpers/utils.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// # Helper Test Utils
|
||||
//
|
||||
// Contains shared code for intialising tests
|
||||
//
|
||||
// @TODO refactor this file out of existence
|
||||
// I believe if we refactor the handlebars instances and helpers to be more self-contained and modular
|
||||
// We can likely have init functions which replace the need for this file
|
||||
|
||||
var hbs = require('express-hbs'),
|
||||
_ = require('lodash'),
|
||||
|
||||
// Stuff we are testing
|
||||
helpers = require('../../../server/helpers'),
|
||||
config = require('../../../server/config'),
|
||||
origConfig = _.cloneDeep(config.get()),
|
||||
utils = {};
|
||||
|
||||
utils.loadHelpers = function () {
|
||||
var adminHbs = hbs.create();
|
||||
helpers.loadCoreHelpers(adminHbs);
|
||||
};
|
||||
|
||||
utils.overrideConfig = function (newConfig) {
|
||||
config.set(newConfig);
|
||||
};
|
||||
|
||||
utils.restoreConfig = function () {
|
||||
config.set(origConfig);
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
module.exports.config = config;
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue