Saving Sitecore 9 Forms in Multiple Locations (other than the default)

If you’ve worked with Sitecore 9 Forms recently, you’ve probably come to know that all forms are saved under a default location, which is ‘/Sitecore/Forms’. This is fine if you have a single site on your instance, but when you get into a multi-site implementation, this gets tricky because you want to separate the folders for the forms for each site – for multiple reasons, a major concern being security, and information architecture in general.

There have been a number of issues reported and resolved by fellow MVPs Jason and Toby. Jason found an issue where the forms don’t show up because it didn’t get added to the index. The second issue Toby found is more related to this post, as it sheds some light on how Sitecore knows where the forms should get saved. It turns out that the item ID of the default forms folder that comes out of the Sitecore installation is stored in an item in the Core database – I believe this value is used to configure the location to index.

Now that we know where the search index looks to index the forms, I got the idea that it reads this value at some point in the SPEAK application that the Forms Designer uses. At this point I figured I could override where it reads this, but I still didn’t know where. I’m not very fluent in SPEAK (yet), so my next approach was to try to investigate what code saves the forms.

After some chrome debugging and dotpeek, I found the web API that gets called:


…and resulting pipeline that saves the forms:

        <processor type="Sitecore.ExperienceForms.Client.Pipelines.SaveForm.CreateModels, Sitecore.ExperienceForms.Client" resolve="true" />
        <processor type="Sitecore.ExperienceForms.Client.Pipelines.SaveForm.GenerateNames, Sitecore.ExperienceForms.Client">
          <defaultItemName>Form Item</defaultItemName>
       <processor type="Sitecore.ExperienceForms.Client.Pipelines.SaveForm.UpdateItems, Sitecore.ExperienceForms.Client" resolve="true" />

The pipeline receives JSON post data from the call, that looks something like this:

You can see here that the JSON data being passed has a bunch of models – they aren’t in any sort of heirarchy, except for sortOrder but that’s really just to make sure the order is correct for the form elements. Each model has a template ID – for the type of item it is, and parent ID – the location where it should be saved. You can also see here that the parentId field of the first model has the magic value – the item ID of the forms folder (‘/Sitecore/Forms’) which is also in the SearchConfig item in the Core database. Now that we know that, all we need to do is override that value. You can do this in the SPEAK application if you are more adept in SPEAK, but I decided to do in the pipeline.

First things first – you can either use the default forms folder and make sub-folders for each site, or you can make a brand new folder and make sub-folder for each site under the new folder. If you do the latter, you must update the SearchConfig item (as noted in Toby’s post) with the item ID of the folder you created.

Next, you can make a sub-folder for each site under the root folder. You probably want to put this in a config setting somewhere, different for each site.

The rest is easy – write a class to set the parentId of the model that contains the information for the main form element, to the item ID of your forms folder:

using Sitecore.Diagnostics;
using Sitecore.ExperienceForms.Client.Models.Builder;
using Sitecore.ExperienceForms.Client.Pipelines.SaveForm;
using Sitecore.Mvc.Pipelines;
using System.Linq;

namespace Custom.Pipelines
    public class UpdateParentLocation : MvcPipelineProcessor<SaveFormEventArgs>

        public override void Process(SaveFormEventArgs args)
            Assert.ArgumentNotNull((object)args, nameof(args));
            if (args.ViewModelWrappers == null)

            //Only look for the model that has the form item (searching by template ID)
            ViewModelWrapper vm = (ViewModelWrapper)(from v in args.ViewModelWrappers
                                                     where v.Model.TemplateId.ToLower() == "{6ABEE1F2-4AB4-47F0-AD8B-BDB36F37F64C}".ToLower()
                                                     select v).FirstOrDefault();

            if (vm == null)

            //Use whatever logic is neccessary to set where the form should be saved
            vm.ParentId = Sitecore.Configuration.Settings.GetSetting("FormsLocationRoot");


Put whatever logic you need to in the above set statment to determine the location where the forms should be saved. It could be based on logic that results in creating more folder, etc, but that’s upto you.

Note: There are multiple models being passed, starting with the main form, then all the form elements. Because each form element is a child of the parent, you must be careful not to set the parentID of ALL the models. Which is why we are looking only for the model that has the templateID of the form.

Make a patch config file to insert this step right before the items are saved/updated, and you should be all done.

<configuration xmlns:patch="" xmlns:role="">
  <sitecore role:require="ContentManagement">
        <processor patch:before="processor[@type='Sitecore.ExperienceForms.Client.Pipelines.SaveForm.UpdateItems, Sitecore.ExperienceForms.Client']" type="Custom.Pipelines.UpdateParentLocation, Custom.Pipelines" />

Addendum: I *think* it would also be possible to save the forms under multiple root folders, but then the indexes would need to be reconfigured to look in multiple locations – being that there is only one item for the SearchConfig item, it would need to get customized to handle a string of item IDs , or something of that sort.

I hope future updates to Sitecore 9 Forms will take multi-site implementations into account, but until then, this pipeline should help!

#Protip: SOLR throws java.lang.OutOfMemoryError when re-indexing Sitecore Indexes

As you may know, Sitecore 9 has it’s default search engine configured with SOLR, instead of Lucene. This requires you to install SOLR, in order for Sitecore 9 to work. There are some really informative, thorough posts on how to accomplish this, so I won’t go into that.

What I wanted to focus on is when you setup the SOLR to run as a windows service. The default command of running the SOLR command:

solr.cmd -f -p 8983

…sets the default memory allocation for the SOLR heap, which is 512MB. This is because we don’t actually set a parameter for the heap size. Depending on the content you have in your Sitecoreinstance, you may get java.lang.OutOfMemoryError errors when re-indexing. To fix this, you need to explicitly specify the heap size, which you set as such:

solr.cmd -f -p 8983 -m 1024m

This sets it to 1GB. You can adjust as needed – how much you need will depend on your situation – this article has some pointers on how to decide the size. This should allow you to re-index your large Sitecore indexes.

Securing your SOLR Instance for Sitecore

If you didn’t know already, SOLR is the default search engine that comes with Sitecore 9, and presumably future versions. The choice to use SOLR instead is very obvious – SOLR is scalable, while lucene is not. SOLR is more fault-tolerant, and can be load-balanced for failover, and thus resulting in a more reliable environment. This also means that Sitecore developers are going to need to know more about managing SOLR. Fortunately, there is plenty of information out there to learn about SOLR, so we aren’t totally in dark.

Security being an important factor in most infrastructures, securing the SOLR instance when being used with Sitecore is an important topic. The default installation for SOLR is open for anonymous visits, so I’m going outline some steps to make it less easy to get into the SOLR instance.

Some simple steps to take:

  1. As mentioned, by default, the SOLR instance is open. First thing you can do is to lock it down by IP, so only your Sitecore instances can see them (CM, CD, xConnect, etc.). This is simple enough and can be done without much effort.
  2. Make sure the SOLR instance is internal (i.e. behind a firewall). SOLR instances for Sitecore does need to be accessed by public visitors, so there is no need for it to be exposed outside your internal network.
  3. Add SSL to your SOLR instance. There are various ways to do this, and a lot of the documentation refers to using a self-signed cert. If you are running SOLR on apache, you’ll need to generate the java keystore with your real SSL certificate (make sure you have the .pfx file, which has both the public and private keys). If you are running SOLR on windows, you can use the .pfx file directly.
  4. Add Basic Authentication

The last step is the most involved and requires a small change in Sitecore as well. It enables basic authentication on SOLR, so Sitecore will need to authenticate to access SOLR. To do this, you’ll need to do the following.

Enable Basic Authentication

Add a new file, security.json to your SOLR instance with the below code – save this file in [path to solr]\server\solr.

   "credentials":{"solr":"IV0EHq1OnNrj6gvRCwvFwTrZ1+z1oBbnQdiVC3otuq0= Ndd7LKvVBAaZIF0QAVi1ekCfAJXr1GGfLtRUXhgrF8c="}

So what does this file do?

  • Enables authentication and authorization
  • A user called solr is created with a password SolrRocks – note that we have to add it so at least you have one user. You can change the password later.
  • The user solr is assigned a role admin
  • The permissions to edit security is now restricted to the role admin. All other resources are unprotected and the user should configure more rules to secure them.

You can very granular with the security rules – I wasn’t able to find all the possible permissions, but there is a short list here.

Make sure Basic Authentication works

Once you do the above, your SOLR instance should be not accessible without logging in anymore. Restart the SOLR service, and give it a try, and you should see this screen:

If you use the solr/SolrRocks credentials, you should be able to get into your SOLR instance.

Configuring Sitecore to use the credentials

At this point, Sitecore can’t access the SOLR instance, so your instance is probably not working correctly. You’ll need to add the credentials to the configs:


Comment this out:

<solrHttpWebRequestFactory type="HttpWebAdapters.HttpWebRequestFactory, SolrNet" />

And add this:

<solrHttpWebRequestFactory type="HttpWebAdapters.BasicAuthHttpWebRequestFactory, SolrNet">
        <param hint="username">solr</param>
        <param hint="password">SolrRocks</param>

…right before:


Note: Don’t edit the config file directly, ya’know – best practice – make an include config patch file.

Manage Users

Now that Sitecore is able to authenticate to access SOLR, you should change the default passwords. The easiest way to do this is via the REST API that SOLR has, and the easiest way to access that is via curl. Once you get this downloaded, open up the command prompt and fire out the command to change the password for the ‘solr’ user:

curl --user solr:SolrRocks https://localhost:8983/solr/admin/authentication -H "Content-type:application/json" -d "{ \"set-user\": {\"solr\" : \"MyNewPassword\"}}

You can also add new users:

curl --user solr:Password.1 https://localhost:8983/solr/admin/authentication -H "Content-type:application/json" -d "{ \"set-user\": {\"newuser\" : \"newpassword\"}}

Note that you have to change admin password for the ‘solr’ in the authentication request, if you changed it prior to running commands

If your request is successful, you should see the security.json file in [path to solr]\server\solr change. The encrypted password in the file should have changed. You should be able to use these credentials without restarting SOLR.

#Bug: Rich Text Editor Insert Sitecore Link wipes out css classes

I found this bug in Sitecore content editor the other day – in the Rich Text Editor, if you are editing an HTML and want to edit a link to point to a Sitecore Link, it will wipe out any existing css classes on your link.

So, something like this:

<a href="http://mylink" class="test">This is the Text</a>

Will become like this:

<a href="http://mynewlink">This is the Text</a>

I figured that the custom command that edits this is not saving any other attributes of the link – the command is in \sitecore\shell\Controls\Rich Text Editor\RichText Commands.js; I opened a support ticket and Sitecore Support managed to fix this for me pretty quickly. It has been registered as a bug, and the fix is available here:

Note: This has been tested in Sitecore 9 update 1

Addendum: Sitecore 9.1 Infrastructure Roles

Sitecore 9.1 was released recently after the Sitecore Symposium 2018, and there are a lot of goodies to be found. Pieter Brinkman has a really good series on all the new features of Sitecore 9.1.

I want to add a little bit of addendum to my original post for all the Sitecore 9 Infrastructure roles. If you visited some of the technical talks at the Symposium, you’ll see a pattern towards breaking a monolithic architecture into a much more manageable micro component architecture. This means some of the functions from the main application will get split out. If you look at the recently updated documentation, you’ll find that there are now a LOT of infrastructure roles. Note – my earlier blog post only refers to application roles. You’ve already seen this initiative in the case of xConnect and the publishing service (and various other cases), which are both separate roles, and are slated to run independently of the main CM or CD instance.

Sitecore Identity Server

Sitecore 9.1 continues this trend, and introduces a new application role, called the Identity Server. The Identity Server is a separate ASP.NET Core application that manages the authentication for Sitecore. After installing 9.1, you’ll see that when you go to log into Sitecore CM, you’ll get redirected from https://{instanceName}/sitecore to https://{instanceName}-IdentityServer.

The beauty of this is that now authentication delegated into a separate role, and we can now use this role to set up SSO (Single Sign-On) across Sitecore services and applications. You can use the SI server is based on OpenID Connect-compliant security token service and can manage/update/refresh tokens. These tokens can then be used to manage authetication for Sitecore Services. This will also allow users to sign into various sites and services that are hosted separately even when you do not have a running instance of Sitecore XP.


From an infrastructure planning perspective, this is a minor change. If you’re on Azure, this is another app service. If you are on-premise, then this is another IIS Site. This can be scaled separately from the other roles if you plan to use it extensively. Otherwise, it has a pretty small footprint. This is a benefit of the split up from the monolithic architecture, otherwise, you would need to scale out the whole CM/CD role just to scale out maybe a single function.

All in all, I think this is a great direction for Sitecore to go into. Initially, it can be overwhelming, but once you understand the roles, it is much easier to manage.

Sitecore 9 Infrastructure Roles

Not to be confused with Server Roles that are required for Configuration, this post is about all the different elements (or infrastructure roles) required to run an instance of Sitecore 9 Experience Platform. Whether you are in the cloud, or on-premise, these roles are all neccessary (albeit they run differently on-premise vs on the cloud) for all the features of the Experience Platform to work.

Content Management and Content Delivery

These are obvious and unchanged – can be scaled up as necessary, and their functions continue to be the same.

Processing and Reporting

Processing servers are responsible for aggregating collected data into a format that is suitable for reporting. It is responsible for processing all the xDB data, and putting it into the reporting database and putting it into a format where it can be reported on. It is another Sitecore instance, configured to serve the processing role.

Reporting servers are primarily used as the instance to get all the data for displaying analytics. Whenever an author/content editor goes into the content management to see data such as Experience Analytics, or similar analytics, the CM server then uses the Reporting API to access the aggregated data that was processed by the Processing server.


Brand new for Sitecore 9, xConnect is a middle layer that sits between the xDB and CM/CD, effectively acting as the abstraction layer for the collection of all xDB data. In addition, it’ open to be used by any trusted client to be able to interact with xDB, so you’re not limited to having on Sitecore data in xDB. It has multiple APIs, and a different set of databases. In short, it is comprised of:

  1. The xConnect Collection service
  2. The xConnect Search service
  3. The xConnect Search Indexer
  4. The Collection databases

This is too short of a topic to cover in a blog post, but there is some fantastic documentation on it in the xConnect developer center documents.

Marketing Automation Service

The Marketing Automation Worker Service is a standalone service (windows server on-premise, web job in Azure) that is responsible for going through the marketing automation plans and enrolling contacts that meet the criteria for those plans. You can install this on any server that has connectivity to the processing pools.

xConnect Search Indexer

The xConnect Search Indexer is a standalone server (windows server on-premise, web job in Azure) that is responsible for adding xDB data into the search index for easy access. A lot of the data is automatically added, but this service primarily adds contact and interaction data, and runs on schedule to check for updates.


Sitecore 9 supports SQL Server 2016 SP1 for it’s the main content databases, xConnect and xDB databases, and MongoDB is not a requirement. In fact, currently, there is no support for MongoDB, but it is forthcoming.

That’s about all the infrastructure elements needed to run a full Sitecore 9 Experience Platform. There are scaled out and scaled down versions of these, which I will cover in the next post.

A Not So Quick Sitecore Search Primer: Part 2

After a quick foray into Sitecore Search, I decided to jot down a more thorough step-by-step of the things needed to make Sitecore Search from start to finish. All this stuff is probably very well known by Sitecore veterans, but I wanted to get something down for somebody just starting to venture into Sitecore Search. In the Part 1, we setup the index. In this post, we’ll write the code for the search. Please note: I’ve abandoned using Lucene for searches since then and use SOLR now. So the indexing setup will be different, but the search mechanism is more or less the same. This is a big benefit of Sitecore Search.

After the Index

Once the index is setup, the basics of searching using Content Search is pretty simple – you literally call the index and query based on the Content field:

using (var context = ContentSearchManager.GetIndex("indexname").CreateSearchContext())
   IQueryable query = context.GetQueryable().Where(p =&gt; p.Content.Contains("someterm"))

   foreach(SearchResultItem sri in query)


Advanced Stuff

The above obviously is not enough for anyone to get by with – even the most basic search requirements are more complex than that.

Basic Field Mapping

One of the first things that will be needed is to map your items entities to the index document entity. SearchResultItem is the base object that Sitecore Content Search provides – this maps to a basic Lucene document.

If you want to search by specific fields other than content (for filters, etc), it would be a good idea to extend this class:

public class ResourceSearchResultItem : SearchResultItem

After that, you can add your own fields from your item templates that you indexed using the IndexField attribute:

public string Title { get; set; }

This works pretty straightforward for string fields – for any multilist values, you have to make sure you defined your property as IEnumerable so that you can get the facets from the search result list.

Computed Fields

If you want a custom value for a field (computed), you will need to create a computed field and then add that to the index, and use that field name for the attribute:

public string FieldString { get; set; }

The computed field class is very simple as well, implemented using IComputedIndexField.

public class FieldContentField : IComputedIndexField
        public string FieldName
                return "FieldContent";

        public string ReturnType
                return "String";
                throw new NotImplementedException();

        public object ComputeFieldValue(IIndexable indexable)
            Assert.ArgumentNotNull(indexable, "indexable");
            string url = null;
                Item item = indexable as SitecoreIndexableItem;
                if (item == null)
                    return null;

                //using the item, you can run business rules and return whatever value you need here
            catch (WebException webExc)
                //log error
            return null;

Once done, it needs to get added to the index as a computed field:

<fields hint="raw:AddComputedIndexField">
     <field fieldName="fieldcontent">FieldContentField, AssemblyName</field>

Once added, you can now use your own class to do searches:

using (var context = ContentSearchManager.GetIndex("indexname").CreateSearchContext())
   IQueryable query = context.GetQueryable().Where(p => p.Title.Contains("someterm"))

   foreach(ResourceSearchResultItem sri in query)



If you are only searching on one field, the above will work fine. If you want to search on multiple fields, you would need build a predicate.

using (var context = ContentSearchManager.GetIndex("indexname").CreateSearchContext())
   //Create an initial predicate - use .True<T> since we'll be AND'ing this clause together 
   var filterPredicate = Sitecore.ContentSearch.Linq.Utilities.PredicateBuilder.True<ResourceSearchResultItem>();
   filterPredicate = filterPredicate.And(x => x.ResourceType == "someValue");


The above is to chain a string of ‘AND’s. To string together a list of ‘OR’s, you would start with a .False and then string together .Or

If you want to have nested conditions, you’ll need to create a new Predicate, and then add it to the parent:

//create a new predicate
var resourceTypePredicate = PredicateBuilder.False<ResourceSearchResultItem>();
resourceTypePredicate = resourceTypePredicate.Or(x => x.CategoryType == "someValue");

//add it to the parent predicate
filterPredicate = filterPredicate.And(resourceTypePredicate);

And then finally, instead of searching on a specific field, you can now search using the Predicate:

IQueryable<ResourceSearchResultItem> query = context.GetQueryable<ResourceSearchResultItem>().Where(filterPredicate);


Once you have a search result, you get facets on the results if you’ve made a property for them in your result object. Getting facets is pretty straightforward:

var categoryFacets = new FacetResults();

categoryFacets = query
		 .FacetOn(x => x.CategoryString)

And then you can get the category values from the Categories and Values properties (see very crude example below):

foreach (var facetCategory in categoryFacets.Categories)
    foreach (var facet in facetCategory.Values)
        string theValue = facet.Name;


Sorting and Paging

Sorting is very simple – just use the LINQ extension methods to sort on one or multiple fields:

IQueryable<ResourceSearchResultItem> query = context.GetQueryable<ResourceSearchResultItem>()
                                                    .OrderBy(x => x.ResourceType)
                                                    .ThenBy(x => x.Created)

Paging is also pretty simple, being the results are IEnumerable – you can use the LINQ methods Skip() and Take():

IQueryable<ResourceSearchResultItem> query = context.GetQueryable<ResourceSearchResultItem>()

One issue with this is that you won’t be able to get the total number of results, but Sitecore Search gives you a way to do that:

var numberOfSearchResults = query.TotalHits;

I’m also aware there is a LINQ method called Page(), but I have never tried it.


The above areas will account for 90% of searches. There are other extension methods for the search, such as Like and Matches() – you can use them as you need it, and get the search behavior you are looking for. A lot of this information is already available, but I was having trouble finding it all in one place. Hopefully, this is informative and convenient for the someone who is looking for a soup-to-nuts guide on searches.