World is now on Opti ID! Learn more

Henrik Fransas
Feb 11, 2015
  5321
(0 votes)

Building custom criteria for Visitor groups in EPiServer 7.5

Magnus Rahl wrote a blog post on how to do this in CMS 6 R2 and this is a complement to that post that shows how to do it in EPiServer 7.5, for explanations on what visitor groups are and so, please see the origin blog post that you can find here: http://world.episerver.com/Blogs/Magnus-Rahl/Dates/2010/12/Building-custom-criteria-for-Visitor-groups-in-CMS-6-R2/.

Buildning a custom criterion: Browser

I am using the same example that Magnus did, with one addition, I add Chrome as a browser as it is now one of the most used browsers out there.

Creating a model

In 7.5 this is even more simple than it was in 6 R2 because EPiServer has a baseclass that does much for you, so this is how the same example looks like in 7.5

using System.ComponentModel.DataAnnotations; using EPiServer.Filters; using EPiServer.Personalization.VisitorGroups; using EPiServer.Web.Mvc.VisitorGroups; namespace DeployToAzureDemo.Models.VisitorGroupCriterion { public class BrowserModel : CriterionModelBase { [DojoWidget( SelectionFactoryType = typeof(EnumSelectionFactory), LabelTranslationKey = "/shell/cms/visitorgroups/criteria/browser/browsertype", AdditionalOptions = "{ selectOnClick: true }"), Required] public BrowserType Browser { get; set; } [DojoWidget( SelectionFactoryType = typeof(EnumSelectionFactory), LabelTranslationKey = "/shell/cms/visitorgroups/criteria/browser/comparecondition", AdditionalOptions = "{ selectOnClick: true }"), Required] public CompareCondition Condition { get; set; } [DojoWidget( DefaultValue = 0, LabelTranslationKey = "/shell/cms/visitorgroups/criteria/browser/majorversion", AdditionalOptions = "{ constraints: {min: 0}, selectOnClick: true }"), Range(0, 0xff)] public int MajorVersion { get; set; } public override ICriterionModel Copy() { return ShallowCopy(); } } public enum BrowserType { IE, FireFox, Chrome, Other } }

 

The main difference is that you are now no longer needed to inherit from IDynamicData and ICloneable, all this is handled inside CriterionModelBase.

Creating the criterion

Here not much has happened, the main thing is that you no longer need to create a enum with compareconditions, this you can get from EPiServer.Filter. The other thing is that Internet Explorer does no longer identify it self as ie (at least not IE11 that I have) so I had to add an else-condition for the name internetexplorer string also. So the code looks like this now:

using System; using System.Security.Principal; using System.Web; using DeployToAzureDemo.Models.VisitorGroupCriterion; using EPiServer.Filters; using EPiServer.Personalization.VisitorGroups; namespace DeployToAzureDemo.Business.VisitorGroupCriterion { [VisitorGroupCriterion( Category = "User Criteria", DisplayName = "Browser", Description = "Criterion that matches type and version of the user's browser", LanguagePath = "/shell/cms/visitorgroups/criteria/browser")] public class BrowserCriterion : CriterionBase<BrowserModel> { public override bool IsMatch(IPrincipal principal, HttpContextBase httpContext) { return MatchBrowserType(httpContext.Request.Browser.Browser) && MatchBrowserVersion(httpContext.Request.Browser.MajorVersion); } protected virtual bool MatchBrowserVersion(int majorVersion) { switch (Model.Condition) { case CompareCondition.LessThan: return majorVersion < Model.MajorVersion; case CompareCondition.Equal: return majorVersion == Model.MajorVersion; case CompareCondition.GreaterThan: return majorVersion > Model.MajorVersion; case CompareCondition.NotEqual: return majorVersion != Model.MajorVersion; default: return false; } } protected virtual bool MatchBrowserType(string browserType) { browserType = (browserType ?? String.Empty).ToLower(); if (browserType.Equals("ie") || browserType.Equals("internetexplorer")) { return Model.Browser == BrowserType.IE; } if (browserType.Equals("firefox")) { return Model.Browser == BrowserType.FireFox; } if (browserType.Equals("chrome")) { return Model.Browser == BrowserType.Chrome; } return Model.Browser == BrowserType.Other; } } }

Translation

For this to work in the editor we need to add translation to it and I am just like the original blog post doing this by adding a language file to /Resources/LanguageFiles so it will automatically be discovered by EPiServer. For that to happen you need to have this code in your EPiServer.Framework section:

<localization fallbackBehavior="Echo, MissingMessage, FallbackCulture" fallbackCulture="en"> <providers> <add virtualPath="~/Resources/LanguageFiles" name="languageFiles" type="EPiServer.Framework.Localization.XmlResources.FileXmlLocalizationProvider, EPiServer.Framework" /> </providers> </localization>

Then the language file looks like this:

<languages> <language name="English" id="en"> <enums> <deploytoazuredemo> <models> <visitorgroupcriterion> <browsertype> <ie>Internet Explorer</ie> <firefox>Firefox</firefox> <chrome>Chrome</chrome> <other>Other browser</other> </browsertype> </visitorgroupcriterion> </models> </deploytoazuredemo> <episerver> <filters> <comparecondition> <lessthan>Less than</lessthan> <equal>Equal to</equal> <greaterthan>More than</greaterthan> <notequal>Not equal to</notequal> <startswith>Starts with</startswith> <endswith>Ends with</endswith> <contained>Contains</contained> </comparecondition> </filters> </episerver> </enums> <shell> <cms> <visitorgroups> <criteria> <browser> <browsertype>Browser type</browsertype> <comparecondition>With version</comparecondition> <majorversion>Major version</majorversion> </browser> </criteria> </visitorgroups> </cms> </shell> </language> </languages>

Notice that the path to the browsertyp element is the same as the namespace for that enum:  DeployToAzureDemo.Models.VisitorGroupCriterion but with only lower case.

After this is done you can create a new visitor group with for example this criterions

criterion

And then applying it to a page like this

example2

So it is very easy to create these on your own and very simple an effective to use!

Happy coding!

Feb 11, 2015

Comments

Please login to comment.
Latest blogs
Make Global Assets Site- and Language-Aware at Indexing Time

I had a support case the other day with a question around search on global assets on a multisite. This is the result of that investigation. This co...

dada | Jun 26, 2025

The remote server returned an error: (400) Bad Request – when configuring Azure Storage for an older Optimizely CMS site

How to fix a strange issue that occurred when I moved editor-uploaded files for some old Optimizely CMS 11 solutions to Azure Storage.

Tomas Hensrud Gulla | Jun 26, 2025 |

Enable Opal AI for your Optimizely products

Learn how to enable Opal AI, and meet your infinite workforce.

Tomas Hensrud Gulla | Jun 25, 2025 |

Deploying to Optimizely Frontend Hosting: A Practical Guide

Optimizely Frontend Hosting is a cloud-based solution for deploying headless frontend applications - currently supporting only Next.js projects. It...

Szymon Uryga | Jun 25, 2025

World on Opti ID

We're excited to announce that world.optimizely.com is now integrated with Opti ID! What does this mean for you? New Users:  You can now log in wit...

Patrick Lam | Jun 22, 2025

Avoid Scandinavian Letters in File Names in Optimizely CMS

Discover how Scandinavian letters in file names can break media in Optimizely CMS—and learn a simple code fix to automatically sanitize uploads for...

Henning Sjørbotten | Jun 19, 2025 |