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

Add annotation access controls at the folder level #905

Merged
merged 18 commits into from
Aug 16, 2022
Merged

Conversation

eagw
Copy link
Contributor

@eagw eagw commented Aug 1, 2022

Transferred from DigitalSlideArchive/HistomicsUI and PR #217

Adds the ability to change the access list for all annotations in a folder (and optionally all annotations in the subfolders of the folder). The control can be found as an entry in the 'Folder actions' dropdown menu; it appears as long as the user owns at least one annotation in the folder (or it's subfolders) and only alters the permissions on the annotations that the user owns.

Closes DigitalSlideArchive/HistomicsUI issue #27

@eagw eagw requested a review from manthey August 1, 2022 17:26
Description('Get the annotations from the items in a folder')
.param('id', 'The ID of the folder', required=True, paramType='path')
.param('recurse', 'Whether or not to retrieve all '
'annotations from subfolders', required=False, default=False, dataType='boolean')
Copy link
Member

Choose a reason for hiding this comment

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

Why did you choose to have recurse default to false for this endpoint (and true on the present endpoint)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thought was that when you think of an endpoint that gets annotations from a folder someone is more likely to assume that it's just returning the annotations from that folder and not all other folders it contains as well. Whereas with the present endpoint I thought they'd be more likely to use it when trying to determine if a user owns any annotations in the folder or its subfolders. But I realize this logic isn't very intuitive, so I could change it so they're both the same.

)
@access.public
def returnFolderAnnotations(self, id, recurse, limit, offset, sort):
return self.getFolderAnnotations(id, recurse, self.getCurrentUser(), limit, offset,
Copy link
Member

Choose a reason for hiding this comment

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

If the returned result has a count method, girder will automatically add a header with the count of results for a list-like entry. This could be done like here: https://github.com/girder/girder/blob/master/girder/utility/acl_mixin.py#L345-L351.

A simple way to do this might be to add a count parameter to getFolderAnnotations where, if True, instead of adding the sort, skip, and limit fields to the pipeline, you'd do pipeline += [{'$count': 'count'}], then this endpoint would become

        result = self.getFolderAnnotations(
            id, recurse, self.getCurrentUser(), limit, offset, sort[0][0], sort[0][1])

        def count():
            try:
                return next(iter(self.getFolderAnnotations(
                    id, recurse, self.getCurrentUser(), count=True)))['count']
            except StopIteration:
                # If there are no values, this won't return the count, in
                # which case it is zero.
                return 0

        result.count = count
        return result

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh that's good to know, thanks!

See 0e1adec

annotations = self.getFolderAnnotations(id, recurse, self.getCurrentUser(), 1)
try:
next(annotations)
yield True
Copy link
Member

Choose a reason for hiding this comment

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

If you use return rather than yield, the endpoint will return true or false rather than [true] or [false], which was more what I would have expected,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good point.

See 06302c7

@@ -0,0 +1,150 @@
import $ from 'jquery';
Copy link
Member

Choose a reason for hiding this comment

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

You need to add the HierarchyWidget to the views/index.js file to get it imported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See d350560

@manthey
Copy link
Member

manthey commented Aug 10, 2022

@eagw Can you merge master? A recent change to one of the upstream libraries broke CI and there is a fix on that was merged to master.

@eagw eagw requested a review from manthey August 15, 2022 14:23
key: annot[key] for key in ('access', 'public', 'publicFlags')
if key in annot
}})
yield annot
Copy link
Member

Choose a reason for hiding this comment

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

We might be change a lot of annotations, so returning them all is probably not desired. We may just want to return a summary (maybe {'updated': <count>})?

Also, since this can take a while to run, we should add setResponseTimeLimit() inside the loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense.

See 666ee38

annot = Annotation().load(annotation['_id'], user=user, getElements=False)
annot = Annotation().setPublic(annot, public)
annot = Annotation().setAccessList(
annot, access, save=False, user=user)
Copy link
Member

Choose a reason for hiding this comment

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

Isn't save=False is the default for both setPublic and setAccessList? If so, do we need to be explicit here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it is.

See 7b3786f


import AnnotationModel from '../models/AnnotationModel';

wrap(HierarchyWidget, 'initialize', function (initialize, settings) {
Copy link
Member

Choose a reason for hiding this comment

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

Since we are doing the same thing in the render method, can we get rid of doing it in the initialize method?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we can.

See 62a6b50

@manthey
Copy link
Member

manthey commented Aug 15, 2022

This is working well in my test case. After the last few comments are addressed, we can merge this. I'd like to have a follow-on PR to add some tests of the end points, so at least we know we don't break them in the future.

Copy link
Member

@manthey manthey left a comment

Choose a reason for hiding this comment

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

Excellent. We should create an issue to add tests for the new endpoints.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants