From b710cc5619e3ba22bc2c81c1fc39aa1e207a9ad7 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Mon, 3 Jun 2019 11:30:52 +0100 Subject: [PATCH] Extract generic WordPress mediaUtils package. (#15521) This PR extracts the mediaUpload function part of @wordpress/editor to an independent package and now calls it uploadMedia. The MediaUpload component used to control WordPress media library is also part of this new package. The package is used to implement media upload in the widgets screen. --- docs/manifest-devhub.json | 6 +++ docs/manifest.json | 8 +++- package-lock.json | 12 +++++ package.json | 1 + .../edit-post/src/hooks/components/index.js | 8 +--- .../src/components/widget-area/index.js | 33 +++++++++++++- .../src/hooks/components/index.js | 13 ++++++ packages/edit-widgets/src/hooks/index.js | 4 ++ packages/edit-widgets/src/index.js | 1 + packages/editor/package.json | 1 + .../editor/src/utils/media-upload/index.js | 8 +--- packages/media-utils/.npmrc | 1 + packages/media-utils/CHANGELOG.md | 5 +++ packages/media-utils/README.md | 45 +++++++++++++++++++ packages/media-utils/package.json | 35 +++++++++++++++ packages/media-utils/src/components/index.js | 1 + .../src}/components/media-upload/index.js | 0 packages/media-utils/src/index.js | 2 + packages/media-utils/src/utils/index.js | 1 + .../src/utils/test/upload-media.test.js} | 22 ++++----- .../src/utils/upload-media.js} | 2 +- 21 files changed, 183 insertions(+), 26 deletions(-) create mode 100644 packages/edit-widgets/src/hooks/components/index.js create mode 100644 packages/edit-widgets/src/hooks/index.js create mode 100644 packages/media-utils/.npmrc create mode 100644 packages/media-utils/CHANGELOG.md create mode 100644 packages/media-utils/README.md create mode 100644 packages/media-utils/package.json create mode 100644 packages/media-utils/src/components/index.js rename packages/{edit-post/src/hooks => media-utils/src}/components/media-upload/index.js (100%) create mode 100644 packages/media-utils/src/index.js create mode 100644 packages/media-utils/src/utils/index.js rename packages/{editor/src/utils/media-upload/test/media-upload.js => media-utils/src/utils/test/upload-media.test.js} (95%) rename packages/{editor/src/utils/media-upload/media-upload.js => media-utils/src/utils/upload-media.js} (99%) diff --git a/docs/manifest-devhub.json b/docs/manifest-devhub.json index f762d1422c454..14b29c43ddb98 100644 --- a/docs/manifest-devhub.json +++ b/docs/manifest-devhub.json @@ -1301,6 +1301,12 @@ "markdown_source": "../packages/list-reusable-blocks/README.md", "parent": "packages" }, + { + "title": "@wordpress/media-utils", + "slug": "packages-media-utils", + "markdown_source": "../packages/media-utils/README.md", + "parent": "packages" + }, { "title": "@wordpress/notices", "slug": "packages-notices", diff --git a/docs/manifest.json b/docs/manifest.json index 6fbe894c39d69..6cee0109f4749 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -449,6 +449,12 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/list-reusable-blocks/README.md", "parent": "packages" }, + { + "title": "@wordpress/media-utils", + "slug": "packages-media-utils", + "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/packages/media-utils/README.md", + "parent": "packages" + }, { "title": "@wordpress/notices", "slug": "packages-notices", @@ -1337,4 +1343,4 @@ "markdown_source": "https://raw.githubusercontent.com/WordPress/gutenberg/master/docs/contributors/outreach.md", "parent": "contributors" } -] \ No newline at end of file +] diff --git a/package-lock.json b/package-lock.json index 00d2e2b1810a0..accdd4ccea7fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3584,6 +3584,7 @@ "@wordpress/html-entities": "file:packages/html-entities", "@wordpress/i18n": "file:packages/i18n", "@wordpress/keycodes": "file:packages/keycodes", + "@wordpress/media-utils": "file:packages/media-utils", "@wordpress/notices": "file:packages/notices", "@wordpress/nux": "file:packages/nux", "@wordpress/url": "file:packages/url", @@ -3725,6 +3726,17 @@ "lodash": "^4.17.11" } }, + "@wordpress/media-utils": { + "version": "file:packages/media-utils", + "requires": { + "@babel/runtime": "^7.4.4", + "@wordpress/api-fetch": "file:packages/api-fetch", + "@wordpress/blob": "file:packages/blob", + "@wordpress/element": "file:packages/element", + "@wordpress/i18n": "file:packages/i18n", + "lodash": "^4.17.11" + } + }, "@wordpress/notices": { "version": "file:packages/notices", "requires": { diff --git a/package.json b/package.json index 54a3034abafc9..7b5d744f8d347 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/list-reusable-blocks": "file:packages/list-reusable-blocks", + "@wordpress/media-utils": "file:packages/media-utils", "@wordpress/notices": "file:packages/notices", "@wordpress/nux": "file:packages/nux", "@wordpress/plugins": "file:packages/plugins", diff --git a/packages/edit-post/src/hooks/components/index.js b/packages/edit-post/src/hooks/components/index.js index 20f16a7461def..709d89d013ed5 100644 --- a/packages/edit-post/src/hooks/components/index.js +++ b/packages/edit-post/src/hooks/components/index.js @@ -2,16 +2,12 @@ * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; - -/** - * Internal dependencies - */ -import MediaUpload from './media-upload'; +import { MediaUpload } from '@wordpress/media-utils'; const replaceMediaUpload = () => MediaUpload; addFilter( 'editor.MediaUpload', - 'core/edit-post/components/media-upload/replace-media-upload', + 'core/edit-post/replace-media-upload', replaceMediaUpload ); diff --git a/packages/edit-widgets/src/components/widget-area/index.js b/packages/edit-widgets/src/components/widget-area/index.js index 4825a45cefc6e..0397a77c02957 100644 --- a/packages/edit-widgets/src/components/widget-area/index.js +++ b/packages/edit-widgets/src/components/widget-area/index.js @@ -1,6 +1,13 @@ +/** + * External dependencies + */ +import { defaultTo } from 'lodash'; + /** * WordPress dependencies */ +import { useMemo } from '@wordpress/element'; +import { uploadMedia } from '@wordpress/media-utils'; import { compose } from '@wordpress/compose'; import { Panel, PanelBody } from '@wordpress/components'; import { @@ -9,13 +16,35 @@ import { } from '@wordpress/block-editor'; import { withDispatch, withSelect } from '@wordpress/data'; +function getBlockEditorSettings( blockEditorSettings, hasUploadPermissions ) { + if ( ! hasUploadPermissions ) { + return blockEditorSettings; + } + const mediaUploadBlockEditor = ( { onError, ...argumentsObject } ) => { + uploadMedia( { + wpAllowedMimeTypes: blockEditorSettings.allowedMimeTypes, + onError: ( { message } ) => onError( message ), + ...argumentsObject, + } ); + }; + return { + ...blockEditorSettings, + __experimentalMediaUpload: mediaUploadBlockEditor, + }; +} + function WidgetArea( { blockEditorSettings, blocks, initialOpen, updateBlocks, widgetAreaName, + hasUploadPermissions, } ) { + const settings = useMemo( + () => getBlockEditorSettings( blockEditorSettings, hasUploadPermissions ), + [ blockEditorSettings, hasUploadPermissions ] + ); return ( @@ -41,11 +70,13 @@ export default compose( [ getBlocksFromWidgetArea, getWidgetArea, } = select( 'core/edit-widgets' ); + const { canUser } = select( 'core' ); const blocks = getBlocksFromWidgetArea( id ); const widgetAreaName = ( getWidgetArea( id ) || {} ).name; return { blocks, widgetAreaName, + hasUploadPermissions: defaultTo( canUser( 'create', 'media' ), true ), }; } ), withDispatch( ( dispatch, { id } ) => { diff --git a/packages/edit-widgets/src/hooks/components/index.js b/packages/edit-widgets/src/hooks/components/index.js new file mode 100644 index 0000000000000..4c24e365661fa --- /dev/null +++ b/packages/edit-widgets/src/hooks/components/index.js @@ -0,0 +1,13 @@ +/** + * WordPress dependencies + */ +import { addFilter } from '@wordpress/hooks'; +import { MediaUpload } from '@wordpress/media-utils'; + +const replaceMediaUpload = () => MediaUpload; + +addFilter( + 'editor.MediaUpload', + 'core/edit-widgets/replace-media-upload', + replaceMediaUpload +); diff --git a/packages/edit-widgets/src/hooks/index.js b/packages/edit-widgets/src/hooks/index.js new file mode 100644 index 0000000000000..c6cbc1d173e86 --- /dev/null +++ b/packages/edit-widgets/src/hooks/index.js @@ -0,0 +1,4 @@ +/** + * Internal dependencies + */ +import './components'; diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index c61d4c2f9bb78..4480c5b0a06ec 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -7,6 +7,7 @@ import { registerCoreBlocks } from '@wordpress/block-library'; /** * Internal dependencies */ +import './hooks'; import './store'; import EditWidgetsInitializer from './components/edit-widgets-initializer'; diff --git a/packages/editor/package.json b/packages/editor/package.json index 74384ff0ce390..9578fdf41caf1 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -38,6 +38,7 @@ "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", "@wordpress/keycodes": "file:../keycodes", + "@wordpress/media-utils": "file:../media-utils", "@wordpress/notices": "file:../notices", "@wordpress/nux": "file:../nux", "@wordpress/url": "file:../url", diff --git a/packages/editor/src/utils/media-upload/index.js b/packages/editor/src/utils/media-upload/index.js index f6572e29c6b0c..dc79320b703be 100644 --- a/packages/editor/src/utils/media-upload/index.js +++ b/packages/editor/src/utils/media-upload/index.js @@ -7,11 +7,7 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { select } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { mediaUpload } from './media-upload'; +import { uploadMedia } from '@wordpress/media-utils'; /** * Upload a media file when the file upload button is activated. @@ -37,7 +33,7 @@ export default function( { const wpAllowedMimeTypes = getEditorSettings().allowedMimeTypes; maxUploadFileSize = maxUploadFileSize || getEditorSettings().maxUploadFileSize; - mediaUpload( { + uploadMedia( { allowedTypes, filesList, onFileChange, diff --git a/packages/media-utils/.npmrc b/packages/media-utils/.npmrc new file mode 100644 index 0000000000000..43c97e719a5a8 --- /dev/null +++ b/packages/media-utils/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/media-utils/CHANGELOG.md b/packages/media-utils/CHANGELOG.md new file mode 100644 index 0000000000000..08666d6e2cf0c --- /dev/null +++ b/packages/media-utils/CHANGELOG.md @@ -0,0 +1,5 @@ +## 0.1.0 (2019-01-03) + +### New Features + +- Implemented first version of the package. diff --git a/packages/media-utils/README.md b/packages/media-utils/README.md new file mode 100644 index 0000000000000..489214c544676 --- /dev/null +++ b/packages/media-utils/README.md @@ -0,0 +1,45 @@ +# Media Utils + +The media utils package provides a set of artifacts to abstract media functionality that may be useful in situations where there is a need to deal with media uploads or with the media library, e.g., artifacts that extend or implement a block-editor. +This package is meant to be used by the WordPress core. It may not work as expected outside WordPress usages. + +## Installation + +Install the module + +```bash +npm install @wordpress/media-utils --save +``` + +_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._ + +## Usage + +### uploadMedia + +Media upload util is a function that allows the invokers to upload files to the WordPress media library. +As an example, provided that `myFiles` is an array of file objects, `onFileChange` on onFileChange is a function that receives an array of objects containing the description of WordPress media items and `handleFileError` is a function that receives an object describing a possible error, the following code uploads a file to the WordPress media library: +```js +wp.mediaUtils.utils.uploadMedia( { + filesList: myFiles, + onFileChange: handleFileChange, + onError: handleFileError +} ); +``` + +The following code uploads a file named foo.txt with foo as content to the media library and alerts its URL: +```js +wp.mediaUtils.utils.uploadMedia( { + filesList: [ new File( ["foo"], "foo.txt", { type: "text/plain"} ) ], + onFileChange: ( [ fileObj] ) => alert( fileObj.url ), + onError: console.error, +} ); +``` + +Beware that first onFileChange is called with temporary blob URLs and then with the final URL's this allows to show the result in an optimistic UI as if the upload was already completed. E.g.: when uploading an image, one can show the image right away in the UI even before the upload is complete. + + +### MediaUpload + +Media upload component provides a UI button that allows users to open the WordPress media library. It is normally used in conjunction with the filter `editor.MediaUpload`. +The component follows the interface specified in https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/media-upload/README.md, and more details regarding its usage can be checked there. diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json new file mode 100644 index 0000000000000..b56795b5d6597 --- /dev/null +++ b/packages/media-utils/package.json @@ -0,0 +1,35 @@ +{ + "name": "@wordpress/media-utils", + "version": "0.1.0", + "description": "WordPress Media Upload Utils.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "media", + "upload", + "media-upload" + ], + "homepage": "https://github.com/WordPress/gutenberg/master/packages/media-utils/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/url" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "dependencies": { + "@babel/runtime": "^7.4.4", + "@wordpress/api-fetch": "file:../api-fetch", + "@wordpress/blob": "file:../blob", + "@wordpress/element": "file:../element", + "@wordpress/i18n": "file:../i18n", + "lodash": "^4.17.11" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/media-utils/src/components/index.js b/packages/media-utils/src/components/index.js new file mode 100644 index 0000000000000..82a89c0d58014 --- /dev/null +++ b/packages/media-utils/src/components/index.js @@ -0,0 +1 @@ +export { default as MediaUpload } from './media-upload'; diff --git a/packages/edit-post/src/hooks/components/media-upload/index.js b/packages/media-utils/src/components/media-upload/index.js similarity index 100% rename from packages/edit-post/src/hooks/components/media-upload/index.js rename to packages/media-utils/src/components/media-upload/index.js diff --git a/packages/media-utils/src/index.js b/packages/media-utils/src/index.js new file mode 100644 index 0000000000000..590a7f4c9d188 --- /dev/null +++ b/packages/media-utils/src/index.js @@ -0,0 +1,2 @@ +export * from './components'; +export * from './utils'; diff --git a/packages/media-utils/src/utils/index.js b/packages/media-utils/src/utils/index.js new file mode 100644 index 0000000000000..509b62f1e8864 --- /dev/null +++ b/packages/media-utils/src/utils/index.js @@ -0,0 +1 @@ +export { uploadMedia } from './upload-media'; diff --git a/packages/editor/src/utils/media-upload/test/media-upload.js b/packages/media-utils/src/utils/test/upload-media.test.js similarity index 95% rename from packages/editor/src/utils/media-upload/test/media-upload.js rename to packages/media-utils/src/utils/test/upload-media.test.js index d9e18cf7bf280..857c139625c02 100644 --- a/packages/editor/src/utils/media-upload/test/media-upload.js +++ b/packages/media-utils/src/utils/test/upload-media.test.js @@ -7,7 +7,7 @@ import apiFetch from '@wordpress/api-fetch'; /** * Internal dependencies */ -import { mediaUpload, getMimeTypesArray } from '../media-upload'; +import { uploadMedia, getMimeTypesArray } from '../upload-media'; jest.mock( '@wordpress/blob', () => ( { createBlobURL: jest.fn(), @@ -18,11 +18,11 @@ jest.mock( '@wordpress/api-fetch', () => jest.fn() ); const xmlFile = new window.File( [ 'fake_file' ], 'test.xml', { type: 'text/xml' } ); const imageFile = new window.File( [ 'fake_file' ], 'test.jpeg', { type: 'image/jpeg' } ); -describe( 'mediaUpload', () => { +describe( 'uploadMedia', () => { it( 'should do nothing on no files', async () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { filesList: [], onError, onFileChange, @@ -35,7 +35,7 @@ describe( 'mediaUpload', () => { it( 'should error if allowedTypes contains a partial mime type and the validation fails', async () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'image' ], filesList: [ xmlFile ], onError, @@ -51,7 +51,7 @@ describe( 'mediaUpload', () => { it( 'should error if allowedTypes contains a complete mime type and the validation fails', async () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'image/gif' ], filesList: [ imageFile ], onError, @@ -70,7 +70,7 @@ describe( 'mediaUpload', () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'image/jpeg' ], filesList: [ imageFile ], onError, @@ -84,7 +84,7 @@ describe( 'mediaUpload', () => { it( 'should error if allowedTypes contains multiple types and the validation fails', async () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'video', 'image' ], filesList: [ xmlFile ], onError, @@ -103,7 +103,7 @@ describe( 'mediaUpload', () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'video', 'image' ], filesList: [ imageFile ], onError, @@ -120,7 +120,7 @@ describe( 'mediaUpload', () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'image' ], filesList: [ imageFile, xmlFile ], onError, @@ -137,7 +137,7 @@ describe( 'mediaUpload', () => { it( 'should error if the file size is greater than the maximum', async () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'image' ], filesList: [ imageFile ], maxUploadFileSize: 1, @@ -154,7 +154,7 @@ describe( 'mediaUpload', () => { it( 'should call error handler with the correct error object if file type is not allowed for user', async () => { const onError = jest.fn(); const onFileChange = jest.fn(); - await mediaUpload( { + await uploadMedia( { allowedTypes: [ 'image' ], filesList: [ imageFile ], onError, diff --git a/packages/editor/src/utils/media-upload/media-upload.js b/packages/media-utils/src/utils/upload-media.js similarity index 99% rename from packages/editor/src/utils/media-upload/media-upload.js rename to packages/media-utils/src/utils/upload-media.js index 3aed464f5c4c6..553584d010ace 100644 --- a/packages/editor/src/utils/media-upload/media-upload.js +++ b/packages/media-utils/src/utils/upload-media.js @@ -61,7 +61,7 @@ export function getMimeTypesArray( wpMimeTypesObject ) { * @param {Function} $0.onFileChange Function called each time a file or a temporary representation of the file is available. * @param {?Object} $0.wpAllowedMimeTypes List of allowed mime types and file extensions. */ -export async function mediaUpload( { +export async function uploadMedia( { allowedTypes, additionalData = {}, filesList,