🐛 Fixed Slack integration using member content in excerpt (#20328)

- refs
[ONC-63](https://linear.app/tryghost/issue/ONC-63/discordslack-webhook-integration)
- fixes [#20304](https://github.com/TryGhost/Ghost/issues/20304)

When a post is published and sent to Slack via webhook, the excerpt
generated could contain member content. This change ensures that the
excerpt does not contain member content. This change also ensures that
the author for the post is correctly shown in Slack
This commit is contained in:
Michael Barrett 2024-06-05 17:46:21 +01:00 committed by GitHub
parent b447a26832
commit 0f283da8eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 5 deletions

View File

@ -63,7 +63,24 @@ function ping(post) {
if (post.custom_excerpt) {
description = post.custom_excerpt;
} else if (post.html) {
description = `${post.html.replace(/<[^>]+>/g, '').split('.').slice(0, 3).join('.')}.`;
const membersContentIdx = post.html.indexOf('<!--members-only-->');
const substringEnd = membersContentIdx > -1 ? membersContentIdx : post.html.length;
description = `${
post.html
// Remove members-only content
.substring(0, substringEnd)
// Strip out HTML
.replace(/<[^>]+>/g, '')
// Split into sentences
.split('.')
// Remove empty strings
.filter(sentence => sentence.trim() !== '')
// Get the first three sentences
.slice(0, 3)
// Join 'em back together
.join('.')
}.`;
} else {
description = null;
}
@ -160,7 +177,10 @@ function slackListener(model, options) {
return;
}
ping(model.toJSON());
ping({
...model.toJSON(),
authors: model.related('authors').toJSON()
});
}
function slackTestPing() {

View File

@ -38,10 +38,22 @@ describe('Slack', function () {
it('listener() calls ping() with toJSONified model', function () {
const testPost = _.clone(testUtils.DataGenerator.Content.posts[2]);
const testAuthor = _.clone(testUtils.DataGenerator.Content.users[0]);
const testModel = {
toJSON: function () {
return testPost;
},
related: function (relation) {
return {
toJSON: function () {
if (relation === 'authors') {
return [testAuthor];
}
return [];
}
};
}
};
@ -52,7 +64,10 @@ describe('Slack', function () {
listener(testModel);
pingStub.calledOnce.should.be.true();
pingStub.calledWith(testPost).should.be.true();
pingStub.calledWith({
...testPost,
authors: [testAuthor]
}).should.be.true();
// Reset slack ping method
resetSlack();
@ -121,7 +136,10 @@ describe('Slack', function () {
let requestUrl;
let requestData;
const post = testUtils.DataGenerator.forKnex.createPost({slug: 'webhook-test'});
const post = testUtils.DataGenerator.forKnex.createPost({
slug: 'webhook-test',
html: `<p>Hello World!</p><p>This is a test post.</p><!--members-only--><p>This is members only content.</p>`
});
urlService.getUrlByResourceId.withArgs(post.id, {absolute: true}).returns('http://myblog.com/' + post.slug + '/');
settingsCacheStub.withArgs('slack_url').returns(slackURL);
@ -140,7 +158,7 @@ describe('Slack', function () {
requestUrl.should.equal(slackURL);
requestData.attachments[0].title.should.equal(post.title);
requestData.attachments[0].title_link.should.equal('http://myblog.com/webhook-test/');
requestData.attachments[0].fields[0].value.should.equal('## markdown.');
requestData.attachments[0].fields[0].value.should.equal('Hello World!This is a test post.');
requestData.attachments[0].should.not.have.property('author_name');
requestData.icon_url.should.equal('http://myblog.com/favicon.ico');