Background
I was at a customer site and they wanted to remove a load of document types from the “New” button on their document libraries. I tried using the SPContentType.Hidden = $true parameter but realised that wasn’t the one. I then spent some more time banging my head against it and just did it by hand and moved on.
Another person asked how to do something similar on PowerShell.org (here: http://powershell.org/discuss/viewtopic.php?f=12&t=1407). I had some time and was irked by my failure before hand so I gave it another go. I met some success but thought that since it’s something that annoyed me, and since there’s no easily found PowerShell specific posts about this, it’s worth doing properly and blogging.
It turns out that the new button is determined by SPList.rootFolder.UniqueContentTypeOrder property. This is an ordered list of content types to display, any item in the list must be in the lists’ content types but not vice versa. Modify this and you modify the same property you set in the GUI. Happy days.
The first step is to see if a content type is available in the new button or not:
Is-ContentTypeInNewButton
Function Is-ContentTypeInNewButton { [CmdletBinding()] Param ([parameter(Mandatory=$true)][string] $ContentTypeName, [parameter(Mandatory=$true)][Microsoft.SharePoint.SPList] $SPList) BEGIN { Write-Verbose "Begining Is-ContentTypeInNewButton" } PROCESS{ #get the uniquecontenttypes from the list root folder $rootFolder = $SPList.RootFolder $contentTypesInPlace = [Microsoft.SharePoint.SPContentType[]] $rootFolder.UniqueContentTypeOrder #Check if any of them are the same as the test content type $results = $contentTypesInPlace | where { $_.Name -eq $ContentTypeName} if ($results -ne $null) { Write-Verbose "$ContentTypeName Found" return $true } else { Write-Verbose "$ContentTypeName Not Found" return $false } } END { Write-Verbose "Exiting Is-ContentTypeInNewButton" } }
Of course there’s a possible gotcha. What if the Content type isn’t even added to the list at all?
Ensure-ContentTypeInList
Function Ensure-ContentTypeInList{ [CmdletBinding()] Param ( [parameter(Mandatory=$true,ValueFromPipeline=$true)][string] $ContentTypeName, [parameter(Mandatory=$true)][Microsoft.SharePoint.SPList] $SPList) BEGIN { Write-Verbose "Begining Ensure-ContentTypeInList" } PROCESS { #Check to see if the content type is already in the list $contentType = $SPList.ContentTypes[$ContentTypeName] if ($ContentType -ne $null) { #Content type already present Write-Verbose "$ContentTypeName already present in list" Return $true } else { Write-Verbose "$ContentTypeName not in list. Attempting to add" if (!$SPList.ContentTypesEnabled) { Write-Verbose "Content Types disabled in list $SPList, Enabling" $SPList.ContentTypesEnabled = $true $SPList.Update() } #Add site content types to the list from the site collection root $ctToAdd = $SPList.ParentWeb.Site.RootWeb.ContentTypes[$ContentTypeName] if($ctToAdd -eq $null) { Write-Error "Error - Content Type could not be found in the Site Collection" #I don't believe this will be called. return $false } $SPList.ContentTypes.Add($ctToAdd) | Out-Null $SPList.Update() Write-Verbose "$ContentTypeName added to list" return $true } } END { Write-Verbose "Exiting Ensure-ContentTypeInList" } }
Well that’s a start. Now we can tell if the content type already exsits, and can add the content type to the list if it doesn’t, let’s put that into something useful:
Ensure-ContentTypeInNewButton
Function Ensure-ContentTypeInNewButton{ [CmdletBinding()] Param ( [parameter(Mandatory=$true,ValueFromPipeline=$true)][string] $ContentTypeName, [parameter(Mandatory=$true)][Microsoft.SharePoint.SPList] $SPList) BEGIN { Write-Verbose "Begining Ensure-ContentTypeInNewButton" #get the uniquecontenttypes from the list root folder $contentTypesInPlace = New-Object 'System.Collections.Generic.List[Microsoft.SharePoint.SPContentType]' $contentTypesInPlace = $SPList.RootFolder.UniqueContentTypeOrder $dirtyFlag = $false } PROCESS { #Check the content type isn't already present in the content type $AlreadyPresent = Is-ContentTypeInNewButton -ContentTypeName $ContentTypeName -SPList $SPList if ($AlreadyPresent) { Write-Verbose "$ContentTypeName is already present in the new button" } else { #Check that there really is such a content type $ContentTypePresent = Ensure-ContentTypeInList $ContentTypeName $SPList #Catch error events if ($ContentTypePresent) { #We now know that the content type is not in the new button and is present in the list. Carry on adding the content type $ctToAdd = $SPList.ContentTypes[$ContentTypeName] #add our content type to the unique content type list $contentTypesInPlace = $contentTypesInPlace + $ctToAdd $dirtyFlag = $true Write-Verbose "$ContentTypeName queued to add to the new button" } else { Write-Error -Message "Content type could not be added to the list." } } } End{ #Set the UniqueContentTypeOrder to the collection we made above if ($dirtyFlag) { $SPList = $SPList.ParentWeb.Lists[$SPList.ID] $rootFolder = $SPList.RootFolder $rootFolder.UniqueContentTypeOrder = [Microsoft.SharePoint.SPContentType[]] $contentTypesInPlace #Update the root folder $rootFolder.Update() Write-Verbose "ContentType(s) added to the new button in list $($SPList.Name)" } else { Write-Verbose "No changes" } Write-Verbose "Exiting Ensure-ContentTypeInNewButton" } }
Awesome. On the other hand the stuff above didn’t lend itself to testing. I had to go into the GUI each time to remove my content types. So let’s have something to help make unwind our changes:
Remove-ContentTypeFromNewButton
Function Remove-ContentTypeFromNewButton{ [CmdletBinding()] Param ( [parameter(Mandatory=$true,ValueFromPipeline=$true)][string] $ContentTypeName, [parameter(Mandatory=$true)][Microsoft.SharePoint.SPList] $SPList) BEGIN { Write-Verbose "Begining Remove-ContentTypeFromNewButton" } PROCESS { #Check the content type isn't already present in the content type $AlreadyPresent = Is-ContentTypeInNewButton -ContentTypeName $ContentTypeName -SPList $SPList if ($AlreadyPresent) { Write-Verbose "$ContentTypeName is present in the new button - removing" #get the uniquecontenttypes from the list root folder $rootFolder = $SPList.RootFolder #Get the content types where the names are different to our content type $contentTypesInPlace = [System.Collections.ArrayList] $rootFolder.UniqueContentTypeOrder $contentTypesInPlace = $contentTypesInPlace | where {$_.Name -ne $contentTypeName} #Set the UniqueContentTypeOrder to the collection we made above $rootFolder.UniqueContentTypeOrder = [Microsoft.SharePoint.SPContentType[]] $contentTypesInPlace #Update the root folder $rootFolder.Update() Write-Verbose "$ContentTypeName removed from the new button in list $($SPList.Name)" } else { Write-Verbose "$ContentTypeName is not present in the new button. No further action required." } } END { Write-Verbose "Exiting Remove-ContentTypeFromNewButton" } }
Done.
So we now have the functions to take a list and content type, run a single command which will add a content type, ensuring it’s added to the new button. Further to that we’ve got some basic help (which WordPress has stripped out), error handling and it’ll take piplines and multiple content types. I love PowerShell.
Tests and examples of code
$CTHubSiteCollectionURL = "http://sharepoint/sites/cthub" $singleContentType = "AlexB_Document" $contentTypesToAddToNewButton = @("AlexB_Document1b","AlexB_Docudddment2") $SPWeb = Get-SPWeb $CTHubSiteCollectionURL $docLib = $spweb.Lists["TestDocLib"] Write-Host "Is Content Type $ContentTypeName in the new button already? $(Is-ContentTypeInNewButton $singleContentType $doclib )" Write-Host "Adding the content type to the new button (using the wonderful Ensure method which won't throw errors if already present)" Ensure-ContentTypeInNewButton -ContentTypeName $singleContentType -SPList $doclib Write-Host "Is Content Type $ContentTypeName in the new button already? $(Is-ContentTypeInNewButton $singleContentType $doclib )" #Victory! "Removing" #$contentTypesToUpdate | Remove-ContentTypeFromNewButton -SPList $doclib Write-Host "Is Content Type in the new button already? $(Is-ContentTypeInNewButton $singleContentType $doclib )" #Also Victory! #Let's try a more interesting example foreach ($contentTypeName in $contentTypesToAddToNewButton) { Write-Host "Is Content Type: $ContentTypeName in the new button already? $(Is-ContentTypeInNewButton $contentTypename $doclib)" } Write-Host "Adding the content types to the new button (using the wonderful Ensure method which won't throw errors if already present)" $contentTypesToAddToNewButton | Ensure-ContentTypeInNewButton -SPList $doclib foreach ($contentTypeName in $contentTypesToAddToNewButton) { Write-Host "Is Content Type: $ContentTypeName in the new button already? $(Is-ContentTypeInNewButton $contentTypename $doclib)" } #Victory!
And now i can rest. Any critiques of the powershell welcomed.
Just for those that are interested in the bit that makes this all possible, error handling etc. stripped out:
#Get the Web that holds the list $SPWeb = Get-SPWeb "http://sharepoint/sites/cthub" #get the library $list = $SPWeb.Lists["Shared Documents"] #Get a content type $contentType = $docLib.ContentTypes | where { $_.Name -eq "AlexB_Document"} #Get the root folder object $rootFolder = $list.RootFolder #Get the current list of content types available $contentTypesInPlace = [Microsoft.SharePoint.SPContentType[]] $rootFolder.UniqueContentTypeOrder #add our content type $contentTypesInPlace = $contentTypesInPlace + $ContentType #set the list to our new list $rootFolder.UniqueContentTypeOrder = [Microsoft.SharePoint.SPContentType[]] $contentTypesInPlace #Update the folder $rootFolder.Update()
References:
Thanks to Praveen Battula who’s blog post pointed me in the right direction and has some nice C# for doing a similar task.
http://praveenbattula.blogspot.co.uk/2011/01/change-content-type-order-in-new-button.html
Link to TechNet article on the UniqueContentTypeOrder: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfolder.uniquecontenttypeorder(v=office.14).aspx.
Thoughts for the future:
It’d be nice to be able to order the items. Not difficult technically but what would the best way to use such a process be?
It seems you can change the new button for different folders in the hierarchy. That’d be handy
Hi Alex, brilliant post. However i did find one bug in your two functions. Ensure-ContentTypeInNewButton & Is-ContentTypeInNewButton
When you make the call to get $contentTypesInPlace via
$contentTypesInPlace = [Microsoft.SharePoint.SPContentType[]] $rootFolder.UniqueContentTypeOrder
the result can be null if the user has never put a custom order on the content types
(see http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfolder.uniquecontenttypeorder(v=office.12).aspx)
in which case you could just use the property ContentTypeOrder. I have adjusted your code accordingly…
if ($contentTypesInPlace -eq $null)
{
$contentTypesInPlace = [Microsoft.SharePoint.SPContentType[]] $rootFolder.ContentTypeOrder
}
originally i tried to reply to this on
http://alexbrassington.wordpress.com/2013/04/20/adding-content-types-to-the-new-button-on-a-document-library-with-powershell/
and it failed so no i’m trying your (newer?) website
http://alexbrassington.com/2013/04/20/adding-content-types-to-the-new-button-on-a-document-library-with-powershell/
Hi, thanks for the scripts, exactly what I was looking for