How to retrieve Images of a User being part of a Group with Collection Permissions in Wagtail?

Viewed 76

I have a wagtail installation using the Multisite pattern, where I have a group of user per site and each group as it's own collection.

When the User logged in the admin interface, they see in the Summary section the image count from all the collections.

enter image description here

But when they click the image menu, they only see the images within their group collection. I found it confusing that they could know the total count of all collections. I wanted to get the count from the collection the user had rights for.

I figured out I could override the ImagesSummaryItem and I ended up coding the following snippet of code:

class CorrectedImagesSummaryItem(SummaryItem):
    order = 200
    template = 'wagtailimages/homepage/site_summary_images.html'

    def get_context(self):
        site_name = get_site_for_user(self.request.user)['site_name']

        permissions = Permission.objects.filter(
            content_type=ContentType.objects.get_for_model(get_image_model()),
            codename__in=['change_image', 'add_image'])

        collections = Collection.objects.filter(
            group_permissions__group__in=self.request.user.groups.all(),
            group_permissions__permission__in=permissions
        ).distinct()

        if collections:
            image_count = get_image_model().objects.filter(collection__in=collections).count()
        else:
            image_count = 0

        return {
            'total_images': image_count,
            'site_name': site_name,
        }

    def is_shown(self):
        return permission_policy.user_has_any_permission(
            self.request.user, ['change', 'add']
        )


@hooks.register('construct_homepage_summary_items')
def add_corrected_images_summary_panel(request, items):
    """Replaces the Images summary panel to hide variants."""
    for index, item in enumerate(items):
        if item.__class__ is ImagesSummaryItem:
            items[index] = CorrectedImagesSummaryItem(request)

This actually works fine, I am now showing the proper images count on the summary section but I am wondering is there a better way to query the collections of the user? Are these querysets right?

permissions = Permission.objects.filter(
    content_type=ContentType.objects.get_for_model(get_image_model()),
    codename__in=['change_image', 'add_image'])

collections = Collection.objects.filter(
    group_permissions__group__in=self.request.user.groups.all(),
    group_permissions__permission__in=permissions
    ).distinct()

Update

I ended up customizing the queryset for the images selection in order to only show the images within the collection the user was having access to.

In addition of the first function, I added the following code in my wagtail_hooks.py file.

@hooks.register('construct_image_chooser_queryset')
def show_collection_images_only(images, request):
    # Show only the images from the collection the User has access.
    collections = get_collections_from_group_permissions(request.user, ['change_image', 'add_image'])

    images = images.filter(collection__in=collections)

    return images

The get_collections_from_group_permissions is just a simplified function that returns exactly the Collection out of the Groups permissions the User has.

def get_collections_from_group_permissions(user, permissions):
    """
    This function gets the Collections from the user groups permissions.

    :param user: the user
    :param permissions: the requested permissions on a Collection object

    :returns: the Collections the selected User has access rights for.
    """
    permissions = Permission.objects.filter(
        content_type=ContentType.objects.get_for_model(get_image_model()),
        codename__in=permissions)

    collections = Collection.objects.filter(
        group_permissions__group__in=user.groups.all(),
        group_permissions__permission__in=permissions
    ).distinct()

    return collections

With this in place, the Summary Item for the images is the number of images within the collections the User can access and when he clicks on a ImageChooserField and gets to the Image chooser, he only gets to see what is in the collections he has been granted access.

1 Answers

This logic is already implemented in Wagtail's permission_policy class, so this could be reduced to:

image_count = permission_policy.instances_user_has_any_permission_for(
    self.request.user, ['change', 'add']
).count()

(Incidentally, the reason Wagtail itself doesn't take permissions into account when displaying this figure is that all users can see the complete set of images via the chooser popup - there's currently no 'choose' permission - so showing a reduced number would be misleading. See the discussion at https://github.com/wagtail/wagtail/issues/5129)

Related