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.

6 comments:

  1. Thanks for the great post. The code is working for the new discussion (NewForm.aspx) page but for some strange reason, I don't have a "forum.aspx" page. I am using SharePoint 2013 and all I have is DispForm.aspx, EditForm.aspx and NewForm.aspx. When I open up a discussion I get a long URL like the following: http://sps.xxx-xxx.ca/projects/Template01/Lists/Project%20Discussion%20Board/Flat.aspx?RootFolder=%2Fprojects%2FTemplate01%2FLists%2FProject%20Discussion%20Board%2Fdiscussion%201&FolderCTID=0x01200200A1AE8634EA9746458EA0C8C91D2CCF9A.

    Is there anything I can do about this?

    Thank you for your help.

    ReplyDelete
    Replies
    1. Hi Brent,

      It's normal that you don't have a "forum.aspx" page by default. As I mentioned in the comments above that part of the code, it applies to the page that contains the discussion board wep part. So instead of "forum.aspx", you just need to fill in the name of the page on which the discussion board will be. In my environment I have multiple pages with a discussion board on it but they all end with the name "forum.aspx". If your environment has pages with discussion boards on them but have different names, then you might need to repeat that piece of code for each individual page holding a discussion board.
      So try replacing the "forum.aspx" part of my code with the name of the page holding your discussion board, and see if that works. If not, check the console for errors and let me know so that I can help you further to find the problem. :)

      Delete
    2. This still works great, thanks.

      Delete
  2. Hi Magali, so glad i found this page! I'm trying to do the same thing but cannot get it to work. I have a page with a discussion web part but when i click on "new discussion" or the title of a thread, it still goes to the discussion list page instead of opening a modal. I changed the URL from forums.aspx to the url of the page where the discussion board web part is located but no luck.

    ReplyDelete
    Replies
    1. Are you sure the clickMe function is called? Or maybe the url of a new discussion isn't newform.aspx but something else?

      Delete
  3. Not sure why but changing out the OpenDialog function to:

    function openDialog(url) {
    var pageUrl=url;
    var title="New Discussion";
    SP.UI.ModalDialog.showModalDialog(
    {
    url: pageUrl,
    autoSize: true,
    title: title,
    dialogReturnValueCallback: function (result){
    if(result== SP.UI.DialogResult.OK){
    //refresh parent window
    window.location.href=window.location.href;
    }
    }
    }
    );
    }


    from https://social.technet.microsoft.com/Forums/en-US/f18062ed-2e17-440e-8e00-2904f5316802/discussion-board-forum-opens-in-other-page?forum=sharepointdevel

    and keeping the "(script src="http://code.jquery.com/jquery-1.10.2.min.js" type="text/javascript"></script)" piece fixed this for me on Sharepoint 2013.

    ReplyDelete