Experience Sitecore ! | Separating content items from definition using Unicorn's NewItemsEvaluator

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Separating content items from definition using Unicorn's NewItemsEvaluator

The vast majority of my readers are already familiar with Unicorn (if not - please take your time to familiarize with it by this link) and understand the principles of how it works. In few words, it serializes your Sitecore items into text files so that you can store them under source control along with the rest of your solution code.Then you can sync these files back into Sitecore, either manually from its admin page, or automatically by PowerShell or deployment script on CI / CD pipeline.  If the item has was changed, Unicorn automatically modifies its serialization file (so that changed file goes to source control to all other developers) and the reverse - newer items from source control will update those in Sitecore. So far, so good.

Problem: your solution infrastructure has numerous environments from lower (Dev) to higher (Prod) where the actual site is running. But not just the site itself - Sitecore also is there. Your content editors will be using Sitecore on prod, bringing plenty changes. But with next deployment all that new content will be automatically overwritten by older serialized items. What is good - now we have Helix principles, that classifies two types of Sitecore items - definition items to be passed along with the rest of solution code and actual content item. For Content items source of truth is Production, while for definition items source of truth is Development. But how do we actually define and set up content items?

Solution: the best automated approach also comes with Unicorn, however almost not documented and very little people know about it. It is called NewItemsEvaluator. Let's take a look at how it works:

By default, Unicorn predicate runs with Master Evaluator, which always overwrites Sitecore item with whatever comes from serialization. Because of Master Evaluator, content items on production will be overwritten by serialization with each deployment's sync unless actions are taken. NewItemsEvaluator also creates an item in Sitecore, but only if that item does not (yet) exist in the target environment. But if the item with such ID already exists there - it won't be affected and overwritten.

Firstly, we need to define what content items are. That typically will be all the items underneath /sitecore/YourProjectName/Home including Home item itself. Also, that will be all items within /sitecore/media library/Project/YourProjectName and items within /sitecore/content/YourProjectName/Global including Global item itself and its folder-based children that have insert options assigned.

Secondly, we need to create a new unicorn predicate for those content items identified. That predicate should be driven by New Item Evaluator:

<evaluator type="Unicorn.Evaluators.NewItemOnlyEvaluator, Unicorn" singleInstance="true" />
Thirdly, we need to exclude content items from the default website predicate driven by Master Evaluator. Luckily, Unicorn comes with handy exclusion syntax (more examples can be found at this link):
<include name="Some children" database="master" path="/sitecore/content/YourWebsite">
  <exclude children="true">
    <except name="Settings" />
    <except name="Global" />
  </exclude>
</include>

Finally, your website project layer serialization configuration file will have two predicate configurations similar to one below:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:localenv="http://www.sitecore.net/xmlconfig/localenv/">
    <sitecore role:require="Standalone or ContentManagement">
        <unicorn>
            <configurations>
                <configuration 
                    name="Project.YourProjectName.Website" 
                    description="YourProjectName content" 
dependencies="Foundation.*,Feature.*,Project.Common" extends="Helix.Project"> <predicate> <include name="Project.YourProjectName.Layouts" database="master" path="/sitecore/layout/layouts/Project/YourProjectName" />
<include name="Project.YourProjectName.PlaceholderSettings" database="master" path="/sitecore/layout/placeholder settings/Project/YourProjectName" />
<include name="Project.YourProjectName.Content" database="master" path="/sitecore/content/YourProjectName">
<exclude path="/sitecore/content/YourProjectName/Home" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Button Styles" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Font Styles" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Image Container Css" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Landing Css Classes" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Links/Footer Menu" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Links/Header Menu" />
<exclude childrenOfPath="/sitecore/content/YourProjectName/Global/Links/Social Media" />
</include> <include name="Project.YourProjectName.Media" database="master" path="/sitecore/media library/Project/YourProjectName">
<exclude children="true" /> </include> <include name="Project.YourProjectName.ExperienceEditor" database="core" path="/sitecore/client/Applications/ExperienceEditor/Pipelines/InitializePageEdit" />
</predicate> <!-- roles and user data stores skipped here --> </configuration> <configuration name="Project.YourProjectName.Website.AuthoredContent"
description="Sample site content (New Item Provider)" dependencies="Foundation.*,Feature.*,Project.YourProjectNamev.Website"
patch:after="configuration[@name='Project.YourProjectName.Website']"
extends="Helix.Project"> <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization\Content" type="Rainbow.Storage.SerializationFileSystemDataStore, Rainbow" useDataCache="false" singleInstance="true" /> <evaluator type="Unicorn.Evaluators.NewItemOnlyEvaluator, Unicorn" singleInstance="true" /> <dataProviderConfiguration enableTransparentSync="false" type="Unicorn.Data.DataProvider.DefaultUnicornDataProviderConfiguration, Unicorn" /> <predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true"> <include name="Project.YourProjectName.Home" database="master" path="/sitecore/content/YourProjectName/Home" />
<include name="Project.YourProjectName.Global.ButtonStyles" database="master" path="/sitecore/content/YourProjectName/Global/Button Styles" />
<include name="Project.YourProjectName.Global.FontStyles" database="master" path="/sitecore/content/YourProjectName/Global/Font Styles" />
<include name="Project.YourProjectName.Global.HeroCss Classes" database="master" path="/sitecore/content/YourProjectName/Global/Hero Css Classes" />
<include name="Project.YourProjectName.Global.HeroMobile Background Image" database="master" path="/sitecore/content/YourProjectName/Global/Hero Mobile Background Image" />
<include name="Project.YourProjectName.Global.ImageContainerCss" database="master" path="/sitecore/content/YourProjectName/Global/Image Container Css" />
<include name="Project.YourProjectName.Global.LandingCssClasses" database="master" path="/sitecore/content/YourProjectName/Global/Landing Css Classes" />
<include name="Project.YourProjectName.Global.Recruiters" database="master" path="/sitecore/content/YourProjectName/Global/Recruiters" />
<include name="Project.YourProjectName.Global.HigllightCssClasses" database="master" path="/sitecore/content/YourProjectName/Global/Higllight Css Classes" />
<include name="Project.YourProjectName.Global.TabsCssClasses" database="master" path="/sitecore/content/YourProjectName/Global/Tabs Css Classes" />
<include name="Project.YourProjectName.Global.Leaders" database="master" path="/sitecore/content/YourProjectName/Global/Leaders" />
<include name="Project.YourProjectName.Global.Links.FooterMenu" database="master" path="/sitecore/content/YourProjectName/Global/Links/Footer Menu" />
<include name="Project.YourProjectName.Global.Links.HeaderMenu" database="master" path="/sitecore/content/YourProjectName/Global/Links/Header Menu" />
<include name="Project.YourProjectName.Global.Links.SocialMedia" database="master" path="/sitecore/content/YourProjectName/Global/Links/Social Media" />
<include name="Project.YourProjectName.Media.Content" database="master" path="/sitecore/media library/Project/YourProjectName" />
</predicate> </configuration> </configurations> </unicorn> </sitecore> </configuration>

Hope this helps!
Comments are closed