Friday 19 December 2014

SharePoint publishing pages: Prompting the user when they might lose changes

Yes, so first of all apologies for the slightly wordy and not particularly "on point" title for this post - I can't quite how to name it, hence the above...

The problem

Anyway, what this post is about is how to give users creating and editing publishing pages in SharePoint a better experience when leaving a page that is in edit mode. This arose from a customer complaining that if they accidentally (or even purposely) click on the back button, or hit F5 or even click on a link somewhere whilst editing a page they get whisked away without a shadow of a warning meaning that there will be a high chance of losing any unsaved work.

I wanted a quick a dirty solution for this scenario that works in SharePoint Online, which means JavaScript. I'm not claiming that what I'm about to show you is the best possible way to manage this, but it works and the customer was really happy that I was able to throw a solution together for them so quickly.

The solution

Standard javascript gives you the window.beforeunload event to hook up to and assign a function that fires each time that event is triggered. window.beforeunload has quite a wide catchment of possible triggers, namely (docco courtesy of MSDN):

  • Close the current window.
  • Navigate to another location by entering a new address or selecting a Favorite.
  • Click an anchor that refers to another document.
  • Invoke the anchor.click method.
  • Invoke the document.write method.
  • Invoke the document.close method.
  • Invoke the window.close method.
  • Invoke the window.navigate or NavigateAndFind method.
  • Invoke the location.replace method.
  • Invoke the location.reload method.
  • Specify a new value for the location.href property.
  • Submit a form to the address specified in the ACTION attribute via the INPUT type=submit control, or invoke the form.submit method.
  • Invoke the window.open method, providing the possible value _self for the window name.
  • Invoke the document.open method.
  • Click the Back, Forward, Refresh, or Home button.

This may be too wide a catchment for your liking/requirements, if it is there are other events you can hook into, or you could filter the events you aren't interested in the function itself (I highly doubt that this could ever be considered good practice, especially if there's an event you could hook up to instead that already misses out the scenarios you don't need).

Within a SharePoint publishing page (the context I'm writing this for), you can check many things using JavaScript, including whether a page is in edit mode. SP.Ribbon.PageState.Handlers.isInEditMode() evaluates as true/false depending on, you guessed it, whether or not the page is in edit mode. Crazy times.

However, you must be careful to test the use of functions like this, as SharePoint commonly restricts the loading of unnecessary JavaScript functions to minimise the JavaScript payload. The whole SP.Ribbon function set only gets loaded when the ribbon is being displayed (what a shock) and so I've thrown in a check to see whether or not the ribbon JS is loaded prior to trying to do anything with it.

On top of this, there are some elements of the ribbon that a user can click that actively preserve or save any changes that might have been made on the page whilst it was in edit mode, namely Save, Check-in and Publish - it would be odd to throw a warning to the user about leaving the page in a context where they've just asked to save or publish it, so I've filtered that out based on the object passed through the onbeforeunload event.

Anyway, that's probably enough chatter, here's the final commented code. I placed this into the PlaceHolderAdditionalPageHead content placeholder of the page layout for article publishing pages that my customer's users work with:

<script type="text/javascript">//<![CDATA[
//hook into onbeforeunload
window.onbeforeunload = function (e) {
//make sure that SP.Ribbon functions are loaded
if(SP.Ribbon)
{
//check if were in edit mode
if(SP.Ribbon.PageState.Handlers.isInEditMode())
{
var e = e || window.event;
//check to make sure we ignore save, checkin and publish
if(e.srcElement.activeElement.id != "Ribbon.EditingTools.CPEditTab.EditAndCheckout.SaveEdit-SelectedItem"
&& e.srcElement.activeElement.id != "Ribbon.EditingTools.CPEditTab.EditAndCheckout.Checkout-SelectedItem"
&& e.srcElement.activeElement.id != "Ribbon.PublishTab.Publishing.Publish-SelectedItem")
{
//configure this message how you like
var msg = "Do you really want to leave this page? You will lose any unsaved changes if you do!"

// For IE and Firefox
if (e) {
e.returnValue = msg;
}

// For Safari / chrome
return msg;
}
}
}
};   
//]]>         
</script>
And there you have it. The onbeforeunload event handles the display of the message box to the user including the two options they are given, it'll look like this (in Chrome):


If they click the "Stay on this Page" button, then the event is cancelled and the browser window state remains unchanged. If they hit "Leave this Page" then, well, I'll let you work that one out...

Caveat

One scenario where this could prove annoying (unless you're going to add some more 'ignore this type of event' clauses into your script) is where users will be adding a lot of web parts to pages as they cause a page refresh, which triggers the script as things currently stand. I'm not in that scenario at the moment so have ignored it. Doubtless it is the first bit of feedback I'll get once it's rolled out and I'll have to make it better. For what it's worth, inserting images, embedding video and things like that do not trigger the script, which is nice.

- rob

No comments:

Post a Comment