2019-08-09 17:11:24 +03:00
|
|
|
const models = require('../../models');
|
2021-05-03 19:29:44 +03:00
|
|
|
const i18n = require('../../../shared/i18n');
|
2020-05-22 21:22:20 +03:00
|
|
|
const errors = require('@tryghost/errors');
|
2020-05-28 13:57:02 +03:00
|
|
|
const urlUtils = require('../../../shared/url-utils');
|
2019-11-06 14:32:43 +03:00
|
|
|
const {mega} = require('../../services/mega');
|
2021-08-05 13:51:47 +03:00
|
|
|
const PostsService = require('../../services/posts/posts-service');
|
2019-11-06 12:30:11 +03:00
|
|
|
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
|
2019-10-08 16:44:27 +03:00
|
|
|
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
2019-08-09 17:11:24 +03:00
|
|
|
|
2021-08-05 13:51:47 +03:00
|
|
|
const postsService = new PostsService({
|
|
|
|
apiVersion: 'canary',
|
|
|
|
mega: mega,
|
|
|
|
urlUtils: urlUtils,
|
|
|
|
i18n: i18n,
|
|
|
|
models: models
|
|
|
|
});
|
|
|
|
|
2019-08-09 17:11:24 +03:00
|
|
|
module.exports = {
|
|
|
|
docName: 'posts',
|
|
|
|
browse: {
|
|
|
|
options: [
|
|
|
|
'include',
|
|
|
|
'filter',
|
|
|
|
'fields',
|
|
|
|
'formats',
|
|
|
|
'limit',
|
|
|
|
'order',
|
|
|
|
'page',
|
|
|
|
'debug',
|
|
|
|
'absolute_urls'
|
|
|
|
],
|
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
values: allowedIncludes
|
|
|
|
},
|
|
|
|
formats: {
|
|
|
|
values: models.Post.allowedFormats
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
unsafeAttrs: unsafeAttrs
|
|
|
|
},
|
|
|
|
query(frame) {
|
|
|
|
return models.Post.findPage(frame.options);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
read: {
|
|
|
|
options: [
|
|
|
|
'include',
|
|
|
|
'fields',
|
|
|
|
'formats',
|
|
|
|
'debug',
|
|
|
|
'absolute_urls',
|
|
|
|
// NOTE: only for internal context
|
|
|
|
'forUpdate',
|
|
|
|
'transacting'
|
|
|
|
],
|
|
|
|
data: [
|
|
|
|
'id',
|
|
|
|
'slug',
|
|
|
|
'uuid'
|
|
|
|
],
|
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
values: allowedIncludes
|
|
|
|
},
|
|
|
|
formats: {
|
|
|
|
values: models.Post.allowedFormats
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
unsafeAttrs: unsafeAttrs
|
|
|
|
},
|
|
|
|
query(frame) {
|
|
|
|
return models.Post.findOne(frame.data, frame.options)
|
|
|
|
.then((model) => {
|
|
|
|
if (!model) {
|
2020-05-22 21:22:20 +03:00
|
|
|
throw new errors.NotFoundError({
|
|
|
|
message: i18n.t('errors.api.posts.postNotFound')
|
2019-08-09 17:11:24 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return model;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
add: {
|
|
|
|
statusCode: 201,
|
|
|
|
headers: {},
|
|
|
|
options: [
|
|
|
|
'include',
|
2020-06-18 15:59:01 +03:00
|
|
|
'formats',
|
2020-11-06 20:32:23 +03:00
|
|
|
'source'
|
2019-08-09 17:11:24 +03:00
|
|
|
],
|
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
values: allowedIncludes
|
|
|
|
},
|
|
|
|
source: {
|
|
|
|
values: ['html']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
unsafeAttrs: unsafeAttrs
|
|
|
|
},
|
|
|
|
query(frame) {
|
|
|
|
return models.Post.add(frame.data.posts[0], frame.options)
|
|
|
|
.then((model) => {
|
|
|
|
if (model.get('status') !== 'published') {
|
|
|
|
this.headers.cacheInvalidate = false;
|
|
|
|
} else {
|
|
|
|
this.headers.cacheInvalidate = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return model;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
edit: {
|
|
|
|
headers: {},
|
|
|
|
options: [
|
|
|
|
'include',
|
|
|
|
'id',
|
2020-06-18 15:59:01 +03:00
|
|
|
'formats',
|
2019-08-09 17:11:24 +03:00
|
|
|
'source',
|
2020-11-06 20:32:23 +03:00
|
|
|
'email_recipient_filter',
|
Fixed backward compatibility for `send_email_when_published` (#12357)
no-issue
* Handled send_email_when_published in Posts API
This restores backwards compatibility of the Posts API allowing existing
clients to continue to use the `send_email_when_published` flag. This
change uses two edits, which is unfortunate. The reason being is that
this is an API compatibility issue, not a model issue, so we shouldn't
introduce code to the model layer to handle it. The visibility property
of the model is used to determine how to fall back, and because it can
be left out of the API request, and relies on a default in the settings,
we require that the model decide on the `visibility` before we run our
fallback logic (or we duplicate the `visibility` default at the cost of
maintenance in the future)
* Dropped send_email_when_published column from posts
Since this column is not used any more, we can drop it from the table.
We include an extra migration to repopulate the column in the event of
a rollback
* Updated importer to handle send_email_when_published
Because we currently export this value from Ghost, we should correctly
import it. This follows the same logic as the migrations for this value.
* Included send_email_when_published in API response
As our v3 API documentation includes `send_email_when_published` we must
retain backward compatibility by calculating the property.
* Fixed fields filter with send_email_when_published
* Added safety checks to frame properties
Some parts of the code pass a manually created "frame" which is missing
lots of properties, so we check for the existence of all of them before
using them.
* Fixed 3.1 migration to include columnDefinition
We require that migrations have all the information they need contained
within them as they run in an unknown state of the codebase, which could
be from the commit they are introduced, to any future commit. In this
case the column definition is removed from the schema in 3.38 and the
migration would fail when run in this version or later.
2020-11-11 16:03:41 +03:00
|
|
|
'send_email_when_published',
|
2020-06-12 20:05:57 +03:00
|
|
|
'force_rerender',
|
2019-08-09 17:11:24 +03:00
|
|
|
// NOTE: only for internal context
|
|
|
|
'forUpdate',
|
|
|
|
'transacting'
|
|
|
|
],
|
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
values: allowedIncludes
|
|
|
|
},
|
|
|
|
id: {
|
|
|
|
required: true
|
|
|
|
},
|
|
|
|
source: {
|
|
|
|
values: ['html']
|
2020-11-06 20:32:23 +03:00
|
|
|
},
|
Fixed backward compatibility for `send_email_when_published` (#12357)
no-issue
* Handled send_email_when_published in Posts API
This restores backwards compatibility of the Posts API allowing existing
clients to continue to use the `send_email_when_published` flag. This
change uses two edits, which is unfortunate. The reason being is that
this is an API compatibility issue, not a model issue, so we shouldn't
introduce code to the model layer to handle it. The visibility property
of the model is used to determine how to fall back, and because it can
be left out of the API request, and relies on a default in the settings,
we require that the model decide on the `visibility` before we run our
fallback logic (or we duplicate the `visibility` default at the cost of
maintenance in the future)
* Dropped send_email_when_published column from posts
Since this column is not used any more, we can drop it from the table.
We include an extra migration to repopulate the column in the event of
a rollback
* Updated importer to handle send_email_when_published
Because we currently export this value from Ghost, we should correctly
import it. This follows the same logic as the migrations for this value.
* Included send_email_when_published in API response
As our v3 API documentation includes `send_email_when_published` we must
retain backward compatibility by calculating the property.
* Fixed fields filter with send_email_when_published
* Added safety checks to frame properties
Some parts of the code pass a manually created "frame" which is missing
lots of properties, so we check for the existence of all of them before
using them.
* Fixed 3.1 migration to include columnDefinition
We require that migrations have all the information they need contained
within them as they run in an unknown state of the codebase, which could
be from the commit they are introduced, to any future commit. In this
case the column definition is removed from the schema in 3.38 and the
migration would fail when run in this version or later.
2020-11-11 16:03:41 +03:00
|
|
|
send_email_when_published: {
|
|
|
|
values: [true, false]
|
2019-08-09 17:11:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
unsafeAttrs: unsafeAttrs
|
|
|
|
},
|
2019-12-17 16:54:27 +03:00
|
|
|
async query(frame) {
|
2020-11-25 16:58:21 +03:00
|
|
|
let model;
|
Fixed backward compatibility for `send_email_when_published` (#12357)
no-issue
* Handled send_email_when_published in Posts API
This restores backwards compatibility of the Posts API allowing existing
clients to continue to use the `send_email_when_published` flag. This
change uses two edits, which is unfortunate. The reason being is that
this is an API compatibility issue, not a model issue, so we shouldn't
introduce code to the model layer to handle it. The visibility property
of the model is used to determine how to fall back, and because it can
be left out of the API request, and relies on a default in the settings,
we require that the model decide on the `visibility` before we run our
fallback logic (or we duplicate the `visibility` default at the cost of
maintenance in the future)
* Dropped send_email_when_published column from posts
Since this column is not used any more, we can drop it from the table.
We include an extra migration to repopulate the column in the event of
a rollback
* Updated importer to handle send_email_when_published
Because we currently export this value from Ghost, we should correctly
import it. This follows the same logic as the migrations for this value.
* Included send_email_when_published in API response
As our v3 API documentation includes `send_email_when_published` we must
retain backward compatibility by calculating the property.
* Fixed fields filter with send_email_when_published
* Added safety checks to frame properties
Some parts of the code pass a manually created "frame" which is missing
lots of properties, so we check for the existence of all of them before
using them.
* Fixed 3.1 migration to include columnDefinition
We require that migrations have all the information they need contained
within them as they run in an unknown state of the codebase, which could
be from the commit they are introduced, to any future commit. In this
case the column definition is removed from the schema in 3.38 and the
migration would fail when run in this version or later.
2020-11-11 16:03:41 +03:00
|
|
|
if (!frame.options.email_recipient_filter && frame.options.send_email_when_published) {
|
2020-11-25 16:58:21 +03:00
|
|
|
await models.Base.transaction(async (transacting) => {
|
|
|
|
const options = {
|
|
|
|
...frame.options,
|
|
|
|
transacting
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 1. We need to edit the post first in order to know what the visibility is.
|
|
|
|
* 2. We can only pass the email_recipient_filter when we change the status.
|
|
|
|
*
|
|
|
|
* So, we first edit the post as requested, with all information except the status,
|
|
|
|
* from there we can determine what the email_recipient_filter should be and then finish
|
|
|
|
* the edit, with the status and the email_recipient_filter option.
|
|
|
|
*/
|
|
|
|
const status = frame.data.posts[0].status;
|
|
|
|
delete frame.data.posts[0].status;
|
|
|
|
const interimModel = await models.Post.edit(frame.data.posts[0], options);
|
|
|
|
frame.data.posts[0].status = status;
|
|
|
|
|
|
|
|
options.email_recipient_filter = interimModel.get('visibility') === 'paid' ? 'paid' : 'all';
|
|
|
|
|
|
|
|
model = await models.Post.edit(frame.data.posts[0], options);
|
|
|
|
});
|
|
|
|
} else {
|
Fixed backward compatibility for `send_email_when_published` (#12357)
no-issue
* Handled send_email_when_published in Posts API
This restores backwards compatibility of the Posts API allowing existing
clients to continue to use the `send_email_when_published` flag. This
change uses two edits, which is unfortunate. The reason being is that
this is an API compatibility issue, not a model issue, so we shouldn't
introduce code to the model layer to handle it. The visibility property
of the model is used to determine how to fall back, and because it can
be left out of the API request, and relies on a default in the settings,
we require that the model decide on the `visibility` before we run our
fallback logic (or we duplicate the `visibility` default at the cost of
maintenance in the future)
* Dropped send_email_when_published column from posts
Since this column is not used any more, we can drop it from the table.
We include an extra migration to repopulate the column in the event of
a rollback
* Updated importer to handle send_email_when_published
Because we currently export this value from Ghost, we should correctly
import it. This follows the same logic as the migrations for this value.
* Included send_email_when_published in API response
As our v3 API documentation includes `send_email_when_published` we must
retain backward compatibility by calculating the property.
* Fixed fields filter with send_email_when_published
* Added safety checks to frame properties
Some parts of the code pass a manually created "frame" which is missing
lots of properties, so we check for the existence of all of them before
using them.
* Fixed 3.1 migration to include columnDefinition
We require that migrations have all the information they need contained
within them as they run in an unknown state of the codebase, which could
be from the commit they are introduced, to any future commit. In this
case the column definition is removed from the schema in 3.38 and the
migration would fail when run in this version or later.
2020-11-11 16:03:41 +03:00
|
|
|
model = await models.Post.edit(frame.data.posts[0], frame.options);
|
|
|
|
}
|
|
|
|
|
2019-12-17 16:54:27 +03:00
|
|
|
/**Handle newsletter email */
|
2021-05-07 13:56:41 +03:00
|
|
|
const emailRecipientFilter = model.get('email_recipient_filter');
|
|
|
|
if (emailRecipientFilter !== 'none') {
|
|
|
|
if (emailRecipientFilter !== 'all') {
|
|
|
|
// check filter is valid
|
|
|
|
try {
|
|
|
|
await models.Member.findPage({filter: `subscribed:true+${emailRecipientFilter}`, limit: 1});
|
|
|
|
} catch (err) {
|
|
|
|
return Promise.reject(new BadRequestError({
|
|
|
|
message: i18n.t('errors.api.posts.invalidEmailRecipientFilter'),
|
|
|
|
context: err.message
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-17 16:54:27 +03:00
|
|
|
const postPublished = model.wasChanged() && (model.get('status') === 'published') && (model.previous('status') !== 'published');
|
|
|
|
if (postPublished) {
|
|
|
|
let postEmail = model.relations.email;
|
2019-11-18 17:28:54 +03:00
|
|
|
|
2019-12-17 16:54:27 +03:00
|
|
|
if (!postEmail) {
|
2021-03-03 03:16:25 +03:00
|
|
|
const email = await mega.addEmail(model, Object.assign({}, frame.options, {apiVersion: 'canary'}));
|
2019-12-17 16:54:27 +03:00
|
|
|
model.set('email', email);
|
|
|
|
} else if (postEmail && postEmail.get('status') === 'failed') {
|
|
|
|
const email = await mega.retryFailedEmail(postEmail);
|
|
|
|
model.set('email', email);
|
2019-08-09 17:11:24 +03:00
|
|
|
}
|
2019-12-17 16:54:27 +03:00
|
|
|
}
|
|
|
|
}
|
2019-08-09 17:11:24 +03:00
|
|
|
|
2021-08-05 13:51:47 +03:00
|
|
|
this.headers.cacheInvalidate = postsService.handleCacheInvalidation(model);
|
|
|
|
|
2019-12-17 16:54:27 +03:00
|
|
|
return model;
|
2019-08-09 17:11:24 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
destroy: {
|
|
|
|
statusCode: 204,
|
|
|
|
headers: {
|
|
|
|
cacheInvalidate: true
|
|
|
|
},
|
|
|
|
options: [
|
|
|
|
'include',
|
|
|
|
'id'
|
|
|
|
],
|
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
values: allowedIncludes
|
|
|
|
},
|
|
|
|
id: {
|
|
|
|
required: true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
unsafeAttrs: unsafeAttrs
|
|
|
|
},
|
|
|
|
query(frame) {
|
|
|
|
frame.options.require = true;
|
|
|
|
|
|
|
|
return models.Post.destroy(frame.options)
|
2020-04-07 09:20:56 +03:00
|
|
|
.then(() => null)
|
2019-08-09 17:11:24 +03:00
|
|
|
.catch(models.Post.NotFoundError, () => {
|
2020-05-22 21:22:20 +03:00
|
|
|
return Promise.reject(new errors.NotFoundError({
|
|
|
|
message: i18n.t('errors.api.posts.postNotFound')
|
2020-04-13 13:20:51 +03:00
|
|
|
}));
|
2019-08-09 17:11:24 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|