diff --git a/docs/designers-developers/developers/data/data-core.md b/docs/designers-developers/developers/data/data-core.md index 2a98c3bb5d397..9b01fe325c295 100644 --- a/docs/designers-developers/developers/data/data-core.md +++ b/docs/designers-developers/developers/data/data-core.md @@ -136,17 +136,50 @@ get back from the oEmbed preview API. Is the preview for the URL an oEmbed link fallback. -### hasUploadPermissions +### hasUploadPermissions (deprecated) -Return Upload Permissions. +Returns whether the current user can upload media. + +Calling this may trigger an OPTIONS request to the REST API via the +`canUser()` resolver. + +https://developer.wordpress.org/rest-api/reference/ + +*Deprecated* + +Deprecated since 5.0. Callers should use the more generic `canUser()` selector instead of + `hasUploadPermissions()`, e.g. `canUser( 'create', 'media' )`. *Parameters* - * state: State tree. + * state: Data state. *Returns* -Upload Permissions. +Whether or not the user can upload media. Defaults to `true` if the OPTIONS + request is being made. + +### canUser + +Returns whether the current user can perform the given action on the given +REST resource. + +Calling this may trigger an OPTIONS request to the REST API via the +`canUser()` resolver. + +https://developer.wordpress.org/rest-api/reference/ + +*Parameters* + + * state: Data state. + * action: Action to check. One of: 'create', 'read', 'update', 'delete'. + * resource: REST resource to check, e.g. 'media' or 'posts'. + * id: Optional ID of the rest resource to check. + +*Returns* + +Whether or not the user can perform the action, + or `undefined` if the OPTIONS request is still being made. ## Actions @@ -213,4 +246,14 @@ Returns an action object used in signalling that Upload permissions have been re *Parameters* - * hasUploadPermissions: Does the user have permission to upload files? \ No newline at end of file + * hasUploadPermissions: Does the user have permission to upload files? + +### receiveUserPermission + +Returns an action object used in signalling that the current user has +permission to perform an action on a REST resource. + +*Parameters* + + * key: A key that represents the action and REST resource. + * isAllowed: Whether or not the user can perform the action. \ No newline at end of file diff --git a/lib/client-assets.php b/lib/client-assets.php index 42624b3e1c294..630a50434c72f 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -1086,6 +1086,7 @@ function gutenberg_editor_scripts_and_styles( $hook ) { sprintf( '/wp/v2/types/%s?context=edit', $post_type ), sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ), array( '/wp/v2/media', 'OPTIONS' ), + array( '/wp/v2/blocks', 'OPTIONS' ), ); /** diff --git a/lib/packages-dependencies.php b/lib/packages-dependencies.php index f1b785f15b011..ef1b2bc22cdb9 100644 --- a/lib/packages-dependencies.php +++ b/lib/packages-dependencies.php @@ -84,6 +84,7 @@ 'lodash', 'wp-api-fetch', 'wp-data', + 'wp-deprecated', 'wp-url', ), 'wp-data' => array( diff --git a/packages/block-library/src/block/edit-panel/index.js b/packages/block-library/src/block/edit-panel/index.js index e91c76cc6bf52..eb620a877b63a 100644 --- a/packages/block-library/src/block/edit-panel/index.js +++ b/packages/block-library/src/block/edit-panel/index.js @@ -53,7 +53,7 @@ class ReusableBlockEditPanel extends Component { } render() { - const { isEditing, title, isSaving, onEdit, instanceId } = this.props; + const { isEditing, title, isSaving, isEditDisabled, onEdit, instanceId } = this.props; return ( @@ -66,6 +66,7 @@ class ReusableBlockEditPanel extends Component { ref={ this.editButton } isLarge className="reusable-block-edit-panel__button" + disabled={ isEditDisabled } onClick={ onEdit } > { __( 'Edit' ) } diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index d95b07aedb915..62e4acf0640d1 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -97,7 +97,7 @@ class ReusableBlockEdit extends Component { } render() { - const { isSelected, reusableBlock, block, isFetching, isSaving } = this.props; + const { isSelected, reusableBlock, block, isFetching, isSaving, canUpdateBlock } = this.props; const { isEditing, title, changedAttributes } = this.state; if ( ! reusableBlock && isFetching ) { @@ -130,6 +130,7 @@ class ReusableBlockEdit extends Component { isEditing={ isEditing } title={ title !== null ? title : reusableBlock.title } isSaving={ isSaving && ! reusableBlock.isTemporary } + isEditDisabled={ ! canUpdateBlock } onEdit={ this.startEditing } onChangeTitle={ this.setTitle } onSave={ this.save } @@ -151,6 +152,8 @@ export default compose( [ __experimentalIsSavingReusableBlock: isSavingReusableBlock, getBlock, } = select( 'core/editor' ); + const { canUser } = select( 'core' ); + const { ref } = ownProps.attributes; const reusableBlock = getReusableBlock( ref ); @@ -159,6 +162,7 @@ export default compose( [ isFetching: isFetchingReusableBlock( ref ), isSaving: isSavingReusableBlock( ref ), block: reusableBlock ? getBlock( reusableBlock.clientId ) : null, + canUpdateBlock: !! reusableBlock && ! reusableBlock.isTemporary && !! canUser( 'update', 'blocks', ref ), }; } ), withDispatch( ( dispatch, ownProps ) => { diff --git a/packages/core-data/package.json b/packages/core-data/package.json index b28d5c2eb0c24..388ab7b45e8f0 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -24,6 +24,7 @@ "@babel/runtime": "^7.0.0", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/data": "file:../data", + "@wordpress/deprecated": "file:../deprecated", "@wordpress/url": "file:../url", "equivalent-key-map": "^0.2.2", "lodash": "^4.17.10", diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index ebbcfb7e76150..c15bd647b9fcd 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -137,7 +137,25 @@ export function* saveEntityRecord( kind, name, record ) { */ export function receiveUploadPermissions( hasUploadPermissions ) { return { - type: 'RECEIVE_UPLOAD_PERMISSIONS', - hasUploadPermissions, + type: 'RECEIVE_USER_PERMISSION', + key: 'create/media', + isAllowed: hasUploadPermissions, + }; +} + +/** + * Returns an action object used in signalling that the current user has + * permission to perform an action on a REST resource. + * + * @param {string} key A key that represents the action and REST resource. + * @param {boolean} isAllowed Whether or not the user can perform the action. + * + * @return {Object} Action object. + */ +export function receiveUserPermission( key, isAllowed ) { + return { + type: 'RECEIVE_USER_PERMISSION', + key, + isAllowed, }; } diff --git a/packages/core-data/src/reducer.js b/packages/core-data/src/reducer.js index bb14a0283b7ca..7785667822d68 100644 --- a/packages/core-data/src/reducer.js +++ b/packages/core-data/src/reducer.js @@ -218,17 +218,21 @@ export function embedPreviews( state = {}, action ) { } /** - * Reducer managing Upload permissions. + * State which tracks whether the user can perform an action on a REST + * resource. * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. + * @param {Object} state Current state. + * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ -export function hasUploadPermissions( state = true, action ) { +export function userPermissions( state = {}, action ) { switch ( action.type ) { - case 'RECEIVE_UPLOAD_PERMISSIONS': - return action.hasUploadPermissions; + case 'RECEIVE_USER_PERMISSION': + return { + ...state, + [ action.key ]: action.isAllowed, + }; } return state; @@ -241,5 +245,5 @@ export default combineReducers( { themeSupports, entities, embedPreviews, - hasUploadPermissions, + userPermissions, } ); diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index b8bd4ed238285..009fe1b7d5b42 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -1,12 +1,13 @@ /** * External dependencies */ -import { find, includes, get, hasIn } from 'lodash'; +import { find, includes, get, hasIn, compact } from 'lodash'; /** * WordPress dependencies */ import { addQueryArgs } from '@wordpress/url'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -16,7 +17,7 @@ import { receiveEntityRecords, receiveThemeSupports, receiveEmbedPreview, - receiveUploadPermissions, + receiveUserPermission, } from './actions'; import { getKindEntities } from './entities'; import { apiFetch } from './controls'; @@ -101,9 +102,57 @@ export function* getEmbedPreview( url ) { /** * Requests Upload Permissions from the REST API. + * + * @deprecated since 5.0. Callers should use the more generic `canUser()` selector instead of + * `hasUploadPermissions()`, e.g. `canUser( 'create', 'media' )`. */ export function* hasUploadPermissions() { - const response = yield apiFetch( { path: '/wp/v2/media', method: 'OPTIONS', parse: false } ); + deprecated( "select( 'core' ).hasUploadPermissions()", { + alternative: "select( 'core' ).canUser( 'create', 'media' )", + } ); + yield* canUser( 'create', 'media' ); +} + +/** + * Checks whether the current user can perform the given action on the given + * REST resource. + * + * @param {string} action Action to check. One of: 'create', 'read', 'update', + * 'delete'. + * @param {string} resource REST resource to check, e.g. 'media' or 'posts'. + * @param {?string} id ID of the rest resource to check. + */ +export function* canUser( action, resource, id ) { + const methods = { + create: 'POST', + read: 'GET', + update: 'PUT', + delete: 'DELETE', + }; + + const method = methods[ action ]; + if ( ! method ) { + throw new Error( `'${ action }' is not a valid action.` ); + } + + const path = id ? `/wp/v2/${ resource }/${ id }` : `/wp/v2/${ resource }`; + + let response; + try { + response = yield apiFetch( { + path, + // Ideally this would always be an OPTIONS request, but unfortunately there's + // a bug in the REST API which causes the Allow header to not be sent on + // OPTIONS requests to /posts/:id routes. + // https://core.trac.wordpress.org/ticket/45753 + method: id ? 'GET' : 'OPTIONS', + parse: false, + } ); + } catch ( error ) { + // Do nothing if our OPTIONS request comes back with an API error (4xx or + // 5xx). The previously determined isAllowed value will remain in the store. + return; + } let allowHeader; if ( hasIn( response, [ 'headers', 'get' ] ) ) { @@ -116,5 +165,7 @@ export function* hasUploadPermissions() { allowHeader = get( response, [ 'headers', 'Allow' ], '' ); } - yield receiveUploadPermissions( includes( allowHeader, 'POST' ) ); + const key = compact( [ action, resource, id ] ).join( '/' ); + const isAllowed = includes( allowHeader, method ); + yield receiveUserPermission( key, isAllowed ); } diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index 95e9f867aa3c0..b9ed8b575e086 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -2,12 +2,13 @@ * External dependencies */ import createSelector from 'rememo'; -import { map, find, get, filter } from 'lodash'; +import { map, find, get, filter, compact, defaultTo } from 'lodash'; /** * WordPress dependencies */ import { select } from '@wordpress/data'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -171,12 +172,46 @@ export function isPreviewEmbedFallback( state, url ) { } /** - * Return Upload Permissions. + * Returns whether the current user can upload media. * - * @param {Object} state State tree. + * Calling this may trigger an OPTIONS request to the REST API via the + * `canUser()` resolver. * - * @return {boolean} Upload Permissions. + * https://developer.wordpress.org/rest-api/reference/ + * + * @deprecated since 5.0. Callers should use the more generic `canUser()` selector instead of + * `hasUploadPermissions()`, e.g. `canUser( 'create', 'media' )`. + * + * @param {Object} state Data state. + * + * @return {boolean} Whether or not the user can upload media. Defaults to `true` if the OPTIONS + * request is being made. */ export function hasUploadPermissions( state ) { - return state.hasUploadPermissions; + deprecated( "select( 'core' ).hasUploadPermissions()", { + alternative: "select( 'core' ).canUser( 'create', 'media' )", + } ); + return defaultTo( canUser( state, 'create', 'media' ), true ); +} + +/** + * Returns whether the current user can perform the given action on the given + * REST resource. + * + * Calling this may trigger an OPTIONS request to the REST API via the + * `canUser()` resolver. + * + * https://developer.wordpress.org/rest-api/reference/ + * + * @param {Object} state Data state. + * @param {string} action Action to check. One of: 'create', 'read', 'update', 'delete'. + * @param {string} resource REST resource to check, e.g. 'media' or 'posts'. + * @param {string=} id Optional ID of the rest resource to check. + * + * @return {boolean|undefined} Whether or not the user can perform the action, + * or `undefined` if the OPTIONS request is still being made. + */ +export function canUser( state, action, resource, id ) { + const key = compact( [ action, resource, id ] ).join( '/' ); + return get( state, [ 'userPermissions', key ] ); } diff --git a/packages/core-data/src/test/actions.js b/packages/core-data/src/test/actions.js index 86e7f50ed53c1..85c94eeaa5224 100644 --- a/packages/core-data/src/test/actions.js +++ b/packages/core-data/src/test/actions.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { saveEntityRecord, receiveEntityRecords } from '../actions'; +import { saveEntityRecord, receiveEntityRecords, receiveUserPermission } from '../actions'; describe( 'saveEntityRecord', () => { it( 'triggers a POST request for a new record', async () => { @@ -58,3 +58,13 @@ describe( 'saveEntityRecord', () => { expect( received ).toEqual( receiveEntityRecords( 'root', 'postType', postType, undefined, true ) ); } ); } ); + +describe( 'receiveUserPermission', () => { + it( 'builds an action object', () => { + expect( receiveUserPermission( 'create/media', true ) ).toEqual( { + type: 'RECEIVE_USER_PERMISSION', + key: 'create/media', + isAllowed: true, + } ); + } ); +} ); diff --git a/packages/core-data/src/test/reducer.js b/packages/core-data/src/test/reducer.js index f6647becf0743..51bc4611ad7d9 100644 --- a/packages/core-data/src/test/reducer.js +++ b/packages/core-data/src/test/reducer.js @@ -7,7 +7,7 @@ import { filter } from 'lodash'; /** * Internal dependencies */ -import { terms, entities, embedPreviews, hasUploadPermissions } from '../reducer'; +import { terms, entities, embedPreviews, userPermissions } from '../reducer'; describe( 'terms()', () => { it( 'returns an empty object by default', () => { @@ -118,21 +118,25 @@ describe( 'embedPreviews()', () => { } ); } ); -describe( 'hasUploadPermissions()', () => { - it( 'returns true by default', () => { - const state = hasUploadPermissions( undefined, {} ); - - expect( state ).toEqual( true ); +describe( 'userPermissions()', () => { + it( 'defaults to an empty object', () => { + const state = userPermissions( undefined, {} ); + expect( state ).toEqual( {} ); } ); - it( 'returns with updated upload permissions value', () => { - const originalState = true; + it( 'updates state with whether an action is allowed', () => { + const original = deepFreeze( { + 'create/media': false, + } ); - const state = hasUploadPermissions( originalState, { - type: 'RECEIVE_UPLOAD_PERMISSIONS', - hasUploadPermissions: false, + const state = userPermissions( original, { + type: 'RECEIVE_USER_PERMISSION', + key: 'create/media', + isAllowed: true, } ); - expect( state ).toEqual( false ); + expect( state ).toEqual( { + 'create/media': true, + } ); } ); } ); diff --git a/packages/core-data/src/test/resolvers.js b/packages/core-data/src/test/resolvers.js index 8b008bc1cad68..325e4ce9c322a 100644 --- a/packages/core-data/src/test/resolvers.js +++ b/packages/core-data/src/test/resolvers.js @@ -1,8 +1,9 @@ /** * Internal dependencies */ -import { getEntityRecord, getEntityRecords, getEmbedPreview } from '../resolvers'; -import { receiveEntityRecords, receiveEmbedPreview } from '../actions'; +import { getEntityRecord, getEntityRecords, getEmbedPreview, canUser } from '../resolvers'; +import { receiveEntityRecords, receiveEmbedPreview, receiveUserPermission } from '../actions'; +import { apiFetch } from '../controls'; describe( 'getEntityRecord', () => { const POST_TYPE = { slug: 'post' }; @@ -68,3 +69,93 @@ describe( 'getEmbedPreview', () => { expect( received ).toEqual( receiveEmbedPreview( UNEMBEDDABLE_URL, UNEMBEDDABLE_RESPONSE ) ); } ); } ); + +describe( 'canUser', () => { + it( 'does nothing when there is an API error', () => { + const generator = canUser( 'create', 'media' ); + + let received = generator.next(); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( apiFetch( { + path: '/wp/v2/media', + method: 'OPTIONS', + parse: false, + } ) ); + + received = generator.throw( { status: 404 } ); + expect( received.done ).toBe( true ); + expect( received.value ).toBeUndefined(); + } ); + + it( 'receives false when the user is not allowed to perform an action', () => { + const generator = canUser( 'create', 'media' ); + + let received = generator.next(); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( apiFetch( { + path: '/wp/v2/media', + method: 'OPTIONS', + parse: false, + } ) ); + + received = generator.next( { + headers: { + Allow: 'GET', + }, + } ); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( receiveUserPermission( 'create/media', false ) ); + + received = generator.next(); + expect( received.done ).toBe( true ); + expect( received.value ).toBeUndefined(); + } ); + + it( 'receives true when the user is allowed to perform an action', () => { + const generator = canUser( 'create', 'media' ); + + let received = generator.next(); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( apiFetch( { + path: '/wp/v2/media', + method: 'OPTIONS', + parse: false, + } ) ); + + received = generator.next( { + headers: { + Allow: 'POST, GET, PUT, DELETE', + }, + } ); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( receiveUserPermission( 'create/media', true ) ); + + received = generator.next(); + expect( received.done ).toBe( true ); + expect( received.value ).toBeUndefined(); + } ); + + it( 'receives true when the user is allowed to perform an action on a specific resource', () => { + const generator = canUser( 'update', 'blocks', 123 ); + + let received = generator.next(); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( apiFetch( { + path: '/wp/v2/blocks/123', + method: 'GET', + parse: false, + } ) ); + + received = generator.next( { + headers: { + Allow: 'POST, GET, PUT, DELETE', + }, + } ); + expect( received.done ).toBe( false ); + expect( received.value ).toEqual( receiveUserPermission( 'update/blocks/123', true ) ); + + received = generator.next(); + expect( received.done ).toBe( true ); + expect( received.value ).toBeUndefined(); + } ); +} ); diff --git a/packages/core-data/src/test/selectors.js b/packages/core-data/src/test/selectors.js index b982ada4f3a9d..f2a2885e77662 100644 --- a/packages/core-data/src/test/selectors.js +++ b/packages/core-data/src/test/selectors.js @@ -11,6 +11,7 @@ import { getEntityRecords, getEmbedPreview, isPreviewEmbedFallback, + canUser, } from '../selectors'; describe( 'getEntityRecord', () => { @@ -117,3 +118,30 @@ describe( 'isPreviewEmbedFallback()', () => { expect( isPreviewEmbedFallback( state, 'http://example.com/' ) ).toEqual( true ); } ); } ); + +describe( 'canUser', () => { + it( 'returns undefined by default', () => { + const state = deepFreeze( { + userPermissions: {}, + } ); + expect( canUser( state, 'create', 'media' ) ).toBe( undefined ); + } ); + + it( 'returns whether an action can be performed', () => { + const state = deepFreeze( { + userPermissions: { + 'create/media': false, + }, + } ); + expect( canUser( state, 'create', 'media' ) ).toBe( false ); + } ); + + it( 'returns whether an action can be performed for a given resource', () => { + const state = deepFreeze( { + userPermissions: { + 'create/media/123': false, + }, + } ); + expect( canUser( state, 'create', 'media', 123 ) ).toBe( false ); + } ); +} ); diff --git a/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js b/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js index 6d39538aa5e5a..2e528a01e7d91 100644 --- a/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js +++ b/packages/editor/src/components/block-settings-menu/reusable-block-convert-button.js @@ -15,7 +15,7 @@ import { compose } from '@wordpress/compose'; export function ReusableBlockConvertButton( { isVisible, - isStaticBlock, + isReusable, onConvertToStatic, onConvertToReusable, } ) { @@ -25,7 +25,7 @@ export function ReusableBlockConvertButton( { return ( - { isStaticBlock && ( + { ! isReusable && ( ) } - { ! isStaticBlock && ( + { isReusable && ( ( - // Guard against the case where a regular block has *just* been converted to a - // reusable block and doesn't yet exist in the editor store. + // Guard against the case where a regular block has *just* been converted !! block && - // Only show the option to covert to reusable blocks on valid blocks. + + // Hide 'Add to Reusable Blocks' on invalid blocks block.isValid && - // Make sure the block supports being converted into a reusable block (by default that is the case). + + // Hide 'Add to Reusable Blocks' when block doesn't support being made reusable hasBlockSupport( block.name, 'reusable', true ) - ) ) + ) ) && + + // Hide 'Add to Reusable Blocks' when current doesn't have permission to do that + !! canUser( 'create', 'blocks' ) ); return { + isReusable, isVisible, - isStaticBlock: isVisible && ( - blocks.length !== 1 || - ! isReusableBlock( blocks[ 0 ] ) || - ! getReusableBlock( blocks[ 0 ].attributes.ref ) - ), }; } ), withDispatch( ( dispatch, { clientIds, onToggle = noop } ) => { diff --git a/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js b/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js index 52f2ad08108f9..5a73912b36ee7 100644 --- a/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js +++ b/packages/editor/src/components/block-settings-menu/reusable-block-delete-button.js @@ -12,8 +12,8 @@ import { __ } from '@wordpress/i18n'; import { isReusableBlock } from '@wordpress/blocks'; import { withSelect, withDispatch } from '@wordpress/data'; -export function ReusableBlockDeleteButton( { reusableBlock, onDelete } ) { - if ( ! reusableBlock ) { +export function ReusableBlockDeleteButton( { isVisible, isDisabled, onDelete } ) { + if ( ! isVisible ) { return null; } @@ -21,8 +21,8 @@ export function ReusableBlockDeleteButton( { reusableBlock, onDelete } ) { onDelete( reusableBlock.id ) } + disabled={ isDisabled } + onClick={ () => onDelete() } > { __( 'Remove from Reusable Blocks' ) } @@ -35,18 +35,27 @@ export default compose( [ getBlock, __experimentalGetReusableBlock: getReusableBlock, } = select( 'core/editor' ); + const { canUser } = select( 'core' ); + const block = getBlock( clientId ); + + const reusableBlock = block && isReusableBlock( block ) ? + getReusableBlock( block.attributes.ref ) : + null; + return { - reusableBlock: block && isReusableBlock( block ) ? getReusableBlock( block.attributes.ref ) : null, + isVisible: !! reusableBlock && !! canUser( 'delete', 'blocks', reusableBlock.id ), + isDisabled: reusableBlock && reusableBlock.isTemporary, }; } ), - withDispatch( ( dispatch, { onToggle = noop } ) => { + withDispatch( ( dispatch, { clientId, onToggle = noop }, { select } ) => { const { __experimentalDeleteReusableBlock: deleteReusableBlock, } = dispatch( 'core/editor' ); + const { getBlock } = select( 'core/editor' ); return { - onDelete( id ) { + onDelete() { // TODO: Make this a component or similar // eslint-disable-next-line no-alert const hasConfirmed = window.confirm( __( @@ -55,7 +64,8 @@ export default compose( [ ) ); if ( hasConfirmed ) { - deleteReusableBlock( id ); + const block = getBlock( clientId ); + deleteReusableBlock( block.attributes.ref ); onToggle(); } }, diff --git a/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap b/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap index c31a0f8e9f9b4..23e876d36a9c6 100644 --- a/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap +++ b/packages/editor/src/components/block-settings-menu/test/__snapshots__/reusable-block-delete-button.js.snap @@ -3,6 +3,7 @@ exports[`ReusableBlockDeleteButton matches the snapshot 1`] = ` diff --git a/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js b/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js index 1aabafc55ef92..c6fba313e31b3 100644 --- a/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js +++ b/packages/editor/src/components/block-settings-menu/test/reusable-block-convert-button.js @@ -27,7 +27,7 @@ describe( 'ReusableBlockConvertButton', () => { const wrapper = getShallowRenderOutput( ); @@ -43,7 +43,7 @@ describe( 'ReusableBlockConvertButton', () => { const wrapper = getShallowRenderOutput( ); diff --git a/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js b/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js index 9da36d7f24e60..39299becf29c2 100644 --- a/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js +++ b/packages/editor/src/components/block-settings-menu/test/reusable-block-delete-button.js @@ -16,11 +16,20 @@ describe( 'ReusableBlockDeleteButton', () => { return renderer.getRenderOutput(); } + it( 'should not render when isVisible is false', () => { + const wrapper = getShallowRenderOutput( + + ); + + expect( wrapper ).toBe( null ); + } ); + it( 'matches the snapshot', () => { const wrapper = getShallowRenderOutput( ); @@ -32,12 +41,13 @@ describe( 'ReusableBlockDeleteButton', () => { const onDelete = jest.fn(); const wrapper = getShallowRenderOutput( ); wrapper.props.onClick(); - expect( onDelete ).toHaveBeenCalledWith( 123 ); + expect( onDelete ).toHaveBeenCalled(); } ); } ); diff --git a/packages/editor/src/components/media-placeholder/index.js b/packages/editor/src/components/media-placeholder/index.js index c1b64eb0d9a64..11b80bcc58b0f 100644 --- a/packages/editor/src/components/media-placeholder/index.js +++ b/packages/editor/src/components/media-placeholder/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { every, get, noop, startsWith } from 'lodash'; +import { every, get, noop, startsWith, defaultTo } from 'lodash'; import classnames from 'classnames'; /** @@ -258,10 +258,10 @@ export class MediaPlaceholder extends Component { } const applyWithSelect = withSelect( ( select ) => { - const { hasUploadPermissions } = select( 'core' ); + const { canUser } = select( 'core' ); return { - hasUploadPermissions: hasUploadPermissions(), + hasUploadPermissions: defaultTo( canUser( 'create', 'media' ), true ), }; } ); diff --git a/packages/editor/src/components/media-upload/check.js b/packages/editor/src/components/media-upload/check.js index d72b48497e0af..5dde4c69fa827 100644 --- a/packages/editor/src/components/media-upload/check.js +++ b/packages/editor/src/components/media-upload/check.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { defaultTo } from 'lodash'; + /** * WordPress dependencies */ @@ -8,9 +13,9 @@ export function MediaUploadCheck( { hasUploadPermissions, fallback = null, child } export default withSelect( ( select ) => { - const { hasUploadPermissions } = select( 'core' ); + const { canUser } = select( 'core' ); return { - hasUploadPermissions: hasUploadPermissions(), + hasUploadPermissions: defaultTo( canUser( 'create', 'media' ), true ), }; } )( MediaUploadCheck );