Tom Clarkson is a SharePoint consultant and entrepreneur based in Sydney, Australia.

Contact Details

Links



Recent Searches



Archives




Past Posts







RSS Feed

SharePoint Discussion with jQuery

SharePoint 2007
Monday May 4 2009
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 < unthreaded.length; i++) {

        for (var j = i - 1; j >= -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.


        <div id="discussion1">
        </div>
            <script type="text/javascript">             var posts = new Array();                 GetTopLevelPosts("http://tqcdev08/2009", "Discussions", "/2009/Lists/Discussions", posts);                 RenderTopLevelPosts($("#discussion1"), posts);             </script>


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;
            }

Comments

On 06 May 2009 02:46, Steve said:
That was very useful information for me.
Thanks tom
On 21 Jul 2009 12:30, Nathaniel Collier said:
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.
On 07 Aug 2009 09:19, paoliz466 said:
On 12 Aug 2009 11:03, CeafOsteose said:
On 19 Aug 2009 12:40, Franklin said:
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.
On 20 Aug 2009 03:59, Franklin said:
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.
On 31 Aug 2009 08:03, aucamancitlali said:
On 11 Sep 2009 07:11, Pevyrewarma said:
On 12 Sep 2009 06:33, giuvfjtnbagt said:
On 14 Sep 2009 10:46, attevalix said:
On 18 Sep 2009 10:56, Agepherresy said:
On 26 Sep 2009 12:23, bibtuecole said:
On 31 Oct 2009 07:38, NuabSoydaySok said:
On 11 Nov 2009 01:42, dietolleAnjam said:
On 18 Nov 2009 04:35, embesceme said:
On 26 Nov 2009 05:24, Ali said:
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.
On 20 Dec 2009 12:45, gipputerist said:
On 30 Dec 2009 11:02, UsalfLasp said:
On 08 Jan 2010 03:20, didsCoows said:
On 09 Mar 2010 06:35, gedeonitazobyla said:

Leave a comment