By Eric Stafford
It's 4:59PM Friday afternoon and your long work week is almost over; one minute away from 48 hours of non-stop, fun-filled, Mountain Dew fueled LARPing. Just as you stand up to leave, your work phone rings and it's your favorite client. Going against your better judgement, you decide to be nice and answer.
"Help Hasagn Sailorslayer! Thoust Dragon Smaug hath breathed fire on our site and tis no more!"… the client says while speaking with a horrible British accent. "I swear, all I did was a publish and now the entire site is erroring!".
The client provides you with all the vague, helpless information you've come to expect and now your weekend of throwing lightning bolts may be delayed.
After a half an hour and a couple hundred "WTFs", you remember Sitecore troubleshooting basics. You check the logs and find an object reference error on the "Main Navigation"; somehow the client deleted the Main Navigation's datasource item while ignoring common sense and Sitecore's warnings. You restore the deleted item and the crisis has been adverted, the client is happy and you begin to rethink the client's request of needing full admin privileges.
There are a lot of ways we could prevent this error from happening. First, we can deny delete permissions, set the item to protected, add try/catches, null checks and you can even place something like this on every view:
Even though I have Experience Editor specific views enabled, the code in the image above is an ugly and tedious approach. I am going to provide you with a more elegant solution to prevent datasource errors by using some code and the mvc.getRenderer and mvc.renderRendering pipelines.
Note: I've left out some minor code, you can download the solution from GitHub.
I created AddDatasourceWrapper that inherits Sitecore.Mvc.ExperienceEditor.Pipelines.Response.RenderRendering.AddWrapper. You can read more on the AddWrapper here.
On line 25, I am checking to see if the rendering item requires a datasource. I am determining this by whether the "Datasource Template" field is populated or not. If it is populated, I then check to see if the rendering's datasource item exists. If it does not, I return the DatasourceRenderingMarker that contains the error message along with the standard Experience Editor chrome.
The RenderingMarker contains the GetStart() and GetEnd() methods. GetStart() calls Placeholder.GetControlStartMarker. GetEnd() calls Placeholder.GetControlEndMarker and along with GetClientData, this is what wraps the view with that fancy Experience Editor chrome. You can also add custom markup here like I am on lines 21-25, but be sure to close all the HTML tags; the Experience Editor tends to hate broken HTML.
Note: You'll need to merge this into your site's CSS.
The custom markup, along with the CSS, produces the following stylized error message to inform the Content Author of the issue:
I am only concerned with two rendering types, Controller Renderings and View Renderings. If the rendering requires a datasource and that datasource is empty or contains a guid to a non-existent item, I set the view path to a blank view. I also want to make sure to log this for troubleshooting purposes. I decided to split these log entries into separate log files so it's easier to find.
Disclosure: I started this proof of concept on December 7th; I have not had the opportunity to fully integrate and test it on a real project. It has been tested on Sitecore version 8.1 and 8.2. Apologies if I forgot something.
The source code can be found and DOWNLOADED(!) from GitHub.
Do you have an interesting idea and no time to research it? Feel free to share. Also, if you have any questions or comments, please feel free to share them. Tweet me or message me on Skype (erickstafford) to get the conversation started.