How to unpublish a published content type if the published content type no longer exists


One of my colleagues (Steffi Buchner) recently got a case where a customer deleted a content type hub and later noticed that some of the content types provided by this hub were already published.

The problem is that the information about which content types are published is stored in the TermStore database outside of the content type hub – leading to orphan published content types.

If you create a new site collection now you will notice that the content types earlier published by the now deleted content type hub show up as subscribed content types.

How can you get rid of such an orphan content type? This is now an hen and egg problem: to unpublish a content type you need to go to the content type hub, select the content type and unpublish it. As the content type hub no longer exists it is not possible use this option. The same applies if only the content type in the content type hub has been deleted without unpublishing it.

Note: there is no extra check in the content type hub which would prevent you from deleting a content type which is already published or which would force it to be unpublished. The same applies to deleting a content type hub. You can delete it even if there are still content types being published from the content type hub. 

After some research my colleague identified a nice workaround: the API used to unpublish a content type does not really bother about whether it is being called on the content type object has been retrieved from the content type hub or in one of the subscribed site collections. The workaround requires an existing content type hub. So if you deleted the hub you would need to create a new one to apply the workaround – after all content types are unpublished you can delete it again if required.

Below is the powershell script that can be used to unpublish orphan published content types:

Add-PSSnapIn Microsoft.SharePoint.PowerShell

if ($args.Count -ne 3)
{
   Write-Host "UnpublishContentTypes.ps1 <web url> <content type hub url> <unpublish>"
   exit
}

$webUrl = $args[0]
$ctUrl = $args[1]
$unpublish = $args[2]

$web = Get-SPWeb $webUrl
$ctsite = Get-SPSite $ctUrl
$ctPublisher = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher($ctsite)

foreach($ct in $web.ContentTypes)
{
    if ( $ct.XmlDocuments -like "*SharedContentType*")
    {
        try
        { 
            if (-not $ctsite.RootWeb.ContentTypes[$ct.Name])
            {
                Write-Host "Found orphan Shared ContentType $($ct.Name) – ($($ct.ID))"
             
                if($unpublish)
                {
                    $ctPublisher.UnPublish($ct)
                    Write-Host "$($ct.Name) – ($($ct.ID)) UnPublished successfully"
                }
            }
        }
        catch
        {
            Write-Host "Error unpublishing ContentType $($ct.Name) – ($($ct.ID)): $error[0]"
        }
    }
}

if($unpublish)
{
    Start-SPTimerJob MetadataSubscriberTimerJob
}

Comments (6)

  1. Anonymous says:

    One of my colleagues (Steffi Buchner) recently got a case where a customer deleted a content type hub

  2. Piotr Siódmak says:

    oh my… using $args for input handling… that's bad

    you should use
    param (
    [Microsoft.SharePoint.Powershell.SPWebPipeBind]$Web,
    [Microsoft.SharePoint.Powershell.SPSitePipeBind]$PublishingSiteCollection
    [switch]$Unpublish
    )

    Use SPWebPipeBind and SPSitePipeBind to let the user pass the url or an SPSite/SPWeb object from the pipeline (use $spWeb = $Web.Read() to get the actual SPWeb)

  3. Stefan Goßner says:

    Hi Piotr,

    excellent comment! You are correct, that would be a much cleaner implementation.

    Cheers,
    Stefan

  4. Alz Martin says:

    how do i get this script to run? it just askes me to save?? im not very good with power shell

    i deleted a content type from the hub without unpublishing it first

    1. Hi Alz,
      you need to add the script to a text file named something with file extension .ps1 – e.g. myscript.ps1
      Then open the SharePoint management shell and run this script by going to the directory where the script file is located and execute it by adding “.\” in front of the command.
      E.g.:

      PS> .\myscript.ps1

      Cheers,
      Stefan

  5. hi says:

    is this applicable for sharepoint online… what should be the in the below code
    {
    Write-Host “UnpublishContentTypes.ps1 ”
    exit