Archives for category: Content Types

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

Advertisements

As i mentioned in my last post it’d be useful to be able to sort the content types in a document library’s new button. This builds on the examples in the previous post but it can be run on it’s own.

This script will attempt to set a default content type, if one is specified, but if it isn’t or the one listed can’t be found it’ll default to alphabetical.

Function Sort-ContentTypesInNewButton{

[CmdletBinding()]
Param ( [parameter(Mandatory=$false)][string] $DefaultContentTypeName,
[parameter(Mandatory=$true,ValueFromPipeline=$true)][Microsoft.SharePoint.SPList] $SPList)
BEGIN {
Write-Verbose "Begining Sort-ContentTypesInNewButton"
}
PROCESS {

$rootFolder = $SPList.RootFolder

#Get content types fromt the button
$contentTypesInPlace = New-Object 'System.Collections.Generic.List[Microsoft.SharePoint.SPContentType]'
$contentTypesInPlace = $rootFolder.UniqueContentTypeOrder

#Has a default content type name been specified?
if ($DefaultContentTypeName)
{
$contentType = $contentTypesInPlace | where { $_.Name -eq $DefaultContentTypeName }
if ($contentType -ne $null)
{
#Add the default content type
$sortedListOfContentTypes = New-Object 'System.Collections.Generic.List[Microsoft.SharePoint.SPContentType]'
$sortedListOfContentTypes += $SPList.ContentTypes[$DefaultContentTypeName]

#Remove the default content type from the list
$contentTypesInPlace = $contentTypesInPlace | where {$_.Name -ne $DefaultContentTypeName}
}
else
{
Write-Error "$DefaultContentTypeName was not found in the list, sorting by Name alone"
}
}
else
{
Write-Verbose "No default content type specified"
}

#sort the remaining content types and add the sorted list
foreach ($contentType in $($contentTypesInPlace | Sort-Object -Property Name ))
{
#Add the content types
$sortedListOfContentTypes = [Microsoft.SharePoint.SPContentType[]] $sortedListOfContentTypes + $contentType
}

$rootFolder.UniqueContentTypeOrder = [Microsoft.SharePoint.SPContentType[]] $sortedListOfContentTypes

#Update the root folder
$rootFolder.Update()
Write-Verbose "ContentType(s) sorted and added to the new button in list $($SPList.Name)"

}
End{
Write-Verbose "Ending Sort-ContentTypesInNewButton"
}

}