Experience editor. Add all editable items on page to related items publishing

Recently I faced one challenge. A Sitecore page has a number of rendered fields and all of them can be marked as “editable” in Sitecore out of the box.
For example, I have a list of items which are being outputted to the page from a rendering which requests Content Search API and gets list of items from a bucket. In my rendering I can easily make those fields editable as long as Sitecore supports this.
As a content editor I can change those field values and save them from the Sitecore Experience Editor just fine. But if I click publish, those items from a bucket won’t be added into the publishing queue even if I select publish related items. This happens because my page item is not connected or referencing in any way those bucket items and that’s good actually.

From this article I figured out that there is a pipeline which defines which items are going to be added into publishing queue as related items and out of the box there are 4 processors added into it.

<getItemReferences>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddItemCloneReferences, Sitecore.Kernel"/>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddFileDropAreaMediaReferences, Sitecore.Kernel"/>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddItemLinkReferences, Sitecore.Kernel"/>
	<processor type="Sitecore.Publishing.Pipelines.GetItemReferences.AddItemAliasReferences, Sitecore.Kernel"/>
</getItemReferences>

So there is a way to extend this.
But before adding required items as related items in the publishing process we need to somehow collect them beforehand.
And what I decided is to implement extra pipeline processors for renderField and getChromeData pipelines that would collect all items which fields on the page are being rendered as “editable” including Sitecore edit frames.
Those processors will add required items into the collection and then I can get all of them in the extra getItemReferences pipeline processor.

I have packed this solution into nuget package and you can install it using

Install-Package Helpfulcore.WebEdit

And the source code is available on github here https://github.com/vhil/helpfulcore-webedit

So first, I implement a collection object where I store collected items.

ItemReferenceCollection

Then I add a new processor to the renderField pipeline with next code:

public class CollectItemReferences
{
	public void Process(RenderFieldArgs args)
	{
		try
		{
			if (this.CanCollectReferences(args))
			{
				ItemReferenceCollection.FromConfiguration().AddItemReference(args.Item.ID.Guid);
			}
		}
		catch(Exception ex)
		{
			Log.Error("Error in CollectItemReferences pipeline processor.", ex, this);
		}
	}

	protected virtual bool CanCollectReferences(RenderFieldArgs args)
	{
		return args != null && args.Item != null && !args.Aborted
			&& !args.DisableWebEdit && !args.DisableWebEditContentEditing
			&& (Context.PageMode.IsPageEditor || Context.PageMode.IsPageEditorEditing);
	}
}

In order to be able to collect items which are present on the page as within Edit Frames I need to inject into getChromeData pipeline and add next processor:

public class EditFrameCollectItemReferences : GetChromeDataProcessor
{
	public override void Process(GetChromeDataArgs args)
	{
		try
		{
			if (this.CanCollectReferences(args))
			{
				ItemReferenceCollection.FromConfiguration().AddItemReference(args.Item.ID.Guid);
			}
		}
		catch (Exception ex)
		{
			Log.Error("Error in EditFrameCollectItemReferences pipeline processor.", ex, this);
		}
	}

	protected virtual bool CanCollectReferences(GetChromeDataArgs args)
	{
		return args != null && args.Item != null && !args.Aborted
			&& args.ChromeType != null && args.ChromeData != null
			&& "editFrame".Equals(args.ChromeType, StringComparison.OrdinalIgnoreCase)
			&& (Context.PageMode.IsPageEditor || Context.PageMode.IsPageEditorEditing);
	}
}

And finally I inject into the getItemReferences pipeline and adding new processor which will add all previously collected items for the specific page in the Experience Editor

public class FromEditableFields : GetItemReferencesProcessor
{
	protected override List<Item> GetItemReferences(PublishItemContext context)
	{
		try
		{
			if (context != null && context.VersionToPublish != null)
			{
				var references = ItemReferenceCollection.FromConfiguration().GetItemReferences(context.VersionToPublish.ID.Guid);

				return references.Select(x => context.VersionToPublish.Database.GetItem(new ID(x))).ToList();
			}
		}
		catch (Exception ex)
		{
			Log.Error("Error in FromEditableFields pipeline processor.", ex, this);
		}

		return new List<Item>();
	}
}

When the item has been published, the collection is being cleared for this item so the Sitecore page remains to be a container for any amount of components on it and removing or adding new renderings will be reflected on related items collection process.
Items are being added uniquely by their IDs to the ItemReferenceCollection so no items are going to be published more than once.
This package has been tested on Sitecore 8.1 update 2 but is also supposed to work on Sitecore 7+ as well.

I will extend this package with more cool features later so to be continued…

Sitecore and SimpleInjector issues with WFFM

I am using Simple Injector as an IoC framework on my Sitecore pojects. And what I faced recently is that Sitecore modules like FXM, WFFM do not work properly.
The reason of that is the default Simple Injector implementation regarding constructor resolution behavior. Lots of Sitecore classes like Controllers implemented the way that they have multiple constructors.
Out of the box, Simple Injector only allows the creation of classes that contain a single public constructor. They explain this as having multiple constructors is an anti-pattern and I agree.

While creating a container and registering types in it I usually register MVC controllers as well to be able to inject dependencies into them by setting the resolver to the default MVC DependencyResolver class.
This means that Sitecore controllers within my solution now are being instantiated by IoC container. When any of them have multiple constructors the Simple Injector throws exception as it can’t instantiate those.
It was quite confusing situation when I installed few Sitecore modules and they just didn’t work as expected and took a while to figure out.

Fortunately there is a solution for that. We can implement a custom constrctor resolution behavior. There are more ways to do that but here is what I use. This class will try to find the most resolvable constructor in the class that is being instantiated. If anything wrong it will just fallback to default “single constructor behavior”.

using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using SimpleInjector;
using SimpleInjector.Advanced;

public class MostResolvableConstructorBehavior : IConstructorResolutionBehavior
{
	private readonly Container container;
	private readonly IConstructorResolutionBehavior fallbackBehavior;

	public MostResolvableConstructorBehavior(Container container)
	{
		this.container = container;
		this.fallbackBehavior = container.Options.ConstructorResolutionBehavior;
	}

	private bool IsCalledDuringRegistrationPhase
	{
		get { return !this.container.IsLocked(); }
	}

	public ConstructorInfo GetConstructor(Type service, Type implementation)
	{
		var constructors = implementation.GetConstructors();

		if (!constructors.Any())
		{
			return this.fallbackBehavior.GetConstructor(service, implementation);
		}

		return constructors.Select(ctor => new { ctor, parameters = ctor.GetParameters() })
				.Where(type => this.IsCalledDuringRegistrationPhase
					|| constructors.Length == 1
					|| type.parameters.All(p => this.CanBeResolved(p, service, implementation)))
				.OrderByDescending(type => type.parameters.Length)
				.Select(type => type.ctor)
			.First();
	}

	private bool CanBeResolved(ParameterInfo p, Type service, Type implementation)
	{
		return this.container.GetRegistration(p.ParameterType) != null || this.CanBuildType(p, service, implementation);
	}

	private bool CanBuildType(ParameterInfo p, Type service, Type implementation)
	{
		try
		{
			this.container.Options.DependencyInjectionBehavior.BuildExpression(new InjectionConsumerInfo(service, implementation, p));
			return true;
		}
		catch (ActivationException)
		{
			return false;
		}
	}
}

And now we just need to do this while initializing the container:

container.Options.ConstructorResolutionBehavior = new MostResolvableConstructorBehavior(container);

So my initializer class looks like this:

using System.Web.Mvc;
using SimpleInjector;
using SimpleInjector.Integration.Web.Mvc;
using Sitecore.Pipelines;

namespace Website
{
	public class DependencyInjectionInitializer
	{
		public void Process(PipelineArgs args)
		{
			Initialize();
		}

		public static void Initialize()
		{
			var container = new Container();
			
			// Change the consturctor resolution behavior here
			container.Options.ConstructorResolutionBehavior = new MostResolvableConstructorBehavior(container);
			
			// Register all solution related types
			RegisterWebsiteTypes(container);
			
			container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
			container.RegisterMvcIntegratedFilterProvider();

			// Register the container as MVC IDependencyResolver.
			DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
        }

		private static void RegisterWebsiteTypes(Container container)
	    {
            // container.Register<ISitecoreContext, SitecoreContext>(Lifestyle.Transient);
			// ...
		}
	}
}

If that’s unclear, I initialize the container in the ‘initialize’ Sitecore pipeline by having next config:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor type="Website.DependencyInjectionInitializer, Website" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

This solution works for me now, all modules came back to normal working, but still need to test that more lol🙂
Hope it was helpful🙂

Sitecore Cache Key Profiling

Not sure what’s the problem with your Sitecore rendering cache setup?
Since Sitecore 8 version has been released I have been experiencing issues with caching. First is that one I described earlier, then I figured out that one of my renderings is being placed inside of another and it applies caching from the parent rendering and that was wrong apparently🙂

In result I came up with idea that I need to see what cache keys are being generated for each of my renderings on the page.
Just wrapped that into a nuget package to share with you guys! So that is just another Helpfulcore module for your Sitecore solution.

If you go to your nuget Package Manager Console and install this to your website project

Install-Package Helpfulcore.CacheKeyProfiling

You will be able to see something like this at your page source html:

<!-- Site name: website -->
<!-- Database: web -->
<!-- Language: da -->

<!-- Cache Key list: -->
<!-- controller::Seo#Metadata_#lang:DA_#area:Seo_#data:/sitecore/content/sites/website/Home_#parm:_#qs:  -->
<!-- view::/Views/Layouts/_Partials/Stylesheets.cshtml_#lang:DA  -->
<!-- view::/Views/Layouts/_Partials/Icons.cshtml_#lang:DA  -->
<!-- view::/Views/Layouts/_Partials/Fonts.cshtml_#lang:DA  -->
<!-- view::/Views/Layouts/_Partials/HeadExtras.cshtml_#lang:DA  -->
<!-- view::/Views/Layouts/_Partials/BrowserUpgrade.cshtml_#lang:DA  -->
<!-- view::/Views/Layouts/_Partials/GlobalCookieMessage.cshtml_#lang:DA  -->
<!-- view::/Views/Layouts/_Partials/SiteLogos.cshtml_#lang:DA  -->
<!-- controller::Navigation#MainNavigation_#lang:DA_#area:Navigation_#data:/sitecore/content/sites/website/Home_#login:True_#parm:_#qs:  -->
<!-- controller::Navigation#MainSideNavigation_#lang:DA_#area:Navigation_#data:/sitecore/content/sites/website/Home_#login:True_#parm:_#qs:  -->
<!-- view::/Views/SimpleContent/Hero.cshtml_#lang:DA_#data:/sitecore/content/sites/website/website-content/heros/hero-test_#parm:_#qs:  -->
<!-- view::/Views/LiveFeeds/JackpotsNow.cshtml_#lang:DA_#data:/sitecore/content/sites/website/website-content/sidebar/website-jackpot-boxes/jackpotter-nu_#parm:_#qs:  -->
<!-- view::/Views/LiveFeeds/NewWinners.cshtml_#lang:DA_#data:/sitecore/content/sites/website/website-content/sidebar/website-winner-boxes/nye-vindere_#parm:_#qs:  -->
<!-- view::/Views/SimpleContent/BoxLink.cshtml_#lang:DA_#data:/sitecore/content/sites/website/website-content/sidebar/website-link-boxes/box-link-test_#parm:_#qs:  -->
<!-- controller::Games#GameList_#lang:DA_#area:Games_#data:/sitecore/content/sites/website/Home_#dev:Default#isMobile:False_#login:True_#parm:Hide Title=1&amp;Associated Tags={EA74A9BD-38C4-4012-80AE-95D443479470}|{2CCE6B9C-398A-457A-B86E-8F7D1B57B369}&amp;Page Size=17&amp;Hide Pagination=1&amp;Autocomplete Page Size=50_#qs:  -->
<!-- view::/Views/SimpleContent/BoxImage.cshtml_#lang:DA_#data:/sitecore/content/sites/website/website-content/sidebar/website-image-boxes/box-image-test-motiejus_#parm:_#qs:  -->
<!-- view::/Views/SimpleContent/BoxText.cshtml_#lang:DA_#data:/sitecore/content/sites/website/website-content/sidebar/website-text-boxes/box_#parm:_#qs:  -->
<!-- controller::Footer#Footer_#lang:DA_#area:Footer_#data:/sitecore/content/sites/website/Home_#parm:_#qs:  -->
<!-- view::/Views/Layouts/_Partials/BottomScripts.cshtml_#lang:DA  -->

This way you can see what cache keys are being generated during the pipeline for each of rendering on your page.
Personally for me that helped a lot to resolve my rendering caching issues on the website.

Will describe the module a bit:

Package contents:

– /bin/Helpfulcore.CacheKeyProfiling.dll
– /App_Config/Include/Helpfulcore/Helpfulcore.CacheKeyProfiling.config
– /Views/Shared/CacheKeyProfiling/CacheKeys.cshtml

Pipeline injection

In the include config file Helpfulcore.CacheKeyProfiling.config I am injecting into pipeline and saving generated cache keys into current request.

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <mvc.renderRendering>
        <processor type="Helpfulcore.CacheKeyProfiling.Pipelines.RenderRendering.GatherCacheKeys, Helpfulcore.CacheKeyProfiling" patch:before="*[@type='Sitecore.Mvc.Pipelines.Response.RenderRendering.RenderFromCache, Sitecore.Mvc']" />
      </mvc.renderRendering>
    </pipelines>
  </sitecore>
</configuration>

Usage

And then I have a view /Views/Shared/CacheKeyProfiling/CacheKeys.cshtml which reads previously saved collection and outputs it as HTML comments.
In order to render it you will need to write one extra line of code on your layout file. Render it at the bottom of the page for convenience.

	@Html.RenderCacheKeys()

And that’s it folks!

The source code for the package is at GitHub here

Inversion of Control and dependency management with Sitecore configuration factory

Today, when we talk about modern software application development, its quite obvious that using Inversion of Control (IoC) design principle is a must have. Having ability to change dependencies of the component without changing the component itself is very important for any kind of application I think.

When you creating a Sitecore website you still can use one of the IoC frameworks and there are an amazing number of them today available. First link in google gives me this post with the list and that is only a small part of existing frameworks.

Having them in your solution is great, but what if you are creating a Sitecore module or a nuget package? That is the case where you need to provide a decent level of extensibility so your module can be re-used, extended, and changed on client side if needed (by client side I mean the solution which uses your module).

In the same time it needs to be de-coupled enough so the IoC principle is still a must have here but there are some limitations:

  • You can’t really use one of those frameworks because you don’t want solution which uses your module to depend on a specific IoC framework
  • Actually you don’t really want your module to depend on any specific IoC framework
  • You need to declare a list of dependencies and instructions on how to register them in the IoC container so your module runs properly

Using Sitecore configuration factory to handle dependencies

This is not a new feature or approach but recently I found out for myself a nice out-of-the-box feature of Sitecore which helps with the dependency management. And that is a default Sitecore.Configuration.Factory.
Here are some links about it:

For start will bring an example of configuring one of my Helpfulcore modules – the logging service.
I have an abstracted logging service which can contain a number of logging providers to write to and they are being injected via constructor:

// source from https://github.com/vhil/helpfulcore-logging/blob/master/Sources/Helpfulcore.Logging/LoggingService.cs
public class LoggingService : ILoggingService
{
	private readonly ILoggingProvider[] loggingProviders;

	public LoggingService(ILoggingProvider provider)
		:this(new []{provider})
	{
	}

	public LoggingService(ILoggingProvider provider1, ILoggingProvider provider2)
		:this(new[] { provider1, provider2 })
	{
	}

	public LoggingService(params ILoggingProvider [] providers)
	{
		this.loggingProviders = providers;
	}
	...
}

And there are two implementations of ILoggingProviders

And now I want to configure a global logging service for my website to use both logging providers. I create a configuration inside default configuration node using include config files so resulting config looks like this:

<sitecore>
  <helpfulcore>
    <logging>
      <loggingService type="Helpfulcore.Logging.LoggingService, Helpfulcore.Logging" singleInstance="true">
        <param name="provider1" ref="helpfulcore/logging/providers/scLoggingProvider"/>
		<param name="provider2" ref="helpfulcore/logging/providers/nlogLoggingProvider"/>
      </loggingService>
      <providers>
	    <scLoggingProvider type="Helpfulcore.Logging.Sc.ScLoggingProvider, Helpfulcore.Logging.Sc" singleInstance="true"/>
        <nlogLoggingProvider type="Helpfulcore.Logging.NLog.NLogLoggingProvider, Helpfulcore.Logging.NLog" filePath="$(dataFolder)/logs/Website.log.${date:format=yyyyMMdd}.txt" singleInstance="true">
          <param name="filePath">$(filePath)</param>
        </nlogLoggingProvider>
      </providers>
    </logging>
  </helpfulcore>
</sitecore>

So there are two providers configured in the section and they are being passed as “ref” attributes into the loggingService as constructor parameters. Im my code now I can get the instance of the logging service just by calling the configuration factory method:

var loggingService = Factory.CreateObject("helpfulcore/logging/loggingService", true) as ILoggingService;

This call will resolve all related dependencies as described in the config and create all configured instances including “singleInstance” lifestyle if declared. Also I can register this instance in my IoC container for my Sitecore website;

container.Register(
		typeof(ILoggingService),
		() => Factory.CreateObject("helpfulcore/logging/loggingService", true) as ILoggingService,
	Lifestyle.Singleton);

Okay. Now I want to implement a service which uses the logging service and injects it as a dependency.  so it can write to log about its actions. So I create a service

public class MyService : IMyService
{
	protected readonly ILoggingService Logger;

	public MyService(ILoggingService logger)
	{
		this.Logger = logger;
	}
	...
}

The key point in all of that is having control under your dependency injections. I am used to inject everything via constructor and MyService implementation here has a constructor dependency on ILoggingService. What I do next is I am creating configuration for it in the sitecore include file:

<sitecore>
  <myFeature>
	  <myService type="MyNamespace.MyService, MyAssembly">
		<param name="logger" ref="helpfulcore/logging/loggingService"/>
	  </myService>
  </myFeature>
</sitecore>

And now I can get an instance of it from the factory method with default logging service being injected:

var myService = Factory.CreateObject("myFeature/myService", true) as IMyService;

That’s it. It all looks and works great now. But at some point I can decide that ok, I want my service to write to its own log file so I can monitor its actions in a more readable way. So what I can do now is to just change the config file by configuring new instance of logging service with one dedicated provider which writes to MyService.log.${date:format=yyyyMMdd}.txt log file and changing the logger reference in myService configuration node.

<sitecore>
  <myFeature>
    <myService type="MyNamespace.MyService, MyAssembly">
      <param name="logger" ref="myFeature/logging/loggingService"/>
    </myService>
    <logging>
      <loggingService type="Helpfulcore.Logging.LoggingService, Helpfulcore.Logging" singleInstance="true">
        <param name="provider1" ref="myFeature/logging/providers/nlogLoggingProvider"/>
      </loggingService>
      <providers>
        <nlogLoggingProvider type="Helpfulcore.Logging.NLog.NLogLoggingProvider, Helpfulcore.Logging.NLog" filePath="$(dataFolder)/logs/MyService.log.${date:format=yyyyMMdd}.txt" singleInstance="true">
          <param name="filePath">$(filePath)</param>
        </nlogLoggingProvider>
      </providers>
    </logging>
  </myFeature>
</sitecore>

No code changes required, all dependencies are controlled in config files and its really simple to use.

Configure Fortis as an example

As a separate example I can configure the Fortis framework  in the include config file. Fortis framework is all built on constructor dependency injection approach so that will be quite simple for me now.

<sitecore>
  <fortis>
    <itemFactory type="Fortis.Model.ItemFactory, Fortis" singleInstance="true">
      <param name="contextProvider" ref="fortis/dependencies/contextProvider" />
      <param name="spawnProvider" ref="fortis/dependencies/spawnProvider" />
    </itemFactory>
    <spawnProvider type="Fortis.Providers.SpawnProvider, Fortis" singleInstance="true">
      <param name="templateMapProvider" ref="fortis/dependencies/templateMapProvider" />
    </spawnProvider>
    <templateMapProvider type="Fortis.Providers.TemplateMapProvider, Fortis" singleInstance="true">
      <param name="modelAssemblyProvider" ref="fortis/dependencies/modelAssemblyProvider" />
    </templateMapProvider>
    <modelAssemblyProvider type="Fortis.Providers.ModelAssemblyProvider, Fortis" singleInstance="true"/>
    <itemSearchFactory type="Fortis.Search.ItemSearchFactory, Fortis" singleInstance="true">
      <param name="templateMapProvider" ref="fortis/dependencies/templateMapProvider" />
      <param name="searchResultsAdapter" ref="fortis/dependencies/searchResultsAdapter" />
    </itemSearchFactory>
    <searchResultsAdapter type="Fortis.Search.SearchResultsAdapter, Fortis" singleInstance="true"/>
    <contextProvider type="Fortis.Mvc.Providers.ContextProvider, Fortis.Mvc" singleInstance="true"/>
  </fortis>
</sitecore>

As described in documentation I need to initialize the Fortis.Global factory class on the application start with three instances so I do it like this:

Global.Initialise(
	Factory.CreateObject("fortis/spawnProvider", true) as ISpawnProvider,
	Factory.CreateObject("fortis/itemFactory", true) as IItemFactory,
	Factory.CreateObject("fortis/itemSearchFactory", true) as IItemSearchFactory
);

And I still can do the same for my IoC if I use one.
Pretty amazing feature and its all a default Sitecore functionality.

Helpfulcore

Talking about “helpfulcore” term referenced above – it became my “pet” project collection for small features that I use from project to project. They all are nuget packages except the Wildcard module.
Will share the list in case you may be interested in using them, for me they are just helpful (read from the name lol :))

Will keep you updated if any new helpful module appears in my collection.
Thanks for reading, now go and implement something amazing!🙂

Fortis & Sitecore Tutorial 3 – using Sitecore ContentSearch API

Sitecore ContentSearch API is a rather powerful functionality that gives you pretty much unlimited capabilities in retrieving sets of items in your Sitecore installation using Lucene or SOLR search engines which Sitecore currently supports.
Using Fortis framework in your Sitecore solution gives you auto-generated model (using TDS or any other way of to generate it) and would be logical if we can query the search index using typed ItemWrapper based models.
Yes, Fortis supports these features out of the box and this topic is about how to use them.
All code mentioned in this post can be found on GitHub here.

Simple query example

Let’s imagine I have a product repository as a Sitecore Bucket item where I store my product items.

products-bucket

And I want to create a rendering which will list them all on the page.

fortis-demo-product-list

So I implement a ProductService class with a method for retrieving my product items

using System;
using System.Collections.Generic;
using System.Linq;
using Fortis.Search;
using FortisDemo.Model.Templates.UserDefined;
using Sitecore;
using Sitecore.ContentSearch;

namespace FortisDemo.Products
{
	public class ProductService : IProductService
	{
		protected readonly IItemSearchFactory ItemSearchFactory;

		public ProductService(IItemSearchFactory itemSearchFactory)
		{
			this.ItemSearchFactory = itemSearchFactory;
		}

		public IEnumerable<IProductPageItem> GetAlProducts()
		{
			var productRepositoryID = new Guid("{C1F3F0A1-145D-44A8-B2A3-2F395F10A653}");
			using (var searchContext = ContentSearchManager.CreateSearchContext((SitecoreIndexableItem)Context.Item))
			{
				var queryable = searchContext.GetQueryable<IProductPageItem>();

				return this.ItemSearchFactory.FilteredSearch(queryable)
					.Where(x => x.Paths.Contains(productRepositoryID))
					.ToList();
			}
		}
	}
}

Yes, as simple as that!
As we can see, the method returns us typed items wrapped into the IProductPageItem. And these item wrappers still have an instance of Sitecore.Data.Items.Item inside of them and when using these objects in the rendering we can access actual values from the Sitecore (and not from the index).

And this method is being called in my ProductController to fill the product list property of my view model:

using System.Web.Mvc;
using Fortis.Model;
using FortisDemo.Model.Templates.UserDefined;
using FortisDemo.Products;
using FortisDemo.Web.Mvc.Controllers;
using FortisDemo.Website.Areas.Product.Models;

namespace FortisDemo.Website.Areas.Product.Controllers
{
	public class ProductController : WebsiteController
	{
		protected readonly IProductService ProductService;

		public ProductController(IItemFactory itemFactory, IProductService productService) 
			: base(itemFactory)
		{
			this.ProductService = productService;
		}

		public ActionResult ProductList()
		{
			var renderingModel = new ProductListRenderingModel(this.ItemFactory.GetRenderingContextItems<IContentPageItem, IContentPageItem>())
			{
				Products = this.ProductService.GetAlProducts()
			};

			return View(renderingModel);
		}
	}
}

Installation & Configuration

In order to be able to use that you will need to install the respective nuget package to your website project depending on which search provider your Sitecore instance uses.

And that will be either


Install-Package Fortis.Search.Lucene

or


Install-Package Fortis.Search.Solr

These packages will install required Fortis dlls and a configuration files.
For Lucene there will be

  • Fortis.Search.Lucene.dll
  • /App_Config/Include/Fortis/Fortis.Search.Lucene.config

and for SOLR

  • Fortis.Search.Solr.dll
  • /App_Config/Include/Fortis/Fortis.Search.Solr.config

Config files will include configuration for index document property mapper so Sitecore can map indexed items to the Fortis ItemWrapper classes. Also there is a declaration of several computed search fields.

Fortis.Search.Lucene.config:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
		<contentSearch>
			<indexConfigurations>
				<defaultLuceneIndexConfiguration>
					<indexDocumentPropertyMapper>
						<patch:attribute name="type">Fortis.Search.LuceneDocumentTypeMapper, Fortis.Search.Lucene</patch:attribute>
						<objectFactory>
							<patch:attribute name="type">Fortis.Search.DefaultDocumentMapperObjectFactory, Fortis</patch:attribute>
						</objectFactory>
					</indexDocumentPropertyMapper>
					<fields hint="raw:AddComputedIndexField">
						<field fieldName="_isstandardvalues">Fortis.Search.ComputedFields.IsStandardValues, Fortis</field>
						<field fieldName="_templates">Fortis.Search.ComputedFields.InheritedTemplates, Fortis</field>
					</fields>
				</defaultLuceneIndexConfiguration>
			</indexConfigurations>
		</contentSearch>
	</sitecore>
</configuration>

Fortis.Search.Solr.config:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
	<sitecore>
		<contentSearch>
			<indexConfigurations>
				<defaultSolrIndexConfiguration>
					<indexDocumentPropertyMapper>
						<patch:attribute name="type">Fortis.Search.SolrDocumentTypeMapper, Fortis.Search.Solr</patch:attribute>
						<objectFactory>
							<patch:attribute name="type">Fortis.Search.DefaultDocumentMapperObjectFactory, Fortis</patch:attribute>
						</objectFactory>
					</indexDocumentPropertyMapper>
					<fields hint="raw:AddComputedIndexField">
						<field fieldName="_isstandardvalues" returnType="bool">Fortis.Search.ComputedFields.IsStandardValues, Fortis</field>
						<field fieldName="_templates" returnType="string">Fortis.Search.ComputedFields.InheritedTemplates, Fortis</field>
					</fields>
				</defaultSolrIndexConfiguration>
			</indexConfigurations>
		</contentSearch>
	</sitecore>
</configuration>

How it works

So there is no magic really and Fortis uses default Sitecore attributes to map the properties to the indexed fields in the auto-generated model classes:
For example for the ContentTitle field we will see two properties:


[IndexField(IndexFieldNames.ContentTitle)]
public virtual ITextFieldWrapper ContentTitle
{
	get { return GetField<TextFieldWrapper>(FieldNames.ContentTitle, IndexFieldNames.ContentTitle); }
}

[IndexField(IndexFieldNames.ContentTitle)]
public string ContentTitleValue
{
	get { return ContentTitle.Value; }
}

The second one ends with “Value” and these properties are intended to be used in LINQ expressions syntax for the IQueryable. This is done because in Fortis each property is an ItemWrapper and expressions not always work properly when writing nested property calls like item.ContentTitle.Value.

ItemSearchFactory

There are few examples on using the ItemSearchFactory here.
Basically there are several options you can use:

  • .Search(IQueryable qyeryable) method. In result you will have an IQueryable and you can do anything with it. Just call .ToList() on it to execute your query.
  • .FilteredSearch(IQueryable qyeryable) – this method will add into your queryable filters so StandardValues items will be skipped, the search will be only in the context language and in the latest item versions. Returns the IQueryable.
  • .GetResults(IQueryable qyeryable) – this method will execute your queryable and return the instance of ISearchResults where you can get some extended search result data.

ISearchResults interface:

public interface ISearchResults<TSource> : IEnumerable<ISearchHit<TSource>>, IEnumerable
{
	FacetResults Facets { get; set; }

	IEnumerable<ISearchHit<TSource>> Hits { get; set; }

	int TotalHits { get; set; }
}

IQueryable extensions

Fortis framework comes with number of IQueryable extension methods to help you writing your queries. They are mostly self-explanatory:

  • .WhereContextLanguage()
  • .WhereLatestVersion()
  • .WhereNotStandardValues()
  • .ApplyFilters() – this extension just calls all three methods above
  • .ContainsAnd(x => x.TagsValue, guidEnumerable) – select items where multilist field “Tags” contain all items in the guidEnumerable
  • .ContainsOr(x => x.TagsValue, guidEnumerable) – select items where multilist field “Tags” contain at least one item in the guidEnumerable

What are you waiting for? Go and try it!🙂

Helpfulcore.RenderingExceptions styling updated!

Recently I wrote about handling the rendering exceptions in Sitecore in the post from Novemver 15th.
Last week I have updated the styling for them:

  • Now error messages should look the same way on any website you install the package
  • The css classes became more unique now so won’t conflict with any classes of your website
  • The “expand” button finally well-styled!
  • Exception types are moved to separate assembly and package so you can use them in any project of your solution without adding Sitecore website specific files.

I have attached new screen-shots to the original blog post.
If you are using this package, you can simply run


Update-Package Helpfulcore.RenderingExceptions

Say “yes” if nuget prompts you to override the .cshtml files (if you didn’t style them completely your way).

Enjoy!

@Html.Sitecore().Rendering(pathOrId) does not apply “VaryBy” settings

This week I have faced with one unexpected issue in Sitecore 8.1.
We are declaring few renderings on the layout using the Rendering method of the SitecoreHelper class in Sitecore.Mvc.dll.


@Html.Sitecore().Rendering("/sitecore/layout/Renderings/MyRendering")

The issue can be reproduced by setting the “Cacheable” to true and applying the “VaryBy” settings. In result I can see that the “Cacheable” is set but the rendering does not vary by anything I am trying to set there. And my rendering looks the same on all pages.
After some de-compiling and digging around I found that the CacheKey is not being properly generated due to wrong code in the Sitecore.Mvc.Helpers.SitecoreHelper class of Sitecore.Mvc dll.
The issue is that by some reason this method tries to read fields from the rendering item with these field names:

"Cache_Timeout"
"Cache_VaryByData"
"Cache_VaryByDevice"
"Cache_VaryByLogin"
"CacheKey"
"Cache_VaryByParameters"
"Cache_VaryByQueryString"
"Cache_VaryByUser"
"DataSource"

And yes, the rendering item does not have these fields. Correct names for some of them are:

"VaryByData"
"VaryByDevice"
"VaryByLogin"
"VaryByParm"
"VaryByQueryString"
"VaryByUser"
"Data Source"

And “Cache_Timeout” and “CacheKey” does not exist at all.

I have reported a bug to Sitecore support and they promised to investigate the issue. Meanwhile I have implemented my own helper class to render the Rendering from markup and the code is below.

using System.IO;
using System.Web;
using Sitecore.Data.Items;
using Sitecore.Mvc.Extensions;
using Sitecore.Mvc.Helpers;
using Sitecore.Mvc.Pipelines;
using Sitecore.Mvc.Pipelines.Response.RenderRendering;
using Sitecore.Mvc.Presentation;

namespace Helpers
{
	public class SitecoreHelper
	{
		public static HtmlString Rendering(string renderingItemPathOrId)
		{
			return Rendering(renderingItemPathOrId, null);
		}

		public static HtmlString Rendering(string renderingItemPathOrId, object parameters)
		{
			return RenderRendering(GetRendering(string.Empty, parameters, "RenderingItemPath", renderingItemPathOrId));
		}

		protected static Rendering GetRendering(string renderingType, object parameters, params string[] defaultValues)
		{
			var rendering = new Rendering
			{
				RenderingType = renderingType
			};

			var index = 0;
			while (index < defaultValues.Length - 1)
			{
				rendering[defaultValues[index]] = defaultValues[index + 1];
				index += 2;
			}

			if (rendering.RenderingItem != null)
			{
				// Changed field names
				var innerItem = rendering.RenderingItem.InnerItem;
				CopyPropertyFromDefinitionItem(rendering, innerItem, "Cacheable");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "VaryByData", "Cache_VaryByData");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "VaryByDevice", "Cache_VaryByDevice");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "VaryByLogin", "Cache_VaryByLogin");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "VaryByParm", "Cache_VaryByParameters");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "VaryByQueryString", "Cache_VaryByQueryString");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "VaryByUser", "Cache_VaryByUser");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "ClearOnIndexUpdate", "Cache_ClearOnIndexUpdate");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "Data Source", "DataSource");
				CopyPropertyFromDefinitionItem(rendering, innerItem, "Model");
			}

			if (parameters != null)
			{
				TypeHelper.GetProperties(parameters)
					.Each(pair => rendering.Properties[pair.Key] = pair.Value.ValueOrDefault(o => o.ToString()));
			}

			return rendering;
		}

		protected static void CopyPropertyFromDefinitionItem(
			Rendering rendering, 
			BaseItem definitionItem, 
			string fieldName, 
			string propertyName = null)
		{
			var field = definitionItem.Fields[fieldName];
			propertyName = propertyName ?? fieldName;

			if (field == null || field.Value.IsWhiteSpaceOrNull())
			{
				return;
			}

			rendering[propertyName] = definitionItem[fieldName];
		}

		protected static HtmlString RenderRendering(Rendering rendering)
		{
			var stringWriter = new StringWriter();
			PipelineService.Get().RunPipeline("mvc.renderRendering", new RenderRenderingArgs(rendering, stringWriter));
			return new HtmlString(stringWriter.ToString());
		}
	}
}

And then I replaced all usages of


@Html.Sitecore().Rendering("/sitecore/layout/Renderings/MyRendering")

to


@SitecoreHelper.Rendering("/sitecore/layout/Renderings/MyRendering")

If you using this method please double check that the caching settings are being properly set, otherwise use the code above.

That’s it Folks.🙂