2013-12-31 03:13:25 +04:00
|
|
|
var downsize = require('downsize'),
|
|
|
|
hbs = require('express-hbs'),
|
2013-09-15 01:34:12 +04:00
|
|
|
moment = require('moment'),
|
2013-11-17 22:40:26 +04:00
|
|
|
path = require('path'),
|
2013-11-28 06:45:01 +04:00
|
|
|
polyglot = require('node-polyglot').instance,
|
2013-12-31 03:13:25 +04:00
|
|
|
_ = require('underscore'),
|
|
|
|
when = require('when'),
|
|
|
|
|
|
|
|
api = require('../api'),
|
|
|
|
config = require('../config'),
|
2013-09-15 01:34:12 +04:00
|
|
|
errors = require('../errorHandling'),
|
2013-11-28 06:45:01 +04:00
|
|
|
filters = require('../filters'),
|
2013-12-31 03:13:25 +04:00
|
|
|
template = require('./template'),
|
2014-01-03 04:37:21 +04:00
|
|
|
schema = require('../data/schema').checks,
|
2014-01-14 23:46:36 +04:00
|
|
|
updateCheck = require('../update-check'),
|
2013-12-31 03:13:25 +04:00
|
|
|
|
|
|
|
assetTemplate = _.template('<%= source %>?v=<%= version %>'),
|
2013-12-23 19:31:36 +04:00
|
|
|
scriptTemplate = _.template('<script src="<%= source %>?v=<%= version %>"></script>'),
|
2013-09-15 01:34:12 +04:00
|
|
|
isProduction = process.env.NODE_ENV === 'production',
|
2013-12-31 03:13:25 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers = {},
|
2014-01-15 03:14:44 +04:00
|
|
|
registerHelpers,
|
2013-12-02 03:31:55 +04:00
|
|
|
|
2014-01-15 03:14:44 +04:00
|
|
|
scriptFiles = {
|
|
|
|
production: [
|
|
|
|
'ghost.min.js'
|
|
|
|
],
|
|
|
|
development: [
|
|
|
|
'vendor.js',
|
|
|
|
'helpers.js',
|
|
|
|
'templates.js',
|
|
|
|
'models.js',
|
|
|
|
'views.js'
|
|
|
|
]
|
|
|
|
};
|
2013-12-02 03:31:55 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
/**
|
|
|
|
* [ description]
|
|
|
|
* @todo ghost core helpers + a way for themes to register them
|
|
|
|
* @param {Object} context date object
|
|
|
|
* @param {*} options
|
|
|
|
* @return {Object} A Moment time / date object
|
|
|
|
*/
|
|
|
|
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;
|
2013-08-26 23:44:19 +04:00
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
}
|
2013-09-06 20:07:25 +04:00
|
|
|
|
2013-12-22 01:43:38 +04:00
|
|
|
// ensure that context is undefined, not null, as that can cause errors
|
|
|
|
context = context === null ? undefined : context;
|
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
var f = options.hash.format || 'MMM Do, YYYY',
|
|
|
|
timeago = options.hash.timeago,
|
|
|
|
date;
|
2013-08-23 13:13:32 +04:00
|
|
|
|
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
if (timeago) {
|
|
|
|
date = moment(context).fromNow();
|
|
|
|
} else {
|
|
|
|
date = moment(context).format(f);
|
|
|
|
}
|
|
|
|
return date;
|
|
|
|
};
|
2013-10-23 00:48:27 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
//
|
|
|
|
// ### URI Encoding helper
|
2013-11-03 06:28:08 +04:00
|
|
|
//
|
2013-09-15 01:34:12 +04:00
|
|
|
// *Usage example:*
|
|
|
|
// `{{encode uri}}`
|
|
|
|
//
|
|
|
|
// Returns URI encoded string
|
|
|
|
//
|
|
|
|
coreHelpers.encode = function (context, str) {
|
|
|
|
var uri = context || str;
|
|
|
|
return new hbs.handlebars.SafeString(encodeURIComponent(uri));
|
|
|
|
};
|
2013-08-21 16:55:58 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### Page URL Helper
|
2013-11-03 06:28:08 +04:00
|
|
|
//
|
2013-09-15 01:34:12 +04:00
|
|
|
// *Usage example:*
|
|
|
|
// `{{pageUrl 2}}`
|
2013-11-03 06:28:08 +04:00
|
|
|
//
|
2013-09-15 01:34:12 +04:00
|
|
|
// Returns the URL for the page specified in the current object
|
|
|
|
// context.
|
|
|
|
//
|
|
|
|
coreHelpers.pageUrl = function (context, block) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2014-01-03 04:37:21 +04:00
|
|
|
return config.paths().subdir + (context === 1 ? '/' : ('/page/' + context + '/'));
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-08-07 13:45:37 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### URL helper
|
|
|
|
//
|
|
|
|
// *Usage example:*
|
|
|
|
// `{{url}}`
|
|
|
|
// `{{url absolute}}`
|
|
|
|
//
|
|
|
|
// 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) {
|
2014-01-03 04:37:21 +04:00
|
|
|
var absolute = options && options.hash.absolute;
|
|
|
|
|
|
|
|
if (schema.isPost(this)) {
|
|
|
|
return config.paths.urlForPost(api.settings, this, absolute);
|
|
|
|
}
|
|
|
|
|
|
|
|
return when(config.paths.urlFor(this, absolute));
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-07-11 20:21:42 +04:00
|
|
|
|
2013-11-27 06:00:55 +04:00
|
|
|
// ### Asset helper
|
|
|
|
//
|
|
|
|
// *Usage example:*
|
2013-11-29 22:44:03 +04:00
|
|
|
// `{{asset "css/screen.css"}}`
|
|
|
|
// `{{asset "css/screen.css" ghost="true"}}`
|
2013-11-27 06:00:55 +04:00
|
|
|
// 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;
|
|
|
|
|
2013-12-28 20:01:08 +04:00
|
|
|
output += config.paths().subdir + '/';
|
2013-11-27 06:00:55 +04:00
|
|
|
|
2014-01-13 01:48:34 +04:00
|
|
|
if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) {
|
2013-12-27 19:12:24 +04:00
|
|
|
if (isAdmin) {
|
|
|
|
output += 'ghost/';
|
|
|
|
} else {
|
|
|
|
output += 'assets/';
|
|
|
|
}
|
2013-11-27 06:00:55 +04:00
|
|
|
}
|
|
|
|
|
2014-01-13 01:48:34 +04:00
|
|
|
// Get rid of any leading slash on the context
|
|
|
|
context = context.replace(/^\//, '');
|
2013-11-27 06:00:55 +04:00
|
|
|
output += context;
|
2013-12-31 03:13:25 +04:00
|
|
|
|
|
|
|
if (!context.match(/^favicon\.ico$/)) {
|
|
|
|
output = assetTemplate({
|
|
|
|
source: output,
|
|
|
|
version: coreHelpers.assetHash
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-27 06:00:55 +04:00
|
|
|
return new hbs.handlebars.SafeString(output);
|
|
|
|
};
|
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### 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) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-09-15 01:34:12 +04:00
|
|
|
return this.author ? this.author.name : '';
|
|
|
|
};
|
2013-07-11 20:21:42 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### 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) {
|
|
|
|
var separator = _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
|
|
|
prefix = _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
|
|
|
suffix = _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
|
|
|
output = '',
|
|
|
|
tagNames = _.pluck(this.tags, 'name');
|
|
|
|
|
|
|
|
if (tagNames.length) {
|
|
|
|
output = prefix + tagNames.join(separator) + suffix;
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
};
|
2013-08-22 18:51:30 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### Content Helper
|
|
|
|
//
|
|
|
|
// *Usage example:*
|
|
|
|
// `{{content}}`
|
2013-11-29 22:44:03 +04:00
|
|
|
// `{{content words="20"}}`
|
|
|
|
// `{{content characters="256"}}`
|
2013-09-15 01:34:12 +04:00
|
|
|
//
|
|
|
|
// 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']);
|
2013-11-29 22:44:03 +04:00
|
|
|
_.keys(truncateOptions).map(function (key) {
|
|
|
|
truncateOptions[key] = parseInt(truncateOptions[key], 10);
|
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2014-01-04 09:30:52 +04:00
|
|
|
if (truncateOptions.hasOwnProperty('words') || truncateOptions.hasOwnProperty('characters')) {
|
|
|
|
// Due to weirdness in downsize the 'words' option
|
|
|
|
// must be passed as a string. refer to #1796
|
|
|
|
// TODO: when downsize fixes this quirk remove this hack.
|
|
|
|
if (truncateOptions.hasOwnProperty('words')) {
|
|
|
|
truncateOptions.words = truncateOptions.words.toString();
|
|
|
|
}
|
2013-08-22 18:51:30 +04:00
|
|
|
return new hbs.handlebars.SafeString(
|
2013-09-15 01:34:12 +04:00
|
|
|
downsize(this.html, truncateOptions)
|
2013-08-22 18:51:30 +04:00
|
|
|
);
|
2013-09-15 01:34:12 +04:00
|
|
|
}
|
2013-08-22 18:51:30 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
return new hbs.handlebars.SafeString(this.html);
|
|
|
|
};
|
2013-08-22 23:57:53 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### Excerpt Helper
|
|
|
|
//
|
|
|
|
// *Usage example:*
|
|
|
|
// `{{excerpt}}`
|
2013-11-29 22:44:03 +04:00
|
|
|
// `{{excerpt words="50"}}`
|
|
|
|
// `{{excerpt characters="256"}}`
|
2013-09-15 01:34:12 +04:00
|
|
|
//
|
|
|
|
// Attempts to remove all HTML from the string, and then shortens the result according to the provided option.
|
|
|
|
//
|
2013-11-29 22:44:03 +04:00
|
|
|
// Defaults to words="50"
|
2013-09-15 01:34:12 +04:00
|
|
|
//
|
|
|
|
// **returns** SafeString truncated, HTML-free content.
|
|
|
|
//
|
|
|
|
coreHelpers.excerpt = function (options) {
|
|
|
|
var truncateOptions = (options || {}).hash || {},
|
|
|
|
excerpt;
|
|
|
|
|
|
|
|
truncateOptions = _.pick(truncateOptions, ['words', 'characters']);
|
2013-11-29 22:44:03 +04:00
|
|
|
_.keys(truncateOptions).map(function (key) {
|
|
|
|
truncateOptions[key] = parseInt(truncateOptions[key], 10);
|
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
|
|
|
|
/*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)
|
|
|
|
);
|
|
|
|
};
|
2013-10-09 22:51:55 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### Filestorage helper
|
|
|
|
//
|
|
|
|
// *Usage example:*
|
|
|
|
// `{{fileStorage}}`
|
|
|
|
//
|
|
|
|
// Returns the config value for fileStorage.
|
|
|
|
coreHelpers.fileStorage = function (context, options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-12-14 20:28:54 +04:00
|
|
|
if (config().hasOwnProperty('fileStorage')) {
|
|
|
|
return config().fileStorage.toString();
|
2013-09-15 01:34:12 +04:00
|
|
|
}
|
|
|
|
return "true";
|
|
|
|
};
|
2013-08-22 23:57:53 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.ghostScriptTags = function () {
|
2014-01-15 03:14:44 +04:00
|
|
|
var scriptList = isProduction ? scriptFiles.production : scriptFiles.development;
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2014-01-15 03:14:44 +04:00
|
|
|
scriptList = _.map(scriptList, function (fileName) {
|
2013-09-15 01:34:12 +04:00
|
|
|
return scriptTemplate({
|
2013-12-31 03:13:25 +04:00
|
|
|
source: config.paths().subdir + '/ghost/scripts/' + fileName,
|
|
|
|
version: coreHelpers.assetHash
|
2013-09-15 01:34:12 +04:00
|
|
|
});
|
|
|
|
});
|
2013-10-09 22:51:55 +04:00
|
|
|
|
2014-01-15 03:14:44 +04:00
|
|
|
return scriptList.join('');
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-10-17 09:21:56 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
/*
|
2013-11-28 06:45:01 +04:00
|
|
|
* Asynchronous Theme Helpers (Registered with registerAsyncThemeHelper)
|
2013-09-15 01:34:12 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
coreHelpers.body_class = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-09-15 01:34:12 +04:00
|
|
|
var classes = [],
|
|
|
|
tags = this.post && this.post.tags ? this.post.tags : this.tags || [],
|
|
|
|
page = this.post && this.post.page ? this.post.page : this.page || false;
|
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
if (_.isString(this.relativeUrl) && this.relativeUrl.match(/\/page/)) {
|
2013-09-15 01:34:12 +04:00
|
|
|
classes.push('archive-template');
|
2014-01-03 04:37:21 +04:00
|
|
|
} else if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '') {
|
2013-09-15 01:34:12 +04:00
|
|
|
classes.push('home-template');
|
|
|
|
} else {
|
|
|
|
classes.push('post-template');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tags) {
|
|
|
|
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page) {
|
|
|
|
classes.push('page');
|
|
|
|
}
|
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
return filters.doFilter('body_class', classes).then(function (classes) {
|
2013-09-15 01:34:12 +04:00
|
|
|
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
|
|
|
return new hbs.handlebars.SafeString(classString.trim());
|
2013-08-22 23:57:53 +04:00
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-08-22 23:57:53 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.post_class = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-09-15 01:34:12 +04:00
|
|
|
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;
|
2013-08-22 23:57:53 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
if (tags) {
|
|
|
|
classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
|
|
|
|
}
|
2013-08-21 16:55:58 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
if (featured) {
|
|
|
|
classes.push('featured');
|
|
|
|
}
|
2013-10-16 15:41:49 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
if (page) {
|
|
|
|
classes.push('page');
|
|
|
|
}
|
2013-10-17 09:21:56 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
return filters.doFilter('post_class', classes).then(function (classes) {
|
2013-09-15 01:34:12 +04:00
|
|
|
var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
|
|
|
|
return new hbs.handlebars.SafeString(classString.trim());
|
2013-08-22 23:57:53 +04:00
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-08-22 23:57:53 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.ghost_head = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2014-01-03 04:37:21 +04:00
|
|
|
var self = this,
|
|
|
|
blog = config.theme(),
|
2013-11-17 22:40:26 +04:00
|
|
|
head = [],
|
2013-09-15 01:34:12 +04:00
|
|
|
majorMinor = /^(\d+\.)?(\d+)/,
|
2013-11-17 22:40:26 +04:00
|
|
|
trimmedVersion = this.version;
|
2013-09-25 01:29:17 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
trimmedVersion = trimmedVersion ? trimmedVersion.match(majorMinor)[0] : '?';
|
2013-08-22 23:57:53 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
head.push('<meta name="generator" content="Ghost ' + trimmedVersion + '" />');
|
2013-11-17 22:40:26 +04:00
|
|
|
|
2013-12-28 20:01:08 +04:00
|
|
|
head.push('<link rel="alternate" type="application/rss+xml" title="'
|
2014-01-03 04:37:21 +04:00
|
|
|
+ _.escape(blog.title) + '" href="' + config.paths.urlFor('rss') + '">');
|
2013-12-28 20:01:08 +04:00
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
return coreHelpers.url.call(self, {hash: {absolute: true}}).then(function (url) {
|
|
|
|
head.push('<link rel="canonical" href="' + url + '" />');
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
return filters.doFilter('ghost_head', head);
|
|
|
|
}).then(function (head) {
|
2013-09-15 01:34:12 +04:00
|
|
|
var headString = _.reduce(head, function (memo, item) { return memo + '\n' + item; }, '');
|
|
|
|
return new hbs.handlebars.SafeString(headString.trim());
|
2013-08-25 17:19:27 +04:00
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-08-25 17:19:27 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.ghost_foot = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-12-28 20:01:08 +04:00
|
|
|
var foot = [];
|
2013-12-23 19:31:36 +04:00
|
|
|
|
|
|
|
foot.push(scriptTemplate({
|
2013-12-28 20:01:08 +04:00
|
|
|
source: config.paths().subdir + '/shared/vendor/jquery/jquery.js',
|
2013-12-31 03:13:25 +04:00
|
|
|
version: coreHelpers.assetHash
|
2013-12-23 19:31:36 +04:00
|
|
|
}));
|
2013-09-18 01:11:02 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
return filters.doFilter('ghost_foot', foot).then(function (foot) {
|
2013-09-15 01:34:12 +04:00
|
|
|
var footString = _.reduce(foot, function (memo, item) { return memo + ' ' + item; }, '');
|
|
|
|
return new hbs.handlebars.SafeString(footString.trim());
|
2013-09-18 01:11:02 +04:00
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-09-18 01:11:02 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.meta_title = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2014-01-21 21:24:24 +04:00
|
|
|
var title = "",
|
2013-09-15 01:34:12 +04:00
|
|
|
blog;
|
2013-12-28 20:01:08 +04:00
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
if (_.isString(this.relativeUrl)) {
|
|
|
|
if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '' || this.relativeUrl.match(/\/page/)) {
|
2013-12-14 20:28:54 +04:00
|
|
|
blog = config.theme();
|
2013-09-15 01:34:12 +04:00
|
|
|
title = blog.title;
|
2014-01-21 21:24:24 +04:00
|
|
|
} else if (this.post) {
|
2013-09-15 01:34:12 +04:00
|
|
|
title = this.post.title;
|
2013-09-18 01:11:02 +04:00
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
}
|
2013-09-18 01:11:02 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
return filters.doFilter('meta_title', title).then(function (title) {
|
2013-09-15 01:34:12 +04:00
|
|
|
title = title || "";
|
|
|
|
return new hbs.handlebars.SafeString(title.trim());
|
2013-09-18 01:11:02 +04:00
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-09-18 01:11:02 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.meta_description = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-09-15 01:34:12 +04:00
|
|
|
var description,
|
|
|
|
blog;
|
2013-08-25 17:19:27 +04:00
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
if (_.isString(this.relativeUrl)) {
|
|
|
|
if (!this.relativeUrl || this.relativeUrl === '/' || this.relativeUrl === '' || this.relativeUrl.match(/\/page/)) {
|
2013-12-14 20:28:54 +04:00
|
|
|
blog = config.theme();
|
2013-09-15 01:34:12 +04:00
|
|
|
description = blog.description;
|
2013-06-25 15:43:15 +04:00
|
|
|
} else {
|
2013-09-15 01:34:12 +04:00
|
|
|
description = '';
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
}
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
return filters.doFilter('meta_description', description).then(function (description) {
|
2013-09-15 01:34:12 +04:00
|
|
|
description = description || "";
|
|
|
|
return new hbs.handlebars.SafeString(description.trim());
|
2013-06-25 15:43:15 +04:00
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
/**
|
|
|
|
* Localised string helpers
|
|
|
|
*
|
|
|
|
* @param String key
|
|
|
|
* @param String default translation
|
|
|
|
* @param {Object} options
|
|
|
|
* @return String A correctly internationalised string
|
|
|
|
*/
|
|
|
|
coreHelpers.e = function (key, defaultString, options) {
|
|
|
|
var output;
|
2013-12-06 12:51:35 +04:00
|
|
|
when.all([
|
|
|
|
api.settings.read('defaultLang'),
|
|
|
|
api.settings.read('forceI18n')
|
|
|
|
]).then(function (values) {
|
|
|
|
if (values[0].value === 'en'
|
|
|
|
&& _.isEmpty(options.hash)
|
|
|
|
&& _.isEmpty(values[1].value)) {
|
|
|
|
output = defaultString;
|
|
|
|
} else {
|
|
|
|
output = polyglot().t(key, options.hash);
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
});
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
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 });
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
} else {
|
|
|
|
for (key in context) {
|
|
|
|
if (context.hasOwnProperty(key)) {
|
|
|
|
j += 1;
|
|
|
|
}
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
for (key in context) {
|
|
|
|
if (context.hasOwnProperty(key)) {
|
2013-06-25 15:43:15 +04:00
|
|
|
if (data) {
|
2013-09-15 01:34:12 +04:00
|
|
|
data.key = key;
|
2013-06-25 15:43:15 +04:00
|
|
|
data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
|
|
|
|
data = setKeys(data, i, j, columns);
|
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
ret = ret + fn(context[key], {data: data});
|
|
|
|
i += 1;
|
2013-06-16 17:12:28 +04:00
|
|
|
}
|
|
|
|
}
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
2013-09-15 01:34:12 +04:00
|
|
|
}
|
2013-06-16 17:12:28 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
if (i === 0) {
|
|
|
|
ret = inverse(this);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
2013-07-11 02:45:13 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
// ### Pagination Helper
|
|
|
|
// `{{pagination}}`
|
|
|
|
// Outputs previous and next buttons, along with info about the current page
|
|
|
|
coreHelpers.pagination = function (options) {
|
2013-10-31 22:02:34 +04:00
|
|
|
/*jslint unparam:true*/
|
2013-09-15 01:34:12 +04:00
|
|
|
if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
|
|
|
|
errors.logAndThrowError('pagination data is not an object or is a function');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages)
|
|
|
|
|| _.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
|
|
|
|
errors.logAndThrowError('All values must be defined for page, pages, limit and total');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ((!_.isUndefined(this.pagination.next) && !_.isNumber(this.pagination.next))
|
|
|
|
|| (!_.isUndefined(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
|
|
|
|
errors.logAndThrowError('Invalid value, Next/Prev must be a number');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages)
|
|
|
|
|| !_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
|
|
|
|
errors.logAndThrowError('Invalid value, check page, pages, limit and total are numbers');
|
|
|
|
return;
|
|
|
|
}
|
2013-12-02 03:31:55 +04:00
|
|
|
return template.execute('pagination', this.pagination);
|
2013-09-15 01:34:12 +04:00
|
|
|
};
|
2013-09-14 06:15:30 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
coreHelpers.helperMissing = function (arg) {
|
|
|
|
if (arguments.length === 2) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
errors.logError('Missing helper: "' + arg + '"');
|
|
|
|
};
|
2013-09-14 06:15:30 +04:00
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
// ## Admin URL helper
|
|
|
|
// uses urlFor to generate a URL for either the admin or the frontend.
|
|
|
|
coreHelpers.adminUrl = 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.paths.urlFor(context, absolute);
|
|
|
|
};
|
|
|
|
|
2014-01-28 01:18:38 +04:00
|
|
|
coreHelpers.updateNotification = function (options) {
|
2014-01-03 19:50:03 +04:00
|
|
|
var output = '';
|
|
|
|
|
|
|
|
if (config().updateCheck === false || !this.currentUser) {
|
|
|
|
return when(output);
|
|
|
|
}
|
|
|
|
|
2014-01-14 23:46:36 +04:00
|
|
|
return updateCheck.showUpdateNotification().then(function (result) {
|
|
|
|
if (result) {
|
2014-01-28 01:18:38 +04:00
|
|
|
if (options && options.hash && options.hash.classOnly) {
|
|
|
|
output = ' update-available';
|
|
|
|
} else {
|
|
|
|
output = '<div class="notification-success">' +
|
|
|
|
'A new version of Ghost is available! Hot damn. ' +
|
|
|
|
'<a href="http://ghost.org/download">Upgrade now</a></div>';
|
|
|
|
}
|
2014-01-03 19:50:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
// Register an async handlebars helper for a given handlebars instance
|
|
|
|
function registerAsyncHelper(hbs, name, fn) {
|
2013-11-28 06:45:01 +04:00
|
|
|
hbs.registerAsyncHelper(name, function (options, cb) {
|
|
|
|
// Wrap the function passed in with a when.resolve so it can
|
|
|
|
// return either a promise or a value
|
|
|
|
when.resolve(fn.call(this, options)).then(function (result) {
|
|
|
|
cb(result);
|
|
|
|
}).otherwise(function (err) {
|
|
|
|
errors.logAndThrowError(err, "registerAsyncThemeHelper: " + name);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
// Register a handlebars helper for themes
|
|
|
|
function registerThemeHelper(name, fn) {
|
|
|
|
hbs.registerHelper(name, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register an async handlebars helper for themes
|
|
|
|
function registerAsyncThemeHelper(name, fn) {
|
|
|
|
registerAsyncHelper(hbs, name, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register a handlebars helper for admin
|
|
|
|
function registerAdminHelper(name, fn) {
|
|
|
|
coreHelpers.adminHbs.registerHelper(name, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register an async handlebars helper for admin
|
|
|
|
function registerAsyncAdminHelper(name, fn) {
|
|
|
|
registerAsyncHelper(coreHelpers.adminHbs, name, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-31 03:13:25 +04:00
|
|
|
registerHelpers = function (adminHbs, assetHash) {
|
2013-11-20 17:58:52 +04:00
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
// Expose hbs instance for admin
|
|
|
|
coreHelpers.adminHbs = adminHbs;
|
|
|
|
|
2013-12-31 03:13:25 +04:00
|
|
|
// Store hash for assets
|
|
|
|
coreHelpers.assetHash = assetHash;
|
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
|
|
|
|
// Register theme helpers
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('asset', coreHelpers.asset);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('author', coreHelpers.author);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('content', coreHelpers.content);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('date', coreHelpers.date);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('encode', coreHelpers.encode);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('excerpt', coreHelpers.excerpt);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('foreach', coreHelpers.foreach);
|
2013-11-27 06:00:55 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('pageUrl', coreHelpers.pageUrl);
|
2013-09-14 06:15:30 +04:00
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
registerThemeHelper('pagination', coreHelpers.pagination);
|
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerThemeHelper('tags', coreHelpers.tags);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerAsyncThemeHelper('body_class', coreHelpers.body_class);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-12-06 12:51:35 +04:00
|
|
|
registerAsyncThemeHelper('e', coreHelpers.e);
|
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerAsyncThemeHelper('ghost_foot', coreHelpers.ghost_foot);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerAsyncThemeHelper('ghost_head', coreHelpers.ghost_head);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerAsyncThemeHelper('meta_description', coreHelpers.meta_description);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerAsyncThemeHelper('meta_title', coreHelpers.meta_title);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
registerAsyncThemeHelper('post_class', coreHelpers.post_class);
|
2013-07-11 02:45:13 +04:00
|
|
|
|
2013-12-06 12:51:35 +04:00
|
|
|
registerAsyncThemeHelper('url', coreHelpers.url);
|
|
|
|
|
2013-07-11 02:45:13 +04:00
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
// Register admin helpers
|
|
|
|
registerAdminHelper('asset', coreHelpers.asset);
|
2013-09-15 01:34:12 +04:00
|
|
|
|
2013-12-02 03:31:55 +04:00
|
|
|
registerAdminHelper('ghostScriptTags', coreHelpers.ghostScriptTags);
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-12-14 20:28:54 +04:00
|
|
|
registerAdminHelper('fileStorage', coreHelpers.fileStorage);
|
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
registerAdminHelper('adminUrl', coreHelpers.adminUrl);
|
2013-12-14 20:28:54 +04:00
|
|
|
|
2014-01-03 19:50:03 +04:00
|
|
|
registerAsyncAdminHelper('updateNotification', coreHelpers.updateNotification);
|
2013-12-14 20:28:54 +04:00
|
|
|
};
|
2013-12-02 03:31:55 +04:00
|
|
|
|
2013-09-15 01:34:12 +04:00
|
|
|
module.exports = coreHelpers;
|
|
|
|
module.exports.loadCoreHelpers = registerHelpers;
|
2013-11-28 06:45:01 +04:00
|
|
|
module.exports.registerThemeHelper = registerThemeHelper;
|
|
|
|
module.exports.registerAsyncThemeHelper = registerAsyncThemeHelper;
|
2014-01-15 03:14:44 +04:00
|
|
|
module.exports.scriptFiles = scriptFiles;
|