First I would like to thank Sitecore for the great honor of being rewarded Sitecore MVP. I am truly thankful, please visit my fellow Sitecore MVP’s all over the world – Sitecore MVPs 2016
In my recent post, Deeplinking to mobile apps using Sitecore Device Detection, I described how easy it is to detect devices using Sitecore’s awesome feature – Sitecore Device Detector. I really love it and it’s so easy to use thanks to the device rules which lets you trigger goals/events and personalize content. This awesome feature is not for free but it’s worth every penny.
This post is for you who only want to use part of the functionality and don’t want to pay for it, I was thinking of calling it “Poor mans device detector” 🙂
I got the idea when I was going through all the nice rules in Sitecore 8.1, there are so many just waiting for you to use
I noticed a rule that really could come in handy – User Agent. It allows you to look at the visitors user agent and do actions.
But it turns out that particular rule is not accessible from renderings when using personalization rules.
Why is that? I used my favorite tool, dotPeek, to do some peeking in the code.
using Sitecore.Rules.Conditions; using System.Web; namespace Sitecore.Rules.Devices { /// <summary> /// User agent condition class. /// /// </summary> /// <typeparam name="T">The rule context.</typeparam> public class UserAgentCondition<T> : StringOperatorCondition<T> where T : DeviceRuleContext { /// <summary> /// Gets or sets the value. /// /// </summary> /// /// <value> /// The value. /// </value> public string Value { get; set; } /// <summary> /// Executes the specified rule context. /// /// </summary> /// <param name="ruleContext">The rule context.</param> /// <returns> /// <c>True</c>, if the condition succeeds, otherwise <c>false</c>. /// </returns> protected override bool Execute(T ruleContext) { HttpContextBase httpContext = ruleContext.HttpContext; if (httpContext == null || string.IsNullOrEmpty(this.Value) || (httpContext.Request == null || string.IsNullOrEmpty(httpContext.Request.UserAgent))) return false; return this.Compare(httpContext.Request.UserAgent, this.Value); } } }
It turns out that the rule inherits DeviceRuleContext and that means it will only work when using devices:
So what to do? We have the code from the rule we want(thanks to dotPeek). We just need a little change, instead of inherit from DeviceRuleContext we inherit directly from RuleContext.
namespace VisionsInCode.Foundation.SitecoreCustomizations.Rules { using System.Web; using Sitecore.Diagnostics; using Sitecore.Rules; using Sitecore.Rules.Conditions; public class PoorMansDeviceDetectorCondition<T> : StringOperatorCondition<T> where T : RuleContext { public string Value { get; set; } /// <summary> /// Testable UserAgent /// </summary> public string UserAgent { get; set; } /// <summary> /// Executes the specified rule context. /// /// </summary> /// <param name="ruleContext">The rule context.</param> /// <returns> /// <c>True</c>, if the condition succeeds, otherwise <c>false</c>. /// </returns> protected override bool Execute(T ruleContext) { Assert.ArgumentNotNull(ruleContext, "ruleContext"); if (string.IsNullOrWhiteSpace(UserAgent)) this.UserAgent = HttpContext.Current.Request.UserAgent; if (string.IsNullOrEmpty(this.Value) || (string.IsNullOrEmpty(this.UserAgent))) return false; return this.Compare(this.UserAgent, this.Value); } } }
I also added the UserAgent property for testing purposes.
To test the rule we will use Sitecore FakeDB. Thanks to the great post Unit Testing Custom Rules, Actions, and Conditions with FakeDb – Part 1 – Testing Conditions by Brian Beckham. This made it easier to write the tests.
namespace VisionsInCode.Foundation.SitecoreCustomizations.Tests.Rules { using FluentAssertions; using Sitecore.FakeDb; using Sitecore.Rules; using VisionsInCode.Foundation.SitecoreCustomizations.Rules; using VisionsInCode.Foundation.SitecoreCustomizations.Tests.Extensions; using Xunit; public class PoorMansDeviceDetectorConditionTests { private void SetupDb(Db database) { database.Add(new DbItem("Settings") { ParentID = Sitecore.ItemIDs.SystemRoot, Children = { new Sitecore.FakeDb.DbItem("Rules") { new Sitecore.FakeDb.DbItem("Definitions") { new Sitecore.FakeDb.DbItem("String Operators") { new Sitecore.FakeDb.DbItem(Constants.StringOperations.Contains.ItemName, Constants.StringOperations.Contains.ItemID), new Sitecore.FakeDb.DbItem(Constants.StringOperations.MatchesTheRegularExpression.ItemName, Constants.StringOperations.MatchesTheRegularExpression.ItemID) } } } } }); } [Theory] [InlineAutoDbData("Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4", "iPhone", true)] public void DoesUserAgentContainValueCondition(string userAgent, string containsUserAgentValue, bool expectedResult, Db database) { SetupDb(database); RuleContext ruleContext = new RuleContext(); PoorMansDeviceDetectorCondition<RuleContext> customUserAgentCondition = new PoorMansDeviceDetectorCondition<RuleContext>() { OperatorId = Constants.StringOperations.Contains.ItemID.ToString(), Value = containsUserAgentValue, UserAgent = userAgent }; var ruleStack = new RuleStack(); // act customUserAgentCondition.Evaluate(ruleContext, ruleStack); // assert ruleStack.Should().HaveCount(1); object value = ruleStack.Pop(); value.Should().Be(expectedResult); } [Theory] [InlineAutoDbData("Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4", "^(?!.*(iPhone|iPod|iPad|Android|BlackBerry|IEMobile))", false)] public void DoesUserAgentMatchesTheRegularExpressionValueCondition(string userAgent, string regularExpressionValue, bool expectedResult, Db database) { SetupDb(database); RuleContext ruleContext = new RuleContext(); PoorMansDeviceDetectorCondition<RuleContext> customUserAgentCondition = new PoorMansDeviceDetectorCondition<RuleContext>() { OperatorId = Constants.StringOperations.MatchesTheRegularExpression.ItemID.ToString(), Value = regularExpressionValue, UserAgent = userAgent }; var ruleStack = new RuleStack(); // act customUserAgentCondition.Evaluate(ruleContext, ruleStack); // assert ruleStack.Should().HaveCount(1); object value = ruleStack.Pop(); value.Should().Be(expectedResult); } } }
What is left now is to add the new rule in Sitecore, lets put it in /sitecore/system/Settings/Rules/Definitions/Elements/Device and name it Poor Mans Device Detector.
Now we have a User Agent rule when we want to personalize content for renderings.
I used my favorite framework – Sitecore Habitat. Feel free to check out the code on the Github – GoranHalvarsson/Habitat
That’s all for now folks 🙂
I use the approximately the same rule on a pre Sitecore 8.1, I wonder why this is not standard, also found little info about useragent rule for sitecore. But great there is post now.
LikeLiked by 1 person
Yes same here, I agree. It should be standard
LikeLike