TryGhost/Ghost

View on GitHub
ghost/core/core/frontend/services/rendering/format-response.js

Summary

Maintainability
A
1 hr
Test Coverage
const _ = require('lodash');
const hbs = require('../theme-engine/engine');
const {prepareContextResource} = require('../proxy');
const {isPage} = require('../data/checks');

/**
 * @description Formats API response into handlebars/theme format.
 *
 * @return {Object} containing page variables
 */
function formatPageResponse(result, pageAsPost = false, locals = {}) {
    const response = {};

    if (result.posts) {
        response.posts = result.posts;
        prepareContextResource(response.posts);
    }

    if (result.meta && result.meta.pagination) {
        response.pagination = result.meta.pagination;
    }

    // when a custom routed page is loaded it can have an associated page object,
    // in that case we want to make sure @page is still available and matches the
    // selected page properties
    if (isPage(result.data?.page?.[0])) {
        const page = result.data?.page?.[0];

        // build up @page data for use in templates
        // - done here rather than `update-local-template-options` middleware because
        //   we need access to the rendered entry's data which isn't available in middleware
        const pageData = {
            show_title_and_feature_image: page.show_title_and_feature_image
        };

        // merge @page into local template options
        const localTemplateOptions = hbs.getLocalTemplateOptions(locals);
        hbs.updateLocalTemplateOptions(locals, _.merge({}, localTemplateOptions, {
            data: {
                page: pageData
            }
        }));
    }

    _.each(result.data, function (data, name) {
        prepareContextResource(data);

        if (data.meta) {
            // Move pagination to be a top level key
            response[name] = data;
            response[name].pagination = data.meta.pagination;
            delete response[name].meta;
        } else {
            // This is a single object, don't wrap it in an array
            response[name] = data[0];
        }
    });

    if (pageAsPost && response.page) {
        response.post = response.page;
    }

    return response;
}

/**
 * @description Format a single resource for handlebars.
 *
 * @TODO
 * In the future, we should return {page: entry} or {post:entry).
 * But for now, we would break the themes if we just change it.
 *
 * @see https://github.com/TryGhost/Ghost/issues/10042.
 *
 * @return {Object} containing page variables
 */
function formatResponse(post, context, locals = {}) {
    // build up @page data for use in templates
    // - done here rather than `update-local-template-options` middleware because
    //   we need access to the rendered entry's data which isn't available in middleware
    const pageData = {
        show_title_and_feature_image: true // default behaviour
    };

    // grab data off of the post that will be deleted in prepareContextResource
    const showTitleAndFeatureImage = post.show_title_and_feature_image;

    prepareContextResource(post);

    let entry = {
        post: post
    };

    // NOTE: preview context is a special case where the internal preview api returns the post model's type field
    if (context?.includes('page') || (context?.includes('preview') && post.type === 'page')) {
        entry.page = post;

        // move properties from the page context object onto @page
        // - makes the value available outside of the page context
        // - data is removed from the post object in prepareContextResource so use of @page is forced
        if (showTitleAndFeatureImage !== undefined) {
            pageData.show_title_and_feature_image = showTitleAndFeatureImage;
        }
    }

    // merge @page into local template options
    const localTemplateOptions = hbs.getLocalTemplateOptions(locals);
    hbs.updateLocalTemplateOptions(locals, _.merge({}, localTemplateOptions, {
        data: {
            page: pageData
        }
    }));

    return entry;
}

module.exports = {
    entries: formatPageResponse,
    entry: formatResponse
};