Sitecore – izabelawlodarska.com https://izabelawlodarska.com Follow me for architecture, .net, sitecore and software engineering tips Thu, 10 Jan 2019 07:29:16 +0000 en-GB hourly 1 https://wordpress.org/?v=6.9.4 https://i0.wp.com/izabelawlodarska.com/wp-content/uploads/2023/01/cropped-android-chrome-512x512-1-1.png?fit=32%2C32&ssl=1 Sitecore – izabelawlodarska.com https://izabelawlodarska.com 32 32 122590972 Getting started with Unicorn serialization for Sitecore Experience Accelerator (SXA) – part 2 https://izabelawlodarska.com/2019/01/07/getting-started-with-unicorn-serialization-for-sitecore-experience-accelerator-sxa-part-2/#utm_source=rss&utm_medium=rss&utm_campaign=getting-started-with-unicorn-serialization-for-sitecore-experience-accelerator-sxa-part-2 https://izabelawlodarska.com/2019/01/07/getting-started-with-unicorn-serialization-for-sitecore-experience-accelerator-sxa-part-2/#comments Mon, 07 Jan 2019 14:00:59 +0000 http://www.izabelawlodarska.com/?p=304 In this article, I will show you my approach to setup Unicorn serialization for Sitecore Experience Accelerator (SXA) 1.8 using Sitecore 9.1.0 and Unicorn 4.0.7. This is the second part of the series of posts about Unicorn serialization. If you would like to start from the very beginning and read an introduction to Unicorn have a look at part 1. If you do not feel comfortable with Helix principles please check the documentation because I assumed that understanding of basic concepts is a prerequisite for the rest of the article.

Sitecore structure

For the demo purpose, I created a single-tenant solution with one shared site and one not shared site. To make it very generic I used “TenantName” as the name of the tenant, “YourSiteName” as the name of the site that is not shared and “SharedSiteName” for the shared site.

Sitecore SXA structure

I made a few assumptions:

  • Shared Site is going to be changed only by developers – this means we want to deploy it always. The Shared Site will be our centralised repository for styles, data sources, page designs, partial designs, and rendering variants with other sites inside of the same tenant.
  • Your Site is going to be changed both by authors and by developers – we have to be careful to do not overwrite content. In my setup, I included root items like Home, Media, Data, Presentation and Settings but excluded anything underneath Home and Data folder. I considered also another approach to deploying only items that do not currently exist in your Sitecore instance but I did not cover this in here.

Visual Studio solution structure

For the Visual Studio solution, I created a regular Helix structure. There are three layers which contain projects inside, let’s have a brief overview of what we have in each of the project’s configurations before we will dive into details.

  • Foundation
    • Serialization – This project will be serialized first because everything else depends on it. It contains Unicorn setup (Unicorn NuGet package was installed in here) and has all the root items for particular layers (Project, Feature, Foundation) serialized.
  • Feature – we will talk about that later.
  • Project
    • Common – Sitecore items that are shared between the sites e.g. tenant root folders, languages, workflows etc.
    • SharedSite – Items related to the Shared Site.
    • YourSite – Content for the particular site.

SXA project solution structure

 

Creating a generic configuration

Considering that in a few months there will be lots of different projects in the solution I wanted to prepare as generic configuration as possible so the developers in my team can just extend it and we can reduce the amount of code and possibility of forgetting about something that actually has to be included.

If you had a look at Sitecore Habitat you can see a file called Unicorn.Helix.config used with base configurations for each layer of the solution. If you add this file to your configuration, in your projects you can extend each of the predefined configurations: Helix.Base, Helix.Foundation, Helix.Feature, Helix.Project.

Unicorn.Helix.config:

<!-- ******************************************************************** Unicorn Helix configurations Defines standard configurations for modules in all layers ******************************************************************** See Unicorn.config for commentary on how configurations operate, or https://github.com/kamsar/Unicorn/blob/master/README.md -->

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
    <sitecore role:require="Standalone or ContentManagement">
        <unicorn>
            <configurations>
                <!-- Base configuration for all modules -->
                <configuration name="Helix.Base" abstract="true">
                    <predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true" />

                    <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true" />
                    <roleDataStore type="Unicorn.Roles.Data.FilesystemRoleDataStore, Unicorn.Roles" physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization\Roles" singleInstance="true" />
                    <rolePredicate type="Unicorn.Roles.RolePredicates.ConfigurationRolePredicate, Unicorn.Roles" singleInstance="true">
                        <!-- Include an invalid predicate or all roles will be synced -->
                        <include domain="invaliddomain" pattern="none" />
                    </rolePredicate>
                </configuration>

                <!-- Foundation modules -->
                <configuration name="Helix.Foundation" abstract="true" extends="Helix.Base">
                    <predicate>
                        <include name="Templates" database="master" path="/sitecore/templates/$(layer)/$(module)" />
                    </predicate>
                </configuration>

                <!-- Feature modules -->
                <configuration name="Helix.Feature" abstract="true" extends="Helix.Base">
                    <predicate>
                        <include name="Templates" database="master" path="/sitecore/templates/$(layer)/$(module)" />
                        <include name="Renderings" database="master" path="/sitecore/layout/renderings/$(layer)/$(module)" />
                        <include name="Media" database="master" path="/sitecore/media library/$(layer)/$(module)" />
                    </predicate>
                </configuration>

                <!-- Project modules -->
                <configuration name="Helix.Project" abstract="true" extends="Helix.Base">
                    <predicate>
                        <include name="Templates" database="master" path="/sitecore/templates/$(layer)/$(module)" />
                        <include name="Renderings" database="master" path="/sitecore/layout/renderings/$(layer)/$(module)" />
                    </predicate>
                </configuration>
                <syncConfiguration type="Unicorn.Loader.DefaultSyncConfiguration, Unicorn" singleInstance="true" updateLinkDatabase="true" updateSearchIndex="true" maxConcurrency="1" />
                <userDataStore type="Unicorn.Users.Data.FilesystemUserDataStore, Unicorn.Users" physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization\Users\" singleInstance="true" />
            </configurations>
        </unicorn>
    </sitecore>
</configuration>

I think that idea to have some base configurations defined in this way is brilliant, so… why do not create something similar for SXA? To do so you have to understand basic SXA concepts pretty well and a few things may not be obvious when you are just starting with your first SXA project.

Before I have decided which Sitecore item goes to which configuration I needed to answer a very important question:

What should be treated as a feature project in SXA?

When I started working on the first SXA project, I felt really confused what should be treated as a feature in SXA. Single component? Or components category? Or module? Is module the actual category from the toolbox or can be a different one? How about features that are not SXA components, but require some sitecore templates to be created? Or fully functional requirements that need to be created as features?

After the analysis, I decided to distinguish three types of features configurations I will have:

  1. SXA modules – A group of renderings with a similar purpose. I decided to create a new module for each toolbox category and for each module create a new feature project.
  2. Non-SXA modules – Contain only some templates that are not really the SXA modules e.g. some settings items.
  3. Purely functional features – Do not require any sitecore items in configuration or require a completely custom configuration, so the generic configuration is not needed here.

The analysis above allowed me to divide the configurations in the following way:

Unicorn SXA Helix base configuration:

  • Helix.Base – base configuration for all the other generic configurations or for custom configurations, defines the path on disk where the Unicorn serialized items are stored (targetDataStore).
  • Helix.Foundation – configuration for Foundation projects which are not SXA modules, contains only /sitecore/templates.
  • Helix.Feature – configuration for Feature projects which are not SXA modules, contains only /sitecore/templates.
  • Helix.Project – my project layer had three quite different projects inside so I cleaned this one up.
  • Helix.XA.Module – this is the biggest difference comparing to the Habitat Unicorn.Helix.config. This contains configuration including all of the items that are automatically created when you add a new SXA module. This does not contain a layer in the name because it is allowed to create SXA modules in Feature and Foundation. You can use this configuration in both cases.

Why do we need Helix.XA.Module configuration?

When SXA module is created you can notice that it creates also:

  • /sitecore/templates/Feature/TenantName/ModuleName
  • /sitecore/templates/Branches/Feature/TenantName/ModuleName
  • /sitecore/system/Settings/Feature/TenantName/ModuleName
  • /sitecore/layout/Renderings/Feature/TenantName/ModuleName
  • /sitecore/media library/Feature/TenantName/ModuleName
  • /sitecore/layout/Placeholder Settings/Feature/TenantName/ModuleName
  • /sitecore/layout/Layouts/Feature/TenantName/ModuleName

Because all of the items are created automatically I cannot see a reason why we should add them all manually to every feature when we create one. So these items will be automatically included if in your foundation/feature project you will extend Helix.XA.Module configuration.

So when finally when we have a look at my version of Unicorn.Helix.config, it looks like below and will be added in the Foundation.Serialization project.

Unicorn.Helix.config:
<!-- ******************************************************************** Unicorn Helix configurations Defines standard configurations for modules in all layers ******************************************************************** See Unicorn.config for commentary on how configurations operate, or https://github.com/kamsar/Unicorn/blob/master/README.md -->

<!-- ******************************************************************** I changed this file to fit SXA purposes ******************************************************************** -->

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <!-- Base configuration for all modules -->
        <configuration name="Helix.Base" abstract="true">
          <predicate type="Unicorn.Predicates.SerializationPresetPredicate, Unicorn" singleInstance="true" />
          <targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true" />
        </configuration>

        <!-- Foundation non-SXA modules -->
        <configuration name="Helix.Foundation" abstract="true" extends="Helix.Base">
          <predicate>
            <include name="Templates" database="master" path="/sitecore/templates/$(layer)/TenantName/$(module)" />
          </predicate>
        </configuration>

        <!-- Foundation/Feature SXA modules -->
        <configuration name="Helix.XA.Module" abstract="true" extends="Helix.Base">
          <predicate>
            <include name="Templates" database="master" path="/sitecore/templates/$(layer)/TenantName/$(module)" />
            <include name="Renderings" database="master" path="/sitecore/layout/renderings/$(layer)/TenantName/$(module)" />
            <include name="System.Settings" database="master" path="/sitecore/system/Settings/$(layer)/TenantName/$(module)" />
            <include name="Templates.Branches" database="master" path="/sitecore/templates/Branches/$(layer)/TenantName/$(module)" />
            <include name="PlaceholderSettings" database="master" path="/sitecore/layout/Placeholder Settings/$(layer)/TenantName/$(module)" />
            <include name="Layouts" database="master" path="/sitecore/layout/Layouts/$(layer)/TenantName/$(module)" />
          </predicate>
        </configuration>
        
        <!-- Feature non-SXA modules -->
        <configuration name="Helix.Feature" abstract="true" extends="Helix.Base">
          <predicate>
            <include name="Templates" database="master" path="/sitecore/templates/$(layer)/TenantName/$(module)" />
          </predicate>
        </configuration>

        <!-- Project modules -->
        <configuration name="Helix.Project" abstract="true" extends="Helix.Base">
          <predicate>
            <!-- This will be configured individually because Project layer is a bit different than the rest. -->
          </predicate>
        </configuration>
        <syncConfiguration type="Unicorn.Loader.DefaultSyncConfiguration, Unicorn" singleInstance="true" updateLinkDatabase="true" updateSearchIndex="true" maxConcurrency="1" />
        <userDataStore type="Unicorn.Users.Data.FilesystemUserDataStore, Unicorn.Users" physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization\Users\" singleInstance="true" />
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

Foundation.Serialization

In this project, the Unicorn 4.0.7 NuGet package has been installed, which created the Unicorn folder with all the default configurations.

Unicorn configuration in “Foundation” folder contains the root items for Project, Feature and Foundation items and root folders for e.g. Workflows and Tasks. This project will be synchronised as a first one and any other configuration depends on that.

“z.Unicorn” folder contains customizations to Unicorn, because they are prefixed with “z” they will be patched after the base Unicorn files. In our case, there is the base configuration (Unicorn.Helix.config) and shared secret.


SXA Foundation.Serialization configuration

 

SitecoreExamples.Foundation.Serialization.Serialization.config:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <configuration name="Foundation.Serialization" description="Root items" extends="Helix.Base">
          <predicate>
            <!-- Templates -->
            <include name="Templates.Foundation" database="master" path="/sitecore/templates/Foundation">
              <exclude children="true" />
            </include>
            <include name="Templates.Feature" database="master" path="/sitecore/templates/Feature">
              <exclude children="true" />
            </include>
            <include name="Templates.Project" database="master" path="/sitecore/templates/Project">
              <exclude children="true" />
            </include>

            <!-- Branches -->
            <include name="Branches.Foundation" database="master" path="/sitecore/templates/Branches/Foundation">
              <exclude children="true" />
            </include>
            <include name="Branches.Feature" database="master" path="/sitecore/templates/Branches/Feature">
              <exclude children="true" />
            </include>
            <include name="Branches.Project" database="master" path="/sitecore/templates/Branches/Project">
              <exclude children="true" />
            </include>

            <!-- Layouts -->
            <include name="Layouts.Foundation" database="master" path="/sitecore/layout/layouts/Foundation">
              <exclude children="true" />
            </include>
            <include name="Layouts.Feature" database="master" path="/sitecore/layout/layouts/Feature">
              <exclude children="true" />
            </include>
            <include name="Layouts.Project" database="master" path="/sitecore/layout/layouts/Project">
              <exclude children="true" />
            </include>

            <!-- Models -->
            <include name="Models.Foundation" database="master" path="/sitecore/layout/models/Foundation">
              <exclude children="true" />
            </include>
            <include name="Models.Feature" database="master" path="/sitecore/layout/models/Feature">
              <exclude children="true" />
            </include>
            <include name="Models.Project" database="master" path="/sitecore/layout/models/Project">
              <exclude children="true" />
            </include>

            <!-- Placeholder Settings -->
            <include name="PlaceholderSettings.Foundation" database="master" path="/sitecore/layout/placeholder settings/Foundation">
              <exclude children="true" />
            </include>
            <include name="PlaceholderSettings.Feature" database="master" path="/sitecore/layout/placeholder settings/Feature">
              <exclude children="true" />
            </include>
            <include name="PlaceholderSettings.Project" database="master" path="/sitecore/layout/placeholder settings/Project">
              <exclude children="true" />
            </include>

            <!-- Renderings -->
            <include name="Renderings.Foundation" database="master" path="/sitecore/layout/renderings/Foundation">
              <exclude children="true" />
            </include>
            <include name="Renderings.Feature" database="master" path="/sitecore/layout/renderings/Feature">
              <exclude children="true" />
            </include>
            <include name="Renderings.Project" database="master" path="/sitecore/layout/renderings/Project">
              <exclude children="true" />
            </include>

            <!--Settings-->
            <include name="Settings.Foundation" database="master" path="/sitecore/system/Settings/Foundation">
              <exclude children="true" />
            </include>
            <include name="Settings.Feature" database="master" path="/sitecore/system/Settings/Feature">
              <exclude children="true" />
            </include>
            <include name="Settings.Project" database="master" path="/sitecore/system/Settings/Project">
              <exclude children="true" />
            </include>

            <!--System-->
            <include database="master" path="/sitecore/system/Tasks">
              <exclude path="Schedules" />
            </include>
            <include database="master" path="/sitecore/system/Workflows" >
              <exclude children="true" />
            </include>

            <!-- Media -->
            <include name="Media.Foundation" database="master" path="/sitecore/media library/Foundation">
              <exclude children="true" />
            </include>
            <include name="Media.Feature" database="master" path="/sitecore/media library/Feature">
              <exclude children="true" />
            </include>
            <include name="Media.Project" database="master" path="/sitecore/media library/Project">
              <exclude children="true" />
            </include>

            <!--Content-->
            <include name="Content" database="master" path="/sitecore/content">
              <exclude children="true" />
            </include>

            <!-- Core templates -->
            <include name="Core.Templates.Feature" database="core" path="/sitecore/templates/Feature">
              <exclude children="true" />
            </include>
            <include name="Core.Templates.Foundation" database="core" path="/sitecore/templates/Foundation">
              <exclude children="true" />
            </include>
            <include name="Core.Templates.Project" database="core" path="/sitecore/templates/Project">
              <exclude children="true" />
            </include>

          </predicate>
          <dataProviderConfiguration enableTransparentSync="false" />
          <syncConfiguration updateLinkdatabase="false" updateSearchIndex="false" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

Project.Common

It contains all of the items that are common between different sites e.g. the tenant related root folders, workflows, languages, SXA templates created with a tenant. I added here also Media library items but I excluded items created by Asset Optimizer (in case you switched it on) because they are created dynamically.

SitecoreExamples.Common.Website.Serialization.config:
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <unicorn>
      <configurations>
        <configuration name="Project.Common" description="Content and settings common between different tenants." extends="Helix.Base" patch:after="configuration[@name='Foundation.Serialization']">
          <predicate>
            <!-- Workflows tenant folders with subitems -->
            <include name="System.Workflows.TenantName" database="master" path="/sitecore/system/Workflows/TenantName" />

            <!-- System languages -->
            <include name="System.Languages" database="master" path="/sitecore/system/Languages" />

            <!-- TenantName tenant root folders -->
            <!-- Templates -->
            <include name="Templates.Foundation.TenantName" database="master" path="/sitecore/templates/Foundation/TenantName">
              <exclude children="true" />
            </include>
            <include name="Templates.Feature.TenantName" database="master" path="/sitecore/templates/Feature/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Templates.Project.TenantName" database="master" path="/sitecore/templates/Project/TenantName" >
              <exclude children="true" >
                <!-- SXA templates added by default with tenant setup -->
                <except name="Home" />
                <except name="Page" />
                <except name="Page Design" />
                <except name="Page Design Folder" />
                <except name="Page" />
                <except name="Page Designs" />
                <except name="Partial Design" />
                <except name="Partial Design Folder" />
                <except name="Partial Designs" />
                <except name="Settings" />
                <except name="Site" />
                <except name="Tenant" />
              </exclude>
            </include>

            <!-- Branches -->
            <include name="Branches.Foundation.TenantName" database="master" path="/sitecore/templates/Branches/Foundation/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Branches.Feature.TenantName" database="master" path="/sitecore/templates/Branches/Feature/TenantName">
              <exclude children="true" />
            </include>
            <include name="Branches.Project.TenantName" database="master" path="/sitecore/templates/Branches/Project/TenantName" >
              <exclude children="true" />
            </include>

            <!-- Layouts -->
            <include name="Layouts.Foundation.TenantName" database="master" path="/sitecore/layout/layouts/Foundation/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Layouts.Feature.TenantName" database="master" path="/sitecore/layout/layouts/Feature/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Layouts.Project.TenantName" database="master" path="/sitecore/layout/layouts/Project/TenantName">
              <exclude children="true" />
            </include>

            <!-- Models -->
            <include name="Models.Foundation.TenantName" database="master" path="/sitecore/layout/models/Foundation/TenantName">
              <exclude children="true" />
            </include>
            <include name="Models.Feature.TenantName" database="master" path="/sitecore/layout/models/Feature/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Models.Project.TenantName" database="master" path="/sitecore/layout/models/Project/TenantName">
              <exclude children="true" />
            </include>

            <!-- Placeholder Settings -->
            <include name="PlaceholderSettings.Foundation.TenantName" database="master" path="/sitecore/layout/placeholder settings/Foundation/TenantName" >
              <exclude children="true" />
            </include>
            <include name="PlaceholderSettings.Feature.TenantName" database="master" path="/sitecore/layout/placeholder settings/Feature/TenantName" >
              <exclude children="true" />
            </include>
            <include name="PlaceholderSettings.Project.TenantName" database="master" path="/sitecore/layout/placeholder settings/Project/TenantName" >
              <exclude children="true" />
            </include>

            <!-- Renderings -->
            <include name="Renderings.Foundation.TenantName" database="master" path="/sitecore/layout/renderings/Foundation/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Renderings.Feature.TenantName" database="master" path="/sitecore/layout/renderings/Feature/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Renderings.Project.TenantName" database="master" path="/sitecore/layout/renderings/Project/TenantName" >
              <exclude children="true" />
            </include>

            <!--Settings-->
            <include name="Settings.Foundation.TenantName" database="master" path="/sitecore/system/Settings/Foundation/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Settings.Feature.TenantName" database="master" path="/sitecore/system/Settings/Feature/TenantName" >
              <exclude children="true" />
            </include>
            <include name="Settings.Project.TenantName" database="master" path="/sitecore/system/Settings/Project/TenantName" >
              <exclude children="true" />
            </include>

            <!--System-->
            <include name="System.Tasks" database="master" path="/sitecore/system/Tasks/Schedules/TenantName" />

            <!-- Media -->
            <include name="Media.Foundation.TenantName" database="master" path="/sitecore/media library/Foundation/TenantName" />
            <include name="Media.Feature.TenantName" database="master" path="/sitecore/media library/Feature/TenantName" />
            <include name="Media.Project.TenantName" database="master" path="/sitecore/media library/Project/TenantName" />
            <include name="Media.TemplateThumbnails" database="master" path="/sitecore/media library/System/Template Thumbnails" />

            <!--Themes-->
            <include name="Media.Themes.TenantName" database="master" path="/sitecore/media library/Themes/TenantName" >
              <!-- Excluding files generated by Asset Optimizer, they are dynamically updated if needed during page requests -->
              <exclude path="TenantName/Scripts/optimized-min" />
              <exclude path="TenantName/styles/optimized-min" />
            </include>

            <!--Content -->
            <include name="Applications.WebEdit.CustomExperienceButtons" database="core" path="/sitecore/content/Applications/WebEdit/Custom Experience Buttons/TenantName" />

            <include name="Content.TenantName" database="master" path="/sitecore/content/TenantName" >
              <exclude children="true" />
            </include>
           </predicate>
          <syncConfiguration updateLinkDatabase="false" updateSearchIndex="true" maxConcurrency="16" type="Unicorn.Loader.DefaultSyncConfiguration, Unicorn" singleInstance="true" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

Besides that, in the Common project, we have dev settings, where the Unicorn “sourceFolder” path was set. You should update the path so it points to the physical location of your project on disk.

z.Common.Website.DevSettings.config:
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:env="http://www.sitecore.net/xmlconfig/env/">
  <sitecore>
    <!-- Required for Unicorn -->
    <sc.variable env:require="dev" name="sourceFolder" value="C:\Projects\SitecoreExamples\src" />
  </sitecore>
</configuration>	

The sourceFolder that you set above will be used for the configuration base in:

Unicorn.Helix.config
<targetDataStore physicalRootPath="$(sourceFolder)\$(layer)\$(module)\serialization" useDataCache="false" singleInstance="true" />

SharedSite.Website

In here I added all of the content items that are related to the Shared Site and templates related to this site. Because this configuration includes content items I decided to update the search index and the link database if there are any changes for this configuration.

SitecoreExamples.SharedSiteName.Website.Serialization.config:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <configuration name="Project.SharedSiteName" description="Shared site specific root folders" dependencies="Foundation.*,Feature.*,Project.Common" extends="Helix.Base" patch:after="configuration[@name='Project.Common']">
          <predicate>
            <include name="Content.TenantName.SharedSiteName" database="master" path="/sitecore/content/TenantName/SharedSiteName" />
            <include name="Templates.Project.TenantName.SharedSiteName" dataSharedSiteName="master" path="/sitecore/templates/Project/TenantName/SharedSiteName" />
          </predicate>
          <dataProviderConfiguration enableTransparentSync="false" />
          <syncConfiguration updateLinkDatabase="false" updateSearchIndex="true" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

YourSite.Website

This configuration is related to the site that is not shared. Because the content will be changed by Authors and Developers I excluded everything underneath Home, Media and Data. Because I assume that the Authors will not be touching the Settings and Presentation I included all the items. The same as for shared site after syncing this configuration we want to rebuild the search index and link database.

SitecoreExamples.YourSiteName.Website.Serialization.config:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <configuration name="Project.YourSiteName" description="Contains all the content items for YourSite" dependencies="Foundation.*,Feature.*,Project.Common,Project.Base" extends="Helix.Base" patch:after="configuration[@name='Project.Base']">
          <predicate>
            <include name="Content.TenantName.YourSiteName" database="master" path="/sitecore/content/TenantName" >
              <exclude path="YourSiteName/Home" />
              <exclude path="YourSiteName/Media" />
              <exclude childrenOfPath="YourSiteName/Data/*" />
            </include>
          </predicate>
          <dataProviderConfiguration enableTransparentSync="false" />
          <syncConfiguration updateLinkDatabase="true" updateSearchIndex="true" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

A feature which is not SXA module

This is an example usage of the configuration for a functional feature which needs only some sitecore templates defined. It seems that this configuration is empty but notice that it extends the Helix.Feature configuration from Unicorn.Helix.config, which means that the templates for this project will be serialized even if you would not include anything extra in this file.

SitecoreExamples.Feature.FeatureWhichIsNotSxaModule.Serialization.config:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <configuration name="Feature.FeatureWhichIsNotSxaModule" description="Use this project as an example of configuration for feature that is not created as SXA module e.g. for functional requirements." dependencies="Foundation.Serialization" extends="Helix.Feature" patch:after="configuration[@name='Foundation.Serialization']">
          <predicate>
            <!-- By default serializes templates only (if this section is empty), check the base configuration in Unicorn.Helix.config -->
          </predicate>
          <dataProviderConfiguration enableTransparentSync="false" />
          <syncConfiguration updateLinkDatabase="false" updateSearchIndex="false" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

A feature which is SXA module

This is an example of a configuration for the feature that contain SXA module. By default includes:

  • /sitecore/templates/Feature/TenantName/ModuleName
  • /sitecore/templates/Branches/Feature/TenantName/ModuleName
  • /sitecore/system/Settings/Feature/TenantName/ModuleName
  • /sitecore/layout/Renderings/Feature/TenantName/ModuleName
  • /sitecore/media library/Feature/TenantName/ModuleName
  • /sitecore/layout/Placeholder Settings/Feature/TenantName/ModuleName
  • /sitecore/layout/Layouts/Feature/TenantName/ModuleName
SitecoreExamples.Feature.SxaModule.Serialization.config:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentManagement">
    <unicorn>
      <configurations>
        <configuration name="Feature.FeatureWhichIsSxaModule" description="Use this project as an example of configuration for feature that is SXA module." dependencies="Foundation.Serialization" extends="Helix.XA.Feature" patch:after="configuration[@name='Foundation.Serialization']">
          <predicate>
            <!-- By default serializes all items created with SXA module, check the base configuration in Unicorn.Helix.config -->
          </predicate>
          <dataProviderConfiguration enableTransparentSync="false" />
          <syncConfiguration updateLinkDatabase="false" updateSearchIndex="false" />
        </configuration>
      </configurations>
    </unicorn>
  </sitecore>
</configuration>

Summary

In this article I explained my approach to set up Unicorn serialization for Sitecore Experience Accelerator (SXA) project. I hope that this was helpful for you. If you would like to see a different approach to Unicorn setup with SXA, Michael West wrote a great article about that some time ago, check it out!

Please leave a comment if you found the post useful or if you have any suggestions on how to improve it. Any feedback welcome!

]]>
https://izabelawlodarska.com/2019/01/07/getting-started-with-unicorn-serialization-for-sitecore-experience-accelerator-sxa-part-2/feed/ 2 304
Setting custom field as Browser Title in SXA https://izabelawlodarska.com/2019/01/03/setting-custom-field-as-browser-title-in-sxa/#utm_source=rss&utm_medium=rss&utm_campaign=setting-custom-field-as-browser-title-in-sxa https://izabelawlodarska.com/2019/01/03/setting-custom-field-as-browser-title-in-sxa/#comments Thu, 03 Jan 2019 07:00:48 +0000 http://www.izabelawlodarska.com/?p=355 This is one of these things that are super obvious when you know about them, but you may waste some time to figure it out if you were not aware that it exists out of the box. This short post is here to save your time and show you how cool is Sitecore Experience Accelerator. 🙂

The new “browser title” field

Imagine that you got a task to create a new field called “Browser title”, which will be a part of every page on your site. As the name suggests, this field will be responsible for rendering the title in the browser tab. The solution below has been applied in Sitecore 9.1 and SXA 1.8.

The implementation of this requirement is super simple. You do not need any custom code for this, it is possible to change the field that will be used to render the browser title using the out of the box SXA settings!

After creating the new field in Sitecore and adding it to the base page, which will be inherited in every newly created page template, you can go to:

/sitecore/content/Tenant/YourSite/Settings/Browser Title/Title

and change the name of the field that will be used to render the page title in the browser. Publish all the modified items and this is it!

Custom Browser Title field in SXA

]]>
https://izabelawlodarska.com/2019/01/03/setting-custom-field-as-browser-title-in-sxa/feed/ 1 355
Getting started with Unicorn serialization for Sitecore – Part 1 https://izabelawlodarska.com/2018/11/24/getting-started-with-unicorn-serialization-for-sitecore/#utm_source=rss&utm_medium=rss&utm_campaign=getting-started-with-unicorn-serialization-for-sitecore https://izabelawlodarska.com/2018/11/24/getting-started-with-unicorn-serialization-for-sitecore/#comments Sat, 24 Nov 2018 11:34:03 +0000 http://www.izabelawlodarska.com/?p=254 I decided to write a series of articles about Unicorn serialization and how to configure it in a Sitecore SXA project. This article is the first one in the series and is just a brief introduction to Unicorn explaining basic concepts, mostly directed towards people that used different serialization tools in the past and are just starting with Unicorn. If you are familiar with the basics you can jump straight away to part 2 where I cover serialization for SXA in detail. In the configuration examples, for the purpose of this article, I used Sitecore 9.0.2 with Unicorn 4.0.2 with Habitat Home Platform.

About Unicorn

Because Sitecore items are stored in the databases, the challenge during the development process is to keep the databases in sync between the developers working on the project and to move items between environments during deployments. This can be achieved in a few ways supplied out of the box with Sitecore e.g. creating Sitecore packages and sharing them after every update to an item, reserializing items and exchanging them or by sharing one local database with all people in the team. Unfortunately, the above solutions are error-prone, they are lacking packages versioning and can cause issues when two people work on the same item and try to merge their changes. This is why it is recommended to use serialization tools like Unicorn or TDS (Team Development for Sitecore).

Unicorn is an open source tool which allows performing Sitecore items serialization from the database to a file system, so they can be shared among developers using version control system like git. After the initial setup Unicorn allows to perform automatic serialisation of changes to the items when they are made in Sitecore. All of the serialized items are stored as text files with YAML extension.

Unicorn works together with Rainbow which is a library that supplies a set of interfaces which allow to wrap the Sitecore items structure and replace the Sitecore serialization format, file system organization and enables cross-source item comparison.

Unicorn serialization basics

The idea behind Unicorn is to serialize only items that you need, by adding their Sitecore item paths to Unicorn configuration files. When the item is controlled by Unicorn all the child – items can be serialized automatically unless excluded, which is very convenient because the developer does not have to remember to serialize items manually after every change they made. The exception is only for newly created items which should be reserialized in order to be stored in the file system correctly – it is called initial serialization and Unicorn will complain during the next sync if you forget doing it.

Every time you pull the code from GIT you should perform Unicorn serialization. Make sure that you committed all your new Sitecore items first, otherwise, you will lose all your changes. If the items are deleted during the Unicorn serialization, they are moved to the bin if it exists so it should be still possible to bring them back.

Unicorn serialization can be performed in two ways:

  • Using URL https://yoursitedomain.com/unicorn.aspx (coming out of the box with Unicorn)

    List of Unicorn configurations on unicorn.aspx

    An example list of Unicorn configurations in Habitat Home Platform.

    The unicorn.aspx page contains a list of configurations defined for the Sitecore instance and allows performing reserialization and sync for each of them separately or for all of them if there are more than one configurations defined. For each of the configurations, you have two options to choose: to reserialize items or to sync them. You can also click on “Sync all the things” to perform sync for all of the configurations at once.

    Sync operation means that we pull the newest version of the database to our local instance.

    Reserialization deletes all the serialized YAML files from the file system and serializes the local version of Sitecore items of the database to file system.

    From your local instance, after the initial reserialization, you always should perform sync operation only unless you know what are you doing! Reserialization of the particular Unicorn configuration should be performed only once during Unicorn initial setup or in case if data is malformed and need to be reinitialized. Using “transparent sync” option allows you skipping the pull & sync step during development because after pulling items from git the configurations will be synced automatically. By default, the transparent sync is switched off.

    Running sync through the unicorn.aspx page automatically publishes items (when the auto-publish feature is on) that got updated and runs re-indexing process which is important in case you serialized content items. Currently, synchronization from Visual Studio does not support this feature.

    For newly added items/fields, you should perform reserialization from Sitecore Content Editor only for the part of CMS tree containing items you added, otherwise you risk data loss. Unicorn by default integrates with Sitecore serialization and you can use the Developer tab on the ribbon to Serialize item or tree.

    Developer toolbar on the ribbon allowing serialization of Sitecore items

    Developer toolbar on the ribbon allowing serialization of Sitecore items.

    Be careful, if the parent item will not be synced first the child items may not be serialized and you will not see any exceptions. It is a good practice to click on one of the items intended to be serialized and check if you can see the yellow strap at the top of the item saying that it is controlled by Unicorn. If the item has been properly serialized you will see a path to the YAML file.Yellow strap presenting the information about Unicorn synchronization for the item.

    Yellow strap presenting the information about Unicorn synchronization for the item.

  • In Visual Studio from gulp tasks runner (only if you have Helix based solution with gulp build setup)

    Gulp tasks capable of running Unicorn sync in Task Runner ExplorerGulp tasks capable of running Unicorn sync in Task Runner Explorer

    To perform only Unicorn sync, right click on the Sync-Unicorn task and select “Run”. This task should be also by default invoked when you use “default” task in Helix gulp setup, so if you do the full project publish you do not need to run it separately. After running the sync check output in the console window if the synchronization succeeded.

YAML files and Serialization File System structure

YAML file is just a text file which stores basic data about the particular items, like item ID, parent item ID, template ID, item path, all fields in particular versions and languages. YAML files are human readable and quite easy to interpret which also makes them easy to merge or even edit manually in comparison to regular Sitecore serialization.

---
ID: "936350e1-6057-4250-986b-72be51833410"
Parent: "8a3283a7-2209-4f06-8eae-31f12eec322c"
Template: "0437fee2-44c9-46a6-abe9-28858d9fee8c"
Path: /sitecore/templates/Feature/Habitat Sites/Demo
DB: master
Languages:
- Language: en
  Versions:
  - Version: 1
    Fields:
    - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
      Hint: __Created
      Value: 20171108T132710Z
    - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
      Hint: __Created by
      Value: |
        sitecore\admin
- Language: "ja-JP"
  Fields:
  - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
    Hint: __Display name
    Value: デモ
  Versions:
  - Version: 1
    Fields:
    - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
      Hint: __Created
      Value: 20181024T125723Z
    - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
      Hint: __Created by
      Value: |
        sitecore\admin

YAML file example.

Serialization File System (SFS) maintain Sitecore tree structure for serialized items and stores each included subtree in its own hierarchy. On the picture below you can find an example of the structure of the items serialized to disk. Notice, that for each item, which also is a parent item for other items e.g. Data, you can see the Data.yaml which defines the particular item, and a Data folder that contains all its children. It is not allowed to serialize items in Data folder without serializing Data.yaml too, but fear not – it does not mean that you have to serialize the whole Sitecore tree from the root items to the bottom. The root items in Unicorn configurations are relative so you can choose the item on which you would like to start the serialization from.

Example of the Serialization File System for Unicorn items.

Example of items in the Serialization File System for Unicorn items.

The serialized items are stored in the file data store defined for particular Unicorn configuration. File data store is just a folder defined to be a parent of all the YAML files for a particular configuration. Because the configurations can be separated in this way you can keep the clean structure inside your different projects. This means that it is easy to keep the folder structure in line with Helix conventions.

More information about Unicorn:

https://github.com/SitecoreUnicorn/Unicorn

https://github.com/SitecoreUnicorn/Rainbow

https://kamsar.net/index.php/category/Unicorn

This is the end of the first part of the introduction to Unicorn. You can find part 2 about serialization for SXA in here.
Feel free to leave a comment below and let me know what you think or if there is anything you would like me to write about.

]]>
https://izabelawlodarska.com/2018/11/24/getting-started-with-unicorn-serialization-for-sitecore/feed/ 2 254