mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-22 10:21:36 +03:00
40d0a745df
no issue This PR adds the server side logic for multiple authors. This adds the ability to add multiple authors per post. We keep and support single authors (maybe till the next major - this is still in discussion) ### key notes - `authors` are not fetched by default, only if we need them - the migration script iterates over all posts and figures out if an author_id is valid and exists (in master we can add invalid author_id's) and then adds the relation (falls back to owner if invalid) - ~~i had to push a fork of bookshelf to npm because we currently can't bump bookshelf + the two bugs i discovered are anyway not yet merged (https://github.com/kirrg001/bookshelf/commits/master)~~ replaced by new bookshelf release - the implementation of single & multiple authors lives in a single place (introduction of a new concept: model relation) - if you destroy an author, we keep the behaviour for now -> remove all posts where the primary author id matches. furthermore, remove all relations in posts_authors (e.g. secondary author) - we make re-use of the `excludeAttrs` concept which was invented in the contributors PR (to protect editing authors as author/contributor role) -> i've added a clear todo that we need a logic to make a diff of the target relation -> both for tags and authors - `authors` helper available (same as `tags` helper) - `primary_author` computed field available - `primary_author` functionality available (same as `primary_tag` e.g. permalinks, prev/next helper etc)
126 lines
3.5 KiB
JavaScript
126 lines
3.5 KiB
JavaScript
/**
|
|
* # Fetch Data
|
|
* Dynamically build and execute queries on the API for channels
|
|
*/
|
|
var api = require('../../api'),
|
|
_ = require('lodash'),
|
|
Promise = require('bluebird'),
|
|
themes = require('../../services/themes'),
|
|
queryDefaults,
|
|
defaultPostQuery = {};
|
|
|
|
// The default settings for a default post query
|
|
queryDefaults = {
|
|
type: 'browse',
|
|
resource: 'posts',
|
|
options: {}
|
|
};
|
|
|
|
/**
|
|
* Default post query needs to always include author, authors & tags
|
|
*
|
|
* @deprecated: `author`, will be removed in Ghost 2.0
|
|
*/
|
|
_.extend(defaultPostQuery, queryDefaults, {
|
|
options: {
|
|
include: 'author,authors,tags'
|
|
}
|
|
});
|
|
|
|
/**
|
|
* ## Fetch Posts Per page
|
|
* Grab the postsPerPage setting
|
|
*
|
|
* @param {Object} options
|
|
* @returns {Object} postOptions
|
|
*/
|
|
function fetchPostsPerPage(options) {
|
|
options = options || {};
|
|
|
|
var postsPerPage = parseInt(themes.getActive().config('posts_per_page'));
|
|
|
|
// No negative posts per page, must be number
|
|
if (!isNaN(postsPerPage) && postsPerPage > 0) {
|
|
options.limit = postsPerPage;
|
|
}
|
|
|
|
// Ensure the options key is present, so this can be merged with other options
|
|
return {options: options};
|
|
}
|
|
|
|
/**
|
|
* @typedef query
|
|
* @
|
|
*/
|
|
|
|
/**
|
|
* ## Process Query
|
|
* Takes a 'query' object, ensures that type, resource and options are set
|
|
* Replaces occurrences of `%s` in options with slugParam
|
|
* Converts the query config to a promise for the result
|
|
*
|
|
* @param {{type: String, resource: String, options: Object}} query
|
|
* @param {String} slugParam
|
|
* @returns {Promise} promise for an API call
|
|
*/
|
|
function processQuery(query, slugParam) {
|
|
query = _.cloneDeep(query);
|
|
|
|
// Ensure that all the properties are filled out
|
|
_.defaultsDeep(query, queryDefaults);
|
|
|
|
// Replace any slugs
|
|
_.each(query.options, function (option, name) {
|
|
query.options[name] = _.isString(option) ? option.replace(/%s/g, slugParam) : option;
|
|
});
|
|
|
|
// Return a promise for the api query
|
|
return api[query.resource][query.type](query.options);
|
|
}
|
|
|
|
/**
|
|
* ## Fetch Data
|
|
* Calls out to get posts per page, builds the final posts query & builds any additional queries
|
|
* Wraps the queries using Promise.props to ensure it gets named responses
|
|
* Does a first round of formatting on the response, and returns
|
|
*
|
|
* @param {Object} channelOptions
|
|
* @returns {Promise} response
|
|
*/
|
|
function fetchData(channelOptions) {
|
|
// @TODO improve this further
|
|
var pageOptions = channelOptions.isRSS ? {options: channelOptions.postOptions} : fetchPostsPerPage(channelOptions.postOptions),
|
|
postQuery,
|
|
props = {};
|
|
|
|
// All channels must have a posts query, use the default if not provided
|
|
postQuery = _.defaultsDeep({}, pageOptions, defaultPostQuery);
|
|
props.posts = processQuery(postQuery, channelOptions.slugParam);
|
|
|
|
_.each(channelOptions.data, function (query, name) {
|
|
props[name] = processQuery(query, channelOptions.slugParam);
|
|
});
|
|
|
|
return Promise.props(props).then(function formatResponse(results) {
|
|
var response = _.cloneDeep(results.posts);
|
|
delete results.posts;
|
|
|
|
// process any remaining data
|
|
if (!_.isEmpty(results)) {
|
|
response.data = {};
|
|
|
|
_.each(results, function (result, name) {
|
|
if (channelOptions.data[name].type === 'browse') {
|
|
response.data[name] = result;
|
|
} else {
|
|
response.data[name] = result[channelOptions.data[name].resource];
|
|
}
|
|
});
|
|
}
|
|
|
|
return response;
|
|
});
|
|
}
|
|
|
|
module.exports = fetchData;
|