Solving some common Sitecore component problems
In Sitecore you have the ability to add sublayouts (also known as components) to placeholders on your page. Sublayouts can point to a datasource item that is used as (you've guessed it) data source during rendering of the sublayout.
There are a few ways to do this, generally by:
- Presetting the sublayout on a placeholder through the page template's Standard Values
- Use the Page Editor to add the sublayout to a placeholder after the page item has been created.
Both methods have a flaw that I wanted to eliminate and this blog post describes those flaws and the methods I used to get rid of them.
So what problems did I run into?
Problem 1: Can't set datasource items for preset components
When you preset sublayouts on the template's Standard Values, there is no way to set a datasource item for them in the preset because it's generally unknown before the page item is created.This is especially problematic if you're going to use a sublayout both as preset component and as Page Editor component. Ideally you want to force the user to select or create a datasource item upon creation of the page item.
Problem 2: The "Select the Associated Content" dialog is only available when adding a component
When setting the datasource item in the Page Editor, only when you add a new component to a placeholder, do you get a user-friendly datasource selector (the "Select the Associated Content" dialog, see figure 1). This dialog allows the user to select or create the datasource item for that component. However, when the user needs to change the configured datasource item, there is no way to get this dialog again (see figure 2). So even if we get our preset sublayouts to point to a datasource, the user still has no user-friendly way to change it (only when they remove it and add it as new component).
Figure 1: The associated content dialog. We would like this to be available at all times, not just when adding a new component.
Figure 2:This is the dialog you get now when you want to change the datasource item of a component
Problem 3: Datasource links are stored as internal link
The links to datasource items are stored as internal link field and therefore they stored by path instead of ID. Moving them around will result in broken links.
For every problem there is a solution
After some googling, posting a Stackoverflow question and talking to Sitecore support, I got to solve all of the above problems.
Solution 1: Creating datasource items for preset components upon item creation
When the sublayout is preset on a template using the Standard Values, I want the data source item to be created upon item creation. That can be done by implementing an eventhandler for the item:created event. This idea was brought up by Mark Ursino on Stackoverflow.
First I've added a new field, "Requires datasource", to the Rendering Options template that is inherited by the Sublayout and Xsl Rendering templates (see figure 3). This field indicates whether or not the sublayout requires a datasource item to be set in order to operate properly.
Figure 3: Add a new field to the rendering options
Then I wrote an eventhandler that is triggered on item:created, checks for configured sublayouts and creates their datasource items if needed.
The source code for the eventhandler is available here: CreateDataSource.cs
You need to configure the eventhandler in your web.config or separate include config like this:
<sitecore>
<events>
<event name="item:created">
<handler type="ParTech.Library.Events.ItemEventHandler, ParTech.Library" method="OnItemCreated" />
</event>
</events>
</sitecore>
And there you have it!
If you have an item template with a sublayout preset on the standard values, that requires a datasource item, the datasource item will now be automatically created (see figure 4 and 5)
Figure 4: Datasource item is automatically created
Figure 5: Mission accomplished!
There is one thing you need to look out for: the datasource item's template should never be the same as the template of the item being created. That would cause the eventhandler to trigger an infinite loop.
Solution 2: Make the "Select the Associated Content" dialog available in the Page Editor
Getting the user-friendly dialog is a little less straight forward and I didn't bother to figure it out for myself. I discussed the issue with Sitecore support and they provided me with a solution that is already built-in to version 6.6, which is not stable yet. They create a new web edit command which opens the Associated Content dialog. In order to use this functionality in version 6.5 you need the add the command manually as follows:
Add the command class to your solution, download it here: SetDatasource.cs
Add the command to the Commands.config:
<command name="webedit:setdatasource" type="ParTech.Library.Commands.SetDatasource, ParTech.Library" />
Create a new web edit button in the Core database as seen in figure 6.
Figure 6: Add a new web edit command in the core database
If you go to your Page Editor now and select a component, you will see the new command button.
Clicking the button will open that lovely Associated Content dialog!
Figure 7: The newly added page editor command
Solution 3: Ensure that the ID is used for datasource item links
This is a pretty big flaw that could have easily been prevented.
I wasn't the first person to run into this. It has actually been discussed a lot before so there are solution readily available. If you replace the datasource item path with the item ID, it will work right away without any modifications.
The thing is that Sitecore will still fill in the path instead of the ID.
Nick Wesselman (Techphoria) has provided a solution for this that uses the item:saving event.
There is also a solution by John West out there that works with the Rules Engine, but I'm old-fashion, so I went with the eventhandler.
Add the eventhandler class to your solution, download it here: EnsureDataSourceIsGUID.cs
You need to configure the eventhandler in your web.config or separate include config like this:
<sitecore>
<events>
<event name="item:saving">
<handler type="ParTech.Library.Events.EnsureDataSourceIsGUID, ParTech.Library" method="OnItemSaving"/>
</event>
</events>
</sitecore>
Now every time you save an item, this code will find datasource items that are linked by path and replace them with their ID.