Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
Experience Sitecore! | Separating content items from definition using Unicorn's NewItemsEvaluator

Experience Sitecore!

Martin Miles on Sitecore

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!
blog comments powered by Disqus