Content Types programmatically added to SharePoint libraries not appearing on New menu

Posted by Rik Hepworth on Monday, April 4, 2011

This one caused some consternation, I can tell you. As usual, the solution could be found on the great wide web, but it took some digging, so as usual I am repeating it here.

As part of a SharePoint migration we did recently, we replaced a SharePoint 2007 feature that the client was using (which added content types to libraries from a central list) with a mix of content type replication and PowerShell to add the content types to the libraries.

The code below scans through any site collections whose url begins with our $hostheader variable, then adds the list of content types to the Shared Document library in every web in the site collection. It’s a simple modification of some code on Phil Childs’ Get-SPScripts.com site and I take no credit for it – it’s all Phil’s work.

# Specify name of the library to look for in each site  
#  
$hostheader = "http://mywebapp*"  
$lookForList = "Shared Documents"  
#find our site collections and run through each in turn  
# get all the site collections, then step into each site in each site collection  
# find the list with the name in the var lookforlist  
# then change content types on the list specified  
#  
# note - this will only find site collections where the url starts with the host header var  
#  
get-spsite where {$_.url -like $hostheader}| Get-SPWeb -Limit all | ForEach-Object {  
  write-host "Checking site:"$_.Title  
  #Make sure content types are allowed on the list specified  
  $docLibrary = $_.Lists[$lookForList]  
  if ($docLibrary -ne $null)  
  {  
    $docLibrary.ContentTypesEnabled = $true  
    $docLibrary.Update()  
    # Add site content types to the list  
    # change the name in the quotes to the name of your content type  
    #  
    $ctToAdd = $site.RootWeb.ContentTypes["Word Document"]  
    $ct = $docLibrary.ContentTypes.Add($ctToAdd)  
    write-host "Content type" $ct.Name "added to list" $docLibrary.Title  
    
    # Add second site content types to the list  
    # change the name in the quotes to the name of your content type  
    #  
    $ctToAdd = $site.RootWeb.ContentTypes["Excel Spreadsheet"]  
    $ct = $docLibrary.ContentTypes.Add($ctToAdd)  
    write-host "Content type" $ct.Name "added to list" $docLibrary.Title  
    
    # Add third site content types to the list  
    # change the name in the quotes to the name of your content type  
    #  
    $ctToAdd = $site.RootWeb.ContentTypes["PowerPoint Presentation"]  
    $ct = $docLibrary.ContentTypes.Add($ctToAdd)  
    write-host "Content type" $ct.Name "added to list" $docLibrary.Title  
    
    # Update the library object to commit changes  
    #  
    $docLibrary.Update()  
  }  
  else  
  {  
    write-host "The list" $lookForList "does not exist in site" $_.Title  
  }  
}

When we ran this through, however, whilst the content types were added correctly to the libraries, the New menu failed to list them.

Much (and I mean much!) digging revealed the cause to be down to a property in the library (spList.RootFolder.UniqueContentTypeOrder) that isn’t automatically set when we add the content types using code (which makes sense when you think about it…). However, all our fiddling with PowerShell failed to work. Adding a content type to the property (which is an array of content types) steadfastly refused to work.

We then found a post on the TechNet forums which appeared to give the answer in the form of C# code. We spent a long time on this, so to cut it short: You can’t simply add an item to the UniqueContentTypeOrder property – you have to set it to Null and rebuild it. The trouble is that in order to do that you have to stuff in an object that has an iList interface. Try as we might, we couldn’t create a PowerShell object that would allow us to store an array of ContentTypes and present the iList interface to the UniqueContentTypeOrder property to set the New menu values. Many people said that ArrayList or SortedList should do it, but they didn’t.

In the end, then I got our devs to knock up a rough and ready command line tool based on the code in the TechNet post. It’s really rough and ready, so I won’t post it here. Follow the posting and get your own tame devs to do the same. I only hope that this article becomes easier to find on the web to save you guys some time.