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:
  1. Find the layout in the gallery.
  2. Check to see if any of the BackwardLinks are to files that really exist.
  3. 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
}

 
Categories: PowerShell | SharePoint

I ran into quite the mystery while building custom workflow activities for SharePoint 2010. It seemed that, no matter what I tried, the new activities would not appear in SharePoint Designer.

At length, I realized that the assembly name did not get resolved in the .actions file. Visual studio left the token $SharePoint.Project.AssemblyFullName$ as-is.

I found a solution on MSDN, which indicated the Visual Studio tools only replace SharePoint tokens on a few file extensions. To register additional file extensions, such as .actions, edit the project file and add the following at the end of the first <PropertyGroup> element.

<TokenReplacementFileExtensions>myextension;yourextension</TokenReplacementFileExtensions>

That did it for me. From that point forward, my workflow activity appeared in SharePoint Designer 2010 as expected.

Cheers!


 
Categories: SharePoint | Workflow

Sometimes it happens that SharePoint provides exceptionally long addresses for pages. If it also happens to be that when adding such a long hyperlink to the Quick Launch, that the link will exceed the 255 character limit to addresses in the Quick Launch. This post addresses strategies to shorten long SharePoint URLs into something a little more manageable.

Take, for example, a typical link to the NewForm.aspx file we get when clicking New Item on a list or document library (shown on multiple lines for convenience):

http://share.demo.com/sites/ExampleSPSiteDemo/Lists/Document%20LibExamples/NewForm.aspx?RootFolder=%2Fsites%2FExampleSPSiteDemo%2FLists%2FDocument%20LibExamples&Source=http%3A%2F%2Fshare%2Edemo%2Ecom%2Fsites%2FExampleSPSiteDemo%2FLists%2FDocument%2520LibExamples%2FAllItems%2Easpx

This obscenely long URL winds up getting truncated when typed or pasted into the quick launch, turning out like:

http://share.demo.com/sites/ExampleSPSiteDemo/Lists/Document%20LibExamples/NewForm.aspx?RootFolder=%2Fsites%2FExampleSPSiteDemo%2FLists%2FDocument%20LibExamples&Source=http%3A%2F%2Fshare%2Edemo%2Ecom%2Fsites%2FExampleSPSiteDemo%2FLists%2FDocument%2520LibE

Oftentimes, an adequate solution is to trim the unwanted muck from the address. Here are some ideas:

  1. Remove the protocol (http://) and host name (share.demo.com) from the URL. The result is an address that starts with a slash (/) and, as a side benefit, improves your administrator's ability to do some maintenance tasks.
    • In this case, removing the protocol and host saves us 21 characters, which is enough to solve the problem at hand, but might be inadequate to completely solve the problem with exceptionally long addresses.
  2. Remove unwanted query string values. The query string is everything after the question mark (?). Query strings are usually formatted like ?key1=value1&key2=value2&key3=value3&..., where each of the values might appear to be gibberish. In the case of the address above, there are two keys: RootFolder, and Source.
    • RootFolder determines which folder will receive the new item. If your list does not use folders, feel free to strip out everything from the word RootFolder to the next ampersand (&).
    • Source determines where the browser should go after the item is inserted. The default source is the default view of the list or library. If redirecting to the default view is not inappropriate, strip out the Source key and value.
    • If you strip out all the keys and values, you can delete the question mark as well.

Supposing you do all the suggested reductions, the URL now looks like this:

/sites/ExampleSPSiteDemo/Lists/Document%20LibExamples/NewForm.aspx

...Which is well under the 255 character Quick Launch limit, and is easier for a real human to digest, anyway.


 
Categories: SharePoint