Many sitecore implementations these days tend to have more than one site. There could be the corporate website and product or service websites for each part their business. You can easily achieve this in sitecore. You make your own set of templates, renderings, CSS styles, images.

More sophisticated solutions would follow the corporate design manuals, styling guides how their corporate design should look so all sites have similar visual representation. Then these sites could share some assets like CSS styles, images and so on. This saves time and money, but still there is a lot of work for sitecore developers to create a new site.

Create a set of reusable components for multiple sites

Having many sites that differ only by content, but not the visual representation implies to use the same components. This means sharing templates, renderings, styles and almost everything, but not the content. The content will be different for each site. Theoretically the only thing you have to setup is the site configuration for each site. IIS must have the binding, not mentioning DNS servers. By sharing the components, a lot of work is saved, and content authors could build up the website all by themselves.

Each site could use different workflow

What about workflow? Does it have to be the same throughout all sites using the same templates and renderings? …….. It does not!

We can have different workflows for each site using the same components and still allow different approval of content etc. I am talking about workflow inheritance. You can setup workflow on a site and it will be inherited in the time of item creation to every new component.

Component‘s datasource must use the same workflow as site, where component is used

To be able to share components templates and renderings the content tree structure for each site must follow the same rules. Certainly, you can have own set of components for specific site which reside in folders somewhere in the content tree. But when you want to reuse components you want to specify the datasource location and this must be easily resolved.

The solution

Components throughout project use the same relative structure for datasource

It is all about the structure.

Every component you create must always reside in the same folder in each site. To simplify this process author can create the initial website structure by some automation process., like action command using branch templates or you can have sample implementation something like components library containing all possible components folder and use sitecore powershell to copy the structure from there.

So when you have for example an image box component in sites resources the relative path to the component folder must be the same throughout all sites. When you know where to find the component in the content tree of each site you can then setup the datasource location relatively and not absolutely. So you can have relative datasource location and source in list field types as well.

Datasource Location example

query:./ancestor-or-self::*[@@templatename='Site Root']/resources/components/#image-boxes#

The solution is to use sitecore query. All sites in the project are based on Site Root template, this way you can find the root of each site and then follow the same folder structure for the components datasource. So, when author creates a new image box component for some product site it will allways be in the image-boxes folder.

And you can use the same logic when referencing items in list field types to setup some value from list of values. This way we can choose to have shared list of values for all projects when you set an absolute source to the field or custom list of values that is unique for each site by using the relative path.

Remark: use # to escape query path.

Site workflow

In this use case each site has its own workflow. So, when you create a new site, setup site specific workflow to it. Each site maintains different team with different workflow requirements. You do not want to allow them to create a component that uses different workflow and you cannot setup the site specific workflow on the components standard values because the component will be reused on more sites.

Default abstract workflow

What you want to achieve is the workflow inheritance. So, each component created inside some website takes over the website specific workflow. The solution is to create some default abstract workflow. This workflow is not used on any content item, it is just a base or abstract workflow.

We call it parent workflow, because what it does is when an item is created the workflow on the component is set up to the same workflow as is on its parent. This parent workflow must be set on standard values of each component.

The event handler configuration

The workflow replacement requires just one event handler listening on item:versionAdded event.

<sitecore>
   <events timingLevel="custom">
      <event name="item:versionAdded">
         <handler type="Custom.Events.SetParentWorkflow,Custom" method="SetWorkflow" />
      </event>
   </events>
</sitecore>

The event handler checks the __Workflow field that is filled by sitecore from Default workflow in standard values. Instead the term „ID of item“ goes GUID of the base workflow.

When it finds that Parent workflow is set it replaces it by workflow of parent item.

So, the key is to set the correct workflow on the site when the site structure is being created and use some Parent workflow on standard values of shared components.

protected void SetWorkflow(object sender, EventArgs args)
{
var item = Event.ExtractParameter(args, 0) as Item;
if (item.Fields["__Workflow"]?.Value != "ID of item")//sitecore/system/Workflows/Parent Workflow
{
    return;
}
try
{
    var master = Sitecore.Configuration.Factory.GetDatabase("master");
    var targetWf = master.GetItem(item.Parent.Fields["__Workflow"].Value);
    item.Editing.BeginEdit();
    item.Fields["__Workflow"].Value = targetWf.ID.ToString();
    item.Fields["__Workflow state"].Value = targetWf.Fields["Initial state"].Value;
}
finally
{
    item.Editing.EndEdit();
    item.Editing.AcceptChanges();
}
}

Avoid pitfalls

The implementation requires special attention. It is easy to forget to setup correct workflow on the site or template and it is always not easy to fix it later. So careful testing is required.

When we started implementing the shared components one folder was copied from another site when implementing a new component base on the component from that site. But the workflow was not changed. We did not notice it until it was on test environment and many items were created in that folder. The parent workflow functionality copied the wrong parent workflow to all subitems until we noticed it.

Reusable component configuration

The same principle applies to every content item. You can have shared template for components configuration so the component can have different behavior on each site.

The sites could have different configuration for each component, for example you can have short date format on one site and long date format on another one. It is just a new item created by the branch template having default values set in standard values and the application reads the correct configuration base on site context.

Follow the rules

You have just to follow the rules:

  • Maintain the same structure – Site Root, components tree
  • Setup relative paths where appropriate – datasource location, field source
  • Setup the abstract as we call it parent workflow in standard values

When you follow these rules, you can have shared services to read components configuration and do not repeat yourself for each site.

Turn up User Experience

Pages built up just from components could contain dozens of components. Users tend to create new pages by copying existing pages and changing the content. For some users is hard to explain, that when you duplicate an item the components are the same. So, when he changes content of the component it would change the content of the original page. To avoid mishaps, we‘ve created command for duplicate item with components. This command extends sitecore item:duplicate command. Every datasource gets duplicated as well and shared and final layout are updated with the new datasources.

Another example would be a Create microsite command. This command creates the whole microsite structure using branch template. When developer creates a new component, he prepares templates, renderings and datasource location for the rendering. He also adds the new datasource folder to the branch template, so it is created automatically for all new sites using the branch template.

One nice sitecore feature is the ability to mark publishing target as Preview publishing target. This allows the publishing target to be assigned to workflow states that are not in the final state. Using this feature users can publish item that is not in the final workflow state to preview site to see exactly how the page will look when published to live site.

Component adjustments

The component could be tweaked the similar way by the site context, so it could stay as a shared component, but yet it could have different style or behavior based on the site context.