I recently had the honor of speaking at Dreamcore North America. Due to time constraints, I had to run through the details pretty quickly. In this series, I will go into the topic in more depth than I could in the 15 minutes I had for the Dreamcore presentation.
Sitecore’s architecture is very flexible. However, every system has its limitations. By default, Sitecore only loads presentation settings from the context item. This leads to the concept of page-level items. Page-level items, are items that have presentation settings and represent a page in the website. Conversely, non-page items hold supporting content and are generally not meant to be visited directly by a browser.
Non-page items can be useful for several reasons. They can hold content that needs to be shared among pages. They can represent a pool of content from which a selection is made on a page-level item. They can also represent repeating elements on an ancestor page-level item. With each of these uses, the content in these non-page items needs to be rendered somehow on a page along with the content from some page-level item.
Specifying the presentation controls for these non-page items within the presentation details of the page-level item is ok, if you are only dealing with a single known type of non-page item. However, there are times when you have multiple types of non-page items that need to be rendered on the page using different presentation controls. Sometimes, we may not even know the complete set of presentation controls during the initial build. Sitecore’s architecture can be somewhat limiting in these situations. Fortunately, Sitecore is infinitely extensible, so you don’t have to reach very far for a workaround.
At Aware, many of the sites that we develop include callouts. These are small elements on the page that highlight featured or related content. We often take it a step further to include elements that provide some functionality as well. The following rough wireframe shows examples of some callouts in the right column.
Usually when a site includes these callouts, requirements dictate that content authors should be able to add a given callout to any page in the site. Inversely, authors also need to be able to add any of the available callouts to a given page. To enable this sharing of content, we store the content items that define the callouts in a global folder.
As the wireframe implies, the callouts can be very diverse. They often are based on several different data templates and require different presentation controls. Usually, there is a basic content callout type that is used for most of the callouts. It would just have, for example, fields for a Title, Image and Body. Then there are generally a few more unique callout types. Some of them are functional rather than content-based like the search and poll callouts in the wireframe.
Traditional Approach: Conditional Statements
A traditional approach to solving this problem, particularly in Sitecore 5.x, would have been to use hard-coded conditional statements. The data template for a page item would include a treelist or multilist field from which the content author could select which callouts he or she wanted to appear on a given page. Then, there would be a sublayout or web control responsible for rendering all of the selected callouts. This control would loop through the selected callouts and based on what template each one was based on, it would load a different control or execute a different method.
This method is not ideal for several reasons. First and foremost, you have to hard-code your conditions and the mapping between templates and presentation controls. If you add a new type of callout after the initial build, you have to recompile your whole project.
If you want to support multiple devices, it gets messy very quickly. You either have to have multiple sublayouts with these hard-coded conditions, or make your one sublayout bloated with even more conditions.
Caching support is also complicated. You could cache the parent sublayout as a whole, but since you are generally using standard .NET controls rather than Sitecore renderings, you can’t cache the rendered callouts individually. If some of your callouts render differently on postback, you can’t even cache the parent sublayout because that would interfere with that functionality.
Traditional Approach: Page Designer
In Sitecore 6, the standard solution to this problem is usually to give the content authors access to the page designer. The author would have to add controls to placeholders on the page and then go off and select a data source. This obviously puts more of a burden on authors. Not only it there an extra step compared to the previous method, but the authors now have to have knowledge of the presentation controls.
Making content authors select a data source for a presentation control can be particularly problematic for the callouts example. There are callout items based on different templates sitting right next to each other in the content tree. It would be very easy for the author to select a data source that does not have the fields that the presentation control is looking for. This would most likely cause errors.
Making authors use the page designer interface also interferes with some of the better features of Sitecore. Since the Renderings field is a shared field, you cannot have different settings for different versions of a content item. This means that when you change the presentation details, the changes do not go through workflow. Also, you cannot use publishing restrictions to schedule a change to the presentation details for some future time.
Another big problem with using the page designer is that it breaks the association with the template standard values. This makes it difficult to make a site-wide change the presentation settings. You would have to make the change to the standard values item and then hunt through the content tree for items that are no longer associated with the standard values so that you could apply the same change. With the callouts example, every page in the site could end up overriding the standard values, making them pretty much useless.
Presentation Inversion of Control
Since the traditional approaches are less than ideal, we need a new solution. The problem would be much more manageable if rather than storing all presentation settings on the page-level items, the non-page items could hold their own presentation settings. Then, the non-page could be added to the page and they would “know how to render themselves”. The concept is very similar to the ideas of IoC or dependency injection, hence the rather unimaginative name.
Offloading the storage of presentation settings to the non-page items themselves has many potential advantages. We would not need to hard-code conditional statements into the rendering logic. It would make it easier to add components after the initial build, even making it possible to do without recompiling the whole project. If done properly, each non-page item could take full advantage of Sitecore’s standard caching functionality. Adding and removing non-page items can be done with a versioned field (e.g. a treelist) so changes would go through workflow and could be scheduled for publishing at a future time. Finally, it would not interfere with the standard values functionality that is so critical for easy maintenance of a Sitecore implementation.
In the next part of this series, I will detail several methods of implementing this concept.