Tom Clarkson

SharePoint, Startups and some other stuff

SharePoint Discussion with jQuery

with 9 comments

Recently I have been doing some work for a large bank with some rather restrictive policies on their SharePoint environment – no custom code can be installed. SharePoint Designer used to be an option, but with some changes to VPN settings I’m limited to IE6 on a painfully slow java  remote desktop that changes the quotes in my code to umlauts.

The sites I was building make extensive use of discussion lists, but the out of box web parts weren’t really adequate – the threaded view in particular doesn’t like appearing on custom pages.

The requirement was for a web part that could display recent posts with threaded comments on the site home page – similar to the blog template, but a bit more flexible. I’ve done similar things in the past, building a custom web part to display comments with a news article, but that wasn’t an option in this environment.

What I came up with was some javascript that could be pasted into a content editor web part to render the required view with jQuery. I kept the rendering and web service access fairly well seperated, rendering from a simple custom data structure – partly to simplify the code and partly so I could debug the javascript independent of any SharePoint issues.

I used Darren Johnstone’s JSAPI for web service access – a very useful piece of code, and much easier than the manual approach I used last time I had to use sharepoint web services from javascript.

The included javascript files (jquery, jsapi and optionally the code below if you want reuse instead of quick copy/paste editing) can be placed in any document library with appropriate permissions.

First, the shared functions – one to retrieve the list data and put it into a structure more easily manipulated in javascript, and another to render the html.

function GetTopLevelPosts(weburl, listname, listurl, postlist, src) {
    var lists = new SPAPI_Lists(weburl);
    var res = lists.getListItems(
               listname,
                "",
                "",
                '',
                10,
'TRUE',

                 null);
    //alert(res);
    var rows = res.responseXML.getElementsByTagName('z:row');
    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];

        var fn = row.getAttribute("ows_FileRef");
        var ih = fn.indexOf("#") + 1;
        fn = fn.substr(ih, fn.length - ih);

        var t = row.getAttribute("ows_Body");

        var np = {
            Title: row.getAttribute("ows_Title"),
            Threading: row.getAttribute("ows_Threading"),
            Date: row.getAttribute("ows_Created"),
            PostedBy: row.getAttribute("ows_PersonViewMinimal"),
            Text: t,
            ReplyLink: listurl + "/NewForm.aspx?RootFolder=/" + fn +

"&ContentTypeID=0x0107&DiscussionParentID=" + row.getAttribute("ows_ID") + "&Source=" + src,
            MoreLink: listurl + "/Threaded.aspx?RootFolder=/" + fn + "",

            Replies: []
        };

        var att = row.getAttribute('ows_Attachments');

        if (att != '0') {
            var at2 = att.split(';#');
            np.AttachmentUrl = at2[1];

            var atfnl = np.AttachmentUrl.split('/');
            np.AttachmentName = atfnl[atfnl.length - 1];
        }
        postlist[postlist.length] = np;

        GetChildPosts(lists, listname, listurl, fn, np.Replies, src);

    }

}

function GetChildPosts(lists, listname, listurl, fn, postlist, src) {

    var unthreaded = new Array();

    var res = lists.getListItems(
               listname,
                "",
                "0",
                '',
                100,
                '/' + fn + 'TRUE',
                 null);
    //alert(res);

    var rows = res.responseXML.getElementsByTagName('z:row');

    for (var i = 0; i < rows.length; i++) {
        var row = rows[i];

        var t = row.getAttribute("ows_Body");
        var ii = t.indexOf("= 0) t = t.substr(0, ii);

        var np = {
            Title: row.getAttribute("ows_Title"),
            Threading: row.getAttribute("ows_Threading"),
            Date: row.getAttribute("ows_Created"),
            PostedBy: row.getAttribute("ows_PersonViewMinimal"),
            Text: t,
            ReplyLink: listurl + "/NewForm.aspx?RootFolder=/" + fn +

"&ContentTypeID=0x0107&DiscussionParentID=" + row.getAttribute("ows_ID") + '&Source=' + src,

            Replies: []
        };

        var att = row.getAttribute('ows_Attachments');

        if (att != '0') {
            var at2 = att.split(';#');
            np.AttachmentUrl = at2[1];
            var atfnl = np.AttachmentUrl.split('/');
            np.AttachmentName = atfnl[atfnl.length - 1];
        }
        unthreaded[unthreaded.length] = np;

    }

    for (var i = 0; i = -1; j--) {
            if (j < 0) {
                postlist[postlist.length] = unthreaded[i];
            }
            else {
                if (unthreaded[i].Threading.indexOf(unthreaded[j].Threading) == 0) {
                    unthreaded[j].Replies[unthreaded[j].Replies.length] = unthreaded[i];
                    break;
                }
            }

        }

    }

}

function RenderPosts(parentdiv, postlist) {

    for (var i = 0; i < postlist.length; i++) {
        var post = postlist[i];
        //console.log(post.Text);
        var postdiv = $(document.createElement("div"));
        var posttextdiv = $(document.createElement("div"));
        var postcommentdiv = $(document.createElement("div"));
        var postheaderdiv = $(document.createElement("div"));
        var postfooterdiv = $(document.createElement("div"));

        parentdiv.append(postdiv);
        postdiv.append(postheaderdiv);
        postdiv.append(posttextdiv);
        postdiv.append(postfooterdiv);
        postdiv.append(postcommentdiv);

        postdiv.addClass("post");
        postheaderdiv.addClass("postheader");
        posttextdiv.addClass("posttext");
        postfooterdiv.addClass("postfooter");
        postcommentdiv.addClass("postcomment");

        postheaderdiv.html(post.PostedBy + " - " + post.Date + " Reply");
        posttextdiv.html(post.Text);
        postfooterdiv.html("");

        RenderPosts(postcommentdiv, post.Replies);

    }

}

function RenderTopLevelPosts(parentdiv, postlist) {
    for (var i = 0; i < postlist.length; i++) {
        var post = postlist[i];
        //console.log(post.Text);
        var postdiv = $(document.createElement("div"));
        var posttextdiv = $(document.createElement("div"));
        var postcommentdiv = $(document.createElement("div"));
        var postheaderdiv = $(document.createElement("div"));
        var postfooterdiv = $(document.createElement("div"));

        parentdiv.append(postdiv);
        postdiv.append(postheaderdiv);
        postdiv.append(posttextdiv);
        postdiv.append(postfooterdiv);
        postdiv.append(postcommentdiv);
        postdiv.append("

"); postdiv.addClass("toppost"); postheaderdiv.addClass("toppostheader"); posttextdiv.addClass("topposttext"); postfooterdiv.addClass("toppostfooter"); postcommentdiv.addClass("toppostcomment"); postheaderdiv.html("" + post.Title + "" + post.PostedBy + " - " + post.Date + ""); posttextdiv.html(post.Text); var alnk = ""; if (post.AttachmentUrl != null) { alnk = "Attachment: " + post.AttachmentName + " - "; } postfooterdiv.html(alnk + post.Replies.length + " comments - Reply"); RenderPosts(postcommentdiv, post.Replies); } }

To use the code just add the html elements and call to the above methods to a content editor web part.

     

  
       
                        var posts = new Array();                 GetTopLevelPosts("http://tqcdev08/2009", "Discussions", "/2009/Lists/Discussions", posts);                 RenderTopLevelPosts($("#discussion1"), posts);            

The css I used is below – this version doesn’t look that great, but is easily customised.

.toppost
            {
                border: 1px solid black;
                font-family: Verdana;
                font-size: 8pt;
                margin-bottom: 20px;
                width: 600px;
            }
            .toppostheader
            {
                margin-bottom: 10px;
            }
            .toppostheader .title
            {
                font-weight: bold;
                font-size: 10pt;
            }
            .toppostheader .byline
            {
                font-size: 8pt;
                color: Gray;
            }
            .post
            {
                margin-top: 10px;
                margin-left: 30px;
            }
Advertisement

Written by Tom Clarkson

May 4, 2009 at 7:57 pm

Posted in SharePoint

9 Responses

Subscribe to comments with RSS.

  1. That was very useful information for me.
    Thanks tom

    Steve

    May 6, 2009 at 9:46 pm

  2. I am having some trouble with this code. On line number 84 the following code is shown:
    var ii = t.indexOf(“= 0) t = t.substr(0, ii);

    Should that code be:
    var ii = t.indexOf(“= 0″);
    t = t.substr(0, ii);

    I have replace that line, with what I guessed it should be. I still can’t get it to work, as I get an object expected at the function call:
    GetTopLevelPosts(“http://tqcdev08/2009″, “Discussions”, “/2009/Lists/Discussions”, posts);

    Any ideas on how I could fix this? I was wondering if you have an example where you put all the code together, including the javascript include statements for jquery and the sharepoint js web service api files.

    Nathaniel Collier

    July 21, 2009 at 7:30 am

  3. If you notice, GetTopLevelPosts() takes 5 parameters while the example usage inputs only 4. This is one case where you may get an ‘Object expected’ error. That aside, I’m getting a blank page when I try to use this.

    Franklin

    August 19, 2009 at 7:40 am

  4. I’ve gone through some debugging on this and found that the call to the SPAPI_Lists web service doesn’t return all the Fields expected.

    Franklin

    August 20, 2009 at 10:59 am

  5. you mentioned that you have done a webpart for a list containing articles linked to another list containing the comments/ratings. I am pretty new to SharePoint and tring to accomplish that. Would you provide some details about it? Thanks.

    Ali

    November 26, 2009 at 12:24 pm

  6. Plz could you correct the code errors.Thanks in advance.

    one more Question where can i add the JavaScript and Jquery functions..?

    in one of the method calls “src” is not passing.

    M

    June 24, 2010 at 8:35 am

  7. this is not working : could you plz correct this..

    var res = lists.getListItems(
    59. listname,
    60. “”,
    61. “0″,
    62. ”,
    63. 100,
    64. ‘/’ + fn + ‘TRUE’,
    65. null);

    M

    June 25, 2010 at 5:33 am

  8. chart plugin with jquery

    Diman

    October 20, 2010 at 2:59 pm


Leave a Reply

Fill in your details below or click an icon to log in:

Gravatar
WordPress.com Logo

Please log in to WordPress.com to post a comment to your blog.

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.