diff --git a/packages/block-editor/src/components/media-upload/index.native.js b/packages/block-editor/src/components/media-upload/index.native.js index 05aabd9a9a3cd1..6ae80bebf44e54 100644 --- a/packages/block-editor/src/components/media-upload/index.native.js +++ b/packages/block-editor/src/components/media-upload/index.native.js @@ -15,16 +15,16 @@ import { __ } from '@wordpress/i18n'; import { Picker } from '@wordpress/components'; import { getOtherMediaOptions, - requestMediaPicker, mediaSources, + requestMediaPicker, } from '@wordpress/react-native-bridge'; import { capturePhoto, captureVideo, + globe, image, - wordpress, mobile, - globe, + wordpress, } from '@wordpress/icons'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { compose } from '@wordpress/compose'; @@ -144,26 +144,38 @@ export class MediaUpload extends Component { allowedTypes = [], __experimentalOnlyMediaLibrary, isAudioBlockMediaUploadEnabled, + canUploadMedia, } = this.props; return this.getAllSources() .filter( ( source ) => { if ( __experimentalOnlyMediaLibrary ) { return source.mediaLibrary; - } else if ( - allowedTypes.every( - ( allowedType ) => - allowedType === MEDIA_TYPE_AUDIO && - source.types.includes( allowedType ) - ) && - source.id !== URL_MEDIA_SOURCE - ) { - return isAudioBlockMediaUploadEnabled === true; } - return allowedTypes.some( ( allowedType ) => + const sourceIsAllowed = allowedTypes.some( ( allowedType ) => source.types.includes( allowedType ) ); + if ( ! sourceIsAllowed ) { + return false; + } + + if ( ! canUploadMedia ) { + // If the user can't upload media, they can only select media + // that has already been uploaded + return source.id === mediaSources.siteMediaLibrary; + } + + const onlyAudioAllowed = allowedTypes.every( + ( allowedType ) => + allowedType === MEDIA_TYPE_AUDIO && + source.types.includes( allowedType ) + ); + if ( onlyAudioAllowed && source.id !== URL_MEDIA_SOURCE ) { + return isAudioBlockMediaUploadEnabled === true; + } + + return true; } ) .map( ( source ) => { return { @@ -311,6 +323,9 @@ export default compose( [ isAudioBlockMediaUploadEnabled: select( blockEditorStore ).getSettings( 'capabilities' ) .isAudioBlockMediaUploadEnabled === true, + canUploadMedia: + select( blockEditorStore ).getSettings( 'capabilities' ) + .canUploadMedia === true, }; } ), ] )( MediaUpload ); diff --git a/packages/block-editor/src/components/media-upload/test/index.native.js b/packages/block-editor/src/components/media-upload/test/index.native.js index e54a9f5219321d..2779af405c02b4 100644 --- a/packages/block-editor/src/components/media-upload/test/index.native.js +++ b/packages/block-editor/src/components/media-upload/test/index.native.js @@ -38,6 +38,7 @@ describe( 'MediaUpload component', () => { const wrapper = render( { return ( <> @@ -59,13 +60,9 @@ describe( 'MediaUpload component', () => { expectOptionForMediaType( MEDIA_TYPE_AUDIO, OPTION_INSERT_FROM_URL ); } ); - const expectMediaPickerForOption = ( - option, - allowMultiple, - requestFunction - ) => { + function openMediaPicker( requestFunction, allowMultiple, canUploadMedia ) { requestFunction.mockImplementation( - ( source, mediaTypes, multiple, callback ) => { + ( _source, mediaTypes, multiple, callback ) => { expect( mediaTypes[ 0 ] ).toEqual( MEDIA_TYPE_VIDEO ); if ( multiple ) { callback( [ { id: MEDIA_ID, url: MEDIA_URL } ] ); @@ -82,6 +79,7 @@ describe( 'MediaUpload component', () => { allowedTypes={ [ MEDIA_TYPE_VIDEO ] } onSelect={ onSelect } multiple={ allowMultiple } + canUploadMedia={ canUploadMedia } render={ ( { open, getMediaOptions } ) => { return ( <> @@ -94,51 +92,96 @@ describe( 'MediaUpload component', () => { } } /> ); - fireEvent.press( wrapper.getByText( 'Open Picker' ) ); - fireEvent.press( wrapper.getByText( option ) ); - const media = { id: MEDIA_ID, url: MEDIA_URL }; - - expect( requestFunction ).toHaveBeenCalledTimes( 1 ); - - expect( onSelect ).toHaveBeenCalledTimes( 1 ); - expect( onSelect ).toHaveBeenCalledWith( - allowMultiple ? [ media ] : media - ); - }; - it( 'can select media from device library', () => { - expectMediaPickerForOption( - 'Choose from device', - false, - requestMediaPicker - ); - } ); + fireEvent.press( wrapper.getByText( 'Open Picker' ) ); + return { onSelect, wrapper }; + } + + describe( 'selecting media', () => { + const expectMediaPickerForOption = ( + option, + allowMultiple, + canUploadMedia, + exists + ) => { + const { onSelect, wrapper } = openMediaPicker( + requestMediaPicker, + allowMultiple, + canUploadMedia + ); - it( 'can select media from WP media library', () => { - expectMediaPickerForOption( - 'WordPress Media Library', - false, - requestMediaPicker - ); - } ); + const optionInstance = wrapper.queryByText( option ); + if ( exists ) { + expect( optionInstance ).toBeDefined(); + fireEvent.press( optionInstance ); + const media = { id: MEDIA_ID, url: MEDIA_URL }; - it( 'can select media by capturing', () => { - expectMediaPickerForOption( 'Take a Video', false, requestMediaPicker ); - } ); + expect( requestMediaPicker ).toHaveBeenCalledTimes( 1 ); - it( 'can select multiple media from device library', () => { - expectMediaPickerForOption( - 'Choose from device', - true, - requestMediaPicker - ); - } ); + expect( onSelect ).toHaveBeenCalledTimes( 1 ); + expect( onSelect ).toHaveBeenCalledWith( + allowMultiple ? [ media ] : media + ); + } else { + expect( optionInstance ).toBeNull(); + } + }; - it( 'can select multiple media from WP media library', () => { - expectMediaPickerForOption( - 'WordPress Media Library', - true, - requestMediaPicker - ); + describe( 'when uploads are enabled', () => { + const expectWhenUploadsEnabled = ( option, allowMultiple ) => { + expectMediaPickerForOption( option, allowMultiple, true, true ); + }; + + it( 'from WP media library', () => { + expectWhenUploadsEnabled( 'WordPress Media Library', true ); + } ); + + it( 'can select media from device library', () => { + expectWhenUploadsEnabled( 'Choose from device', false ); + } ); + + it( 'can select media by capturing', () => { + expectWhenUploadsEnabled( 'Take a Video', false ); + } ); + + it( 'can select multiple media from device library', () => { + expectWhenUploadsEnabled( 'Choose from device', true ); + } ); + } ); + + describe( 'when uploads are disabled', () => { + const expectWhenUploadsDisabled = ( + option, + allowMultiple, + exists + ) => { + expectMediaPickerForOption( + option, + allowMultiple, + false, + exists + ); + }; + + it( 'from WP media library', () => { + expectWhenUploadsDisabled( + 'WordPress Media Library', + true, + true + ); + } ); + + it( 'can select media from device library', () => { + expectWhenUploadsDisabled( 'Choose from device', false, false ); + } ); + + it( 'can select media by capturing', () => { + expectWhenUploadsDisabled( 'Take a Video', false, false ); + } ); + + it( 'can select multiple media from device library', () => { + expectWhenUploadsDisabled( 'Choose from device', true, false ); + } ); + } ); } ); } ); diff --git a/packages/block-library/src/gallery/test/helpers.native.js b/packages/block-library/src/gallery/test/helpers.native.js index da99a166384480..a80c0e63f9a524 100644 --- a/packages/block-library/src/gallery/test/helpers.native.js +++ b/packages/block-library/src/gallery/test/helpers.native.js @@ -105,7 +105,12 @@ export const initializeWithGalleryBlock = async ( { generateGalleryBlock( numberOfItems, media, { useLocalUrl, } ); - const screen = await initializeEditor( { initialHtml } ); + const screen = await initializeEditor( { + initialHtml, + capabilities: { + canUploadMedia: true, + }, + } ); const { getByA11yLabel } = screen; const galleryBlock = getByA11yLabel( /Gallery Block\. Row 1/ ); diff --git a/packages/react-native-bridge/CHANGELOG.md b/packages/react-native-bridge/CHANGELOG.md index b3811036ce7a75..fb064f0b0df97c 100644 --- a/packages/react-native-bridge/CHANGELOG.md +++ b/packages/react-native-bridge/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +## 1.73.0 + +- [**] Remove upload options for users without permission to upload media [#39769] + ## 1.70.1 - [***] Fix launching video preview on Android 11+ [#38377] diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt index 33ca536e49b076..66926a7b4ec7fb 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt @@ -17,6 +17,7 @@ data class GutenbergProps @JvmOverloads constructor( val enableXPosts: Boolean, val enableUnsupportedBlockEditor: Boolean, val canEnableUnsupportedBlockEditor: Boolean, + val canUploadMedia: Boolean, val isAudioBlockMediaUploadEnabled: Boolean, val enableReusableBlock: Boolean, val localeSlug: String, @@ -62,7 +63,10 @@ data class GutenbergProps @JvmOverloads constructor( putBoolean(PROP_CAPABILITIES_MEDIAFILES_COLLECTION_BLOCK, enableMediaFilesCollectionBlocks) putBoolean(PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, enableUnsupportedBlockEditor) putBoolean(PROP_CAPABILITIES_CAN_ENABLE_UNSUPPORTED_BLOCK_EDITOR, canEnableUnsupportedBlockEditor) - putBoolean(PROP_CAPABILITIES_IS_AUDIO_BLOCK_MEDIA_UPLOAD_ENABLED, isAudioBlockMediaUploadEnabled) + putBoolean(PROP_CAPABILITIES_CAN_UPLOAD_MEDIA, canUploadMedia) + putBoolean(PROP_CAPABILITIES_IS_AUDIO_BLOCK_MEDIA_UPLOAD_ENABLED, + isAudioBlockMediaUploadEnabled + ) putBoolean(PROP_CAPABILITIES_REUSABLE_BLOCK, enableReusableBlock) putBoolean(PROP_CAPABILITIES_FACEBOOK_EMBED_BLOCK, enableFacebookEmbed) putBoolean(PROP_CAPABILITIES_INSTAGRAM_EMBED_BLOCK, enableInstagramEmbed) @@ -105,6 +109,7 @@ data class GutenbergProps @JvmOverloads constructor( const val PROP_CAPABILITIES_XPOSTS = "xposts" const val PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR = "unsupportedBlockEditor" const val PROP_CAPABILITIES_CAN_ENABLE_UNSUPPORTED_BLOCK_EDITOR = "canEnableUnsupportedBlockEditor" + const val PROP_CAPABILITIES_CAN_UPLOAD_MEDIA = "canUploadMedia" const val PROP_CAPABILITIES_IS_AUDIO_BLOCK_MEDIA_UPLOAD_ENABLED = "isAudioBlockMediaUploadEnabled" const val PROP_CAPABILITIES_REUSABLE_BLOCK = "reusableBlock" diff --git a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift index 98b4c721caff1b..939868088fd5b3 100644 --- a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift +++ b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift @@ -24,6 +24,7 @@ public enum Capabilities: String { case xposts case unsupportedBlockEditor case canEnableUnsupportedBlockEditor + case canUploadMedia case isAudioBlockMediaUploadEnabled case reusableBlock case facebookEmbed diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java index 447f2d35ee4ded..d76c2f1a9e4a04 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java @@ -48,6 +48,7 @@ protected Bundle getLaunchOptions() { capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_XPOSTS, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_REUSABLE_BLOCK, false); + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_CAN_UPLOAD_MEDIA, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_IS_AUDIO_BLOCK_MEDIA_UPLOAD_ENABLED, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_TILED_GALLERY_BLOCK, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_FACEBOOK_EMBED_BLOCK, true); diff --git a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift index 9c722452c19d9b..05ffa62f84a906 100644 --- a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift +++ b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift @@ -318,6 +318,7 @@ extension GutenbergViewController: GutenbergBridgeDataSource { .canEnableUnsupportedBlockEditor: unsupportedBlockCanBeActivated, .mediaFilesCollectionBlock: true, .tiledGalleryBlock: true, + .canUploadMedia: true, .isAudioBlockMediaUploadEnabled: true, .reusableBlock: false, .facebookEmbed: true,