Coveo for Sitecore 5 is now available!

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:

  1. Create a new C# project that references the Sitecore.Kernel. This assembly is located under: C:\inetpub\wwwroot\<instance name>\website\bin. For this tutorial, you can call the project Samples.

  2. Create a new class. For this tutorial, you can call your namespace Samples.Rules, in which you can add a class called IsItemPartOfCurrentSite.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Samples.Rules
    {
        public class IsItemPartOfCurrentSite
        {
        }
    }
    
  3. Add the WhenCondition<T> base class to your class and the 3 Sitecore 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, the IsItemPartOfCurrentSite class inherits from Sitecore.Rules.Conditions.WhenCondition. Inheriting from the WhenCondition instead of RuleCondition makes it easier to implement.

  4. 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();
            }
        }
    }
    
  5. In this case, the Execute function expects to return a Boolean value, so a true 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 have created your custom rule condition, you need to make it available in Coveo for Sitecore. Here are the steps you need to follow.

  1. In your C# project, add a reference to the Coveo.UIBase.dll assembly, which is located under C:\inetpub\wwwroot\<instance name>\website\bin.

  2. Add a using Coveo.UI.Rules statement.

  3. Modify the IsItemPartOfCurrentSite class so that it implements the Coveo.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;
            }
        }
    }
    
  4. 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 and standardValuesConstant: 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.

Recommended Articles