1

I have a 3 levels of membership with a field on my entries matching those groups to restrict access.

I know I can get a user's groups with currentUser.getGroups(), but how can I compare that with my entry.access field? Basically, I want to do something like:

{% set userGroups = currentUser.getGroups() %}
{% set access = entry.access %}
{% if userGroups in access %}
   Grant access
{% else %}
   Access denied
{% endif %}

I'm just not quite sure how to compare the group handles with checkbox values...

0

1 Answer 1

2

There isn't an officially-supported way of gating read-access like this, so it's up to the template or view layer (or potentially a custom Module) to implement the logic.

I believe the behavior you're looking for is most closely associated with PHP's array_intersect function, which returns an array of values that are present in all the passed arrays. Checking that the length of the return value is greater than zero would indicate that at least one of the User’s Groups’ handles are in the Entry’s access values.

However, because there is no equivalent functionality in Twig (unless you want to add it via a Twig extension!), we're left with having to implement it directly in the template. This might look something like:

{% set groupHandles = currentUser.getGroups() | column('handle') %}
{% set isPermitted = entry.access | reduce((allowed, level) => allowed or (level.value in groupHandles), false) %}

This basically takes the access "levels" declared by the Entry and compares it to the list of the user's Group handles.

In greater detail:

  1. Capture the User's groups, and take the handle of each;
  2. Take the Entry's allowed access levels (Technically this is a MultiOptionsData object), and "reduce" it:
    • Start with false, to suggest "not allowed" by default (second argument to the | reduce(fn, initial) filter);
    • Iterate over the access levels, calling the closure each time. The closure is provided the previous value (or the default we set, above, for the first item) and the current value, and returns a new "allowed" value—which is used in the next iteration.
    • Test the "current" level to see if it's among the group handles we stashed in step #1;
    • The or makes sure we either return true if something has already passed an access check, or if the current item is in the list;
    • The process repeats until all items in entry.access have been processed;
  3. The final result of the reduce operation is stored in isPermitted.

Alternatively, here's a one-line check that makes use of some special methods on the currentUser:

{% set isPermitted = entry.access | reduce((allowed, level) => allowed or currentUser.isInGroup(level), false) %}

The same reduce logic still applies, except we're asking the User object whether or not it's in a group with the passed handle, rather than comparing the handles ourselves.

1
  • Wow, this is amazing. Thanks so much! I quickly dropped in the first code and things look great! A lot to learn from this so thanks again for the extremely detailed answer.
    – supazu
    Dec 7, 2021 at 6:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.