Skip to content
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

Verify 'upload_files' capability when displaying upload UI in media blocks #4155

Merged
merged 7 commits into from
Nov 15, 2018

Conversation

imath
Copy link
Contributor

@imath imath commented Dec 23, 2017

Closes #1536
Closes #3672

I've been looking for a way to solve this issue #3672 and I think one possible way is to add a capability attribute to blocks and check the current user has this cap before registering the block.

Description

In the classic editor, the upload button is disabled if the current user does not have the upload_files capability (eg: contributor). But when a contributor uses Gutenberg he can use "file" relative blocks. When he tries to use one of them as this role hasn't got the upload_files capability, the REST reply to site.url/wp-json/wp/v2/media is rest_cannot_create (403).

How Has This Been Tested?

I have tested this PR for the contributor role. A contributor does not have the upload_files capability and the PR is making sure he can't use blocks involving a file upload (eg: image, core-image, audio, video, gallery).

Screenshots (jpeg or gifs if applicable):

See #3672 for a video of the issue.

Types of changes

I think this PR is first a bug fix, but i also think it could be interesting to be able to restrict block usage according to the user's role.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows has proper inline documentation.

@imath

This comment has been minimized.

@@ -59,6 +59,8 @@ registerBlockType( 'core/image', {
},
},

capability: 'upload_files',

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks a lot for your review @mtias

On one hand I totally agree a contributor should be able to pick an existing media from the WordPress Media Library.

On the other hand, he can't even do that 😢 Being logged in as a contributor, when you click on the button to pick an image, a video or an audio file, you get an empty library as the ajax action that performs the attachments query is checking for the upload_files capability very early. See /wp-admin/includes/ajax-actions.php.

So today these "media" blocks are pretty useless for contributors, that's why I suggested to remove them as it's an experience that is a bit frustrating 😬

To only allow picking to contributors would require a WordPress core change that would be a bit more difficult than simply allowing uploading and picking just like the other roles, imho. I can think of UI/feature changes to remove the Uploader & the Upload tab of the WordPress Media Editor for instance.
Adding the upload_files capability to the contributor role would be much easier, but it would probably require some wide communication to WordPress users as I can imagine some are using this role for the users they wan't to restrict from uploading files...

Copy link
Member

Choose a reason for hiding this comment

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

Being logged in as a contributor, when you click on the button to pick an image, a video or an audio file, you get an empty library as the ajax action that performs the attachments query is checking for the upload_files capability very early.

I hadn't realized this, and it's interesting, especially since the new media REST API endpoints don't have the same restriction, it'd be entirely possible to create a media explorer based on the REST API given current permissions checks. This hints to me that perhaps in the interim to get to that point, perhaps these restrictions ought to be loosened (i.e. those in admin-ajax.php and in the current media library implementation).

Copy link
Member

Choose a reason for hiding this comment

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

This makes sense. I think it'd be ok to remove them from the inserter, but how should we handle editing an existing post that has images? Or a post that has an image placeholder?

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aduth This is handled into the PR actually. Images or any other media in existing post will be displayed. Just like in the classic editor the contributor can remove the image from the post content (but not from the Media Library) or edit display attributes (alignment, etc..)

@mtias mtias added [Feature] Block API API that allows to express the block paradigm. Needs Design Feedback Needs general design feedback. labels Jan 4, 2018
* @type {Object}
*/
const currentUser = get(
Object.values( pickBy( window._wpAPIDataPreload, ( value, key ) => key.indexOf( '/wp/v2/users/me' ) !== -1 ) ),

This comment was marked as outdated.

This comment was marked as outdated.

@@ -59,6 +59,8 @@ registerBlockType( 'core/image', {
},
},

capability: 'upload_files',
Copy link
Member

Choose a reason for hiding this comment

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

Being logged in as a contributor, when you click on the button to pick an image, a video or an audio file, you get an empty library as the ajax action that performs the attachments query is checking for the upload_files capability very early.

I hadn't realized this, and it's interesting, especially since the new media REST API endpoints don't have the same restriction, it'd be entirely possible to create a media explorer based on the REST API given current permissions checks. This hints to me that perhaps in the interim to get to that point, perhaps these restrictions ought to be loosened (i.e. those in admin-ajax.php and in the current media library implementation).

@imath
Copy link
Contributor Author

imath commented Jan 4, 2018

FWIW i've tested in the classic editor the situation where a contributor can edit a post containing an image. In this case, the contributor can edit the markup (alignment, caption, link, classes, rel attribute) and can remove the image from the content.

When it's a gallery of images, the contributor cannot see it. He sees this placeholder instead
gallery
When he tries to edit it, he has the same issue we have here: he can't add images to the gallery or list the images of the gallery. But he can still edit the markup and remove the gallery.
As in the existing 90 tickets about gallery none are talking about this "improvable" experience, it makes me think the possibility for a contributor to edit a pending post containing an image might be an edge case.

Anyway & imho, a possible approach could be to hide the media blocks from the block pickers, so that a contributor cannot be the one who inserts one of these blocks. If another user with the upload_files capability added a media block and kept the post as pending. The media should be displayed to the contributor and he should be able to edit the markup, alignment or even remove the block. So this means the only change to the block there would be in the block's toolbar where the edit button shouldn't be displayed so that the contributor cannot open the Media Editor.

@aduth about WP_REST_Attachments_Controller: creating items checks for the same capability (upload_files). Getting media is more permissive, but do you know if it would be that permissive if it was used in the context of the Media Editor ?

@imath
Copy link
Contributor Author

imath commented Jan 7, 2018

@mtias @aduth

In 1216849 I'm using the approach I've described in my previous comment. But instead of not showing the blocks, I'm using the feature that disables them when the property useOnce is true (eg: the more block).

So when the contributor is using the inserter he sees:

disable

When another user with an upper role added an image or a gallery and kept the post as pending.. The contributor can see the corresponding block, remove it or modify markup, caption, link, alignment etc.. But he can't use the Edit button to open the WP Media Editor and can't transform it to another type (as the other types also require the upload_files capability).

toolbars

I haven't edited the Block autocompleter because i think there's another issue to fix in blockAutocompleter function as it doesn't seem to check for disabled blocks. As you can see below it's possible to include more than once more block using the autocompleter.

more-more

@aduth

This comment has been minimized.

@imath

This comment has been minimized.

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

Looping in @noisysocks , who's been working on relevant Inserter refactoring that might overlap with some of the changes proposed here (#4497).


if ( isLocked || ! allowedBlocks.length ) {
return null;
}

const userAllowedBlocks = allowedBlocks.filter( b => ! b.capability || userCapabilities[ b.capability ] );

This comment was marked as outdated.

@@ -59,6 +59,8 @@ registerBlockType( 'core/image', {
},
},

capability: 'upload_files',

This comment was marked as outdated.

const wrapper = shallow(
<VisualEditorInserter insertBlock={ insertBlock } mostFrequentlyUsedBlocks={ mostFrequentlyUsedBlocks } />
<VisualEditorInserter insertBlock={ insertBlock } mostFrequentlyUsedBlocks={ mostFrequentlyUsedBlocks } user={ user } />

This comment was marked as outdated.

@imath

This comment has been minimized.


if ( ! isDisabled && requiredCapability ) {
isDisabled = ! get( this.props.user.data, [ 'capabilities', requiredCapability ] );
}

This comment was marked as outdated.

@imath

This comment has been minimized.

* make sure he cannot use it disabling it.
*/
if ( userCapabilities && blockType.capability && ! get( userCapabilities, blockType.capability, false ) ) {
blockType.isDisabled = true;

This comment was marked as outdated.

@imath

This comment has been minimized.

@youknowriad

This comment has been minimized.

@imath

This comment has been minimized.

@gziolo gziolo self-requested a review March 7, 2018 14:10
@imath

This comment has been minimized.

@gziolo

This comment has been minimized.

@imath

This comment has been minimized.

@gziolo

This comment has been minimized.

@imath
Copy link
Contributor Author

imath commented Nov 13, 2018

Ouch sorry but I have a very old computer, i can't install docker so i'm not able to see why a e2e test is failing.. sysctl kern.hv_support is returning 0.

@antpb
Copy link
Contributor

antpb commented Nov 14, 2018

I think the tests are good now @imath . Maybe someone kicked them off again. I've seen this in some of my PRs.

@imath
Copy link
Contributor Author

imath commented Nov 15, 2018

Awesome !! Thanks for your comment @antpb.

@gziolo @aduth @youknowriad do we have a chance to finally have this fixed after almost a year working on it ? 😊

Or is there anything else that needs to be done ?

@talldan talldan removed their assignment Nov 15, 2018
Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

Really great work on this PR 👏

I left some minor comments. And not blocking here but I'd appreciate an e2e test (maybe as a follow-up PR) to ensure we don't regress.

export default withFilters( 'editor.MediaPlaceholder' )( MediaPlaceholder );
const applyWithSelect = withSelect( ( select ) => {
let hasUploadPermissions = false;
if ( undefined !== select( 'core' ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this check is not necessary, to be consistent with other withSelect usage, we should assume the "core" store to be available as it's a dependency of the editor package.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I saw in the comments that it's because of the unit tests. Two suggestions:

  • Avoid running this code in unit tests (test the inner component)
  • Import the @wordpress/core-data in the unit tests to ensure that the store is available.


export default withSelect( ( select ) => {
let hasUploadPermissions = false;
if ( undefined !== select( 'core' ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as above.

@gziolo gziolo dismissed aduth’s stale review November 15, 2018 09:14

It had several iterations since the time Andrew reviewed it

@gziolo
Copy link
Member

gziolo commented Nov 15, 2018

@gziolo @aduth @youknowriad do we have a chance to finally have this fixed after almost a year working on it ? 😊

Congrats on keeping this PR updated for so long and ensuring it gets merged 💯 Thanks for this contributions. I will address comments raised by Riad in the follow-up PR.

@imath
Copy link
Contributor Author

imath commented Nov 15, 2018

Thanks a lot for your help everyone 😍🎉

@youknowriad
Copy link
Contributor

This would require some changes to be back-ported in the 5.0 branch (for the preloading changes in PHP). Up for a ticket+commit @gziolo :P

@imath
Copy link
Contributor Author

imath commented Nov 15, 2018

@youknowriad the preloading part is already in Core thanks to https://core.trac.wordpress.org/changeset/43833

@youknowriad
Copy link
Contributor

Oh good thanks @imath

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Media Anything that impacts the experience of managing media
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"upload_files" capability and Image relative blocks Insert from URL missing when adding an image