November Happy Hour will be moved to Thursday December 5th.

Erik Norberg
May 30, 2018
  5794
(4 votes)

How to use visitor groups to control access to non-content

This post is intended for developers and is mostly code examples.

How to use visitor groups to control access to content is well documented here at episerver world: https://world.episerver.com/documentation/developer-guides/CMS/personalization/

It also goes in depth on how easy you can create your own custom visitor groups, but I found it lacking when it came to describe a way to use visitor groups for non-content.

Background

A customer with a commerce site came to me and requested a way to limit access to certain shipping methods so that only companies could use them, he also wanted a way to define who should be counted as a company himself. Had shipping methods been content we could have used visitor groups just out of the box but as it is not, but rather a very old part of commerce that doesn't even know visitor groups exist, it proved to be a challenge to figure out how to get it to work.

Solution

To begin with we need a way to define which visitor groups define companies, this was added to a settings page in the cms as a custom property:

[Display(Name = "Visitor groups for companies")]
[UIHint("visitorgroupselector")]
public virtual IList<Guid> CompanyVisitorGroups { get; set; }

Turns out visitor groups are stored with their Guid:s in an IList and the magic is added with a UIHint attribute as so often is the case. Unfortunately it is not present among the EPiServer.Web.UIHint constants, you must know that the selector is named "visitorgroupselector" and enter it as a string manually.

That took care of the problem with letting the customer configure who is a company now we just need a way to check if the current visitor is a member in any of those groups, for that i wrote a little service:

public class VisitorGroupService
{
	private readonly IVisitorGroupRepository _visitorGroupRepository;
	private readonly IVisitorGroupRoleRepository _visitorGroupRoleRepository;

	public VisitorGroupService(IVisitorGroupRepository visitorGroupRepository, IVisitorGroupRoleRepository visitorGroupRoleRepository)
	{
		_visitorGroupRepository = visitorGroupRepository;
		_visitorGroupRoleRepository = visitorGroupRoleRepository;
	}

	public bool IsCurrentUserInAnyVisitorGroup(IList<Guid> visitorGroupIds)
	{
		if (HttpContext.Current == null)
		{
			return false;
		}
		var context = new HttpContextWrapper(HttpContext.Current);
		return IsPrincipalInAnyVistorGroup(context.User, visitorGroupIds, context);
	}

	public bool IsPrincipalInAnyVistorGroup(IPrincipal principal, IList<Guid> visitorGroupIds, HttpContextBase context)
	{
		if (context == null || visitorGroupIds == null)
		{
			return false;
		}

		foreach (var groupId in visitorGroupIds)
		{
			var group = _visitorGroupRepository.Load(groupId);
			if (_visitorGroupRoleRepository.TryGetRole(group.Name, out var groupRole) && groupRole.IsMatch(principal, context))
			{
				return true;
			}
		}

		return false;
	}
}

Fairly easy to follow the code but not much is documented around these classes. Some points of interest here is that we must load the VisitorGroup just because the IVisitorGroupRoleRepository only takes the Name and not the Id, the TryGetRole is also the single function defined on this interface.

All that is left for us is to call the service and pass in the page property:

var isCompany = _visitorGroupService.IsCurrentUserInAnyVisitorGroup(page.CompanyVisitorGroups);

Now we are free to filter whatever we want based on visitor groups. I hope this can provide you with a clear guide to improve personalization on all your sites. :)

Footnote

There is a EPiServer.Personalization.VisitorGroups.VisitorGroupHelper you can use to simplify the code a little bit, it wraps the IVisitorGroupRoleRepository and does the call to VisitorGroupRole.IsMatch, I used the repository directly more for transparency than anything else.

May 30, 2018

Comments

May 31, 2018 04:08 PM

Nice article.

I just want to add that the UI hint constant can be found at EPiServer.Commerce.Catalog.DataAnnotations.SystemUiHint.VisitorGroupSelector. The actual editor for this is in Commerce (it was made for the marketing UI, to select a visitor group for a campaign), hence the placement of the constant.

Patrik
Patrik Jun 7, 2018 09:29 AM

Great advice!

I am not sure, but maybe this problem also could be solved by using Episerver Permissions to functions (https://world.episerver.com/documentation/developer-guides/CMS/security/permissions-to-functions/).

// Patrik Pochill

Erik Norberg
Erik Norberg Jun 7, 2018 04:15 PM

Permissions to functions could indeed be used to solve the same problem provided the administrators would be willing to maintain the permissions.

Visitor groups are far more versatile as they can define more than just group and role membership as well as be used for anonymous users.

In this specific case the customer is already using visitor groups to control other content and the cost of administrators having to learn and maintain a second interface should never be ignored.

Please login to comment.
Latest blogs
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog