-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Editor: Move media upload from utils #7978
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
00f2494
33ff195
5cc81ee
0bcf43b
12d589b
8cbaf6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,20 @@ | ||
| export { default as editorMediaUpload } from './editor-media-upload'; | ||
| /** | ||
| * WordPress dependencies | ||
| */ | ||
| import deprecated from '@wordpress/deprecated'; | ||
|
|
||
| /** | ||
| * Internal dependencies | ||
| */ | ||
| import mediaUpload from './media-upload'; | ||
|
|
||
| export { mediaUpload }; | ||
|
|
||
| export function editorMediaUpload( ...params ) { | ||
| deprecated( 'wp.editor.editorMediaUpload', { | ||
| version: '3.6', | ||
| alternative: 'wp.editor.mediaUpload', | ||
| plugin: 'Gutenberg', | ||
| } ); | ||
| mediaUpload( ...params ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,37 +7,46 @@ import { noop } from 'lodash'; | |
| * WordPress dependencies | ||
| */ | ||
| import { select } from '@wordpress/data'; | ||
| import { mediaUpload } from '@wordpress/utils'; | ||
|
|
||
| /** | ||
| * Internal dependencies | ||
| */ | ||
| import { mediaUpload } from './media-upload'; | ||
|
|
||
| /** | ||
| * Upload a media file when the file upload button is activated. | ||
| * Wrapper around mediaUpload() that injects the current post ID. | ||
| * | ||
| * @param {Object} $0 Parameters object passed to the function. | ||
| * @param {string} $0.allowedType The type of media that can be uploaded, or '*' to allow all. | ||
| * @param {?Object} $0.additionalData Additional data to include in the request. | ||
| * @param {Array} $0.filesList List of files. | ||
| * @param {?number} $0.maxUploadFileSize Maximum upload size in bytes allowed for the site. | ||
| * @param {Function} $0.onError Function called when an error happens. | ||
| * @param {Function} $0.onFileChange Function called each time a file or a temporary representation of the file is available. | ||
| */ | ||
| export default function editorMediaUpload( { | ||
| export default function( { | ||
| allowedType, | ||
| filesList, | ||
| maxUploadFileSize, | ||
| onError = noop, | ||
| onFileChange, | ||
| } ) { | ||
| const postId = select( 'core/editor' ).getCurrentPostId(); | ||
| const { | ||
| getCurrentPostId, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, in retrospect, I think this is the one critical piece where having this be an editor-specific function could make sense, in effecting that the media be attached to the post being edited. That said, with recent refactoring for store registries (#7527), exposing this as a simple function on the editor global does not seem tenable. If we want it, it seems like something which would be a higher-order component to inject the |
||
| getEditorSettings, | ||
| } = select( 'core/editor' ); | ||
| const allowedMimeTypes = getEditorSettings().allowedMimeTypes; | ||
| maxUploadFileSize = maxUploadFileSize || getEditorSettings().maxUploadFileSize; | ||
|
|
||
| mediaUpload( { | ||
| allowedType, | ||
| filesList, | ||
| onFileChange, | ||
| additionalData: { | ||
| post: postId, | ||
| post: getCurrentPostId(), | ||
| }, | ||
| maxUploadFileSize, | ||
| onError: ( { message } ) => onError( message ), | ||
| allowedMimeTypes, | ||
| } ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| /** | ||
| * External Dependencies | ||
| */ | ||
| import { compact, flatMap, forEach, get, has, includes, map, noop, startsWith } from 'lodash'; | ||
|
|
||
| /** | ||
| * WordPress dependencies | ||
| */ | ||
| import apiFetch from '@wordpress/api-fetch'; | ||
| import { __, sprintf } from '@wordpress/i18n'; | ||
|
|
||
| /** | ||
| * Browsers may use unexpected mime types, and they differ from browser to browser. | ||
| * This function computes a flexible array of mime types from the mime type structured provided by the server. | ||
| * Converts { jpg|jpeg|jpe: "image/jpeg" } into [ "image/jpeg", "image/jpg", "image/jpeg", "image/jpe" ] | ||
| * The computation of this array instead of directly using the object, | ||
| * solves the problem in chrome where mp3 files have audio/mp3 as mime type instead of audio/mpeg. | ||
| * https://bugs.chromium.org/p/chromium/issues/detail?id=227004 | ||
| * | ||
| * @param {?Object} wpMimeTypesObject Mime type object received from the server. | ||
| * Extensions are keys separated by '|' and values are mime types associated with an extension. | ||
| * | ||
| * @return {?Array} An array of mime types or the parameter passed if it was "falsy". | ||
| */ | ||
| export function getMimeTypesArray( wpMimeTypesObject ) { | ||
| if ( ! wpMimeTypesObject ) { | ||
| return wpMimeTypesObject; | ||
| } | ||
| return flatMap( wpMimeTypesObject, ( mime, extensionsString ) => { | ||
| const [ type ] = mime.split( '/' ); | ||
| const extensions = extensionsString.split( '|' ); | ||
| return [ mime, ...map( extensions, ( extension ) => `${ type }/${ extension }` ) ]; | ||
| } ); | ||
| } | ||
|
|
||
| /** | ||
| * Media Upload is used by audio, image, gallery, video, and file blocks to | ||
| * handle uploading a media file when a file upload button is activated. | ||
| * | ||
| * TODO: future enhancement to add an upload indicator. | ||
| * | ||
| * @param {Object} $0 Parameters object passed to the function. | ||
| * @param {string} $0.allowedType The type of media that can be uploaded, or '*' to allow all. | ||
| * @param {?Object} $0.additionalData Additional data to include in the request. | ||
| * @param {Array} $0.filesList List of files. | ||
| * @param {?number} $0.maxUploadFileSize Maximum upload size in bytes allowed for the site. | ||
| * @param {Function} $0.onError Function called when an error happens. | ||
| * @param {Function} $0.onFileChange Function called each time a file or a temporary representation of the file is available. | ||
| * @param {?Object} $0.allowedMimeTypes List of allowed mime types and file extensions. | ||
| */ | ||
| export function mediaUpload( { | ||
| allowedType, | ||
| additionalData = {}, | ||
| filesList, | ||
| maxUploadFileSize, | ||
| onError = noop, | ||
| onFileChange, | ||
| allowedMimeTypes = null, | ||
| } ) { | ||
| // Cast filesList to array | ||
| const files = [ ...filesList ]; | ||
|
|
||
| const filesSet = []; | ||
| const setAndUpdateFiles = ( idx, value ) => { | ||
| filesSet[ idx ] = value; | ||
| onFileChange( compact( filesSet ) ); | ||
| }; | ||
|
|
||
| // Allowed type specified by consumer | ||
| const isAllowedType = ( fileType ) => { | ||
| return ( allowedType === '*' ) || startsWith( fileType, `${ allowedType }/` ); | ||
| }; | ||
|
|
||
| // Allowed types for the current WP_User | ||
| const allowedMimeTypesForUser = getMimeTypesArray( allowedMimeTypes ); | ||
| const isAllowedMimeTypeForUser = ( fileType ) => { | ||
| return includes( allowedMimeTypesForUser, fileType ); | ||
| }; | ||
|
|
||
| files.forEach( ( mediaFile, idx ) => { | ||
| if ( ! isAllowedType( mediaFile.type ) ) { | ||
| return; | ||
| } | ||
|
|
||
| // verify if user is allowed to upload this mime type | ||
| if ( allowedMimeTypesForUser && ! isAllowedMimeTypeForUser( mediaFile.type ) ) { | ||
| onError( { | ||
| code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER', | ||
| message: __( 'Sorry, this file type is not permitted for security reasons.' ), | ||
| file: mediaFile, | ||
| } ); | ||
| return; | ||
| } | ||
|
|
||
| // verify if file is greater than the maximum file upload size allowed for the site. | ||
| if ( maxUploadFileSize && mediaFile.size > maxUploadFileSize ) { | ||
| onError( { | ||
| code: 'SIZE_ABOVE_LIMIT', | ||
| message: sprintf( | ||
| // translators: %s: file name | ||
| __( '%s exceeds the maximum upload size for this site.' ), | ||
| mediaFile.name | ||
| ), | ||
| file: mediaFile, | ||
| } ); | ||
| return; | ||
| } | ||
|
|
||
| // Set temporary URL to create placeholder media file, this is replaced | ||
| // with final file from media gallery when upload is `done` below | ||
| filesSet.push( { url: window.URL.createObjectURL( mediaFile ) } ); | ||
| onFileChange( filesSet ); | ||
|
|
||
| return createMediaFromFile( mediaFile, additionalData ) | ||
| .then( ( savedMedia ) => { | ||
| const mediaObject = { | ||
| alt: savedMedia.alt_text, | ||
| caption: get( savedMedia, [ 'caption', 'raw' ], '' ), | ||
| id: savedMedia.id, | ||
| link: savedMedia.link, | ||
| title: savedMedia.title.raw, | ||
| url: savedMedia.source_url, | ||
| mediaDetails: {}, | ||
| }; | ||
| if ( has( savedMedia, [ 'media_details', 'sizes' ] ) ) { | ||
| mediaObject.mediaDetails.sizes = get( savedMedia, [ 'media_details', 'sizes' ], {} ); | ||
| } | ||
| setAndUpdateFiles( idx, mediaObject ); | ||
| } ) | ||
| .catch( ( error ) => { | ||
| // Reset to empty on failure. | ||
| setAndUpdateFiles( idx, null ); | ||
| let message; | ||
| if ( has( error, [ 'message' ] ) ) { | ||
| message = get( error, [ 'message' ] ); | ||
| } else { | ||
| message = sprintf( | ||
| // translators: %s: file name | ||
| __( 'Error while uploading file %s to the media library.' ), | ||
| mediaFile.name | ||
| ); | ||
| } | ||
| onError( { | ||
| code: 'GENERAL', | ||
| message, | ||
| file: mediaFile, | ||
| } ); | ||
| } ); | ||
| } ); | ||
| } | ||
|
|
||
| /** | ||
| * @param {File} file Media File to Save. | ||
| * @param {?Object} additionalData Additional data to include in the request. | ||
| * | ||
| * @return {Promise} Media Object Promise. | ||
| */ | ||
| function createMediaFromFile( file, additionalData ) { | ||
| // Create upload payload | ||
| const data = new window.FormData(); | ||
| data.append( 'file', file, file.name || file.type.replace( '/', '.' ) ); | ||
| forEach( additionalData, ( ( value, key ) => data.append( key, value ) ) ); | ||
| return apiFetch( { | ||
| path: '/wp/v2/media', | ||
| body: data, | ||
| method: 'POST', | ||
| } ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know that long-term we want these to be considered editor settings, since they apply to the site as a whole. Seems like something which should ideally live in
@wordpress/core-data, though I do not believe they are yet available from the REST API.