diff --git a/ghost/external-media-inliner/lib/ExternalMediaInliner.js b/ghost/external-media-inliner/lib/ExternalMediaInliner.js index 08da62816c..3899a57ac5 100644 --- a/ghost/external-media-inliner/lib/ExternalMediaInliner.js +++ b/ghost/external-media-inliner/lib/ExternalMediaInliner.js @@ -108,7 +108,10 @@ class ExternalMediaInliner { async #inlineMibiledoc(mobiledoc, domains) { for (const domain of domains) { - const regex = new RegExp(`"src":"(${domain}.*?)"`, 'igm'); + // NOTE: the src could end with a quote, apostrophe or double-backslash. backlashes are added to mobiledoc + // as an escape character + const srcTerminationSymbols = `"|'|\\\\`; + const regex = new RegExp(`(${domain}.*?)(${srcTerminationSymbols})`, 'igm'); const matches = mobiledoc.matchAll(regex); for (const [,src] of matches) { diff --git a/ghost/external-media-inliner/test/ExternalMediaInliner.test.js b/ghost/external-media-inliner/test/ExternalMediaInliner.test.js index 4aa40ceb04..df04a2d8c1 100644 --- a/ghost/external-media-inliner/test/ExternalMediaInliner.test.js +++ b/ghost/external-media-inliner/test/ExternalMediaInliner.test.js @@ -110,6 +110,56 @@ describe('ExternalMediaInliner', function () { })); }); + it('inlines the image from post\'s mobiledoc containing html card', async function () { + const imageURL = 'https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/39719fcb-5af0-4764-bf8b-d375f37a09e5_1141x860'; + const requestMock = nock('https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com') + .get('/public/images/39719fcb-5af0-4764-bf8b-d375f37a09e5_1141x860') + .reply(200, GIF1x1); + + const postModelInstanceStub = { + id: 'inlined-post-with-htmlcard-id', + get: sinon.stub() + .withArgs('mobiledoc') + .returns(`{"version":"0.3.1","atoms":[],"cards":[["html",{"html":"Lorem ipsum"}]],"markups":[],"sections":[[10,0],[1,"p",[]]],"ghostVersion":"4.0"}`) + }; + + postModelStub = { + findPage: sinon.stub().returns({ + data: [postModelInstanceStub] + }), + edit: sinon.stub().resolves() + }; + + sinon.stub(path, 'relative') + .withArgs('/content/images', '/content/images/unique-image.jpg') + .returns('unique-image.jpg'); + const inliner = new ExternalMediaInliner({ + PostModel: postModelStub, + PostMetaModel: postMetaModelStub, + TagModel: tagModelStub, + UserModel: userModelStub, + getMediaStorage: sinon.stub().withArgs('.jpg').returns({ + getTargetDir: () => '/content/images', + getUniqueFileName: () => '/content/images/unique-image.jpg', + saveRaw: () => '/content/images/unique-image.jpg' + }) + }); + + await inliner.inline(['https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com']); + + assert.ok(requestMock.isDone()); + assert.ok(postModelStub.edit.calledOnce); + assert.deepEqual(postModelStub.edit.args[0][0], { + mobiledoc: `{"version":"0.3.1","atoms":[],"cards":[["html",{"html":"Lorem ipsum"}]],"markups":[],"sections":[[10,0],[1,"p",[]]],"ghostVersion":"4.0"}` + }); + assert.deepEqual(postModelStub.edit.args[0][1], { + id: 'inlined-post-with-htmlcard-id', + context: { + internal: true + } + }); + }); + it('logs an error when fetching an external media fails', async function () { const imageURL = 'https://img.stockfresh.com/files/f/image.jpg'; const requestMock = nock('https://img.stockfresh.com')