Populating sharepoint discussion boards using code

I recently was tasked to import a legacy discussion board into SharePoint 2010. So I wrote a small application, that could dump the old discussions to XML and then import them into SharePoint 2010 using the SharePoint Object model. Nothing more easy than this. A discussion-board is just some kind of list after all.

Well, kinda. If you look closely, you will notice, that discussion-boards differ in some details from regular lists. For one: a discussion-board has threads and replies. The replies can be shown threaded, so I have to maintain which post is a reply to what other post.

SharePoint does some strange stuff to manage this. Each discussion thread is a folder. With this knowledge one might expect to find all replies in this folder, which sound total reasonable to me. But that’s not the SharePoint way. Instead all replies are siblings. Folders are just the SharePoint-way to distinguish threads from replies. I don’t really know how SharePoint manages the reply-hierarchy, but it does work – somehow.

Well, after these discoveries, let’s write some code. I assume the new discussion board already exists and all there is to do is to import the existing posts from the old forum. First I have to create a new thread like this:

var newThread = SPUtility.CreateNewDiscussion(discussionBoard.Items, oldPost.Title);
newThread[SPBuiltInFieldId.Body] = oldPost.Text;
newThread[SPBuiltInFieldId.Created] = FormatDate(oldPost.Date);
string author = CheckUser(oldPost.Creator, web);
newThread[SPBuiltInFieldId.Author] = author;
newThread[SPBuiltInFieldId.Editor] = author;
newThread.Update();

Then I check the oldPost for replies and append them to my newly created thread.

foreach (var oldReply in oldPost.Replies)
{
    var newReply = SPUtility.CreateNewDiscussionReply(newThread);
    string replyAuthor = CheckUser(oldReply.Creator, web);
    newReply[SPBuiltInFieldId.Body] = oldReply.Text;
    newReply[SPBuiltInFieldId.Created] = FormatDate(oldReply.Date);
    newReply[SPBuiltInFieldId.Author] = replyAuthor ;
    newReply[SPBuiltInFieldId.Editor] = replyAuthor ;
    newReply[SPBuiltInFieldId.Title] = oldReply.Title;
    newReply.Update();
    newThread.Update();
}

The replies can be created recursively for replies to replies as well.

This is actually all that is needed. So with some little magic of the SPUtility namespace I was able to import a couple of thousand messages in no time.

Going multi-tenancy with SharePoint 2010

I recently needed to do a multi-tenancy installation of SharePoint 2010 for a customer. I was very impressed by the AutoSPInstaller scripts to do a full install of SharePoint, so I figured: I want to use this!

So I modified the script to handle multi-tenancy installations as well. This was actually not that complicated. I altered the Service-Applications to have an additional attribute “PartitionMode”, which can be true or false. This will trigger the corresponding PowerShell switch for creating the Service-Application.

When doing a multi-tenancy installation I also modified the installation of the root web-application for the hosting as described by Spence Harbar in his Rational Guide to Multi Tenancy.

I also added new Configuration-Elements to setup tenants (aka Subscriptions).

This code was originally based on the 2.5.5 version and was recently re-based to 2.5.7.

The patch can be found on the CodePlex project site.

No ULS Logging in SharePoint 2010

Sometimes you encounter errors, that seem to stick around forever. I recently noticed on my SharePoint 2010 dev-machine, that the ULS doesn’t seem to be working anymore. For some strange reason the ULS is complete empty. No logmessages whatsoever. Changing the loglevel doesn’t seem to have any impact at all.

Only messages while deploying solutions from Visual Studio 2010 where showing up in the log.

Can you spot the error?

After ignoring this problem for a couple of weeks – who needs logfiles anyway, when you’re doing everyhting the perfect way! – I actually did run into a problem with a custom component and I just couldn’t figure out what’s going wrong. Having SharePoint present me a correlation-id doesn’t help at all if you have no logmessages!

The Solution!

The solution is actually quite simple, and it’s always the same cause when working with SharePoint: permissions! I get the feeling, that 90% of all errors and problems are related to permissions and security. This kinda leads me to thinking, that no two SharePoint installations are the same. When I comes down to security and permission settings all installations differ. Even if it’s just a tiny bit – this is sometimes all it needs to take a system down.

Back to my originial problem, well actually to the solution of my problem. The solution was, to add the account for application pools to the “Performance Log Users” group. So far the accounts where just a member of the “Performance Monitor Users”.

Heaven knows why and how this happened. A quick comparision with another SharePoint installation revealed, that on that machine the application pool account was just a member of the local users group. The account was not a member of any other group (WTF?).

Delete SharePoint Application Pools

Even though you deleted an application pool in IIS, sometimes SharePoint doesn’t seem to care. I recently had some trouble setting up the user profile service, and needed to delete the UPS because of some misconfiguration. When I wanted to recreate the application pool I got an error, stating the application already exists.

app_pool_1

But what when I can’t see this app-pool in IIS? Well PowerShell to the rescue:

Remove-SPServiceApplicationPool [AppPoolName]

This is almost to easy to be true.

BTW: when deleting the UPS (the service application), the application pool does not get deleted in IIS. This is extremely ugly, especially since the app-pool name is just some guid. When re-creating the service-application using the same settings as before (especially the same app pool name), this fails, because the name of the app-pool and the corresponding guid name for IIS seems to be stored in the configuration database.

SharePoint 2010 version numbers

I already created a list of version numbers for SharePoint 2007 (which seems to be a little outdated as it seems).

Well, so now I give that a new try for SharePoint 2010:

Update Name Version number
RTM (all components) 14.0.4763.1000
RTM (farm build version) 14.0.4762.1000
June 2010 CU 14.0.5114.5003
August 2010 CU 14.0.5123.5000
October 2010 CU 14.0.5128.5000
December 2010 CU 14.0.5130.5002
February 2011 CU 14.0.5136.5002
April 2011 CU 14.0.5138.5000

One Content-type, multiple templates

Let’s assume I want to store different documents in a document-library in SharePoint, based on different templates. The easiest way would be to create different content-types for each kind of document. Each content-type can have an individual template in turn.

But what if I have just one logical content-type, but I have different document templates? I could create master content-type, which holds all properties. Then I create sub content-types and assign the different templates to those. This way I can maintain the properties on the master content-type and don’t have to worry about the sub content-types.

Problem solved!

But what I actually wanted was to have dedicated links to add new documents based on a certain template to my document library. I achieved this, but I needed to created three content-types in order to do so.

A different approach.

Instead of creating different content-types with different templates, I just create two hyperlinks “create document a” and “create document b”. With a couple of lines of javascript I’m ready to go.

First of I need a document library to store my templates. In the next step I add a content editor webpart to my listview. Because I cannot add javascript code to the content editor webpart directly, I placed my code in a separate file, which I also placed in the template document library (to keep thinks simple for now). So I reference this file in the content editor webpart.

This file basically contains some javascript to add new documents based on a certain template to the library:

function NewWord()
{
    var strTemplate = makeAbsUrl("/Template/WordTemplate.docx");
    var strSaveLocation = makeAbsUrl("/Shared Documents");
    var strProgId = "SharePoint.OpenDocuments";
    createNewDocumentWithProgID(strTemplate, strSaveLocation, strProgId, false);
    return false;
}
function NewExcel()
{
    var strTemplate = makeAbsUrl("/Template/ExcelTemplate.xlsx");
    var strSaveLocation = makeAbsUrl("/Shared Documents");
    var strProgId = "SharePoint.OpenDocuments";
    createNewDocumentWithProgID(strTemplate, strSaveLocation, strProgId, false);
    return false;
}

This with some little HTML …

<span style="width: 10px; height: 10px; overflow: hidden; display: inline-block; position: relative;">
    <img style="left: 0px !important; top: -128px !important; position: absolute;" src="/_layouts/images/fgimg.png" alt=""/>
</span>&#160;<a onclick="NewWord();" href="#">New Word-Document</a>&#160;|&#160;
<span style="width: 10px; height: 10px; overflow: hidden; display: inline-block; position: relative;">
    <img style="left: 0px !important; top: -128px !important; position: absolute;" src="/_layouts/images/fgimg.png" alt=""/>
</span>&#160;<a onclick="NewExcel();" href="#">New Excel-Document</a>

… could just do.

SharePoint 2010: to RBS or not to RBS

… that’s the question.

Joel Olsen recently wrote a blog post about the pros and cons of using Remote Blob Storage (RBS) (formerly know as External Blob Storage – EBS). This post also contains a bunch of links to various resources from Microsoft and other vendors of RBS technology.

The bottom line is, that using RBS can greatly improve performance when working with large documents in SharePoint, while the actual data-size is much lower than storing those documents in the SharePoint content-database.

On the other side RBS is would be the wrong choice, when mostly small documents or listdata is stored in SharePoint, because in this case the overhead of moving the data out of SQL-server and into the filestream is too costly. Back in 2006 Microsoft already published a paper called “To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem”, which concludes that files smaller than 256 KB would be best stored in the database, while files larger than 1 MB are most efficiently sored in the filesystem. Everything in between “depends”.

Chuck Norris and SharePoint 2010

Top ten facts about Chuck Norris and SharePoint 2010:

  1. Chuck Norris never starts an Approval workflow in SharePoint. Chuck doesn’t need approval.
  2. Chuck Norris isn’t afraid to customize, one look and SharePoint runs at peak performance.
  3. Putting Chuck Norris in the SharePoint Visitors group is futile. Chuck always has Full Control.
  4. Chuck Norris doesn’t have a SharePoint Disaster Recovery Plan. He doesn’t recover data, only hostages.
  5. Chuck does not subscribe to Alerts. He knows what is happening before it happens.
  6. Chuck Norris IS the Governance Plan.
  7. In SharePoint 2010, the Farm Administrators group is being renamed to ‘Chuck’s Group’.
  8. Chuck Norris never get’s unexpected errors with SharePoint because he is always ready for everything.
  9. Chuck Norris doesn’t prepare for SharePoint 2010. SharePoint 2010 prepares for him.
  10. SharePoint 2010 was a Chuck Norris idea.

chucknorris