mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 11:55:03 +03:00
Added collections helper
This is an initial implementation which uses the Posts Content API rather than the Collections Content API, this is because we haven't added the Collections Content API yet, but we can added it later when necessary.
This commit is contained in:
parent
be0dbe3e91
commit
605aa18d6a
145
ghost/core/core/frontend/helpers/collection.js
Normal file
145
ghost/core/core/frontend/helpers/collection.js
Normal file
@ -0,0 +1,145 @@
|
||||
// # Get Helper
|
||||
// Usage: `{{#get "posts" limit="5"}}`, `{{#get "tags" limit="all"}}`
|
||||
// Fetches data from the API
|
||||
const {config, api, prepareContextResource} = require('../services/proxy');
|
||||
const {hbs} = require('../services/handlebars');
|
||||
|
||||
const logging = require('@tryghost/logging');
|
||||
const errors = require('@tryghost/errors');
|
||||
const tpl = require('@tryghost/tpl');
|
||||
|
||||
const messages = {
|
||||
mustBeCalledAsBlock: 'The {\\{{helperName}}} helper must be called as a block. E.g. {{#{helperName}}}...{{/{helperName}}}',
|
||||
invalidResource: 'Invalid "{resource}" resource given to get helper'
|
||||
};
|
||||
|
||||
const createFrame = hbs.handlebars.createFrame;
|
||||
|
||||
/**
|
||||
* ## Parse Options
|
||||
* Ensure options passed in make sense
|
||||
*
|
||||
* @param {Object} options
|
||||
* @returns {*}
|
||||
*/
|
||||
function parseOptions(options) {
|
||||
if (options.limit === 'all' || !options.limit) {
|
||||
return {
|
||||
limit: 3
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
limit: options.limit
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} resource
|
||||
* @param {String} controllerName
|
||||
* @param {String} action
|
||||
* @param {Object} apiOptions
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async function makeAPICall(resource, controllerName, action, apiOptions) {
|
||||
const controller = api[controllerName];
|
||||
|
||||
let timer;
|
||||
|
||||
try {
|
||||
let response;
|
||||
|
||||
if (config.get('optimization:getHelper:timeout:threshold')) {
|
||||
const logLevel = config.get('optimization:getHelper:timeout:level') || 'error';
|
||||
const threshold = config.get('optimization:getHelper:timeout:threshold');
|
||||
|
||||
const apiResponse = controller[action](apiOptions);
|
||||
|
||||
const timeout = new Promise((resolve) => {
|
||||
timer = setTimeout(() => {
|
||||
logging[logLevel](new errors.HelperWarning({
|
||||
message: `{{#get}} took longer than ${threshold}ms and was aborted`,
|
||||
code: 'ABORTED_GET_HELPER',
|
||||
errorDetails: {
|
||||
api: `${controllerName}.${action}`,
|
||||
apiOptions
|
||||
}
|
||||
}));
|
||||
|
||||
resolve({[resource]: []});
|
||||
}, threshold);
|
||||
});
|
||||
|
||||
response = await Promise.race([apiResponse, timeout]);
|
||||
clearTimeout(timer);
|
||||
} else {
|
||||
response = await controller[action](apiOptions);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
clearTimeout(timer);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Get
|
||||
* @param {string} slug
|
||||
* @param {object} options
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
module.exports = async function collection(slug, options) {
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
options.data = options.data || {};
|
||||
|
||||
const self = this;
|
||||
const data = createFrame(options.data);
|
||||
|
||||
let apiOptions = options.hash;
|
||||
|
||||
if (!options.fn) {
|
||||
data.error = tpl(messages.mustBeCalledAsBlock, {helperName: 'collection'});
|
||||
logging.warn(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
const resource = 'posts';
|
||||
const controllerName = 'postsPublic';
|
||||
const action = 'browse';
|
||||
|
||||
// Parse the options we're going to pass to the API
|
||||
apiOptions = parseOptions(apiOptions);
|
||||
apiOptions.context = {member: data.member};
|
||||
|
||||
try {
|
||||
const response = await makeAPICall(resource, controllerName, action, apiOptions);
|
||||
|
||||
// prepare data properties for use with handlebars
|
||||
if (response[resource] && response[resource].length) {
|
||||
response[resource].forEach(prepareContextResource);
|
||||
}
|
||||
|
||||
// block params allows the theme developer to name the data using something like
|
||||
// `{{#get "posts" as |result pageInfo|}}`
|
||||
const blockParams = [response[resource]];
|
||||
if (response.meta && response.meta.pagination) {
|
||||
response.pagination = response.meta.pagination;
|
||||
blockParams.push(response.meta.pagination);
|
||||
}
|
||||
|
||||
// Call the main template function
|
||||
return options.fn(response, {
|
||||
data: data,
|
||||
blockParams: blockParams
|
||||
});
|
||||
} catch (error) {
|
||||
logging.error(error);
|
||||
data.error = error.message;
|
||||
return options.inverse(self, {data: data});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.async = true;
|
@ -11,7 +11,7 @@ describe('Helpers', function () {
|
||||
'asset', 'authors', 'body_class', 'cancel_link', 'concat', 'content', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get',
|
||||
'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'link', 'link_class', 'meta_description', 'meta_title', 'navigation',
|
||||
'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'price', 'raw', 'reading_time', 't', 'tags', 'title','total_members', 'total_paid_members', 'twitter_url',
|
||||
'url', 'comment_count'
|
||||
'url', 'comment_count', 'collection'
|
||||
];
|
||||
const experimentalHelpers = ['match', 'tiers', 'comments', 'search'];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user