Model Admin showing incorrect results count

Silverstripe Version: 4.5.1

Question:
I have a Business DataObject which has permissions based on the logged in user (e.g. they can only view DataObjects assigned to them). This works fine in the model admin, but the results count is incorrect.

This is a screenshot of the ModelAdmin, with just one row, but showing 2 results:

Is this a bug, or have I missed something? :thinking:

Interesting. Are there only 2 records total, and you’re using canView()?

Yes, 2 results in total (but only one for that particular user).

The canView() is structured like this:

	public function canView($member = null) 
    {
		if(is_null($member))
			$member 	= Security::getCurrentUser();
		
		if(is_null($member))
			return false;

		if((Permission::check('ADMIN', 'any', $member)) || ($this->MemberID == $member->ID))
			return true;
		
		return false;
    }

I had some issues with $member being null, so the checks there were added following advice from SS Slack. Happy to take advice though!

I think what’s happening here is that your canView() will fire late in the pipeline - in this case, probably only when it’s time to render the row. The underlying DataList will contain all of your Business objects, so the ‘total’ count will show 2 instead of 1.

If that’s true, this bug will probably be more severe once you have more records, because I would expect that if you had many pages of records and your user was only allowed to see a handful of them, they may see an empty result set initially (but it would say e.g. page 1 of 10) and have to click ‘Next’ a few times before they get a page with one or more visible records.

It’s probably not a bug (or at least not an easy one to fix) because the overhead of properly filtering the list by firing the canView() method on every item in the DataList could be enough to crash a server, depending on how many items are in the unfiltered list.

To get around this you probably want to hack your ModelAdmin a bit. I can’t remember the API, but I think you can override e.g. GetRecords() (not called that but you get the idea) and you could implement your business logic there so you get a DataList that only contains the records that user should see.

1 Like

Thanks @JonoM - appreciate your help!

I found the function in the API docs, so trying to adapt this and get this working: SilverStripe\Admin\ModelAdmin | SilverStripe API

For reference, this is what I added to my ModelAdmin:

	public function getList()
	{
		$list = DataObject::singleton($this->modelClass)->get();

		/** If they're not an admin, we need to filter so the user only views their records */
		if(!Permission::check('ADMIN', 'any'))
		{
			$member 	= Security::getCurrentUser();
			return $list->filter('MemberID', $member->ID);
		}
		else
		{
			return $list;
		}
	}

@Parker1090 looks about right, but in case you use this ModelAdmin for multiple classes, you should scope this logic to your Business class.

public function getList()
{
    $list = DataObject::singleton($this->modelClass)->get();

    if ($this->modelClass == 'Business' && !Permission::check('ADMIN', 'any')) {
        /** If they're not an admin, we need to filter so the user only views their records */
        $member = Security::getCurrentUser();
        return $list->filter('MemberID', $member->ID);
    }

    return $list;
}
1 Like