Skip to content

Sharepoint Discussion Board

Aymeric edited this page Oct 18, 2017 · 3 revisions

Since SharepointPlus v5, you can deal with Discussion Board.

You can either use $SP().webService() with AddDiscussionBoard following the format explained here or here or here, or you can use $SP().list().add(), but, in that case, your whole board will need to use this method (you cannot mix normal board update with programmatic update)

I'll explain here how to use the second method.

First, a board discussion item returns the below fields:

<?xml version="1.0" encoding="utf-8" ?>
<z:row xmlns:z='#RowsetSchema' 
       ows_ContentTypeId='0x010700135BC7419F95B04C8EA15325163C1444' 
       ows_Body='&lt;div class=ExternalClassAAF51B42E3834F8D90CA3A8DA7739064&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;br&gt;&lt;hr&gt;&lt;b&gt;From: &lt;/b&gt;System Account&lt;br&gt;&lt;b&gt;Posted: &lt;/b&gt;Wednesday, June 04, 2008 5:14 PM&lt;br&gt;&lt;b&gt;Subject: &lt;/b&gt;sample item one&lt;br&gt;&lt;br&gt;&lt;div class=ExternalClassF19C451E2C284124AB09D103E8A70A18&gt;&lt;div&gt;first sample item&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;' 
       ows_TrimmedBody='&lt;div class=ExternalClass4D66DF23C3C649C4985CFE81A1E2E0E2&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;/div&gt;&lt;/div&gt;' 
       ows_ParentFolderId='1' 
       ows_ID='2' 
       ows_ContentType='Message' 
       ows_Modified='2008-06-04 17:14:33' 
       ows_Created='2008-06-04 17:14:33' 
       ows_Author='1073741823;#System Account' 
       ows_Editor='1073741823;#System Account' 
       ows_owshiddenversion='1' 
       ows_WorkflowVersion='1'
       ows__UIVersion='512' 
       ows__UIVersionString='1.0' 
       ows_Attachments='0' 
       ows__ModerationStatus='0' 
       ows_SelectTitle='2' 
       ows_Order='200.000000000000' 
       ows_GUID='{8FAC9A20-6343-48A3-8BE3-CD7E17E45894}' 
       ows_FileRef='2;#SiteDirectory/mktg/Lists/TeamDiscussion/sample item one/2_.000' 
       ows_FileDirRef='2;#SiteDirectory/mktg/Lists/Team Discussion/sample item one' 
       ows_Last_x0020_Modified='2;#2008-06-04 17:14:33'
       ows_Created_x0020_Date='2;#2008-06-04 17:14:33' 
       ows_FSObjType='2;#0' 
       ows_PermMask='0x7fffffffffffffff' 
       ows_FileLeafRef='2;#2_.000' 
       ows_UniqueId='2;#{87C5CD04-7D68-4B26-B00C-DD76AE7B96D8}' 
       ows_ProgId='2;#' 
       ows_ScopeId='2;#{6ED2F3B8-3D0A-4B4F-B092-C9A6D1942AE2}' 
       ows__EditMenuTableStart='2_.000' 
       ows__EditMenuTableEnd='2'
       ows_LinkFilenameNoMenu='2_.000' 
       ows_LinkFilename='2_.000' 
       ows_ServerUrl='/SiteDirectory/mktg/Lists/Team Discussion/sample item one/2_.000' 
       ows_EncodedAbsUrl='http://moss.litwareinc.com/SiteDirectory/mktg/Lists/Team%20Discussion/sample%20item%20one/2_.000' 
       ows_BaseName='2_' 
       ows_MetaInfo='2;#' 
       ows__Level='1' 
       ows__IsCurrentVersion='1' 
       ows_ThreadIndex='0x01CA94B8313E972C16929A454ACFA141B2588F7291DB000004B927' 
       ows_ShortestThreadIndexIdLookup='1;#' 
       ows_DiscussionTitleLookup='1;#sample item one' 
       ows_DiscussionTitle='sample item one' 
       ows_ReplyNoGif='SiteDirectory/mktg/Lists/Team Discussion/sample item one' 
       ows_ThreadingControls='0x01CA94B8313E972C16929A454ACFA141B2588F7291DB000004B927' 
       ows_IndentLevel='0x01CA94B8313E972C16929A454ACFA141B2588F7291DB000004B927' 
       ows_Indentation='0x01CA94B8313E972C16929A454ACFA141B2588F7291DB000004B927' 
       ows_StatusBar='2008-06-05T00:14:33Z' 
       ows_BodyAndMore='&lt;div class=ExternalClassAAF51B42E3834F8D90CA3A8DA7739064&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;br&gt;&lt;hr&gt;&lt;b&gt;From: &lt;/b&gt;System Account&lt;br&gt;&lt;b&gt;Posted: &lt;/b&gt;Wednesday, June 04, 2008 5:14 PM&lt;br&gt;&lt;b&gt;Subject: &lt;/b&gt;sample item one&lt;br&gt;&lt;br&gt;&lt;div class=ExternalClassF19C451E2C284124AB09D103E8A70A18&gt;&lt;div&gt;first sample item&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;' 
       ows_MessageBody='&lt;div class=ExternalClassAAF51B42E3834F8D90CA3A8DA7739064&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;br&gt;&lt;hr&gt;&lt;b&gt;From: &lt;/b&gt;System Account&lt;br&gt;&lt;b&gt;Posted: &lt;/b&gt;Wednesday, June 04, 2008 5:14 PM&lt;br&gt;&lt;b&gt;Subject: &lt;/b&gt;sample item one&lt;br&gt;&lt;br&gt;&lt;div class=ExternalClassF19C451E2C284124AB09D103E8A70A18&gt;&lt;div&gt;first sample item&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;' 
       ows_BodyWasExpanded='{8FAC9A20-6343-48A3-8BE3-CD7E17E45894}' 
       ows_QuotedTextWasExpanded='{8FAC9A20-6343-48A3-8BE3-CD7E17E45894}' 
       ows_CorrectBodyToShow='&lt;div class=ExternalClass4D66DF23C3C649C4985CFE81A1E2E0E2&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;/div&gt;&lt;/div&gt;' ows_FullBody='&lt;div class=ExternalClassAAF51B42E3834F8D90CA3A8DA7739064&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;br&gt;&lt;hr&gt;&lt;b&gt;From: &lt;/b&gt;System Account&lt;br&gt;&lt;b&gt;Posted: &lt;/b&gt;Wednesday, June 04, 2008 5:14 PM&lt;br&gt;&lt;b&gt;Subject: &lt;/b&gt;sample item one&lt;br&gt;&lt;br&gt;&lt;div class=ExternalClassF19C451E2C284124AB09D103E8A70A18&gt;&lt;div&gt;first sample item&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;' 
       ows_LimitedBody='&lt;div class=ExternalClassAAF51B42E3834F8D90CA3A8DA7739064&gt;&lt;div&gt;second sample item&lt;br&gt;&lt;br&gt;&lt;hr&gt;&lt;b&gt;From: &lt;/b&gt;System Account&lt;br&gt;&lt;b&gt;Posted: &lt;/b&gt;Wednesday, June 04, 2008 5:14 PM&lt;br&gt;&lt;b&gt;Subject: &lt;/b&gt;sample item one&lt;br&gt;&lt;br&gt;&lt;div class=ExternalClassF19C451E2C284124AB09D103E8A70A18&gt;&lt;div&gt;first sample item&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;' 
       ows_MoreLink='2' 
       ows_LessLink='2' 
       ows_ToggleQuotedText='2' 
       ows_Threading='0x01CA94B8313E972C16929A454ACFA141B2588F7291DB000004B927' 
       ows_PersonImage='System Account' 
       ows_PersonViewMinimal='System Account' 
       ows_IsRootPost='0' 
       ows_ItemChildCount='2;#0' 
       ows_ServerRedirected='0'/>

Some explanations:

  • DiscussionTitle returns the Subject/Title for the discussion thread (only for replies)
  • ItemChildCount returns the number of replies for the discussion thread
  • Title is null for replies, but represents the subject for topics
  • Body is the body content for both topic and replies
  • ParentFolderId the item ID of the parent topic
  • ContentType is Discussion for topic, and Message for replies
  • ThreadIndex permits to connect messages together and to order them into the discussion
  • ServerUrl will be used for the RootFolder parameter for direct replies to topics
  • FileDirRef will be used for the RootFolder parameter for replies of replies

Let's speak a bit about ThreadIndex. There is a very technical documentation about it, and it's difficult to duplicate. However, it's possible to define our own index! We just need to respect the number of bits.

Examples of ThreadIndex from a Sharepoint Discussion Board:

Topic:              "0x01D51560C1C20E7086D03C904FC9941D033E62126633"
  Reply1:           "0x01D51560C1C20E7086D03C904FC9941D033E621266330000073AC4"
    SubReply1:      "0x01D51560C1C20E7086D03C904FC9941D033E621266330000073AC40000034446"
      SubSubReply1: "0x01D51560C1C20E7086D03C904FC9941D033E621266330000073AC400000344460004848064"
  Reply2:           "0x01D51560C1C20E7086D03C904FC9941D033E62126633000008DC95"

(Disclaimer: I'll use the term characters instead of speaking about bits or bytes, to simplify the explanation)

The idea is to have a first index of 44 characters (e.g. 01D51560C1C20E7086D03C904FC9941D033E62126633), and all direct replies will have the same first 44 characters, followed by 10 other characters. Each time we have a sub-reply, we need to add 10 more characters.

To handle our index we'll use the current timestamp.

So let's do some code:

// Define a Thread Index for the topic
var threadIndex = Array(35).join('0') + Math.round(Date.now()/10).toString(16);

// Create a new topic
$SP().list("MyBoard").add({
  Title:"Topic 1",
  Body:"Discussion about first topic",
  ContentType:"Discussion",
  ThreadIndex:threadIndex
})

The ThreadIndex just created has 44 characters: 34 zero (Array(35).join('0')) and the current timestamp represented as an hexadecimal of 10 characters (Math.round(Date.now()/10).toString(16)).

To add a reply for this thread:

// We first get the details from the topic
$SP().list("MyBoard").get({
  fields:"ID,ThreadIndex,ServerUrl",
  where:"ID = 1", // ID of the topic, or where:"Title = 'Topic 1'"
  folderOptions:{show:"FilesAndFolders_Recursive"}  // we need this option to get all
}).then(function(data) {
  if (data.length===0) throw "ERROR: topic not found";
  else {
    // we can now create the reply
    var parentID = data[0].getAttribute("ID");
    // threadIndex  = original ThreadIndex + 10 characters that represent the current time in hexadecimal
    var threadIndex = data[0].getAttribute("ThreadIndex") + Math.round(Date.now()/10).toString(16);
    var rootFolder = $SP().cleanResult(data[0].getAttribute("ServerUrl"));
    return $SP().list("MyBoard").add({
      Body:"Reply for the first topic",
      ContentType:"Message",
      ParentFolderId:parentID,
      ThreadIndex:threadIndex
    }, {rootFolder:rootFolder}); // don't forget the "rootFolder" option!
  }
}).then(function(res) {
  if (res.passed.length===1) alert("Done!");
  else alert("Failed :(");
})

if you want to add a reply to a reply:

// We first get the details for the reply we want to comment
$SP().list("MyBoard").get({
  fields:"ID,ThreadIndex,FileDirRef,ParentFolderId",
  where:"ID = 2", // ID of the reply
  folderOptions:{show:"FilesAndFolders_Recursive"}
}).then(function(data) {
  if (data.length===0) throw "ERROR: message not found";
  else {
    // we can now create the reply of the reply
    var parentID = data[0].getAttribute("ParentFolderId");
    // threadIndex  = original ThreadIndex + 10 characters that represent the current time in hexadecimal
    var threadIndex = data[0].getAttribute("ThreadIndex") + Math.round(Date.now()/10).toString(16);
    // here we need to use FileDirRef
    var rootFolder = '/' + $SP().cleanResult(data[0].getAttribute("FileDirRef"));
    return $SP().list("MyBoard").add({
      Body:"Reply of the Reply for the first topic",
      ContentType:"Message",
      ParentFolderId:parentID,
      ThreadIndex:threadIndex
    }, {rootFolder:rootFolder});
  }
}).then(function(res) {
  if (res.passed.length===1) alert("Done!");
  else alert("Failed :(");
})

And get all messages from the board:

$SP().list("MyBoard").get({
  fields:"ID,DiscussionTitle,Title,Body,ItemChildCount,ContentType,ThreadIndex",
  where:"ID > 0",
  orderby:"ThreadIndex",
  folderOptions:{show:"FilesAndFolders_Recursive"} // we need this option to get all topics and replies
}).then(function(data) {
  data.forEach(function(d) {
    var blank = d.getAttribute("ThreadIndex").slice(46).length/5;
    console.log(Array(blank).join(" ")
               + (d.getAttribute("ContentType")==="Discussion"?"[TOPIC] "+d.getAttribute("Title"):"[REPLY in "+d.getAttribute("DiscussionTitle")+"] "+d.getAttribute("Body")))
  })
})

In our example the above code will return into the console:

[TOPIC] Topic 1
  [REPLY in Topic 1] Reply for the first topic
    [REPLY in Topic 1] Reply of the Reply for the first topic

And finally, to edit or delete, just use the regular functions:

$SP().list("MyBoard").update({ID:2, Body:"Updated Body"});
$SP().list("MyBoard").remove({ID:2});