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:
oh my... using $args for input handling... that's badyou should useparam ([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)
Hi Piotr,excellent comment! You are correct, that would be a much cleaner implementation.Cheers,Stefan