Getting Started Quickly with #Sitecore CDP (Customer Data Platform)

Sitecore CDP formerly Boxever is something you are probably hearing a lot about these days. As a developer you can implement it easily and as a marketer you can take advantage of some of the features as soon as it is implemented. Here are the steps in order I did to get started.

1. Get a sandbox account.

Should be able to get a login for the CDB Sandbox from your Sitecore rep. You can find the sandbox site here. This is a shared sandbox so you will need to be careful not to change anything that someone else has setup. It is ok to look and see what others have setup. That is one advantage of a shared sandbox.

When you first login to your sandbox account you should see a message like this:

2. Get your keys and Create Point of Sale Value.

To get your keys go to the gear icon at the bottom left and select System Settings then API Access.

Copy both keys. Please note in my example I did not use the API Token.

3. Create Point of Sale

Selected System Setting then Point of Sale. Click on the create button and fill out the required fields. In my case since I am using the Lighthouse Demo I called my POS LighthousePOS. Well I can’t remember if I created or it was already out there.

4. Implement code. (SXA Example)

The next step is to implement the script needed to integrate CDP with your site. The script should look like the following. As of this blog the target version should be 1.2 and s.src should be 1.4.8. You will need to add your client key.

// Define the Boxever queue
   var _boxeverq = _boxeverq || [];
   // Define the Boxever settings
   var _boxever_settings = {
       client_key: 'client key goes here', // Replace with your client key
       target: 'https://api.boxever.com/v1.2', // Replace with your API target endpoint specific to your data center region
       cookie_domain: '.lighthouse.localhost', // Replace with the top level cookie domain of the website that is being integrated e.g ".example.com" and not "www.example.com"
       pointOfSale: "LighthousePOS",
       web_flow_target: "https://d35vb5cccm4xzp.cloudfront.net"
};
   // Import the Boxever library asynchronously
   (function() {
        var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true;
        s.src = 'https://d1mj578wat5n4o.cloudfront.net/boxever-1.4.8.min.js';
        var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x);
    })();

I kept this pretty simple and for now created a script and put it under the Base Themes\Main Theme\Scripts folder. I created a script item called cdp-script.

5. Create an Experiment

From the menu expand Experiments and choose Web. Click on the Create Experiment button.

From there you want to click on the add variant button.

After you click on Add Variant there will be a side menu that will appear. Choose My Library Templates.

We will choose a simple popup takeover.

Change anything you want to. In this case I changed the background and font colors. You can also change HTML and script

Once done, make sure you click on the Save button then Close when you see the checkmark.

You will see the new Popup in a list. Click on the Preview button, enter in your site with the CDP script and click the go button. If everything worked correctly you should see the popup.

Once the site opens, in this case my local demo site you will see the popup created.

Another option to try is Experiences the setup is similar to Experiments. You can find on the same menu as Experiments.

This should get you started. I would take a look at what others have done since it is a public sandbox. There are a lot of possibilities with CDP and we are only scratching the surface. For more information on getting started please take a look at this video that was released as I was putting together this blog.

#Sitecore Lucene to SOLR Index Upgrade Tips

Recently I did a Lucene to SOLR upgrade. Even though SOLR has been out a while you may still run into instances that you may need to upgrade. This I hope gives you a small guide on some of the things you need to do other than installing the SOLR server.

1. Turn on switch and rebuild on indexes. You can find more information about that here. Don’t forget to add your custom indexes as well.

2. Convert string to string[]. I ran into an issue where a field was coming back with a null. Looking at the SOLR server and doing a query I found that the following would come back as null.

["27ae232e906349b083d612f2ebb7a173", "3fd1ef6aac954936bb8133d5f60ebfee", "9b8e4140b9fe4ecb99c41e0454b31b2e"]

Looking at it further found that the following code needed to be changed.

From:
  [IndexField("tags")]
  public string Tags { get; set; }
To:
  [IndexField("tags")]
  public string[] Tags { get; set; }

Basically the string needed to be a string array.

3. Make sure you check the class references.

From:
Sitecore.ContentSearch.LuceneProvider.LuceneSearchIndex, Sitecore.ContentSearch.LuceneProvider
To:
Sitecore.ContentSearch.SolrProvider.SolrSearchIndex, Sitecore.ContentSearch.SolrProvider

4. Don’t forget to rebuild the indexes and make sure everything you expect is there.

On a side not every time I hear SOLR I think of this movie.

#Sitecore Hackathon 2022 Adventure. #SCHackathon

Six years doing the Sitecore Hackathon and one thing I learned is each year brings a new challenge. First off I had a great team. Our team name was R-Bacon-Sulting. I worked with two co-workers from RBA. One being a front-end developer and the other being Devops. All we needed was a project manager and a business analyst to complete the team. We chose the third category for the hackathon which was to make an enhancement to the MVP site.

As hard as we tried the goal of adding to the MVP site did not work out since we had trouble getting the site up and running and ran out of time. We did have a great idea and hope to one day we can implement it.

The bottom line is we all learned something and as co-workers we got to know each other better. In the end isn’t this what this mainly is about?

So here we go with the summary of what I learned this year (in Ted Lasso speak) because let’s face it there is always room for improvement. That is why I love what I do.

Under pressure is when we learn that we really have what it takes. We may be in unfamiliar territory, but we adapt and get better because of it.
So many things happen when you don’t succeed. Life is full of surprises. We just have to go with the flow and hope for the best as we learn. Even if it is not the fairy tale you want it is the fairy tale that is best for you to grow professionally.
Yeah I love to code, but you know what else I love to do? I love to teach people about coding. What you learn is always crucial to pass it on to others. Glad I got the opportunity to pass on some Sitecore things to the team and I also learned things from them.
Everyone that participated in the Hackathon will remember the good and the bad. You know what though, next year we need to be a goldfish and approach it like it is something new and exciting. Forget about what went wrong, but focus on the task at hand.
Special shout out to all that put on the Sitecore Hackathon every year and our team from RBA.

Extending the BizFX Commerce Tools for #Sitecore Commerce for Simpletons

Sitecore commerce has some really great features and hidden gems. Recently I was given an assignment to create some custom forms for the BizFX tools that come with Sitecore commerce. Excited as I was to get started I found there was not too much documentation on this. I had some help thanks to Andrew Sutherlands blog. There were things though that I ran in to that I had to figure out how to get through. So I want to make sure I can help others who may get into the same situation as me and also if I ever need to to extend the tools again I can look back at this blog and it will help me figure some of the things out.

Understanding the Master Form and Children

Basically you have a master view. This is usually a summary of your records. Clicking on that master form will show its children views. The children could be any type of detail block of information. So lets say you have a summary of services as your master. The children view when breaking it down further could be notes and description, service dates, vendor information etc… The code below is an example on how to add children view(s) to a master view. You need to make sure you are on the correct master view.

[PipelineDisplayName(Constants.Blocks.GetServicesItemsBlock)]
    public class GetServicesItemsBlock : GetTableViewBlock
    {
        private readonly IServiceService _servicesService;

        public GetServicesItemsBlock(IServiceService servicesService)
        {
            _servicesService = servicesService;
        }

        public override Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
        {
            Condition.Requires(entityView).IsNotNull($"{this.Name}: The argument can not be null");

            var entityViewArgument = context.CommerceContext.GetObjects<EntityViewArgument>().FirstOrDefault();
            if (entityViewArgument == null || string.IsNullOrEmpty(entityViewArgument.EntityId)) return Task.FromResult(entityView);

            if (!EntityViewExtensions.IsOnServicesView(entityViewArgument)) //Make sure you are on the correct master view
            {
                return Task.FromResult(entityView);
            }
            
            var entityId = long.Parse(entityViewArgument.EntityId.Substring(entityViewArgument.EntityId.LastIndexOf('-') + 1));
            var services =  _servicesService.GetServiceById(entityId);

            EntityView subView = new EntityView
            {
                EntityId = entityId.ToString(), Name = Constants.Headers.Items, UiHint = "Table"
            };

            entityView.ChildViews.Add(subView);

            var servicesItemList = services.Result.ServiceItems;
            if (servicesItemList == null) return Task.FromResult(entityView);

            foreach (var subitem in servicesItemList)
            {
                EntityView lineView = new EntityView
                {
                    EntityId = subView.EntityId, ItemId = services.Result.OrderId, Name = Constants.Headers.Items
                };

                lineView.AddServiceItemChildView(subitem);
                subView.ChildViews.Add(lineView);
            }

            return Task.FromResult(entityView);
        }
    }

For the master you will have code like this if you need to change the properties:

//Current entity which is the the master view is passed in to the block.
public override async Task<EntityView> Run(EntityView arg, CommercePipelineExecutionContext context)

var status = entityViewArgument.ViewName.Replace("ServicesList-", string.Empty);
            arg.Properties.Add(new ViewProperty
            {
                Name = "ListName",
                RawValue = status,
                IsReadOnly = true,
                IsHidden = true,
                IsRequired = false
            });
            arg.UiHint = "Table";
Example Summary

To sum it up the master/child view structure is simple. You have your master view, but then you can add children views and those children can have children views.

For the children you want to generate a new view.

It would be done in this order (see code for reference):

  1. Get the master view.
  2. Create a subview (child view).
  3. Create and add children to the subview.
  4. Add subview to the master entity view.
using System.Linq;
using System.Threading.Tasks;
using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Framework.Conditions;
using Sitecore.Framework.Pipelines;

namespace PSP.Commerce.Plugin.Service.Pipelines.Blocks
{
    ///<summary>
    ///displays the service items view in BizFx/services.
    ///</summary>
    [PipelineDisplayName(Constants.Blocks.GetServicesItemsBlock)]
    public class GetServicesItemsBlock : GetTableViewBlock
    {
        private readonly IServiceService _serviceService;

        public GetServicessItemsBlock(IServiceService serviceService)
        {
            _serviceService = serviceService;
        }

        public override Task<EntityView> Run(EntityView entityView, CommercePipelineExecutionContext context)
        {
            Condition.Requires(entityView).IsNotNull($"{this.Name}: The argument can not be null");

            var entityViewArgument = context.CommerceContext.GetObjects<EntityViewArgument>().FirstOrDefault();
            if (entityViewArgument == null || string.IsNullOrEmpty(entityViewArgument.EntityId)) return Task.FromResult(entityView);

            if (!EntityViewExtensions.IsOnSubcriptionsView(entityViewArgument))
            {
                return Task.FromResult(entityView);
            }
            
            var entityId = long.Parse(entityViewArgument.EntityId.Substring(entityViewArgument.EntityId.LastIndexOf('-') + 1));
            var service=  _serviceService.GetServiceById(entityId);

            EntityView subView = new EntityView
            {
                EntityId = entityId.ToString(), Name = Constants.Headers.Items, UiHint = "Table"
            };

            entityView.ChildViews.Add(subView);

            var serviceItemList = service.Result.ServiceItems;
            if (serviceItemList == null) return Task.FromResult(entityView);

            foreach (var subitem in serviceItemList)
            {
                EntityView lineView = new EntityView
                {
                    EntityId = subView.EntityId, ItemId = service.Result.OrderId, Name = Constants.Headers.Items
                };

                lineView.AddServiceItemChildView(subitem);
                subView.ChildViews.Add(lineView); 
            }

            return Task.FromResult(entityView);
        }
    }
}

Example child item.

There are many field types you can use for views, but two of them that stood out for me was the EntityLink and HTML field types.

Below is an example of an link type. Which we can create to navigate to other types of views. The Id for instance could be used to retrieve records for the child views.

var idViewProperty = new ViewProperty
                {
                    Name = "Id",
                    RawValue = service.Id,
                    IsReadOnly = true,
                    UiType = "EntityLink"
                };
                entityView.Properties.Add(idViewProperty);

Below is an example of using a UitType of Html to create an image column value.

  var imageLinkProperty = new ViewProperty
            {
                Name = "Image",
                RawValue =  "<a><img src="+ serviceItem.ImageUrl +" alt=" + serviceItem.Name + " width=\"50\" height=\"50\"></a>", //MediaManager.GetMediaUrl(serviceItem.ImageUrl),
                IsReadOnly = true,
                UiType = "Html"
            };
            properties.Add(imageLinkProperty);

Last thing I want to mention is action view buttons. You can easily defined them (see below).

            ActionsPolicy actionsPolicy = entityView.GetPolicy<ActionsPolicy>();
            List<EntityActionView> entityActionView = actionsPolicy.Actions;
            EntityActionView skipEntityActionView = new EntityActionView
            {
                Name = context.GetPolicy<KnownServiceCancelPolicy>().CancelService,
                DisplayName = Constants.Actions.CancelService,
                Description = Constants.Actions.CancelService,
                IsEnabled = true,
                RequiresConfirmation = true,
                EntityView = string.Empty,
                Icon = "delete"
            };
            entityActionView.Add(skipEntityActionView);

            List<EntityActionView> actions2 = actionsPolicy.Actions;
            EntityActionView cancelEntityActionView = new EntityActionView
            {
                Name = context.GetPolicy<KnownServiceSkipPolicy>().SkipService,
                DisplayName = Constants.Actions.SkipService,
                Description = Constants.Actions.SkipService,
                IsEnabled = true,
                RequiresConfirmation = true,
                EntityView = string.Empty,
                Icon = "hand_stop2"
            };
            actions2.Add(cancelEntityActionView);

Here are the results you will see in the view.

Hope this helps you if you are finding this blog. If not please contact me. Thanks.

#Sitecore Hackathon 2021 The Good and Some Bad. #SCHackathon

Hard to believe this was my fifth year doing the Sitecore Hackathon. It has become a tradition though and would not want to miss it. This year my teammate was a co-worker and we were team Alpha Centauri. Get it? We both work for Alpha Solutions. Anyway here is how it went.

Keep Things Simple

Learned this many times in the past. So anything that wasn’t out of the box was off the table. Nothing wrong with trying something new, but as a marathoner I have to stick to the important rule of not trying anything you haven’t practiced on race day.

Well I Missed Something

So I should of read this more closely. We were supposed to use Sitecore 10.1, but for some reason I installed Sitecore 10 update 1 beforehand. So guess what? I broke my own rule and just learned something new. It was McAvoy vs Stewart. Love the reskin BTW.

Some Great Topics to Choose from, but We Need a Solid Idea

This year was the fastest year my hackathon team came up with an idea. One of the topics was Best of SXA. Working with SXA everyday we had come up with a much needed change we felt is needed.

Divide and Conquer

As in past hackathons that I have worked with others, it is always good to come up with roles on the team. I would handle the documentation/video and my teammate would handle the coding. And we were off.

The Finale

After lots of time spent coding and documenting we put something together. Hardest thing for me was getting the video right. There is a reason why I am not a YouTube star. So this is what we came up with.

So that is a wrap. See you all next year!

Basic Authentication with #Sitecore 9.3

A few months back I was given a task to put in basic authentication into Sitecore 9.3. It was mainly from preventing anyone to get into staging sites. I came across and older blog that is currently missing. I wanted to give them credit since it was the inspiration for this blog. You can find the original blog post on the web archives here. I have made some of my own updates including Rules Based Configuration.

using System;
using System.Web;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.HttpRequest;
using System.Text;
using System.Net.Http.Headers;
using System.Linq;

namespace Abc.SharedSource.SitecoreProcessors
{
    public class BasicAuthentication : HttpRequestProcessor
    {
        private bool CheckPassword(string username, string password)
        {
            string[] userlist = Sitecore.Configuration.Settings.GetSetting("BasicAuthUsername").Split(',');
            string[] passwords = Sitecore.Configuration.Settings.GetSetting("BasicAuthPassword").Split(',');

            if(userlist.Contains(username) && passwords.Contains(password))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private void AuthenticateUser(string credentials)
        {
            try
            {
                var encoding = Encoding.GetEncoding("iso-8859-1");
                credentials = encoding.GetString(Convert.FromBase64String(credentials));

                int separator = credentials.IndexOf(':');
                string name = credentials.Substring(0, separator);
                string password = credentials.Substring(separator + 1);
             
                if (!CheckPassword(name, password))
                {
                    HttpContext.Current.Response.StatusCode = 401;
                }
            }
            catch
            {
                HttpContext.Current.Response.StatusCode = 401;
            }
        }
        //Basic Auth Code End

        public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            if (Sitecore.Context.Item != null || Sitecore.Context.Database == null || args.Url.ItemPath.Length == 0)
                return;

            if (Sitecore.Configuration.Settings.GetSetting("TurnonBasicAuth") != "True" || Sitecore.Configuration.Settings.GetSetting("TurnonBasicAuth") == "") return;
            if (PatternMatch()) return;
            var request = args.HttpContext.Request;

            var authHeader = request.Headers["Authorization"];
            if (authHeader != null)
            {
                var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);

                // RFC 2617 sec 1.2, "scheme" name is case-insensitive
                if (authHeaderVal.Scheme.Equals("basic",
                        StringComparison.OrdinalIgnoreCase) &&
                    authHeaderVal.Parameter != null)
                {
                    AuthenticateUser(authHeaderVal.Parameter);
                }
            }
            else
            {
                args.HttpContext.Response.StatusCode = 401;
            }

            if (HttpContext.Current.Response.StatusCode == 401)
            {
                string Realm = Sitecore.Context.Site.TargetHostName;//HttpContext.Current.Request.Url.AbsoluteUri;
                args.HttpContext.Response.Clear();
                args.HttpContext.Response.Headers.Add("WWW-Authenticate",
                    string.Format("Basic realm=\"{0}\"", Realm));
                args.HttpContext.Response.Flush();
                args.HttpContext.Response.End();
            }
        }
        bool PatternMatch()
        {         
            string[] mockUrls = Sitecore.Configuration.Settings.GetSetting("ExcludedPaths").Split(',');
            string url = Sitecore.Context.Site.TargetHostName;// HttpContext.Current.Request.Url.AbsoluteUri;
            foreach (var urlval in mockUrls)
            {
                var containsurl = url.Contains(urlval);
                if(containsurl)
                {
                    return true;
                }
            }
            return false;
        }
    }
}

Since this is Sitecore 9.3 the configuration below is using rules based. 🙂 More than likely you would want to require the ContentManagement role, but you can modify the configuration to use any roles and environments. I put the username and settings in the configuration since Sitecore will also have its own and in this case is only for preventing anyone who accidently finds the site from seeing anything.

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:localenv="http://www.sitecore.net/xmlconfig/localenv/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore role:require="ContentManagement, Standalone">
    <pipelines localenv:require="DevBuild or LocalDeveloper">
      <httpRequestBegin>
        <processor type="Abc.SharedSource.SitecoreProcessors.BasicAuthentication, BrookfieldResidential.Extensions"
						   patch:before="processor[@type='Sitecore.Pipelines.HttpRequest.UserResolver, Sitecore.Kernel']"/>
      </httpRequestBegin>
    </pipelines>
    <settings localenv:require="DevBuild or LocalDeveloper">
      <setting name="TurnonBasicAuth" value="True"></setting>
      <setting name="ExcludedPaths" value="media,layouts,speak,/sitecore,/sitecore/admin,brpsc.dev.local/about" />
      <setting name="BasicAuthUsername" value="UserTest1,TestUser2,TesUser3" />
      <setting name="BasicAuthPassword" value="testpass1,testpass2,testpass3" />
    </settings>
  </sitecore>
</configuration>

You should then get the default login screen that looks like this:

#Sitecore Virtual Symposium Kickoff and My Plans #SitecoreSYM @AlphaSolutionUS

2020 sure has had its changes and challenges. Right now Chicago should be alive with Sitecore fans enjoying the best pizza in the world, but that is not possible this year. I am excited nonetheless to enjoy they symposium in the Chicago suburbs and can’t wait for the next few days.

My goal for this year is to concentrate on content marketing and learn as much as I can about the Sitecore Content Hub. That is something I have been excited to learn more about and can’t wait to dive in.

Things kicked off tonight with Happy Hour. I will let the pictures speak, but there was drink making, a chef and magic. Most of all enthusiasm.

Happy Hour
Making Drinks
Some great appetizers.
Magic!
Da Bears! Okay they were not part of the kickoff, but they are playing tonight.

Disabling Identity Server in #Sitecore Installation with #PowerShell

Recently I was given the task to disable the identity login for a dev server. It can be done easily by renaming Sitecore.Owin.Authentication.Disabler.config.example and Sitecore.Owin.Authentication.IdentityServer.Disabler.config.example in the [sitefolder]\App_Config\Include\Examples\ folder. We needed an automated way though. Using the PowerShell script below did the trick.

#SitePhysicalRoot and Prefix are optional. If this script is inserted into a PowerShell install script that has these variables already. In my case XP0-SingleDeveloper.ps1.
#$SitePhysicalRoot = "F:\Sites"
#$Prefix = "testsite123"

$filepath = $SitePhysicalRoot + '\' + $Prefix +'.sc\App_Config\Include\Examples\'
$filelist = @()
$fn1 = 'Sitecore.Owin.Authentication.Disabler.config.example'
$fn2 = 'Sitecore.Owin.Authentication.IdentityServer.Disabler.config.example'
$path1 = $filepath + $fn1
$path2 = $filepath + $fn2
$filelist = @($path1,$path2)

 foreach ($file in $filelist) {
    If (Test-Path $file){
        $rn = $file.replace(".example","")
        Rename-Item -Path $file -NewName $rn
        Write-Output $file was renamed to $rn
    } else {
    "{0} does not exist or already renamed" -f $file
    }
}

#Sitecore Rule Based Configuration for Newbies

If you are like me, you must play around with a new feature to understand it better. When Rule Based configuration was introduced in Sitecore 9.3 that was the case for me. Now that I understand it better and have used it in a real application, I will explain it so if I ever read this blog again, I will understand it.

cavemanrules

Role:Define

Depending on if you are running Sitecore locally or have setup a Content Management and Content Delivery server etc… this value will be different for each environment. In the Sitecore 9.3 you will find the following.

<!-- SUPPORTED SERVER ROLES 
Specify the roles that you want this server to perform. A server can perform one or more roles. Enter the roles in a comma separated list. The supported roles are:
ContentDelivery
ContentManagement
ContentDelivery, Indexing
ContentManagement, Indexing
Processing
Reporting
Standalone
Default value: Standalone
-->

In this case we will just use the default for our example.

<add key="role:define" value="Standalone"/>

Localenv:Define

This is used to denote the type of local environment installed. For instance, you can have a CM server setup for staging and production. You might then have one value on the CM for staging that says “StageCM” the one on the production CM could be “ProdCM”. This is done so you can break down the configuration settings further. I will go into this more later. In this case we will pretend the role:define value is Standalone and this is a developer’s install. We are going to add the following:

<add key="localenv:define" value="DevEnviroment"/>

Let’s bring this together. Below is a simple example of how to make sure the configuration looks for the role Standalone and localenv value of DevEnviroment. Notice the xmlns:localenv=http://www.sitecore.net/xmlconfig/localenv/. That is important to tell the process to look for that localenv key and xmlns:role=http://www.sitecore.net/xmlconfig/role/ is used to tell the process to look for the role key. In this example we are doing this for sites, but this can be done for other tags.

<?xml version="1.0"?>
<configuration
xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:localenv="http://www.sitecore.net/xmlconfig/localenv/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore role:require="Standalone">
<sites localenv:require="DevEnviroment">
//Add your transformation code here.
</sites>
</sitecore>
</configuration>

You can do many combinations. Keep in mind that you can use logical expressions as well. Like or and !.

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"xmlns:localenv="http://www.sitecore.net/xmlconfig/localenv/"xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore role:require="Standalone or ContentDelivery">
<sites localenv:require="LocalDeveloper">
//Add your transformation code here.
</sites>
<sites localenv:require="DevBuild"> //Add your transformation code here. </sites> <sites localenv:require="Prod"> //Add your transformation code here. </sites> <sites localenv:require="Stage"> //Add your transformation code here. </sites> </sitecore> <sitecore role:require="ContentManagement"> <sites localenv:require="Prod"> //Add your transformation code here. </sites> <sites localenv:require="Stage"> //Add your transformation code here. </sites> </sitecore> </configuration>

What is a good way to test all this? Well Sitecore has though of that.

Using [siteaddress]/sitecore/admin/showconfiglayers.aspx you can select a role and see what the configuration will look like. The result is similar to what you would see with showconfig.aspx.

One thing I should mention is so far, I have not gotten it to work with a non-Sitecore configuration transform. In those cases, you may still need to use something like SlowCheetah. Let me know if you have any questions.

Check out the links below for more reading on the rule based configurations.

https://doc.sitecore.com/developers/93/platform-administration-and-architecture/en/use-a-rule-based-configuration.html

https://jammykam.wordpress.com/2017/10/17/rules-based-configuration/

#Sitecore Data Exchange Framework Gems

There are a lot of things I have found with each version of the Sitecore Data Exchange Framework that I wanted to explore, and others have asked me about. I will call them gems. These are things that might not always be used but can serve a good purpose I believe. I also wanted to get a higher understanding of what they do so here we are. I hope to go into more detail for each one in other blogs.

Telemetry Enabled

With telemetry enabled checkbox checked you can track the performance of each process. This information can be useful to IT support group as well as developers needed to know how the DEF process performs so adjustments can be made to processes/hardware for instance.

Create New Item

In the checkbox option below, it is defaulted as unchecked. Normally based on a unique field value a new item would be created when unchecked after checking if an existing item exists. Checking it the item will be only created in memory. There could be a variety of reasons to have an item in memory only. One of which would be for testing, it saves clean up time when you just need to test processing without worrying about getting rid of the Sitecore items.

Runtime Settings

This setting allows the pipeline step to be run on a different server. This saves resources on the Sitecore server. I can see this used for something where a lot of data is involved from a separate data source and/or if you finish extracting data from Sitecore another system can pick it up and continue. The DEF process can wait for that to be done and continue.

Verification

I have not used this, but from what I understand xDB analytics data migration would be logged here if you have a DEF process that does that. If you do not, make sure not to check any of the options. It can cause the DEF process not to work.

There are some more little gems I am going to look at for DEF in another blog. Also like I said before I am going to try and expand and go into more detail on each one of these. Let me know what you would like to see and I will try and cover it.