This was a triumph.
I'm making a note here: HUGE SUCCESS.

Search This Blog

Wednesday, February 12, 2014

How to have a discussion board web part with modal dialogs and auto-refreshing content in SharePoint 2013

Imagine you added a discussion board as a web part to a page. Whenever you want to add a new discussion, by default it will always open in a new page rather than in a modal dialog (even if you ticked the "Display forms in a modal dialog" option under "Advanced settings" of the discussion board). As a matter of fact, existing discussions also open in new pages.

For my end users, this is annoying since they will lose track of the page they were originally on. I made it so that they will always know on what page they are, by highlighting the link of the current page in the term-driven subsite navigation. And when they want to create a new discussion or want to look at an existing one, that term-driven subsite navigation can't tell you the path of the "AllItems.aspx" page your user has ended on.

So! In order to stick to the page that has the discussion board web part, I had to write a small script.

How do we create a new discussion or open an existing discussion in a dialog rather than on a new page?

You'll need two scripts for this: one that runs only on the page that has the discussion board web part, and one that is referenced on the master page (so that it will always run, on any page, regardless whether it has a discussion board or not).

I named the first script "discussion-board.js" and gave it the following code. Comments have been added to the code to further explain it.
// When called, this function opens the dialog.
function openDialog(pUrl) { 
   var options = {
      url : pUrl,
      dialogReturnValueCallback: OnDialogClose
   };
   SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', 
    options);
}

// When the user closes the dialog by either pressing OK when adding a new 
// item, by clicking the cancel button or by closing it with the X on the 
// top right corner, this function will run.
function OnDialogClose(dialogResult, returnValue) {
   // The line below will refresh the content of the web part, by acting as 
   // if the refresh button was clicked. 
   $('.ms-comm-refreshIcon').trigger('click');
   // The line below will run the clickMe() function, I also added a timeout
   // because I noticed that it sometimes doesn't run properly. This timeout 
   // should make sure that the function will always run. 
   clickMe();
   setTimeout(function() { 
      clickMe();
   }, 500);
}

// When called, this function makes sure that a new discussion or an existing
// discussion is opened in a modal dialog instead of on a new page.
function clickMe() {
   // The lines below replaces the value of the default onclick and href
   // attributes so that it won't open on a new page, but in a dialog. 
   $('a[href*="NewForm.aspx"]').each(function() {
      $(this).attr('onclick', 'openDialog("' +  $(this).attr('href') + '")');
      $(this).attr('href','javascript:void(0)');
   });
   // Same for the following lines, when the user is on the page that 
   // contains the discussion board web part, we want the existing 
   // discussions to be displayed in a dialog as well rather than on a new 
   // page. 
   $('a[href*="Forum.aspx"]').each(function() {
      $(this).attr('onclick', 'openDialog("' +  $(this).attr('href') + '")');
      $(this).attr('href','javascript:void(0)');
   });
}

// When the window first loads, we want to be sure that the discussions are 
// already set to open in a dialog. So we will run the clickMe() function on
// load, and set the timeout again just to be sure (in my case, it doesn't 
// work without the timeout).
window.onload = function () {
   clickMe();
   setTimeout(function() { 
      clickMe();
   }, 500);
}; 

// The lines below are needed as well, because when a user changes the view 
// of the web part (to for example "Recent" or "My discussions"), then the
// values of the attributes  href and onclick get back their original value.
// We don't want a user to see the discussions on a new page, so we once 
// again set it so that the clickMe() function runs as soon as the user 
// clicks anywhere inside the content div (which in my case, has "content" 
// for its ID. 
$("#content").click(function() {
   clickMe();
   setTimeout(function() {
      clickMe();
   }, 500);
});

So that's the first script that you'll need. Include this script to the page that contains the discussion board web part. Do so by adding a script editor web part at the bottom of the page and add the following code tot it:
<script src="/Style%20Library/Scripts/discussion-board.js" 
 type="text/javascript"></script>

Please do note that your path may be different, so change it if necessary.

And now for the script that has to have a reference on the master page. I already have a file called "scripts.js" which runs on every page, so I just added the following code to that script:
var ref = document.referrer; 
var url = window.location.pathname; 
$(document).ready(function(){
   // If the current url ends with "AllItems.aspx" and the previous url 
   // contained "IsDlg=", stating that it was a dialog, then run the 
   // following code. 
   if ( (url.indexOf('AllItems.aspx') > -1 && ref.indexOf('IsDlg=') > -1) ) {
      // The line below will close the dialog. 
   SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.Cancel);
   }
   else {
      // Do nothing.
   }
});

The reason why we have to state that the dialog has be closed, is because when you delete a discussion through a modal dialog, it will show you the AllItems.aspx page inside the dialog instead of returning to the original page (in my case, Forum.aspx). So to fix this, I check if the dialog came from a page that had "IsDlg=" in the url, and if the url of the dialog is currently "AllItems.aspx" then that must mean that a discussion was deleted. So then we can close the dialog.

Check if your second script is added to your master page. I use a HTML master page so my reference looks like this:
<!--SPM:<SharePoint:ScriptLink language="javascript" OnDemand="false"
  name="~sitecollection/Style Library/Scripts/scripts.js"
  ID="scriptLink4" runat="server" Localizable="false"/>-->

Again, do note that your path may be different, so change it where necessary.

If you did everything as I explained it here, you will now have a discussion board web app on a page that will:
  • open all existing discussions in a modal dialog;
  • open all new discussions in a modal dialog;
  • automatically refresh the content of the web part whenever a new discussion is adde;
  • automatically refresh the content of the web part whenever an existing discussion has been edited or deleted;
  • returns to the page with the web part and closes the dialog after deleting a discussion.

As always, if you have any questions or need any help do let me know by placing a comment!
This post can also be found as an answer on SharePoint StackExchange.