Some time ago, Martin Davies started a thread on the Sitecore MVP community forum about where to store page content. It caught a lot of attention and many replies. Since that’s a closed forum, I thought I’d might as well write a public post on my view on this topic, but also put into a wider perspective. It almost ended up in a recipe on how to build Sitecore websites. Nothing in this post is new really, so I’ve kept each topic as brief as possible.
This post is only about the basics of Sitecore as a CMS. Sitecore can do so much more. But I’ve seen so many, in my opinion, implementation failures, where the customer can’t get passed the content management hell and never starts walking up the experience maturity ladder. The implementing partners plays a very important role here ensuring the content authoring environment doesn’t become messy, time consuming or non-intuitive.
Few projects are equal, so what I describe here is not a silver bullet in any way. But it’s an approach that works for us at Stendahls and we’re constantly refining it and adjusting it for each customer needs. The starting point here is building website that in a good way supports multi-language, mulit-brand, multi-market website (or sites) where there are multiple authors. Content governance is important and there are limited resources among authors.
Regarding organising content, one has to decide if one shared tree is used for all languages or if they could diverge. We typically create one tree per market, where some markets may use more than one language, such as Switzerland, Canada and so on, having two or more languages. On such markets, the client typically wants the same content and structure just translated into two or more languages, compared to other markets.
We typically define a site root item that only acts as a container for the site content. A Home item is created as child of the website container, and pages are created as descendants of the Home item. Thereby we can have other site related items as siblings of the Home item outside its web root.
Creating a master site is usually a good idea that we can clone into each market. The master site we do central translation into multiple languages, but we don’t publish it live. Thereby we can efficiently manage translations and let the content inherit to market sites where local adaptations are made.
When cloning the master site into a market site, we keep only the target language versions. When governing this kind of structure, switching between authoring language is a pain unless it’s addressed.
Pages should be organised in a clear item structure reflecting the URL structure. Site navigation, menus etc is something else. They are usually very similar, but don’t build you item structure based on site navigation alone. The item structure is also important when it comes to security configuration so that inheritance can be used as much as possible. These factors are also important when it comes to items living outside the site root or items that doesn’t have a rendering.
Pages is typically built from components, i.e a set of renderings with datasources pointing to other items containing its content. Usually those are bound only to the current page, but some may be shared between pages and some even shared between sites. Some people embraces a fully atomic design where everything is divided into small pieces. We find there is a risk of breaking it up into too small pieces, so sometimes it’s better to have to have larger building blocks with rendering parameters (or compatible renderings) to gain speed in the Experience Editor, make it easier for editors and ensuring personalisation works as expected. Rendering parameters should of course be templated and when using compatible renderings, one should ensure that the compatible rendering links goes both ways.
We store page local content items in a folder hierarchy as descendants of the page, site shared content items in a folder hierarchy as sibling to the site home, and globally shared items as siblings to the site roots. When defining datasource locations on renderings, the pipe character can be used to define multiple locations, so that authors can easily use local page contents as default, but also easily select the right location for site shared and global components as well.
We create the folder hierarchy automatically, as needed, during content creation. We create a custom set of folder templates with a clearly diverging appearance from items representing pages. Those folders have configured insert options etc on their standard values and are marked as hidden items. Content authors usually don’t have to see those. Even though they are hidden, they are displayed in the datasource selection dialog boxes. Content folders are also marked as protected (
__Read Only) in standard values, since authors should not be able to accidentally move or edit those items. Datasource locations are of course created using Sitecore relative axes queries making them universal between sites. The folder hierarchy is typically two levels deep, where the first is just a “Content” folder and the second defines the content type based on template. This is because the experience editor displays all items based on the datasource location query regardless if their type, but only compatible types are selectable. By having this two level structure, only selectable items are shown in the UI.
An area that’s sometime confusing for authors is knowing when (s)he is editing content of a local component or shared component that affects multiple pages. We’ve targeted this by highlighting shared components in the UI with a warning sign telling the author that when editing the selected content, the content will be changed on other pages as well. We also did a list of all those pages, so that it can easily be identified if a content change is supposed to go to all the affect pages or not. From Sitecore 8.2 this feature is built-in into the Experience Editor.
One of the advantages of having local page components as descendants of the page, is that security is inherited from the page and they follow along when renaming, moving, copying and cloning a page. Keeping track of all existing URL’s and automatically create 301 redirects as pages are moved or renamed, also helps authors to keep the site structure clean and friendly without compromising SEO.
As the sites evolve, components are replaced and eventually there may be a lot of unused page component items. We typically don’t want to remove those automatically because there could be valuable content in them. But we indicate those orphan items so that they can be (bulk)archived when a content governor decides to do so.
One should spend some time to get this right from the beginning. Correcting it afterwards is a real pain. Don’t make it a hassle for authors to work with the experience editor.
User friendly components
With minimal training, authors should be able to work fast in the experience editor, so a lot of focus should go into structuring layouts and renderings. It should be intuitive to create and work with pages in the experience editor. One have to invest time to configure everything to get this right. Pages needs insert options/insert rules, placeholder settings has to be configured correctly and so on.
Both page templates and renderings should have friendly naming, be filtered according to what fits the location, have a representative screen shots or at least a decent icon.
Sometimes there is a need to have container items that doesn’t represent a page. In the experience editor, those items has to have a rendering to make it possible to create sub items using the experience editor insert page command. We typically create a separate layout for those that isn’t available on the published site. We give those more of a “Sitecore layout” rather than the website layout, so that it is clear to the authors that those pages are not part of the site.
The component code also needs some love and care for the Experience Editor to work properly. There are some caveats to handle where components are new or empty on the page. For example, empty sections should usually be collapsed on preview/published pages, but when in editing mode they have to be visible so that authors can write content.
An annoying thing with Sitecore is its lack of rewriting links when copying/cloning items. In essence, when you clone a set of pages, their internal links needs to be rewritten so that they become internal in the copy/clone branch. This is a big topic, on its own, so I’m only gonna touch on a few areas. I guess all Sitecore partners already have their own cloning tool, but here are some of the features I like with ours:
We use a custom notification provider where we can configure that some notifications should be automatically accepted/rejected. This is really useful on fields where you wan’t values to be automatically inherited, or children in specific branches to be synced etc. We made this pluggable and configurable on a per template and per field level, so that it can be customized to suit the site information architecture.
We’ve also added a lot of functions to handle local changes to a field. This is particularly useful for field types such as rich text and the Rendering fields. We can highlight pages with gutters and fields with suggestion validators where the local content is modified compared to the clone master. This requires some field parsing to avoid false positives, since rewritten links in a clone is not null and thereby not inherited. We also ensured that a reset field operation doesn’t results in incorrect links.
It’s typically a good idea to add the Rendering and Final Rendering fields to the
Gutters and validators
Gutters and validators are really helpful for both content authors and people governing content. Keep in mind though that validators shouldn’t be intrusive and information overload may result in authors ignoring validator warnings etc.
Sitecore comes with a few built-in validators, but we rarely use any of these. They are too hard coded, not very universal and some of them are buggy as well. We create parameter driven validators where we can for example define on the item what severity it should have and so on.
Languages, links etc can also be a pain in the clone hell, so gutters and validators can help us here as well. We sometimes decorate our website definitions with information on what languages are expected on the site and so on. With this we can more easily find items with missing language versions or surplus languages, items having unexpected links that perhaps links to another site due to clone/copy issues etc. Tools for jumping between languages are really helpful too when working centrally with multiple language versions.
On a page level, we also validate related rendering items, so that a page is validated as a whole instead of item-by-item. It’s more intuitive for authors to validate and approve a whole page instead of the default piece-by-piece approach. This is fixed in Sitecore 8.2.
Workflow and publishing
Workflow can be a pain and there’s no universal solution to it. One has to start with investigating the clients internal processes. No system can solve non-existing or non-functional processes. Identify what the process looks like. When doing this, my experience is that customers, naturally, mix workflows with security rights, roles, publish restrictions and versioning. Mapping this correctly and have a common understanding is well worth the effort.
When pages are built from multiple components, it can be hard for authors to manage the workflow of all page local components. Those items still needs to be in a workflow in order to have proper versioning. We try to have those in the same workflow as the page itself. By adding actions to the workflow commands, we can automatically bring the rendering datasource items to the next workflow state, thereby approving a whole page as it is seen by the author in edit/preview mode. This makes it much more intuitive for authors and saves a lot of panic phone calls from desperate authors that published half-broken pages.
I hope Sitecore in a near future will implement work packages, so that this can be handled more smoothly. In the meantime we’ll have to live with the item-by-item workflow. A lot of this has changed already in 8.2, but there more things to do in this area.
To further improve the workflow process, we modify the getValue and publishing pipelines in such way that the author sees what’s actually going to be published. A problem with the default behaviour are the different kinds of item value resolving, such as standard values, cloning and language fallback. For example; if a field value is null and it fallbacks to a clone parent value and that item version is not approved, publishable or for any other reason can’t be published, Sitecore will by default show one value in preview and another after publish. By tapping into these pipelines, we can improve the user experience of both the content editor and experience editor.
We typically flatten the web database(s) at publish time. This also avoids headaches when local authors doesn’t stand a chance to approve changes performed by master site editors. This also has a good side effect, by reducing time consuming fallback resolving at run-time. Strategies on clearing caches is also important as more sites and authors are added to the application. Clearing all html caches just because one page on one site is published isn’t really effective.
One need to consider what impact publishing has on the site performance. On larger sites, it’s probably not a good idea to publish items automatically as they reaches the final workflow step. It usually better to train authors that approving content means it is ready for publish and anyone can hit the publish button at any time. A scheduled publishing engine is also a must have, since the default behavior of Sitecore isn’t really logical to end users.
We also like to make item locking as seamless as possible to the authors. They shouldn’t really have to care about that. So we let Sitecore auto-lock the items in the usual way and then use an unlocking tool to ensure that items are unlocked some time after editing sessions have ended.