diff --git a/packages/netlify-cms-media-library-uploadcare/src/__tests__/index.spec.js b/packages/netlify-cms-media-library-uploadcare/src/__tests__/index.spec.js index 28cd20f1..b2742eab 100644 --- a/packages/netlify-cms-media-library-uploadcare/src/__tests__/index.spec.js +++ b/packages/netlify-cms-media-library-uploadcare/src/__tests__/index.spec.js @@ -51,9 +51,10 @@ describe('uploadcare media library', () => { /** * Mock to manually call the close dialog registered callback. */ - simulateCloseDialog = result => + simulateCloseDialog = (result, files) => openDialogCallback({ promise: () => Promise.resolve(result), + ...(files ? { files: () => files.map(file => Promise.resolve(file)) } : {}), }); /** @@ -90,7 +91,7 @@ Object { }); }); - describe('configuration', () => { + describe('widget configuration', () => { const options = { config: { foo: 'bar', @@ -179,13 +180,106 @@ Object { options.config.multiple = true; const { result, cdnUrl } = generateMockUrl({ count: 3, cdnUrl: true }); const mockDialogCloseResult = { cdnUrl, count: 3 }; + const mockDialogCloseFiles = result.map((cdnUrl, idx) => ({ + cdnUrl, + isImage: true, + name: `test${idx}.png`, + })); const integration = await uploadcareMediaLibrary.init({ options, handleInsert }); await integration.show(); - await simulateCloseDialog(mockDialogCloseResult); + await simulateCloseDialog(mockDialogCloseResult, mockDialogCloseFiles); expect(handleInsert).toHaveBeenCalledWith(result); }); }); + describe('settings', () => { + describe('defaultOperations', () => { + it('should append specified string to the url', async () => { + const options = { + config: { + publicKey: TEST_PUBLIC_KEY, + }, + settings: { + defaultOperations: '/preview/', + }, + }; + const url = generateMockUrl(); + const mockResult = { cdnUrl: url, isImage: true }; + const integration = await uploadcareMediaLibrary.init({ + options, + handleInsert, + }); + await integration.show(); + await simulateCloseDialog(mockResult); + expect(handleInsert).toHaveBeenCalledWith(url + '-/preview/'); + }); + + it('should work along with `autoFilename` setting enabled', async () => { + const options = { + config: { + publicKey: TEST_PUBLIC_KEY, + }, + settings: { + autoFilename: true, + defaultOperations: '/preview/', + }, + }; + const url = generateMockUrl(); + const mockResult = { cdnUrl: url, isImage: true, name: 'test.png' }; + const integration = await uploadcareMediaLibrary.init({ + options, + handleInsert, + }); + await integration.show(); + await simulateCloseDialog(mockResult); + expect(handleInsert).toHaveBeenCalledWith(url + '-/preview/test.png'); + }); + + it('should overwrite filename with `autoFilename` setting enabled', async () => { + const options = { + config: { + publicKey: TEST_PUBLIC_KEY, + }, + settings: { + autoFilename: true, + defaultOperations: '/preview/another_name.png', + }, + }; + const url = generateMockUrl(); + const mockResult = { cdnUrl: url, isImage: true, name: 'test.png' }; + const integration = await uploadcareMediaLibrary.init({ + options, + handleInsert, + }); + await integration.show(); + await simulateCloseDialog(mockResult); + expect(handleInsert).toHaveBeenCalledWith(url + '-/preview/another_name.png'); + }); + }); + + describe('autoFilename', () => { + it('should append filename to the url', async () => { + const options = { + config: { + publicKey: TEST_PUBLIC_KEY, + }, + settings: { + autoFilename: true, + }, + }; + const url = generateMockUrl(); + const mockResult = { cdnUrl: url, isImage: true, name: 'test.png' }; + const integration = await uploadcareMediaLibrary.init({ + options, + handleInsert, + }); + await integration.show(); + await simulateCloseDialog(mockResult); + expect(handleInsert).toHaveBeenCalledWith(url + 'test.png'); + }); + }); + }); + describe('enableStandalone method', () => { it('returns false', async () => { const integration = await uploadcareMediaLibrary.init(); diff --git a/packages/netlify-cms-media-library-uploadcare/src/index.js b/packages/netlify-cms-media-library-uploadcare/src/index.js index 7ea2bda1..0463976c 100644 --- a/packages/netlify-cms-media-library-uploadcare/src/index.js +++ b/packages/netlify-cms-media-library-uploadcare/src/index.js @@ -73,24 +73,47 @@ function getFile(url) { * Open the standalone dialog. A single instance is created and destroyed for * each use. */ -function openDialog(files, config, handleInsert) { - uploadcare.openDialog(files, config).done(({ promise }) => - promise().then(({ cdnUrl, count }) => { - if (config.multiple) { - const urls = Array.from({ length: count }, (val, idx) => `${cdnUrl}nth/${idx}/`); - handleInsert(urls); +function openDialog({ files, config, handleInsert, settings = {} }) { + if (settings.defaultOperations && !settings.defaultOperations.startsWith('/')) { + console.warn( + 'Uploadcare default operations should start with `/`. Example: `/preview/-/resize/100x100/image.png`', + ); + } + + const buildUrl = fileInfo => { + const { cdnUrl, name, isImage } = fileInfo; + + let url = + isImage && settings.defaultOperations ? `${cdnUrl}-${settings.defaultOperations}` : cdnUrl; + const filenameDefined = !url.endsWith('/'); + + if (!filenameDefined && settings.autoFilename) { + url = url + name; + } + + return url; + }; + + uploadcare.openDialog(files, config).done(({ promise, files }) => { + const isGroup = Boolean(files); + + return promise().then(info => { + if (isGroup) { + return Promise.all( + files().map(promise => promise.then(fileInfo => buildUrl(fileInfo))), + ).then(urls => handleInsert(urls)); } else { - handleInsert(cdnUrl); + handleInsert(buildUrl(info)); } - }), - ); + }); + }); } /** * Initialization function will only run once, returns an API object for Netlify * CMS to call methods on. */ -async function init({ options = { config: {} }, handleInsert } = {}) { +async function init({ options = { config: {}, settings: {} }, handleInsert } = {}) { const { publicKey, ...globalConfig } = options.config; const baseConfig = { ...defaultConfig, ...globalConfig }; @@ -118,9 +141,21 @@ async function init({ options = { config: {} }, handleInsert } = {}) { * from the Uploadcare library will have a `state` method. */ if (files && !files.state) { - return files.then(result => openDialog(result, resolvedConfig, handleInsert)); + return files.then(result => + openDialog({ + files: result, + config: resolvedConfig, + settings: options.settings, + handleInsert, + }), + ); } else { - return openDialog(files, resolvedConfig, handleInsert); + return openDialog({ + files, + config: resolvedConfig, + settings: options.settings, + handleInsert, + }); } }, diff --git a/website/content/docs/uploadcare.md b/website/content/docs/uploadcare.md index 9d8eb1a4..28804473 100644 --- a/website/content/docs/uploadcare.md +++ b/website/content/docs/uploadcare.md @@ -78,3 +78,25 @@ For example: multiple: true previewStep: false ``` + +## Integration settings + +There are several settings that control the behavior of integration with the widget. + +* `autoFilename` (`boolean`) - specify whether to add a filename to the end of the url. + Example: `http://ucarecdn.com/:uuid/filename.png` +* `defaultOperations` (`string`) - specify a string added at the end of the url. + This could be useful to apply a set of CDN operations to each image, + for example resizing or compression. All the possible operations are listed + [here](https://uploadcare.com/docs/api_reference/cdn/). + + +```yaml +media_library: + name: uploadcare + config: + publicKey: demopublickey + settings: + autoFilename: true + defaultOperations: '/resize/800x600/' +```