2018-09-18 16:59:56 +03:00
|
|
|
const Promise = require('bluebird'),
|
2017-09-12 18:31:14 +03:00
|
|
|
_ = require('lodash'),
|
2016-05-19 14:49:22 +03:00
|
|
|
moment = require('moment'),
|
2018-09-27 17:06:57 +03:00
|
|
|
pipeline = require('../../lib/promise/pipeline'),
|
2017-12-14 00:14:19 +03:00
|
|
|
localUtils = require('./utils'),
|
2018-09-27 17:06:57 +03:00
|
|
|
models = require('../../models'),
|
|
|
|
config = require('../../config'),
|
|
|
|
common = require('../../lib/common'),
|
2018-09-20 18:36:47 +03:00
|
|
|
postsAPI = require('./posts');
|
2016-05-19 14:49:22 +03:00
|
|
|
|
|
|
|
/**
|
2017-04-19 16:53:23 +03:00
|
|
|
* Publish a scheduled post
|
|
|
|
*
|
|
|
|
* `apiPosts.read` and `apiPosts.edit` must happen in one transaction.
|
|
|
|
* We lock the target row on fetch by using the `forUpdate` option.
|
|
|
|
* Read more in models/post.js - `onFetching`
|
2016-05-19 14:49:22 +03:00
|
|
|
*
|
|
|
|
* object.force: you can force publishing a post in the past (for example if your service was down)
|
|
|
|
*/
|
|
|
|
exports.publishPost = function publishPost(object, options) {
|
|
|
|
if (_.isEmpty(options)) {
|
|
|
|
options = object || {};
|
|
|
|
object = {};
|
|
|
|
}
|
|
|
|
|
2018-09-18 16:59:56 +03:00
|
|
|
let post, publishedAtMoment,
|
2016-09-13 18:41:14 +03:00
|
|
|
publishAPostBySchedulerToleranceInMinutes = config.get('times').publishAPostBySchedulerToleranceInMinutes;
|
2016-05-19 14:49:22 +03:00
|
|
|
|
|
|
|
// CASE: only the scheduler client is allowed to publish (hardcoded because of missing client permission system)
|
|
|
|
if (!options.context || !options.context.client || options.context.client !== 'ghost-scheduler') {
|
2017-12-12 00:47:46 +03:00
|
|
|
return Promise.reject(new common.errors.NoPermissionError({message: common.i18n.t('errors.permissions.noPermissionToAction')}));
|
2016-05-19 14:49:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
options.context = {internal: true};
|
|
|
|
|
|
|
|
return pipeline([
|
2017-12-14 00:14:19 +03:00
|
|
|
localUtils.validate('posts', {opts: localUtils.idDefaultOptions}),
|
2018-09-18 16:59:56 +03:00
|
|
|
(cleanOptions) => {
|
2016-05-19 14:49:22 +03:00
|
|
|
cleanOptions.status = 'scheduled';
|
|
|
|
|
2018-09-18 16:59:56 +03:00
|
|
|
return models.Base.transaction((transacting) => {
|
2017-04-19 16:53:23 +03:00
|
|
|
cleanOptions.transacting = transacting;
|
|
|
|
cleanOptions.forUpdate = true;
|
|
|
|
|
2017-12-15 00:07:53 +03:00
|
|
|
// CASE: extend allowed options, see api/zip-folder.js
|
2017-04-19 16:53:23 +03:00
|
|
|
cleanOptions.opts = ['forUpdate', 'transacting'];
|
|
|
|
|
2017-09-12 18:31:14 +03:00
|
|
|
return postsAPI.read(cleanOptions)
|
2018-09-18 16:59:56 +03:00
|
|
|
.then((result) => {
|
2017-04-19 16:53:23 +03:00
|
|
|
post = result.posts[0];
|
|
|
|
publishedAtMoment = moment(post.published_at);
|
2016-05-19 14:49:22 +03:00
|
|
|
|
2017-04-19 16:53:23 +03:00
|
|
|
if (publishedAtMoment.diff(moment(), 'minutes') > publishAPostBySchedulerToleranceInMinutes) {
|
2017-12-12 00:47:46 +03:00
|
|
|
return Promise.reject(new common.errors.NotFoundError({message: common.i18n.t('errors.api.job.notFound')}));
|
2017-04-19 16:53:23 +03:00
|
|
|
}
|
2016-05-19 14:49:22 +03:00
|
|
|
|
2017-04-19 16:53:23 +03:00
|
|
|
if (publishedAtMoment.diff(moment(), 'minutes') < publishAPostBySchedulerToleranceInMinutes * -1 && object.force !== true) {
|
2017-12-12 00:47:46 +03:00
|
|
|
return Promise.reject(new common.errors.NotFoundError({message: common.i18n.t('errors.api.job.publishInThePast')}));
|
2017-04-19 16:53:23 +03:00
|
|
|
}
|
2016-05-19 14:49:22 +03:00
|
|
|
|
2017-11-01 16:44:54 +03:00
|
|
|
return postsAPI.edit(
|
|
|
|
{
|
|
|
|
posts: [{status: 'published'}]
|
|
|
|
},
|
2017-04-19 16:53:23 +03:00
|
|
|
_.pick(cleanOptions, ['context', 'id', 'transacting', 'forUpdate', 'opts'])
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2016-05-19 14:49:22 +03:00
|
|
|
}
|
|
|
|
], options);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get all scheduled posts/pages
|
|
|
|
* permission check not needed, because route is not exposed
|
|
|
|
*/
|
|
|
|
exports.getScheduledPosts = function readPosts(options) {
|
|
|
|
options = options || {};
|
|
|
|
options.context = {internal: true};
|
|
|
|
|
|
|
|
return pipeline([
|
2017-12-14 00:14:19 +03:00
|
|
|
localUtils.validate('posts', {opts: ['from', 'to']}),
|
2018-09-18 16:59:56 +03:00
|
|
|
(cleanOptions) => {
|
2016-05-19 14:49:22 +03:00
|
|
|
cleanOptions.filter = 'status:scheduled';
|
|
|
|
cleanOptions.columns = ['id', 'published_at', 'created_at'];
|
|
|
|
|
|
|
|
if (cleanOptions.from) {
|
2018-09-18 16:59:56 +03:00
|
|
|
cleanOptions.filter += `+created_at:>='${moment(cleanOptions.from).format('YYYY-MM-DD HH:mm:ss')}'`;
|
2016-05-19 14:49:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cleanOptions.to) {
|
2018-09-18 16:59:56 +03:00
|
|
|
cleanOptions.filter += `+created_at:<='${moment(cleanOptions.to).format('YYYY-MM-DD HH:mm:ss')}'`;
|
2016-05-19 14:49:22 +03:00
|
|
|
}
|
|
|
|
|
2017-09-12 18:31:14 +03:00
|
|
|
return models.Post.findAll(cleanOptions)
|
2018-09-18 16:59:56 +03:00
|
|
|
.then((result) => {
|
2016-05-19 14:49:22 +03:00
|
|
|
return Promise.resolve({posts: result.models});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
], options);
|
|
|
|
};
|