I've been working on a new internal portal for my employer using SharePoint 2010. As the project progressed, we experimented with several custom page layouts and, at length, settled on our two favorites.
With the decision final, we reassigned all the pages to the authorized page layouts and proceeded to delete the extras... all except one.
This page layout didn't want to get deleted. I was persistent, but the file was more so.
- Deleting the file in SharePoint Designer yielded the error "Server error: This item cannot be deleted because it is still referenced by other pages." Mind you, it wasn't, but SharePoint thought it was.
- Several online sources indicated that moving it to a sub-folder should allow deletion of the folder. However, in my case, doing so in SharePoint Designer yielded the error "Server error: This folder cannot be deleted because it contains the following page layout that is still in use: LayoutPage01.aspx."
- I could open the master page gallery in Windows Explorer, attempt to delete the file, or move it to a folder and delete the folder, and yet the file remained.
- I fired up PowerShell and attempted to delete the file with code, and received the same error.
At this point I didn't quite know what to do. As a matter of fact, poking around the code with PowerShell didn't help me root out the source of the error; or, at least, not at first.
I was working on another task--one related to event receivers--when I noticed the following event receiver attached to the master page gallery:
Type : ItemDeleting
SequenceNumber : 1000
Assembly : Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
Class : Microsoft.SharePoint.Publishing.Internal.BlockDeletionOfInUseItemsEventReceiver
"Well, well," I thought. This must be the source of the error.
Upon inspection, I learned about the
BackwardLinks property of the
SPFile class. This collection provides information as to *which* files still lay claim on the page layout.
I took a look at the BackwardLinks collection on my particular page layout in PowerShell, and discovered four backward links, all from files that didn't exist anymore!
Well, enough story. Here is the PowerShell I used to resolve the problem. It will probably break your system and destroy all that you love... Don't say I didn't warn you, if you try to use it. The steps are simple:
- Find the layout in the gallery.
- Check to see if any of the BackwardLinks are to files that really exist.
- If non of the files exist, remove the event receiver that prevents deletion, delete the file, and then re-register the event receiver.
Happy Coding!
function Remove-PageLayout($siteUrl, $layoutName) {
$site = Get-SPSite $siteUrl
$web = $site.RootWeb
$gallery = $web.lists["Master Page Gallery"]
$layoutItem = $gallery.Items |? { $_.Name -eq $layoutName }
if(!$layoutItem) { throw (new-object IO.FileNotFoundException) }
$layoutFile = $layoutItem.File
$layoutFile.BackwardLinks |% {
if(Test-Page $_.Url) {
throw "Cannot delete page because it is referenced by $($_.Url)."
}
}
$receiverType = [Microsoft.SharePoint.Publishing.Internal.BlockDeletionOfInUseItemsEventReceiver]
$unregisterMethod = $receiverType.GetMethod("Unregister", [reflection.bindingflags]"Static,NonPublic")
$registerMethod = $receiverType.GetMethod("RegisterIfNeeded", [reflection.bindingflags]"Static,NonPublic")
$unregisterMethod.Invoke($null, $gallery)
$layoutItem.Delete()
$registerMethod.Invoke($null, $gallery)
$site.Dispose();
}
function Test-Page($url) {
$site = $web = $null
trap [exception] {
if($web) { $web.dispose() }
if($site) { $site.dispose() }
return $false
}
$site = new-object Microsoft.SharePoint.SPSite($url)
$web = $site.openweb()
$file = $web.GetFile($url)
$result = $file.exists
$web.dispose()
$site.dispose()
return $result
}