Creating a Custom Rule Condition for Coveo for Sitecore Using the Coveo for Sitecore Legacy Search UI Framework
Creating a Custom Rule Condition for Coveo for Sitecore Using the Coveo for Sitecore Legacy Search UI Framework
Coveo for Sitecore 4.1 (November 2018)
To create a custom rule condition that can be implemented in Sitecore, you need to follow these instructions.
This page will show you how to create a custom rule condition that can be implemented in Sitecore. It will serve as the basis to implement the filtering or boosting rule.
Step 1: Creating the Rule
To create the rule, you need to follow these steps:
-
Create a new C# project that references the
Sitecore.Kernel
. This assembly is located under:<SITECORE_INSTANCE_ROOT>\website\bin
. For this tutorial, you can call the projectSamples
. -
Create a new class. For this tutorial, you can call your namespace
Samples.Rules
, in which you can add a class calledIsItemPartOfCurrentSite
.using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Samples.Rules { public class IsItemPartOfCurrentSite { } }
-
Add the
WhenCondition<T>
base class to your class and the 3Sitecore
using statements.Your code should now look like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sitecore.Data.Items; using Sitecore.Rules; using Sitecore.Rules.Conditions; namespace Samples.Rules { public class IsItemPartOfCurrentSite<T> : WhenCondition<T> where T : RuleContext { } }
All rule conditions must inherit from the
Sitecore.Rules.Conditions.RuleCondition
class. In this example, theIsItemPartOfCurrentSite
class inherits fromSitecore.Rules.Conditions.WhenCondition
. Inheriting from theWhenCondition
instead ofRuleCondition
makes it easier to implement. -
Right click
WhenCondition
, and choose Implement Abstract Class.Your code should now look like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sitecore.Data.Items; using Sitecore.Rules; using Sitecore.Rules.Conditions; namespace Samples.Rules { public class IsItemPartOfCurrentSite<T> : WhenCondition<T> where T : RuleContext { protected override bool Execute(T ruleContext) { throw new NotImplementedException(); } } }
-
In this case, the
Execute
function expects to return a Boolean value, so atrue
value can be returned.Your code should now look like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sitecore.Data.Items; using Sitecore.Rules; using Sitecore.Rules.Conditions; namespace Samples.Rules { public class IsItemPartOfCurrentSite<T> : WhenCondition<T> where T : RuleContext { protected override bool Execute(T ruleContext) { return true; } } }
This code is only meant to be used for Coveo filtering and boosting rules. If you want to use this rule outside of Coveo components, you would need to properly implement the Execute
method.
Step 2: Making it Available in Coveo for Sitecore
Now that you’ve created your custom rule condition, you need to make it available in Coveo for Sitecore. Here are the steps you need to follow.
-
In your C# project, add a reference to the
Coveo.UIBase.dll
assembly, which is located under<SITECORE_INSTANCE_ROOT>\website\bin
. -
Add a
using Coveo.UI.Rules
statement. -
Modify the
IsItemPartOfCurrentSite
class so that it implements theCoveo.UI.Rules.ICoveoCondition
interface.Your code should now look like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sitecore.Data.Items; using Sitecore.Rules; using Sitecore.Rules.Conditions; using Coveo.UI.Rules; namespace Samples.Rules { public class IsItemPartOfCurrentSite<T> : WhenCondition<T>, ICoveoCondition<T> where T : RuleContext { protected override bool Execute(T ruleContext) { return true; } } }
-
Right click
ICoveoCondition
, and choose Implement Interface > Implement Interface.Your code should now look like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sitecore.Data.Items; using Sitecore.Rules; using Sitecore.Rules.Conditions; using Coveo.UI.Rules; namespace Samples.Rules { public class IsItemPartOfCurrentSite<T> : WhenCondition<T>, ICoveoCondition<T> where T : RuleContext { protected override bool Execute(T ruleContext) { return true; } public System.Linq.Expressions.Expression GetQueryExpression(ConditionContext p_Context) { throw new NotImplementedException(); } public RuleCondition<T> GetWrappedCondition() { throw new NotImplementedException(); } public void ValidateCondition(Coveo.UI.ErrorReport p_Report) { throw new NotImplementedException(); } } }
Step 3: Implementing the ICoveoCondition
Methods
The generated code is designed to throw exceptions when calling the methods. For this reason, you need to properly implement them.
Implement the GetQueryExpression
Method
This method does all the work when you implement your own rule condition. It creates a LINQ expression that represents the current rule condition. By doing so, the Search Provider is able to translate the LINQ expression into a Coveo query expression.
-
Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath)
: this method dynamically gets the home item of the current site. -
Expressions
readNameProperty
andstandardValuesConstant
: these methods aren’t yet implemented in your class, but will be used by the Search Provider to get the properties for your items.
Your method should now look like this.
public Expression GetQueryExpression(ConditionContext p_Context)
{
Item currentHomeItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);
Expression readNameProperty = GetFieldIndexerExpression(p_Context.ParameterExpression, "_path");
Expression standardValuesConstant = Expression.Constant(currentHomeItem.ID.ToShortID().ToString());
return Expression.Equal(readNameProperty, standardValuesConstant);
}
Implement the GetFieldIndexerExpression
Method
To use item fields in a LINQ Expression
, you need to add the following method and using statements to your class. This is a general technique, and works for any field.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
/// <summary>
/// Gets the <see cref="Expression"/> that will invoke the indexer for the field <paramref name="p_FieldName"/>.
/// </summary>
/// <remarks>The indexer must be called as a method, not as a property.</remarks>
/// <param name="p_Instance">The parameter instance of the Linq expression. This is the part of the Linq expression before the <c>=></c> symbol.
/// <example>item => item["fieldName"]</example>
/// </param>
/// <param name="p_FieldName">The field name to reference in the indexer.</param>
/// <returns>The <see cref="Expression"/> that will invoke the field indexer when executed.</returns>
Expression GetFieldIndexerExpression(ParameterExpression p_Instance, string p_FieldName)
{
string indexerPropertyName = "Item";
// By default the indexer corresponds to the "Item" property, but one can change it.
// To find the name, if modified, we must search for DefaultMemberAttribute attribute.
// Fortunately, only one indexer name can be defined.
object[] attributes = p_Instance.Type.GetCustomAttributes(typeof(DefaultMemberAttribute), true);
if (attributes.Any()) {
indexerPropertyName = (attributes[0] as DefaultMemberAttribute).MemberName;
}
PropertyInfo indexerProperty = p_Instance.Type.GetProperty(indexerPropertyName,
new Type[] { typeof(string) });
MethodInfo indexerGetMethod = indexerProperty.GetMethod;
Expression callIndexerExpression = Expression.Call(p_Instance,
indexerGetMethod,
Expression.Constant(p_FieldName));
return callIndexerExpression;
}
Implement the GetWrappedCondition
Method
This method allows Coveo for Sitecore to reuse existing rule conditions provided by Sitecore, and translate them to LINQ expressions, as they’re wrapper classes that implement the ICoveoCondition
interface. Because the IsItemPartOfCurrentSite
class already inherits from RuleCondition
, you only need to return the current instance of the IsItemPartOfCurrentSite
class.
Your method should look like this:
public RuleCondition<T> GetWrappedCondition()
{
return this;
}
Implement the ValidateCondition
Method
This method is used to report warnings and errors in the Experience Editor. You can keep the body of the method empty when your custom rule doesn’t have configurable properties.
public void ValidateCondition(Coveo.UI.ErrorReport p_Report)
{
}
Your class is now complete, and should look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Sitecore.Data.Items;
using Sitecore.Rules;
using Sitecore.Rules.Conditions;
using Coveo.UI.Rules;
namespace Samples.Rules
{
public class IsItemPartOfCurrentSite<T> : WhenCondition<T>, ICoveoCondition<T> where T : RuleContext
{
protected override bool Execute(T ruleContext)
{
return true;
}
public Expression GetQueryExpression(ConditionContext p_Context)
{
Item currentHomeItem = Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath);
Expression readNameProperty = GetFieldIndexerExpression(p_Context.ParameterExpression, "_path");
Expression standardValuesConstant = Expression.Constant(currentHomeItem.ID.ToShortID().ToString());
return Expression.Equal(readNameProperty, standardValuesConstant);
}
public RuleCondition<T> GetWrappedCondition()
{
return this;
}
public void ValidateCondition(Coveo.UI.ErrorReport p_Report)
{
}
/// <summary>
/// Gets the <see cref="Expression"/> that will invoke the indexer for the field <paramref name="p_FieldName"/>.
/// </summary>
/// <remarks>The indexer must be called as a method, not as a property.</remarks>
/// <param name="p_Instance">The parameter instance of the Linq expression. This is the part of the Linq expression before the <c>=></c> symbol.
/// <example>item => item["fieldName"]</example>
/// </param>
/// <param name="p_FieldName">The field name to reference in the indexer.</param>
/// <returns>The <see cref="Expression"/> that will invoke the field indexer when executed.</returns>
Expression GetFieldIndexerExpression(ParameterExpression p_Instance, string p_FieldName)
{
string indexerPropertyName = "Item";
// By default the indexer corresponds to the "Item" property, but one can change it.
// To find the name, if modified, we must search for DefaultMemberAttribute attribute.
// Fortunately, only one indexer name can be defined.
object[] attributes = p_Instance.Type.GetCustomAttributes(typeof(DefaultMemberAttribute), true);
if (attributes.Any())
{
indexerPropertyName = (attributes[0] as DefaultMemberAttribute).MemberName;
}
PropertyInfo indexerProperty = p_Instance.Type.GetProperty(indexerPropertyName,
new Type[] { typeof(string) });
MethodInfo indexerGetMethod = indexerProperty.GetMethod;
Expression callIndexerExpression = Expression.Call(p_Instance,
indexerGetMethod,
Expression.Constant(p_FieldName));
return callIndexerExpression;
}
}
}
What’s Next?
You can now proceed to Integrating the Custom Rule Condition in Sitecore.