Sitecore RTE Button Postprocessing

There may be times that you want to modify the way stock Sitecore RTE buttons work without actually modifying stock Sitecore files.  An easy way to accomplish this is to override the Telerik editor commands manually using a custom js file.

Some common uses of this technique could include

  1. Adding classing to injected elements.
  2. Wrapping injected elements in a wrapping element.
  3. Adding a sibling html element for an icon perhaps.
  4. Modifying the markup for SEO needs.
  5. Modifying the markup to build a responsive website.

Find the operation to patch

The first thing you need to do is find the RTE command for the button you’d like to add post processing to.  Easiest way to do this is by using your browsers inspect feature on the button you’d like to enhance.


The class of the span element that makes up the button is the name of the command you’re interested in.  At this point you can start writing your javascript.

The Javascript

var	RadEditorCommandList = Telerik.Web.UI.Editor.CommandList;

var table = RadEditorCommandList["InsertTable"];
RadEditorCommandList["InsertTable"] = function (commandName, editor, args) {
	table(commandName, editor, args);
	var p = editor.getSelectedElement().parentNode.parentNode.parentNode;

This code will modify the insert table button to add a class of editor-table to the table after it’s injected.

So what are we doing here, let’s analyze it.

  1. Get the telerik editor command list object.  This object stores the javascript that drives each of the buttons in the editor.
  2. Save the original command into a custom variable called table
  3. Replace the method attacked to the telerik editor command list with our own function
  4. Using the telerik editor object to get the selected element after the table is inserted and traverse up to the
  5. Add a class of editor-table to the table root

You’ll likely need to utilize the debugger to drop breakpoints down in your code and use the console to identify the correct element you’ll need to manipulate.

Having Sitecore add your javascript to the editor

There’s a simple config patch to add your javascript to the RTE editor.

<configuration xmlns:patch="">
				<script key="customsrc" src="/relative/path/to/customSitecore.js" language="JavaScript"/>

Using this method is not limited to ONLY postprocessing, but you could essentially take stock methods and do whatever you please with them. Sky’s the limit, so go have fun with it.

Installing Azure Search in Sitecore

Azure search became a new option as of Sitecore 8.2 update 1.  It’s a great search provider with all kinds of features out of the box.  Most notably:

  • Supports Lucene query syntax for queries
  • Autocomplete and type-ahead
  • Hit highlighting (show the context in which the keywords appeared)
  • Geo-spacial awareness (show geographical search results to a user)

While Azure search can be utilized by any install of sitecore, it is most notably useful in an azure cloud implementation.  Which has also changed in 8.2 update 1 and my friend and fellow MVP Bas Lijten wrote an amazing blog post about.  Important note, this install can be done automatically if you’re using the Azure ARM scripts that Sitecore provides.  In other words If your Sitecore is on Azure, make it easy on yourself and use the ARM scripts.

I’m also extremely happy to say that it’s quite easy to set up Azure Search in Sitecore.  Those of you who went through the toil of setting up Solr should be particularly pleased about that.

Get it installed

Step 1

Install Azure Search on an azure subscription.

  • Add new
  • Web + Mobile
  • Select Azure Search
  • Click Create


Step 2

Get your access id

  • Find your new Azure Search in the resources list
  • Click Settings
  • Click Keys
  • Copy your primary key


Step 3

Add connection string

  • serviceUrl – The url which Azure has given your search service, see above screenshot for where to find it (it’s blurred out, you can’t use mine!)
  • apiVersion – The version of the rest api to utilize for Azure search.  You’re unlikely to need to change this right now
  • apiKey – The key you retrieved in step 2
	<!-- Your other connection strings -->
	<add name="" connectionString="serviceUrl=https://********;apiVersion=2015-02-28;apiKey=***********************************" />

Step 4

Configure Sitecore

  • Remove or disable
    • App_Config\Include\Sitecore.Speak.ContentSearch.Lucene.config
    • App_Config\Include\ContentTesting\Sitecore.ContentTesting.Lucene.IndexConfiguration.config
    • App_Config\Include\FXM\Sitecore.FXM.Lucene.DomainsSearch.DefaultIndexConfiguration.config
    • App_Config\Include\FXM\Sitecore.FXM.Lucene.DomainsSearch.Index.Master.config
    • App_Config\Include\FXM\Sitecore.FXM.Lucene.DomainsSearch.Index.Web.config
    • App_Config\Include\ListManagement\Sitecore.ListManagement.Lucene.Index.List.config
    • App_Config\Include\ListManagement\Sitecore.ListManagement.Lucene.IndexConfiguration.config
    • App_Config\Include\Social\Sitecore.Social.Lucene.Index.Analytics.Facebook.config
    • App_Config\Include\Social\Sitecore.Social.Lucene.Index.Master.config
    • App_Config\Include\Social\Sitecore.Social.Lucene.Index.Web.config
    • App_Config\Include\Social\Sitecore.Social.Lucene.IndexConfiguration.config
    • App_Config\Include\Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config
    • App_Config\Include\Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.Xdb.config
    • App_Config\Include\Sitecore.ContentSearch.Lucene.Index.Analytics.config
    • App_Config\Include\Sitecore.ContentSearch.Lucene.Index.Core.config
    • App_Config\Include\Sitecore.ContentSearch.Lucene.Index.Master.config
    • App_Config\Include\Sitecore.ContentSearch.Lucene.Index.Web.config
    • App_Config\Include\Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Lucene.Index.Master.config
    • App_Config\Include\Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Lucene.Index.Web.config
    • App_Config\Include\Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Lucene.IndexConfiguration.config
    • App_Config\Include\Sitecore.Marketing.Lucene.Index.Master.config
    • App_Config\Include\Sitecore.Marketing.Lucene.Index.Web.config
    • App_Config\Include\Sitecore.Marketing.Lucene.IndexConfiguration.config
  • Enable
    • App_Config\Include\Sitecore.ContentSearch.Azure.DefaultIndexConfiguration.config.disabled
    • App_Config\Include\Sitecore.ContentSearch.Azure.Index.Analytics.config.disabled
    • App_Config\Include\Sitecore.ContentSearch.Azure.Index.Core.config.disabled
    • App_Config\Include\Sitecore.ContentSearch.Azure.Index.Master.config.disabled
    • App_Config\Include\Sitecore.ContentSearch.Azure.Index.Web.config.disabled
    • App_Config\Include\Sitecore.Marketing.Azure.Index.Master.config.disabled
    • App_Config\Include\Sitecore.Marketing.Azure.Index.Web.config.disabled
    • App_Config\Include\Sitecore.Marketing.Azure.IndexConfiguration.config.disabled
    • App_Config\Include\Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Azure.Index.Master.config.disabled
    • App_Config\Include\Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Azure.Index.Web.config.disabled
    • App_Config\Include\Sitecore.Marketing.Definitions.MarketingAssets.Repositories.Azure.IndexConfiguration.config.disabled
    • App_Config\Include\ContentTesting\Sitecore.ContentTesting.Azure.IndexConfiguration.config.disabled
    • App_Config\Include\FXM\Sitecore.FXM.Azure.DomainsSearch.DefaultIndexConfiguration.config.disabled
    • App_Config\Include\FXM\Sitecore.FXM.Azure.DomainsSearch.Index.Master.config.disabled
    • App_Config\Include\FXM\Sitecore.FXM.Azure.DomainsSearch.Index.Web.config.disabled
    • App_Config\Include\ListManagement\Sitecore.ListManagement.Azure.Index.List.config.disabled
    • App_Config\Include\ListManagement\Sitecore.ListManagement.Azure.IndexConfiguration.config.disabled
    • App_Config\Include\Social\Sitecore.Social.Azure.Index.Master.config.disabled
    • App_Config\Include\Social\Sitecore.Social.Azure.Index.Web.config.disabled
    • App_Config\Include\Social\Sitecore.Social.Azure.IndexConfiguration.config.disabled

Step 5

Rebuild your index

  • From desktop Sitecore button -> Control Panel.  From launchpad click the Control Panel button
  • Indexing
  • Indexing Manager
  • Select all indexes
  • Execute

Now you’re good to go!

Sitecore doesn’t ship with wrappers to achieve the advanced search features that Azure Search provides, so you’ll likely need to create your own search service to utilize these features until Sitecore has a plan to wrap this in it’s linq to search feature.

Watch out!

If you have a custom search field with a preceding underscore, you need to make sure you give it an additional attribute of cloudFieldName like you can see on Sitecore’s _templatename field.  This is because Azure Search actually doesn’t allow a field to start with an underscore.

              <field fieldName="_templatename"        cloudFieldName="templatename_1"      boost="1f" type="System.String"   settingType="Sitecore.ContentSearch.Azure.CloudSearchFieldConfiguration, Sitecore.ContentSearch.Azure" />

Personalization State For Tag Managers

There have often been clients of mine who have asked the question “how do we use Google Analytics to track the value of our personalization campaigns?”.  The first answer is usually that there isn’t a good way to do so, not built in anyway.  So we build something!  I’ve set up a static method to generate personalization stats for the current page and drops it in a meta-tag.  That way you can easily set up any tag management analytics tool to keep track of a user based on personalization states.

How it works

Every time a user sets up a block of rules to trigger a personalization state.  They give it a name, i propose that they use this name as a sort of campaign tag withing the tag manager.  Here’s an example of a block of rules.


Using the above highlighted name as the key value for this particular personalization block would allow us to generate a meta-tag that looks like this:

<meta name="personalizationState" content="Target User">

If multiple personalization states were to have been triggered their names would be added to the content of the meta-tag as a comma delimited list.

Show me the code

	public class PersonalizationState
		public static string GenerateMetaTags()
			StringBuilder sb = new StringBuilder();
			RuleStack stack = new RuleStack();
			var references = Sitecore.Context.Item.Visualization.GetRenderings(Sitecore.Context.Device, false);
				foreach (RenderingReference reference in references)
					ConditionalRenderingsRuleContext context = new ConditionalRenderingsRuleContext(references.ToList(), reference);
					foreach (Rule<ConditionalRenderingsRuleContext> rule in reference.Settings.Rules.Rules)
						rule.Condition.Evaluate(context, stack);
						if ((stack.Count != 0) && (bool) stack.Pop() && rule.UniqueId != ID.Null)
			catch (Exception e)
				Log.Warn("Problem aggregating rules for personalization context, metatag may be incorrect", e, e);
			return sb.Length > 0 ? $@"<meta name=""personalizationState"" content=""{sb.ToString(1, sb.Length - 1)}"">" : "";

It would then be utilized in your cshtml (or using similar techniques in aspx, if you’re into that sort of thing) like so.


Token Manager Custom Token Button

After hearing from some content authors that Token Manager was difficult to train newer people to use, i set out to build a system to create RTE toolbar shortcuts to tokens, to make it as easy and simple as possible to insert a token


AutoTokens have a shortcut for this feature, if you look at the implementation of the AutoToken interface you’ll see you can override a property for TokenButton.

    public override TokenButton TokenButton()
        return new TokenButton()
            Name = "Insert a spiffy date token",
            Icon = "Office/32x32/explosion.png"

However you can add this for any token manually in the RTE profile.

  1. Create a new button using the template {3C8BD8A1-280B-4278-BB8B-21FA3B87AF0F} – Rich Editor Button
  2. in the field “Click” add “TokenManager” + {unique identifier without white space}.  It doesn’t really matter what the identifier is you choose as long as it’s unique.
    1. for example it could be TokenManagerDateToken
  3. Under shortcut put the query string variables for the token category and name
    1. for example ?Category=Demo Token&Token=Date Token

This can ease up on the learning curve for Token Manager and make it a system that any level of author can use.

Token Manager AutoToken

While using Token Manager for some simple dynamic property tokens i realized that as it was the process is a little too complicated.  There are several steps needed to create a token, it involves creating a template that inherits a token base template, implementing the class, registering it with the config.  Quite a lot of effort and runaround, and this is all well and good for tokens that are created by content authors.  However, most of the tokens I’ve created are dynamic tokens generated by the backend without the need of a consistent content author managed source.

AutoTokens fix all that.  With AutoTokens there is one single step to create a token.  Implement the interface AutoToken in an assembly that’s loaded in the app pool.

	public class DemoToken : AutoToken
		public override TokenButton TokenButton()
			return new TokenButton()
				Name = "Insert a spiffy date token",
				Icon = "Office/32x32/explosion.png"

		public DemoToken() : base("Demo Token", "Office/32x32/explosion.png", "Date Token")

		public override string Value(NameValueCollection extraData)
			int month;
			int.TryParse(extraData["month"], out month);
			return CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(month) + extraData["day"];

		public override IEnumerable<ITokenData> ExtraData()
			yield return new MonthData();
			yield return new DayData();

That’s it!  One Step!  Token Manager will generate a token collection and add a token to it

Notice the use of MonthData and DayData. These are custom Token Manager fields, which can easily be implemented with some light knowledge of angularjs. Ultimately an angularjs snippet just needs to assign a value to the javascript variable[field.Name]. For example this is the MonthData class

	public class MonthData : ITokenData
		private string options;

		public MonthData()
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < 12; i++)
<option value={i+1}>{CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(i+1)}</option>");
			options = sb.ToString();
		public object GetValue(string value)
			return value;

		public string Name { get; set; } = "month";
		public string Label { get; set; } = "Select the month";
		public bool Required { get; set; } = true;
		public string AngularMarkup
<div class=""field-row {{{{field.class}}}}"">
        <span class=""field-label"">{{{{field.Label}}}} </span>
<div class=""field-data"">
            <select ng-model=""[field.Name]"" >{options}</div>
		public dynamic Data { get; }

Out of the box Token Manager comes with TokenData fields for

  1. IdTokenData – Opens a Sitecore tree at a specified root to select a Sitecore item
  2. StringTokenData – Enter any string value
  3. BooleanTokenData – Checkbox
  4. IntegerTokenData – Enter number

This is what we end up with.


Sitecore Sidekick – App Tutorial / Log Viewer

IMPORTANT: this is for Sitecore Sidekick 1.1-1.2 for Sidekick 1.3+ follow this guide: Building a custom Sidekick app
Here in lies a tutorial for building your own Sidekick app.  In this example we’ll be building an app that exposes running and recently completed Sitecore jobs (like publishing, and index rebuilds).  I believe Sitecore 8.2 has something to do this out of the box, however those of us on older versions of Sitecore are at the mercy of the black box if we happen to accidentally close the publishing dialog.  It’s still going in the background and blocking further publishing operations, but is it stuck?  We don’t know!  Lets arm ourselves with knowledge!


You can grab the source code here in a stand alone project HERE
Note that this requires the Sitecore nuget feed.

Angularjs parts

In your visual studio solution create a folder for resources.  In this folder put all your angular code and very important edit the file properties on each js file to compile it as an embedded resource instead of the default “content”. Sidekick has a special way of serving these content that aggregates all resources into a single dll file. This minimizes installation and file system mess. As well as making the final product of any Sidekick app be simply a DLL and a .config file. No mess, no fuss!

Step one:

Build an angular factory.  To act as a hub to access the server side information.

(function () {
    'use strict';

        .factory('JVFactory', JVFactory);

    JVFactory.$inject = ['$http'];

    function JVFactory($http) {
        var service = {

            getJobs: function (running) {
                var data = { "running": running};
                return $"/scs/jvgetjobs.json", data);

        return service;

        function getData() { }

Step two:

Build an angular directive. This will be the root of our app from a DOM perspective.

(function () {
    'use strict';

        .directive('jvdirective', jvdirective);

    function jvdirective() {

        var directive = {
            templateUrl: "/scs/jvmaster.scs",
            restrict: 'EA'
        return directive;


Step three:

Build an angular js html template. This will be the actual markup for your app. Note that instead of the traditional html templates. In Sidekick i would recommend you use .scs files to prevent rare collisions in Sitecore’s routing of html files

<div ng-controller="jvcontroller as vm" class="jvmain">
<div class="btn" ng-if="vm.runningJobs" ng-click="vm.runningJobs = false">Show Completed Jobs</div>
<div class="btn" ng-if="!vm.runningJobs" ng-click="vm.runningJobs = true">Show Running Jobs</div>
<div ng-repeat="job in">
				<a href="#" ng-click="vm.toggleDetails(job.Name+job.Category+job.TimeStarted)">{{job.Name}}</a></h4>
<strong>Processed:</strong> {{job.ProcessedItemsCount}}
<div ng-if="vm.selectedJob === job.Name+job.Category+job.TimeStarted">

					<strong>Category:</strong> {{job.Category}}

					<strong>Started:</strong> {{job.TimeStarted}}

					<strong>Job State:</strong> {{job.State}}

					<strong>Job Messages</strong>

Step four:

Build an angular controller. This will control the angular scope that we’re going to use to control the data on the front end.

(function () {
    'use strict';

        .controller('jvcontroller', jvcontroller);

    jvcontroller.$inject = ['JVFactory', '$interval'];

    function jvcontroller(JVFactory, $interval) {
        /* jshint validthis:true */
        var vm = this;
        vm.runningJobs = true;
        vm.getJobs = function () {
            JVFactory.getJobs(vm.runningJobs).then(function (response) {
            }, function (response) {
                vm.error =;
        vm.toggleDetails = function (jobkey) {
            if (vm.selectedJob === jobkey)
                vm.selectedJob = false;
                vm.selectedJob = jobkey;
        $interval(function () {
        }, 500);

Misc Resources

In the resources folder you created for the angular code put all your file based assets and very important edit the file properties on each file to compile it as an embedded resource instead of the default “content”

Step one:

Css. We need to make our app look reasonable. It’s important to note that you need to make your css as app specific as possible, since all Sidekick apps run in the same angular app if you set up styles to say the div tag as a whole, it will effect other apps.

.jvmain div > div {
    border: 2px solid black;
	border-radius: 5px;
    margin-bottom: 5px;
    padding-left: 5px;
    background-color: #eee;
    display: inline-block;
    width: 96%;
    margin: 10px;
    word-wrap: break-word;
.jvmain div > div > div {
    margin-right: 5px;
    border: 2px solid blue;
    background-color: #ddd;
    width: 96%;
.jvmain strong {
	font-weight: 700;
	font-size: 14px;
.jvmain h4 {
	font-size: 16px;
	font-weight: 700;

Step Two:

Logo. For the Sidekick tile.

jvgearwheels parts

Step one:

Http Handler.  In Sidekick we have a special Http handler that’s wired up automatically through the Sidekick’s core.  It’s an abstract class called ScsHttpHandler.  We need to implement one of those. You’ll notice that this is where a lot of the magic bindings happen, for instance this is where you give the namespace of your resources folder that all our previous assets were created into. Note that there’s a method here GetPostData which gets all post data sent by angular and builds a dynamic C# object for you to use.

	public class JobViewerHandler : ScsHttpHandler
		public JobViewerHandler(string roles, string isAdmin, string users)
			: base(roles, isAdmin, users)

		public override string Directive { get; set; } = "jvdirective";
		public override NameValueCollection DirectiveAttributes { get; set; }
		public override string ResourcesPath { get; set; } = "ScsJobViewer.Resources";
		public override string Icon => "/scs/jvgearwheels.png";
		public override string Name => "Job Viewer";
		public override string CssStyle => "width:600px";
		public override void ProcessRequest(HttpContextBase context)
			string file = this.GetFile(context);

			if (file == "jvgetjobs.json")
				this.ReturnJson(context, this.GetJobs(context));
		private object GetJobs(HttpContextBase context)
			var data = GetPostData(context);
			var model = JobManager.GetJobs().Where(x => data.running ? !x.IsDone : x.IsDone).OrderBy(x => x.QueueTime);
			return model.Select(x => new JobModel(x));

Step two:

In order to run the above code we need a model

	public class JobModel
		private readonly Job _job;
		public JobModel(Job job)
			this._job = job;
		public string Name => this._job.Name;
		public string Category => this._job.Category;
		public string State => this._job.Status.State.ToString();
		public string Messages
				StringBuilder sb = new StringBuilder();
				sb.AppendLine("Job Details for " + this.Name);

				if (this._job.Options.ContextUser != null)
					sb.AppendLine("Context User: " + this._job.Options.ContextUser.Name);

				sb.AppendLine("Priority: " + this._job.Options.Priority);

				foreach (string s in this._job.Status.Messages)

				return sb.ToString();
		public bool IsDone => this._job.IsDone;
		public long ProcessedItemsCount => this._job.Status.Processed;
		public long TotalItemsCount => this._job.Status.Total;
		public bool HasTotalItems => this.TotalItemsCount > 0;
		public string TimeStarted => this._job.QueueTime.ToLocalTime().ToString("G");

The configuration

This step is simple, we just need to register our app with sidekick.

<?xml version="1.0"?>
<configuration xmlns:patch="">
        <processor type="ScsJobViewer.JobViewerHandler, ScsJobViewer" >
          <!-- leave blank for any role -->
          <param name="roles"></param>
          <!-- set to "true" to only allow admins-->
          <param name="isAdmin"></param>
          <!-- leave blank for any users -->
          <param name="users"></param>

Sitecore Sidekick – Content Migrator

Something that has come up for me consistently is the ability to move content from one Sitecore instance to another, usually something like prod server to test server.  Now most people reading this will know that there are several ways to accomplish this such as Sitecore packages or database restores.  However those solutions are time consuming and difficult to manage.  The Content Migrator Sidekick app is my solution to this problem.


What does it do?

At the core of this app is Kamsar‘s Rainbow serialization.  If that sounds familiar then you may have been using Unicorn to manage your develop level assets in sitecore (layout, template, etc..).  Yes it’s basically a way to Unicorn sync your content items.  However it is a completely stand alone product and takes no dependency on Unicorn whatsoever.

The Content Migrator will move content in a multi-threaded way.  The system is designed to be as fast as the Sitecore Item API can go.  When the operation starts it spins up two separate thread pools, one for transmission of serialized item data from the remote server and another to ingest that data into Sitecore.  Since the inhibiting factor here is the rate in which Sitecore can write item data to the database you can virtually eliminate the penalty of pulling items over the network in most cases (the obvious exceptions being transmission over a slow connection).

While the operation is running any event will be recorded in real time providing your with an immediate report of what is happening and a sense at the rate of ingestion.  A queue count is reported while the operation is running.  This is how many serialized items are in the queue to be written to Sitecore and can act as a time remaining notification

At any time during the life cycle of a migration event any user that has access to the content migrator can view a real time report of what’s happening or even cancel the operation.  There are four buckets that operations can fit into

  1. Currently Running Operations
  2. Completed Operations
  3. Preview Operations
  4. Cancelled Operations


I’ve run a few time trials against a normal Sitecore package install.  Completely disregarding the time to actually create and generate the Sitecore package.  For a content migration that was large (24k items) but the majority of the content was unchanged between the source and the target.

Content migrator: 41 seconds

Sitecore Package Install: 70 minutes

yes that’s 102x faster.  Granted this is the use case this tool was designed for, but regardless, it should make synchronizing content between environments a snap!


<?xml version="1.0"?>
<configuration xmlns:patch="">
				<processor type="ScsContentMigrator.ContentMigrationHandler, ScsContentMigrator" >
					<!-- leave blank for any role -->
					<param name="roles"></param>
					<!-- set to "true" to only allow admins-->
					<param name="isAdmin">true</param>
					<!-- leave blank for any users -->
					<param name="users"></param>
					<!-- number of threads that are going out to the remote server to queue up item data to be installed-->
					<param name="remotePullingThreads">20</param>
					<!-- number of threads taking queued up data and updating or installing the item data in the database-->
					<param name="databaseWriterThreads">10</param>
					<!-- if the root is from the core database, add an attribute database="core" -->
					<roots hint="raw:BuildRoot">
						<root>/sitecore/media library</root>
						<root>/sitecore/system/Marketing Control Panel</root>
					<servers hint="raw:BuildServerList">

From the <roots> node you can control what item roots the current server will allow other servers to pull.  You’ll likely only want general content to be pull-able, however any Sitecore items are fair game.  You also need to maintain a white list of servers that your current server is allowed to pull from.

Additionally you can fine tune the number of threads used in the pools to match the robustness of your server.

Operation Configuration


Step 1:

Select a server.  A white list of servers is configured in the content migrator config file.  Keep in mind that the target server also needs to have the content migrator installed on it.

CMSelect Server.png

Step 2:

Select an item.  Select the main item to be pulled over from the remote server.


Step 3:

Configure options for your operation.

  1. Migrate all children of selected item.
    1.  You would uncheck this option if you only wanted a single item.
  2. Overwrite all existing content with new content from the server.
    1. You would uncheck this option if you had content on the local server that you would rather not have deleted but you wanted to gather all new content items.
  3. If parent doesn’t exist locally add that too.
    1. You would uncheck this option if you only wanted the selected item or it’s children.
  4. Make the local content tree mirror the remote content tree.
    1. You would check this option if you wanted to get rid of all local content in favor for the content from the target, this would commonly be the case if you wanted to overwrite QA or Dev content with production content and dispose of the clutter.


Step 4:

Configure advanced sitecore options.

  1. Run using the event disabler.
    1. Unchecking this option will make all events run when installing, moving, deleting, etc.. Items.  This can slow down the operation but if you have lots of important custom on save events, it might be important.
    2. Checking this option will eliminate all events, this can greatly increase speed on installation.
  2. Run using the bulk update context.
    1. Unchecking this option will make the operation run without the bulk update context.  This makes it update the search indexes for each operation, as well as likely other side processes (it’s kind of a black box, if anyone can clarify further please comment).
    2. Checking this option will make the operation skip all index updates.  This is a good idea if you’re updating a lot of content at one time, just don’t forget to run an index rebuild when you’re done.


Step 5:

Select your operation type

  1. Pull – Execute the operation immediately.
  2. Preview – Execute an estimation of the results where no actual changes are made.  Note that this isn’t 100% accurate as some insertion operations could result in unforeseen errors.


Now just imagine when someone is having a content problem in production you can now mirror down production in a matter of minutes and save yourself hours of debugging.

Sitecore Sidekick Audit Logger


The Audit Logger Sidekick app was created to graphically track events that occur withing Sitecore.  You can see the default set of tracked events in the image above, however any event within Sitecore can be easilly tracked.  In fact, any code can trigger a tracking event using the audit logger API provided with the app.

What is sidekick?  Check out this to learn more.

Searching and filtering events

The system traces events, gathers information about those events and stores them in a special Lucene search index to provide robust searching capabilities.

  1. Search by content.  Whenever an item incurs an event that’s tracked terms are generated for the item and the specific point in time.  This allows you to see what events had to do with an item that referenced a particular word.  In addition there’s auto-complete on the field.
  2. Search by item.  Each item that incurs an event has it’s id tracked.  This allows you to filter events by a particular item if you’d like to see the history of the item.
  3. Search by user.  The user who invoked the action is recorded in the event.  This allows you to filter by a particular user.  This would be useful if someone was having trouble remembering what changes they made to an item that day.  Note that this is populated based on actions recorded, so if someone went on vacation for a week and it’s only tracking 4 days his/her name would drop off the list until they performed a tracked event.
  4. Filter by event type.  The type of event is tracked, so a user can select exctly what events they are interested in.  This filtering type that can have multiple filters applied as an “OR” operator, also this is applied alongside the previous filters.

Event details

Once you have the results filtering as you want them to, if you click the Get Details button it reports a list sorted chronologically.  You will also notice an edit item button, clicking this will open a lightbox modal for the content editor for the item related to the particular event. auditlogdetails.png

Each event is color coded as defined in the filter for a quick visual sweep of activity types.  You can additionally see more details on a particular event by clicking on “see details”.  This is particularly useful for the save events as it displays an HTML diff of your change.



				<processor type="ScsAuditLog.AuditLogHandler, ScsAuditLog">
					<!-- 0 for indefinately -1 for no backup-->
					<param name="keepBackups">5</param>
					<!-- 0 for indefinately-->
					<param name="keepRecords">3</param>
					<!-- leave blank for any role, seperate multiple roles by | character -->
					<param name="roles"></param>
					<!-- set to "true" to only allow admins-->
					<param name="isAdmin">true</param>
					<!-- leave blank for any users, seperate multiple users by | character -->
					<param name="users"></param>
					<events hint="raw:AddEventProcessor">
						<onSaved type="ScsAuditLog.Pipelines.OnSave, ScsAuditLog" color="blue" id="0" label="Item was saved" event="item:saved" />
						<onSaved type="ScsAuditLog.Pipelines.OnMoved, ScsAuditLog" color="brown" id="1" label="Item was moved" event="item:moved" />
						<onSaved type="ScsAuditLog.Pipelines.OnDeleted, ScsAuditLog" color="red" id="2" label="Item was deleted" event="item:deleting" />
						<onSaved type="ScsAuditLog.Pipelines.OnRename, ScsAuditLog" color="orange" id="3" label="Item was renamed" event="item:renamed" />
						<onSaved type="ScsAuditLog.Pipelines.OnCreated, ScsAuditLog" color="green" id="4" label="Item was created" event="item:created" />
						<onSaved type="ScsAuditLog.Pipelines.GeneralEvent, ScsAuditLog" color="#dd33ff" id="7" label="Item was unlocked" event="item:unlocked" />
						<onSaved type="ScsAuditLog.Pipelines.GeneralEvent, ScsAuditLog" color="#aaaaff" id="8" label="Item was locked" event="item:locked" />
					<customEvents hint="raw:RegisterCustomEventType">
						<publish color="purple" id="5" label="Publish Initiated" />
						<!--<publish color="#eee" id="6" label="Item Published" />-->

Backups and record duration can be configured in the above configuration file.  It is recommended that you keep only what would be useful as due to the volume of data collected, these logs can get get large on a site with a high number of events if keeping logs for more than a week.

Additional events can be tracked using additional onSaved nodes.  Note that new registered events need to have a unique id assigned to it and while it’s not required a unique color would be preferable from a user experience standpoint.

Custom Events

If you’d like to track something that’s not a sitecore event, such as a pipeline processor.  You can invoke a logger singleton to perform the tracking of the event.

AuditLogger.Current.Log(MyEventRelatedItem, "5", $"{HtmlForDetailedInformation");


  1. Sitecore Item related to the event
  2. string Id for the registered event in the configuration file
  3. Optional markup for the “see details” link to expose

Sitecore Sidekick – Editing Context


Ever wish you didn’t have to switch databases to do things like add custom experience buttons or change the ribbon.  Well that was the idea behind the Sitecore Sidekick app Editing Context.

what does it do?

There is four main functions to this app.  It keeps a history of items that are visited.  It has shortcuts to common editor locations, controlled via the config file.  The final two are to give shortcuts to common developer location in the core and master databases.

Anytime an item is visited in the edit context, it opens it up in a light box modal over the top of the app.  This makes it so you never lose your context in Sitecore.  To get back you can simply close the modal.

An important thing to note is that the developer functions are admin only. Whereas the editor locations and item history is available to any user by default. User security is further configurable through the configuration file to white list users or roles.

How does it work?

Editing Context is mainly driven by configuration files.  you can add items to any section via xml.

 <EditorLocations hint ="raw:AddEditorLocation">
	<location id="{0DE95AE4-41AB-4D01-9EB0-67441B7C2450}" description="The root content item"></location>
<CoreLocations hint="raw:AddCoreLocation">
	<location id="{DB662EDF-5965-4848-AD14-BA6BF0D55D1E}" description="Add or modify a custom experiance button"></location>
	<location id="{E77EEE1C-4F50-4A68-918C-47E9A8FC0957}" description="Add or modify an edit frame button"></location>
	<location id="{C0EB664B-4DC7-4A7C-98F0-9884EAA2F1E6}" description="Manage rich text editor profiles"></location>
	<location id="{110D080F-ED5C-42DE-80BD-4B751C98AE93}" description="Add to or modify the content editor ribbon"></location>
	<location id="{CFC141D6-0558-4FCD-9137-A723ADB3C019}" description="Add to or modify the experience editor ribbon"></location>
<MasterLocations hint="raw:AddMasterLocation">
	<location id="{1057C235-C5C0-4EB7-8F77-EA51EB9E20EE}" description="Add to or modify Rules"></location>
	<location id="{05592656-56D7-4D85-AACF-30919EE494F9}" description="Add to or modify Workflows"></location>
	<location id="{64C4F646-A3FA-4205-B98E-4DE2C609B60F}" description="Add to or modify Languages"></location>
	<location id="{B3031702-6BFF-4061-B92D-291E9EC865D5}" description="Add to or modify Validation Rules"></location>
	<location id="{3E0F7CA2-6990-42E1-BDA3-9B8FCE8DD874}" description="Manage Analytics"></location>

Common use cases

Creating a rendering – if you create a template, add fields, then create a rendering item and forget your datasource template item’s ID.  You can utilize Editing Context and it’s history capabilities to open the template item in a lightbox.  Avoiding needing to save the rendering item and traversing back to the template.  If you then decide you want a custom experience button, you can utilize Editing Context again to open a modal into the core database to create one, again without needing to save the rendering item.

Adding fields to a template – if you created a template but forgot to add a certain field to it and closed the template editor window already.  You can use Editing Context to open it back up and quickly add the field.

Working in Experience Editor – if you’re working in experience editor and need to edit something behind the scenes.  Instead of using the goofy sitecore edit context item split screen, you can utilize Editing Context to open a lightbox to the content editor and do whatever you need to.

Sitecore Sidekick



shift+alt+s will open the sidekick.

Live presentation at the Portland OR Sitecore User Group can be found here.  Video on YouTube

Where do i get it?

  1. Marketplace package (Pending sitecore approval)
  2. Nuget Package
  3. Source on Github

How do i install it?

Great care was taken to insure that installation was as easy as possible, so there are no manual steps to get Sidekick installed (other than actually installing it in any of the three methods above).  There are however configuration options for individual applications within Sidekick that should be customized.  See documentation for individual apps for more details (coming soon…)

Sidekick requires Sitecore 7.1 or higher.

What can i build with it?

learn to build your own Sidekick app here

What is it?

Sidekick is a platform in which to serve angularjs apps with a micro-service back-end from within Sitecore. This makes it a flexible and extensible platform to provide highly reactive and responsive single page applications for Sitecore. Some features include:

  1. Resources are delivered from an embedded location in the dll.  This makes it so that the installation footprint is extremely small since it ends up simply being a dll and a config file.
  2. Applications may be hidden or shown to specific roles, users, or admin only.
  3. Each application’s stylesheets and javascript are dynamically compiled into a single document to reduce http requests.
  4. The sidekick is available in three contexts, Content Editor, Experience Editor, and Desktop.
  5. Each application is driven from an individual binary as well as an individual configuration file, so individual apps can easilly be removed if they are not desired by removeing the binary and config for the app that’s not desired.


  1. Editing context
  2. Content Migrator
  3. Audit Log

How does it work?

When Sidekick starts up for the first time it’ll check to see if the desktop icon exists, if it doesn’t it’ll create one with the icon appropriate to the version of Sitecore.  It then wires up some MVC routing to direct traffic that starts with /scs to sidekick which it then uses a custom http handler to deliver content out of an embedded dll.  After this it runs a custom pipeline to populate all the applications contained within Sidekick.  Each time an application is loaded it aggregates all javascript and stylesheets into a cached single js and css file to minimize requests.  Finally a bit of javascript is injected into Sitecore to allow for registration of the key command shift+alt+s to open it in each context.