Wednesday, March 1, 2017

Local datasources on standard values with branch templates

Local datasources module

The local datasource module on the Sitecore marketplace has a second version. The first version managed to create local datasource items when editing in the experience editor. In this second version we anticipated on renderings that are added by default on a template.

Branch templates with "standard values"

The idea was simple: 
  1. create a branch template based on the page template
  2. edit the created item (in the branch template - probably called $name) in the experience editor
  3. add a rendering with a local datasource (the local datasource module will create the children - a data folder and the datasource item)
  4. repeat 4 if wanted (add more renderings)
  5. use the branch template when creating items
Sounds simple, but of course when creating the items the datasource value of the rendering is still set to the item in the branch templates section - not to the child item we just created.

To fix that we added some code that I'll explain here. Code can be found on GitHub.

Event

I used the item:added event and not the item:created event. We need to be able to detect whether the item was created from a branch and can use the BranchId property of the Item for that. The item:created event however comes too soon to do that as the BranchId is not yet set at that moment. In the item:added event, we can check the BranchId. 

So we create a handler to add to item:added and patch it as first handler: patch:before="*[1]".

OnItemAdded

Our main function starts with some checks to get out as fast as possible when it is not applicable. When it is, we call our action method for the current item and all descendants. 
In case of a branch, the item:added event is called only once for the main item. 
But as this is a branch, we might also have a child with local datasources set. So we check all children - they are already created at this time.

Correct the datasources

For each item with a layout (item.Visualization.Layout != null) we grab the renderings and change the datasource value if it should be local.
var layout = LayoutDefinition.Parse(item[Sitecore.FieldIDs.LayoutField]);
var devices = layout.Devices;
var changed = devices.Cast<DeviceDefinition>().Sum(device => SetDatasourcesToLocal(device, item));
if (changed > 0)
{
    UpdateLayout(item, layout, Sitecore.FieldIDs.LayoutField);
}
We use the Parse method to get a LayoutDefinition of the "Shared layout" field. This does mean indeed that we only cover renderings placed on the shared layout. Doing the same for a rendering on the final layout is for a future version...

We loop through the devices and call a function to change the datasource values. This function loops over all renderings and checks the datasource item. If the datasource items path starts with /sitecore/template we know this was meant to be a local one. To find the correct local item, we explicitly go into the local data folder because a search over all descendants might also find a similar datasource item on another page in the branch. Once found, we set the value to the rendering in the layout.

Saving the changes

We keep track of the changes made because they are not automatically saved back into the item. The "layout" object has all the changes, but the item does not. So if we had any changes, we save the new value back into the shared layout field:
item.Editing.BeginEdit();
item.Fields[Sitecore.FieldIDs.LayoutField].Value = layout.ToXml();
item.Editing.EndEdit(false, false);

Conclusion

Version 2 is now published on the marketplace, and to be honest version 3 will not be for any time soon. But if you like the module, I'm always open for comments, ideas and contributions...

Enjoy the local datasources!

3 comments:

  1. Isn't the item:added event technically deprecated? Why not use the new pipeline included in the relatively new Item Provider pipelines? I use the rules engine to do pretty much the same thing that you are doing here, using the pipeline to kick everything off, so I know it works for this. Have a look at this blog post that I wrote about a year ago for more about that pipeline and feel free to reach out or post up on SSE if you have questions: http://zacharykniebel.com/blog/sitecore/2016/march/30/using-sitecores-new-addfromtemplate-item-provider-pipeline

    ReplyDelete
    Replies
    1. Thx for the feedback - didn't know about that pipeline yet. And by checking that out, I even found some code from Kamsar doing exactly what I need. I'll have a chat with him to see what I can use for further improvements in a next version. Gotta love this community :)

      Delete
  2. I wanted to thank you for this great read!! I definitely enjoying every little bit of it I have you bookmarked to check out new stuff you post.
    Whmcs templates

    ReplyDelete