I’ve recently been sparring with a very annoying Sitecore error. It occurs only in Experience Editor, after the Rendering Parameter properties dialog of a rendering is opened, and the user then tries to save the page.
This particular Experience Editor error is very frustrating because:
1. It blocks Content Admins from doing their work, and…
2. The fix involves manually tweaking JavaScript files on every affected Sitecore instance.
Fix – Take 1
I’ve been working with Sitecore 8.1 instances for the past year (from initial release to most recent update), and have been fixing the issue all over the place by following the advice from this Stack Overflow post: http://stackoverflow.com/questions/35029713/save-return-error-in-sitecore-page-editor/35443212#35443212.
That post identified the JavaScript file and function that was the source of the issue and recommended corrections, which worked perfectly. Easy peasy.
Now Sitecore 8.2 is out, and our function has received a facelift (functionlift?). The error still occurs, and the fix from above no longer helps.
Fix – Take 2
I found a newer Stack Exchange post — http://sitecore.stackexchange.com/a/2647 — which gave us a solution for 8.2 from Sitecore Support.
Unfortunately, it did not resolve the error for me. Could be because that post was for 8.2 Initial Release and I’m on 8.2 Update 1. But the JavaScript function where the error is happening is encoding presentation details XML, and the structure of presentation details XML has not changed, so it should be possible to support all 8+ versions with the same code.
I decided to dig in myself. Here are my findings:
Sitecore Support Code
Instructions are to go to Website/sitecore/shell/client/Sitecore/ExperienceEditor/ExperienceEditor.js and to replace the code for postServerRequest function with this updated version.
postServerRequest: function (requestType, commandContext, handler, async) { var token = $('input[name="__RequestVerificationToken"]').val(); var ajaxData = unescape(JSON.stringify(commandContext)); if (commandContext && commandContext.scLayout){ var obj = JSON.parse(commandContext.scLayout); if (obj && obj.r && obj.r.d && obj.r.d.forEach){ obj.r.d.forEach(function(x,y){ if (x.r && x.r.forEach) { x.r.forEach(function(a,z){ var val = a["@par"]; if (val && val.length > 0){ ajaxData = ajaxData.replace(unescape(val), val); } }); } }); } } jQuery.ajax({ url: "/-/speak/request/v1/expeditor/" + requestType, data: { __RequestVerificationToken: token, data: ajaxData }, success: handler, type: "POST", async: async != undefined ? async : false }); }
Identity Crisis
It turns out that the type of obj.r.d varies depending on whether your page is using one or multiple device layouts. This property can be either an object or an array.
Seriously, check it out:
That’s why the code from Sitecore support didn’t work for me. That version depends on obj.r.d being an array since it calls obj.r.d.forEach. We typically use a single device layout for our pages (there’s no need for a Print layout since custom print displays can be handled with CSS media queries), so obj.r.d is an object and obj.r.d.forEach is undefined. As a result, the check “if (obj && obj.r && obj.r.d && obj.r.d.forEach)” just fails silently and the dreaded Experience Editor red ribbon of doom appears.
Fix – 3rd Time’s A Charm
Here is my version of the fix. It normalizes the obj.r.d property, so it won’t matter how many layouts you have.
postServerRequest: function (requestType, commandContext, handler, async) { function normalizeDeviceProp(d) { if (typeof(d) !== "object") throw new Error("Unexpected presentation details XML: cannot find device property"); if (d instanceof Array) return d; var normalized = []; normalized.push(d); return normalized; } var token = $('input[name="__RequestVerificationToken"]').val(); // Custom Brainjocks code to fix Experience Editor error. var ajaxData = unescape(JSON.stringify(commandContext)); if (commandContext && commandContext.scLayout) { var obj = JSON.parse(commandContext.scLayout); if (obj && obj.r) { normalizeDeviceProp(obj.r.d).forEach(function (d) { if (d.r instanceof Array) { d.r.forEach(function (r) { var val = r["@par"]; if (val && val.length > 0) { ajaxData = ajaxData.replace(unescape(val), val); } }); } }); } } jQuery.ajax({ url: "/-/speak/request/v1/expeditor/" + requestType, data: { __RequestVerificationToken: token, data: ajaxData }, success: handler, type: "POST", async: async != undefined ? async : false }); }
If you have any feedback or questions on resolving this Experience Editor error, please let me know in the comments. Thanks!
Hi, You customized solution was helpful. Thank you
This was a great fix. Haven’t had a chance to look into what Sitecore support actually gave us, but this one worked right away. Thanks!
Thank you. This one fixes my issue.
Lifesaver! Thank you!
Thank you very very much for sharing this.
Hi Anastasiya,
You saved my day, thank you!
Great work, this worked perfectly for me in Sitecore 8.2, update 5. Thank you so much for posting your findings!
Thanks for the great post. I did have to make one change to your code for Sitecore 8.2 Update 6. Basically, handling the case were d.r is not an array.
postServerRequest: function (requestType, commandContext, handler, async) {
function normalizeDeviceProp(d) {
if (typeof(d) !== “object”)
throw new Error(“Unexpected presentation details XML: cannot find device property”);
if (d instanceof Array)
return d;
var normalized = [];
normalized.push(d);
return normalized;
}
function fixAjaxData(r) {
var val = r[“@par”];
if (val && val.length > 0) {
ajaxData = ajaxData.replace(unescape(val), val);
}
}
var token = $(‘input[name=”__RequestVerificationToken”]’).val();
// Custom Brainjocks code to fix Experience Editor error.
var ajaxData = unescape(JSON.stringify(commandContext));
if (commandContext && commandContext.scLayout) {
var obj = JSON.parse(commandContext.scLayout);
if (obj && obj.r) {
normalizeDeviceProp(obj.r.d).forEach(function (d) {
if (d.r instanceof Array) {
d.r.forEach(function (r) {
fixAjaxData(r);
});
} else {
fixAjaxData(d.r);
}
});
}
}
jQuery.ajax({
url: “/-/speak/request/v1/expeditor/” + requestType,
data: {
__RequestVerificationToken: token,
data: ajaxData
},
success: handler,
type: “POST”,
async: async != undefined ? async : false
});
}
Thank you so much! Fix 3 worked for me. You’re a lifesaver