SharePoint Discussion with jQuery
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;
}
That was very useful information for me.
Thanks tom
Steve
May 6, 2009 at 9:46 pm
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
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
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
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
[...] http://tqcblog.com/2009/05/sharepoint-discussion-with-jquery/ [...]
Working with SharePoint’s Discussion Lists Programmatically – Part 1 - itaysk
May 2, 2010 at 5:07 pm
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
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
chart plugin with jquery
Diman
October 20, 2010 at 2:59 pm