Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
19 changes: 1 addition & 18 deletions editor/utils/editor-media-upload/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { noop } from 'lodash';
*/
import { select } from '@wordpress/data';
import { mediaUpload } from '@wordpress/utils';
import { __, sprintf } from '@wordpress/i18n';

/**
* Upload a media file when the file upload button is activated.
Expand All @@ -31,22 +30,6 @@ export default function editorMediaUpload( {
} ) {
const postId = select( 'core/editor' ).getCurrentPostId();

const errorHandler = ( { file, sizeAboveLimit, generalError } ) => {
let errorMsg;
if ( sizeAboveLimit ) {
errorMsg = sprintf(
__( '%s exceeds the maximum upload size for this site.' ),
file.name
);
} else if ( generalError ) {
errorMsg = sprintf(
__( 'Error while uploading file %s to the media library.' ),
file.name
);
}
onError( errorMsg );
};

mediaUpload( {
allowedType,
filesList,
Expand All @@ -55,6 +38,6 @@ export default function editorMediaUpload( {
post: postId,
},
maxUploadFileSize,
onError: errorHandler,
onError: ( { message } ) => onError( message ),
} );
}
3 changes: 2 additions & 1 deletion lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,8 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
}
// Initialize media settings.
wp_add_inline_script( 'wp-editor', 'window._wpMediaSettings = ' . wp_json_encode( array(
'maxUploadSize' => $max_upload_size,
'maxUploadSize' => $max_upload_size,
'allowedMimeTypes' => get_allowed_mime_types(),
) ), 'before' );

// Prepare Jed locale data.
Expand Down
44 changes: 41 additions & 3 deletions utils/mediaupload.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/**
* External Dependencies
*/
import { compact, forEach, get, noop, startsWith } from 'lodash';
import { compact, forEach, get, includes, noop, startsWith } from 'lodash';

/**
* WordPress dependencies
*/
import { __, sprintf } from '../node_modules/@wordpress/i18n';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't figure out why it's unable to resolve the module normally with just from '@wordpress/i18n'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain of the reason why '@wordpress/i18n' can not be used here I don't think we have a circular dependency. It may be something intentional to keep utils generic but this util here is something specific to WordPress so it makes sense to import @wordpress/i18n here.
We already have '@wordpress/is-shallow-equal' so another option for us is to have '@wordpress/mediaupload'.
@gziolo would it be possible to share your thoughts on this? Are you aware of the reason why '@wordpress/i18n' cannot be imported in utils/mediaupload.js?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wordpress/i18n lives inside WordPress/packages so definitely there is no circular dependency. I have no idea. It should work without workarounds because otherwise, you duplicate this package in the bundles shipped to all users.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure, does this problem reproduce in your environments? @nb tells me a normal import { __, sprintf } from '@wordpress/i18n'; here is working for him.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm able to reproduce the problem. Everything seems to work until an error message is triggered.
To easily trigger error messages we can limit file uploads on the site to 3MB by adding the following line to .htaccess:

php_value upload_max_filesize 3M
php_value post_max_size 3M

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we are in the presence of an intermitent error or something that just happens in some setups.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick check of all the things we’re setting as @wordpress-prefixed externals in the webpack config, to see if they can be imported correctly from utils/index.js. The majority of them cannot. Here are the results in case someone has a clue or encounters the same issue:

Can be imported:
- editor
- blob
- deprecated
- dom
- api-request
- a11y

Can’t be imported:
- blocks
- components
- viewport
- plugins
- edit-post
- core-blocks
- nux
- data
- date
- element
- core-data
- dom-ready
- hooks
- i18n
- is-shallow-equal

Does this need a bug report? This won't be a problem for this PR since we're moving mediaupload.js out of utils, but it could potentially create some hard-to-catch bugs down the line, since they don't trigger an error immediately on build.

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 there is anything to report. It all boils down to the fact that each module need to specify its dependencies when enqueuing the scripts. See:
https://github.com/WordPress/gutenberg/blob/master/lib/client-assets.php#L188

array( 'lodash', 'wp-blob', 'wp-deprecated', 'wp-dom', 'wp-api-request' ),

The list you shared more or less aligns with the dependencies set for this module.

In general, we are trying to split utils into smaller modules, so it will no longer existing a few weeks from now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With all the changes introduced in here (translations, WordPress specific settings), I don't think this file makes sense in utils module anymore. Should we move it to the editor module? and keep it as an internal helper for editorMediaUpload? By the way, is it used somewhere else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with moving mediaUpload to the editor module, and given that mediaUpload is not used anywhere besides editorMediaUpload probably the two functions can be consolidated together.
If doing this we should leave mediaUpload as it was in utils for 2 versions with a deprecation message.

Copy link
Member

@gziolo gziolo Jun 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be great. I was planning to move it out of utils anyway. We are getting rid of this module as it doesn't make sense as an npm package. See #3955 if you want to see more details about it.


/**
* Media Upload is used by audio, image, gallery and video blocks to handle uploading a media file
Expand Down Expand Up @@ -33,15 +38,41 @@ export function mediaUpload( {
filesSet[ idx ] = value;
onFileChange( compact( filesSet ) );
};

// Allowed type specified by consumer
const isAllowedType = ( fileType ) => startsWith( fileType, `${ allowedType }/` );

// Allowed types for the current WP_User
const allowedMimeTypesForUser = get( window, [ '_wpMediaSettings', 'allowedMimeTypes' ] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered adding an optional property allowedMimeTypes that defaults to get( window, [ '_wpMediaSettings', 'allowedMimeTypes' ] ); ? (similar to what we do with maxUploadFileSize) This would allow us to avoid the usage of global in the test case, and would make this function more generic without errors whose triggering can only be parameterized using a global variable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided against it because although it would make testing simpler, I didn't think it made sense to expose allowedMimeTypes as an overridable property. It is explicitly for checking what WP says is allowed, and the allowedTypes property should be used instead for consumer-side restrictions.

But I do understand your point about testing, and maybe that is more important than exposing too many properties. What do you think? I probably don't have a full grasp of all the ramifications yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowedMimeTypes as an overridable property. It is explicitly for checking what WP says is allowed, and the allowedTypes property should be used instead for consumer-side restrictions.

A use case may appear where we want to upload something but we want to verify if the upload is of a given mimeType. But I understand your point this is to verify if the mimeType is allowed by WordPress. If later we want to parameterize this it should verify the mimetype of the parameter and this one and only allow the upload if both mimetypes allow it.
I think we can keep this version.

const isAllowedMimeTypeForUser = ( fileType ) => {
return includes( allowedMimeTypesForUser, fileType );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If because of some reason allowedMimeTypesForUser is undefined I think we should allow the upload and not reject the upload with a mimeType error. So maybe we should add here a check to see if allowedMimeTypesForUser is undefined.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's doing the check right here, should be good to go 👍

};

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( { sizeAboveLimit: true, file: mediaFile } );
onError( {
code: 'SIZE_ABOVE_LIMIT',
message: sprintf(
__( '%s exceeds the maximum upload size for this site.' ),
mediaFile.name
),
file: mediaFile,
} );
return;
}

Expand All @@ -66,7 +97,14 @@ export function mediaUpload( {
() => {
// Reset to empty on failure.
setAndUpdateFiles( idx, null );
onError( { generalError: true, file: mediaFile } );
onError( {
code: 'GENERAL',
message: sprintf(
__( 'Error while uploading file %s to the media library.' ),
mediaFile.name
),
file: mediaFile,
} );
}
);
} );
Expand Down
26 changes: 24 additions & 2 deletions utils/test/mediaupload.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe( 'mediaUpload', () => {
expect( console.error ).not.toHaveBeenCalled();
} );

it( 'should call error handler with the correct message if file size is greater than the maximum', () => {
it( 'should call error handler with the correct error object if file size is greater than the maximum', () => {
const onError = jest.fn();
mediaUpload( {
allowedType: 'image',
Expand All @@ -54,6 +54,28 @@ describe( 'mediaUpload', () => {
maxUploadFileSize: 512,
onError,
} );
expect( onError.mock.calls ).toEqual( [ [ { sizeAboveLimit: true, file: validMediaObj } ] ] );
expect( onError ).toBeCalledWith( {
code: 'SIZE_ABOVE_LIMIT',
file: validMediaObj,
message: `${ validMediaObj.name } exceeds the maximum upload size for this site.`,
} );
} );

it( 'should call error handler with the correct error object if file type is not allowed for user', () => {
const onError = jest.fn();
global._wpMediaSettings = {
allowedMimeTypes: { aac: 'audio/aac' },
};
mediaUpload( {
allowedType: 'image',
filesList: [ validMediaObj ],
onFileChange,
onError,
} );
expect( onError ).toBeCalledWith( {
code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
file: validMediaObj,
message: 'Sorry, this file type is not permitted for security reasons.',
} );
} );
} );