0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Moved gated content block processing behind labs flag (#22101)

ref https://app.incident.io/ghost/incidents/137

- some sites make use of the Content API to fetch all posts in a single
request with high traffic volumes which results in a lot of data
processing to check for gated content that may cause higher CPU usage
and slowdown
- under an abundance of caution we're moving the related code behind a
labs flag whilst further performance testing is performed
- it should be noted that whilst this flag-conditional is in place, any
content that has already used gated blocks as part of alpha testing will
become visible if the flag is subsequently disabled
This commit is contained in:
Kevin Ansfield 2025-02-03 17:19:53 +00:00 committed by GitHub
parent 1a8d8061cd
commit dafe5ac09b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 38 deletions

View file

@ -1,4 +1,5 @@
const membersService = require('../../../../../../services/members');
const labs = require('../../../../../../../shared/labs');
const htmlToPlaintext = require('@tryghost/html-to-plaintext');
const {PERMIT_ACCESS} = membersService.contentGating;
@ -106,11 +107,13 @@ const forPost = (attrs, frame) => {
}
}
const hasGatedBlocks = HAS_GATED_BLOCKS_REGEX.test(attrs.html);
if (hasGatedBlocks) {
attrs.html = module.exports.stripGatedBlocks(attrs.html, frame.original.context.member);
_updatePlaintext(attrs);
_updateExcerpt(attrs);
if (labs.isSet('contentVisibility')) {
const hasGatedBlocks = HAS_GATED_BLOCKS_REGEX.test(attrs.html);
if (hasGatedBlocks) {
attrs.html = module.exports.stripGatedBlocks(attrs.html, frame.original.context.member);
_updatePlaintext(attrs);
_updateExcerpt(attrs);
}
}
if (!Object.prototype.hasOwnProperty.call(frame.options, 'columns') || (frame.options.columns.includes('access'))) {

View file

@ -2,6 +2,7 @@ const assert = require('assert/strict');
const sinon = require('sinon');
const gating = require('../../../../../../../../core/server/api/endpoints/utils/serializers/output/utils/post-gating');
const membersContentGating = require('../../../../../../../../core/server/services/members/content-gating');
const labs = require('../../../../../../../../core/shared/labs');
describe('Unit: endpoints/utils/serializers/output/utils/post-gating', function () {
afterEach(function () {
@ -90,45 +91,73 @@ describe('Unit: endpoints/utils/serializers/output/utils/post-gating', function
assert.equal(attrs.html, '<p>Can read this</p>');
});
it('does not call stripGatedBlocks when a post has no gated blocks', function () {
const attrs = {
visibility: 'public',
html: '<p>no gated blocks</p>'
};
describe('contentVisibility', function () {
let contentVisibilityStub;
const stripGatedBlocksStub = sinon.stub(gating, 'stripGatedBlocks');
gating.forPost(attrs, frame);
sinon.assert.notCalled(stripGatedBlocksStub);
});
beforeEach(function () {
contentVisibilityStub = sinon.stub(labs, 'isSet').withArgs('contentVisibility').returns(true);
});
it('calls stripGatedBlocks when a post has gated blocks', function () {
const attrs = {
visibility: 'public',
html: '<!--kg-gated-block:begin nonMember:true--><p>gated block</p><!--kg-gated-block:end-->'
};
afterEach(function () {
sinon.restore();
});
const stripGatedBlocksStub = sinon.stub(gating, 'stripGatedBlocks');
gating.forPost(attrs, frame);
sinon.assert.calledOnce(stripGatedBlocksStub);
});
it('does not call stripGatedBlocks when a post has no gated blocks', function () {
const attrs = {
visibility: 'public',
html: '<p>no gated blocks</p>'
};
it('updates html, plaintext, and excerpt when a post has gated blocks', function () {
const attrs = {
visibility: 'public',
html: `
<!--kg-gated-block:begin nonMember:false memberSegment:"status:free,status:-free"--><p>Members only.</p><!--kg-gated-block:end-->
<p>Everyone can see this.</p>
<!--kg-gated-block:begin nonMember:true--><p>Anonymous only.</p><!--kg-gated-block:end-->
`,
plaintext: 'Members only. Everyone can see this. Anonymous only.',
excerpt: 'Members only. Everyone can see this. Anonymous only.'
};
const stripGatedBlocksStub = sinon.stub(gating, 'stripGatedBlocks');
gating.forPost(attrs, frame);
sinon.assert.notCalled(stripGatedBlocksStub);
});
gating.forPost(attrs, frame);
it('calls stripGatedBlocks when a post has gated blocks', function () {
const attrs = {
visibility: 'public',
html: '<!--kg-gated-block:begin nonMember:true--><p>gated block</p><!--kg-gated-block:end-->'
};
assert.match(attrs.html, /<p>Everyone can see this\.<\/p>\n\s+<p>Anonymous only.<\/p>/);
assert.match(attrs.plaintext, /^\n+Everyone can see this.\n+Anonymous only.\n$/);
assert.match(attrs.excerpt, /^\n+Everyone can see this.\n+Anonymous only.\n$/);
const stripGatedBlocksStub = sinon.stub(gating, 'stripGatedBlocks');
gating.forPost(attrs, frame);
sinon.assert.calledOnce(stripGatedBlocksStub);
});
it('updates html, plaintext, and excerpt when a post has gated blocks', function () {
const attrs = {
visibility: 'public',
html: `
<!--kg-gated-block:begin nonMember:false memberSegment:"status:free,status:-free"--><p>Members only.</p><!--kg-gated-block:end-->
<p>Everyone can see this.</p>
<!--kg-gated-block:begin nonMember:true--><p>Anonymous only.</p><!--kg-gated-block:end-->
`,
plaintext: 'Members only. Everyone can see this. Anonymous only.',
excerpt: 'Members only. Everyone can see this. Anonymous only.'
};
gating.forPost(attrs, frame);
assert.match(attrs.html, /<p>Everyone can see this\.<\/p>\n\s+<p>Anonymous only.<\/p>/);
assert.match(attrs.plaintext, /^\n+Everyone can see this.\n+Anonymous only.\n$/);
assert.match(attrs.excerpt, /^\n+Everyone can see this.\n+Anonymous only.\n$/);
});
it('does not process gated blocks with contentVisibility flag disabled', function () {
contentVisibilityStub.returns(false);
const regexSpy = sinon.spy(RegExp.prototype, 'test');
const stripGatedBlocksStub = sinon.stub(gating, 'stripGatedBlocks');
const attrs = {
visibility: 'public',
html: '<!--kg-gated-block:begin nonMember:true--><p>gated block</p><!--kg-gated-block:end-->'
};
gating.forPost(attrs, frame);
sinon.assert.notCalled(regexSpy);
sinon.assert.notCalled(stripGatedBlocksStub);
});
});
});