Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core-blocks/file/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
MediaPlaceholder,
BlockControls,
RichText,
editorMediaUpload,
mediaUpload,
} from '@wordpress/editor';
import { compose } from '@wordpress/compose';

Expand Down Expand Up @@ -55,7 +55,7 @@ class FileEdit extends Component {
if ( this.isBlobURL( href ) ) {
const file = getBlobByURL( href );

editorMediaUpload( {
mediaUpload( {
allowedType: '*',
filesList: [ file ],
onFileChange: ( [ media ] ) => this.onSelectFile( media ),
Expand Down
4 changes: 2 additions & 2 deletions core-blocks/gallery/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
MediaUpload,
MediaPlaceholder,
InspectorControls,
editorMediaUpload,
mediaUpload,
} from '@wordpress/editor';

/**
Expand Down Expand Up @@ -137,7 +137,7 @@ class GalleryEdit extends Component {
addFiles( files ) {
const currentImages = this.props.attributes.images || [];
const { noticeOperations, setAttributes } = this.props;
editorMediaUpload( {
mediaUpload( {
allowedType: 'image',
filesList: files,
onFileChange: ( images ) => {
Expand Down
4 changes: 2 additions & 2 deletions core-blocks/gallery/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { filter, every } from 'lodash';
*/
import { __ } from '@wordpress/i18n';
import { createBlock } from '@wordpress/blocks';
import { RichText, editorMediaUpload } from '@wordpress/editor';
import { RichText, mediaUpload } from '@wordpress/editor';
import { createBlobURL } from '@wordpress/blob';

/**
Expand Down Expand Up @@ -135,7 +135,7 @@ export const settings = {
const block = createBlock( 'core/gallery', {
images: files.map( ( file ) => ( { url: createBlobURL( file ) } ) ),
} );
editorMediaUpload( {
mediaUpload( {
filesList: files,
onFileChange: ( images ) => onChange( block.clientId, { images } ),
allowedType: 'image',
Expand Down
4 changes: 2 additions & 2 deletions core-blocks/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
MediaPlaceholder,
MediaUpload,
BlockAlignmentToolbar,
editorMediaUpload,
mediaUpload,
} from '@wordpress/editor';
import { withViewportMatch } from '@wordpress/viewport';
import { compose } from '@wordpress/compose';
Expand Down Expand Up @@ -84,7 +84,7 @@ class ImageEdit extends Component {
const file = getBlobByURL( url );

if ( file ) {
editorMediaUpload( {
mediaUpload( {
filesList: [ file ],
onFileChange: ( [ image ] ) => {
setAttributes( { ...image } );
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/deprecated.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
Gutenberg's deprecation policy is intended to support backwards-compatibility for two minor releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version.

## 3.6.0

- `wp.editor.editorMediaUpload` has been removed. Please use `wp.editor.mediaUpload` instead.
- `wp.utils.getMimeTypesArray` has been removed.
- `wp.utils.mediaUpload` has been removed. Please use `wp.editor.mediaUpload` instead.
- `wp.utils.preloadImage` has been removed.

## 3.5.0

- `wp.components.ifCondition` has been removed. Please use `wp.compose.ifCondition` instead.
Expand Down
4 changes: 2 additions & 2 deletions editor/components/media-placeholder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import deprecated from '@wordpress/deprecated';
*/
import './style.scss';
import MediaUpload from '../media-upload';
import editorMediaUpload from '../../utils/editor-media-upload';
import { mediaUpload } from '../../utils/';

class MediaPlaceholder extends Component {
constructor() {
Expand Down Expand Up @@ -84,7 +84,7 @@ class MediaPlaceholder extends Component {
onFilesUpload( files ) {
const { onSelect, type, multiple, onError } = this.props;
const setMedia = multiple ? onSelect : ( [ media ] ) => onSelect( media );
editorMediaUpload( {
mediaUpload( {
allowedType: type,
filesList: files,
onFileChange: setMedia,
Expand Down
6 changes: 6 additions & 0 deletions editor/store/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,10 @@ export const EDITOR_SETTINGS_DEFAULTS = {

// Allowed block types for the editor, defaulting to true (all supported).
allowedBlockTypes: true,

// Maximum upload size in bytes allowed for the site.
maxUploadFileSize: 0,
Copy link
Member

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.


// List of allowed mime types and file extensions.
allowedMimeTypes: null,
};
21 changes: 20 additions & 1 deletion editor/utils/index.js
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
Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The 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 uploadMedia prop into the underlying component to take advantage of the contextualized store.

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,
} );
}
168 changes: 168 additions & 0 deletions editor/utils/media-upload/media-upload.js
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',
} );
}
Loading