Add Lexical support to the external-media-inliner (#19149)
This commit is contained in:
parent
57e767860d
commit
9432fc0f4b
@ -129,13 +129,21 @@ class ExternalMediaInliner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async inlineMobiledoc(mobiledoc, domains) {
|
/**
|
||||||
|
* Find & inline external media from a JSON sting.
|
||||||
|
* This works with both Lexical & Mobiledoc, so no separate methods are needed here.
|
||||||
|
*
|
||||||
|
* @param {string} content - stringified JSON of post Lexical or Mobiledoc content
|
||||||
|
* @param {String[]} domains - domains to inline media from
|
||||||
|
* @returns {Promise<string>} - updated stringified JSON of post content
|
||||||
|
*/
|
||||||
|
async inlineContent(content, domains) {
|
||||||
for (const domain of domains) {
|
for (const domain of domains) {
|
||||||
// NOTE: the src could end with a quote, apostrophe or double-backslash. backlashes are added to mobiledoc
|
// NOTE: the src could end with a quote, apostrophe or double-backslash. backlashes are added to content
|
||||||
// as an escape character
|
// as an escape character
|
||||||
const srcTerminationSymbols = `"|'|\\\\`;
|
const srcTerminationSymbols = `"|'|\\\\`;
|
||||||
const regex = new RegExp(`(${domain}.*?)(${srcTerminationSymbols})`, 'igm');
|
const regex = new RegExp(`(${domain}.*?)(${srcTerminationSymbols})`, 'igm');
|
||||||
const matches = mobiledoc.matchAll(regex);
|
const matches = content.matchAll(regex);
|
||||||
|
|
||||||
for (const [,src] of matches) {
|
for (const [,src] of matches) {
|
||||||
const response = await this.getRemoteMedia(src);
|
const response = await this.getRemoteMedia(src);
|
||||||
@ -151,16 +159,16 @@ class ExternalMediaInliner {
|
|||||||
if (filePath) {
|
if (filePath) {
|
||||||
const inlinedSrc = `__GHOST_URL__${filePath}`;
|
const inlinedSrc = `__GHOST_URL__${filePath}`;
|
||||||
|
|
||||||
// NOTE: does not account for duplicate images in mobiledoc
|
// NOTE: does not account for duplicate images in content
|
||||||
// in those cases would be processed twice
|
// in those cases would be processed twice
|
||||||
mobiledoc = mobiledoc.replace(src, inlinedSrc);
|
content = content.replace(src, inlinedSrc);
|
||||||
logging.info(`Inlined media: ${src} -> ${inlinedSrc}`);
|
logging.info(`Inlined media: ${src} -> ${inlinedSrc}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mobiledoc;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -250,11 +258,27 @@ class ExternalMediaInliner {
|
|||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
try {
|
try {
|
||||||
const inlinedMobiledoc = await this.inlineMobiledoc(post.get('mobiledoc'), domains);
|
const mobiledocContent = post.get('mobiledoc');
|
||||||
|
const lexicalContent = post.get('lexical');
|
||||||
|
|
||||||
const updatedFields = await this.inlineFields(post, postsInilingFields, domains);
|
const updatedFields = await this.inlineFields(post, postsInilingFields, domains);
|
||||||
|
|
||||||
if (inlinedMobiledoc !== post.get('mobiledoc')) {
|
if (mobiledocContent) {
|
||||||
updatedFields.mobiledoc = inlinedMobiledoc;
|
const inlinedContent = await this.inlineContent(mobiledocContent, domains);
|
||||||
|
|
||||||
|
// If content has changed, update the post
|
||||||
|
if (inlinedContent !== mobiledocContent) {
|
||||||
|
updatedFields.mobiledoc = inlinedContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexicalContent) {
|
||||||
|
const inlinedContent = await this.inlineContent(lexicalContent, domains);
|
||||||
|
|
||||||
|
// If content has changed, update the post
|
||||||
|
if (inlinedContent !== lexicalContent) {
|
||||||
|
updatedFields.lexical = inlinedContent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(updatedFields).length > 0) {
|
if (Object.keys(updatedFields).length > 0) {
|
||||||
|
@ -69,11 +69,13 @@ describe('ExternalMediaInliner', function () {
|
|||||||
.get('/files/f/image.jpg')
|
.get('/files/f/image.jpg')
|
||||||
.reply(200, GIF1x1);
|
.reply(200, GIF1x1);
|
||||||
|
|
||||||
|
const postStub = sinon.stub();
|
||||||
|
postStub.withArgs('mobiledoc').returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`);
|
||||||
|
postStub.withArgs('lexical').returns(null);
|
||||||
|
|
||||||
const postModelInstanceStub = {
|
const postModelInstanceStub = {
|
||||||
id: 'inlined-post-id',
|
id: 'inlined-post-id',
|
||||||
get: sinon.stub()
|
get: postStub
|
||||||
.withArgs('mobiledoc')
|
|
||||||
.returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`)
|
|
||||||
};
|
};
|
||||||
postModelStub = {
|
postModelStub = {
|
||||||
findPage: sinon.stub().returns({
|
findPage: sinon.stub().returns({
|
||||||
@ -117,11 +119,13 @@ describe('ExternalMediaInliner', function () {
|
|||||||
.get('/public/images/39719fcb-5af0-4764-bf8b-d375f37a09e5_1141x860')
|
.get('/public/images/39719fcb-5af0-4764-bf8b-d375f37a09e5_1141x860')
|
||||||
.reply(200, GIF1x1);
|
.reply(200, GIF1x1);
|
||||||
|
|
||||||
|
const postStub = sinon.stub();
|
||||||
|
postStub.withArgs('mobiledoc').returns(`{"version":"0.3.1","atoms":[],"cards":[["html",{"html":"<img src="${imageURL}" alt="Lorem ipsum">"}]],"markups":[],"sections":[[10,0],[1,"p",[]]],"ghostVersion":"4.0"}`);
|
||||||
|
postStub.withArgs('lexical').returns(null);
|
||||||
|
|
||||||
const postModelInstanceStub = {
|
const postModelInstanceStub = {
|
||||||
id: 'inlined-post-with-htmlcard-id',
|
id: 'inlined-post-with-htmlcard-id',
|
||||||
get: sinon.stub()
|
get: postStub
|
||||||
.withArgs('mobiledoc')
|
|
||||||
.returns(`{"version":"0.3.1","atoms":[],"cards":[["html",{"html":"<img src="${imageURL}" alt="Lorem ipsum">"}]],"markups":[],"sections":[[10,0],[1,"p",[]]],"ghostVersion":"4.0"}`)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
postModelStub = {
|
postModelStub = {
|
||||||
@ -161,6 +165,161 @@ describe('ExternalMediaInliner', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('inlines image in the post\'s lexical content', async function () {
|
||||||
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
||||||
|
const requestMock = nock('https://img.stockfresh.com')
|
||||||
|
.get('/files/f/image.jpg')
|
||||||
|
.reply(200, GIF1x1);
|
||||||
|
|
||||||
|
const postStub = sinon.stub();
|
||||||
|
postStub.withArgs('mobiledoc').returns(null);
|
||||||
|
postStub.withArgs('lexical').returns(`{"root":{"children":[{"type":"image","version":1,"src":"${imageURL}","width":1480,"height":486,"title":"","alt":"","caption":"","cardWidth":"regular","href":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}`);
|
||||||
|
|
||||||
|
const postModelInstanceStub = {
|
||||||
|
id: 'inlined-post-id',
|
||||||
|
get: postStub
|
||||||
|
};
|
||||||
|
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://img.stockfresh.com']);
|
||||||
|
|
||||||
|
assert.ok(requestMock.isDone());
|
||||||
|
assert.ok(postModelStub.edit.calledOnce);
|
||||||
|
assert.ok(postModelStub.edit.calledWith({
|
||||||
|
lexical: '{"root":{"children":[{"type":"image","version":1,"src":"__GHOST_URL__/content/images/unique-image.jpg","width":1480,"height":486,"title":"","alt":"","caption":"","cardWidth":"regular","href":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'
|
||||||
|
}, {
|
||||||
|
id: 'inlined-post-id',
|
||||||
|
context: {
|
||||||
|
internal: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inlines the image from post\'s lexical containing html card', async function () {
|
||||||
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
||||||
|
const requestMock = nock('https://img.stockfresh.com')
|
||||||
|
.get('/files/f/image.jpg')
|
||||||
|
.reply(200, GIF1x1);
|
||||||
|
|
||||||
|
const postStub = sinon.stub();
|
||||||
|
postStub.withArgs('mobiledoc').returns(null);
|
||||||
|
postStub.withArgs('lexical').returns(`{"root":{"children":[{"type":"html","version":1,"html":"<img src="${imageURL}" alt="Lorem ipsum">"}],"direction":null,"format":"","indent":0,"type":"root","version":1}}`);
|
||||||
|
|
||||||
|
const postModelInstanceStub = {
|
||||||
|
id: 'inlined-post-with-htmlcard-id',
|
||||||
|
get: postStub
|
||||||
|
};
|
||||||
|
|
||||||
|
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://img.stockfresh.com']);
|
||||||
|
|
||||||
|
assert.ok(requestMock.isDone());
|
||||||
|
assert.ok(postModelStub.edit.calledOnce);
|
||||||
|
assert.deepEqual(postModelStub.edit.args[0][0], {
|
||||||
|
lexical: `{"root":{"children":[{"type":"html","version":1,"html":"<img src="__GHOST_URL__/content/images/unique-image.jpg" alt="Lorem ipsum">"}],"direction":null,"format":"","indent":0,"type":"root","version":1}}`
|
||||||
|
});
|
||||||
|
assert.deepEqual(postModelStub.edit.args[0][1], {
|
||||||
|
id: 'inlined-post-with-htmlcard-id',
|
||||||
|
context: {
|
||||||
|
internal: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('inlines image in the post\'s mobiledoc & lexical content', async function () {
|
||||||
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
||||||
|
const requestMock = nock('https://img.stockfresh.com')
|
||||||
|
.get('/files/f/image.jpg')
|
||||||
|
.reply(200, GIF1x1)
|
||||||
|
.get('/files/f/image.jpg')
|
||||||
|
.reply(200, GIF1x1);
|
||||||
|
|
||||||
|
const postStub = sinon.stub();
|
||||||
|
postStub.withArgs('mobiledoc').returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`);
|
||||||
|
postStub.withArgs('lexical').returns(`{"root":{"children":[{"type":"image","version":1,"src":"${imageURL}","width":1480,"height":486,"title":"","alt":"","caption":"","cardWidth":"regular","href":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}`);
|
||||||
|
|
||||||
|
const postModelInstanceStub = {
|
||||||
|
id: 'inlined-post-id',
|
||||||
|
get: postStub
|
||||||
|
};
|
||||||
|
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://img.stockfresh.com']);
|
||||||
|
|
||||||
|
assert.ok(requestMock.isDone());
|
||||||
|
assert.ok(postModelStub.edit.calledOnce);
|
||||||
|
assert.ok(postModelStub.edit.calledWith({
|
||||||
|
mobiledoc: '{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"__GHOST_URL__/content/images/unique-image.jpg"}]]}',
|
||||||
|
lexical: '{"root":{"children":[{"type":"image","version":1,"src":"__GHOST_URL__/content/images/unique-image.jpg","width":1480,"height":486,"title":"","alt":"","caption":"","cardWidth":"regular","href":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'
|
||||||
|
}, {
|
||||||
|
id: 'inlined-post-id',
|
||||||
|
context: {
|
||||||
|
internal: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it('logs an error when fetching an external media fails', async function () {
|
it('logs an error when fetching an external media fails', async function () {
|
||||||
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
const imageURL = 'https://img.stockfresh.com/files/f/image.jpg';
|
||||||
const requestMock = nock('https://img.stockfresh.com')
|
const requestMock = nock('https://img.stockfresh.com')
|
||||||
@ -262,11 +421,13 @@ describe('ExternalMediaInliner', function () {
|
|||||||
.get('/files/f/image.jpg')
|
.get('/files/f/image.jpg')
|
||||||
.reply(200, GIF1x1);
|
.reply(200, GIF1x1);
|
||||||
|
|
||||||
|
const postStub = sinon.stub();
|
||||||
|
postStub.withArgs('mobiledoc').returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`);
|
||||||
|
postStub.withArgs('lexical').returns(null);
|
||||||
|
|
||||||
postModelStub = {
|
postModelStub = {
|
||||||
id: 'errored-post-id',
|
id: 'errored-post-id',
|
||||||
get: sinon.stub()
|
get: postStub
|
||||||
.withArgs('mobiledoc')
|
|
||||||
.returns(`{"version":"0.3.1","atoms":[],"cards":[["image",{"src":"${imageURL}"}]]}`)
|
|
||||||
};
|
};
|
||||||
postModelStub = {
|
postModelStub = {
|
||||||
findPage: sinon.stub().returns({
|
findPage: sinon.stub().returns({
|
||||||
|
Loading…
Reference in New Issue
Block a user