Monday, March 19, 2012

Powershell script to get SharePoint Page Layouts inventory and its usage across site collection

I was writing a PowerShell script that needed to do 2 things primarily:

1. Get an inventory of all Page Layouts deployed across a SharePoint 2010 Farm.
2. To find out whether or not any of these Page Layouts were in use or not within its various sites/sub-sites. To simply put it the other way, did any of the sites/sub-sites had Pages created out of any of the available Page Layouts in the farm.

The first requirement was not too hard to accomplish, we have the PublishingSite.GetPageLayouts in the SharePoint OM, but I had to scratch my head a little bit for the second one though.

Iterating through Pages library within numerous site/sub-sites for approximately 20,000 site collections in the Farm, in order to find out the Page Layout usage would be too expensive and time consuming. There had to be a better/faster approach to handle this scenario.

The clue was SharePoint 2010's OOTB related resources inventory. Go to “Site Actions” -> “Site Content and Structure” and select “Master Page Gallery”. See snapshot below:



The OOTB Show Related Resources functionality allows a user to select any of the Page Layouts/resources within the Master Page and Page Layouts Gallery and mentions files that uses it across the site collection.

Now there has to be a way to achieve the same programmatically as well.
I started exploring SPFile object model and voila, I found the SPFile.BackwardLinks property, see the MSDN article: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfile.backwardlinks.aspx

So my sample PowerShell code snippet does 2 things:

1. Gets an Inventory of all Page Layouts for a Publishing Site, see sample PowerShell script code :

$objSite = Get-SPSite "http://sharepointfix.com/sites/PageLayoutUsageReport"

[Microsoft.Sharepoint.Publishing.PublishingSite]$publishingSite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($objSite)

#Gets all Page Layouts for the current Site Collection, no need to iterate all subwebs
$pageLayouts = $publishingSite.GetPageLayouts($false)

#I can make use of Export-CSV to generate a .csv file for reporting purposes. That works like a charm.
#But Powershell also provides you with a lovely Grid View interface, try doing something like this:

$pageLayouts | Out-GridView

## Of course, I will wrap the above script in a foreach block to iterate through all web applications and site collections in the SP 2010 Farm.

2. Get Page Layout Usage - The sample code now makes the use of SPFile.BackwardLinks property in order to find whether the Layout is in use or not

## Iterate through all the Publishing Page Layouts
foreach($layout in $pageLayouts)
{
      ## Get Page Layout File object
      [Microsoft.SharePoint.SPFile]$file = $publishingSite.RootWeb.GetFile($layout.ServerRelativeUrl);

#Backward links contain the URLs of all files that link to the current file from the current site collection and its # sites/subsites.
# Pages that link to the current file from another Web site or site collection are not included in the backward
# link information.

# returns $true, if SPFile.BackwardLinks.Count is greater than 0
 $IsPageLayoutInUse = ($file -ne $null -and $file.BackwardLinks -ne $null -and       $file.BackwardLinks.Count -gt 0)
}

The above script is just a primer to the wide array of possibilities that PowerShell offers as a tool.
I hope this post has helped you in someway or the other. Happy Programming :)

3 comments:

  1. Hi,
    This line returns an error that "Exception calling "GetPageLayouts" with "1" argument(s): "Object reference not set to an instance of an object."
    At line:1 char:46
    + $pageLayouts = $publishingSite.GetPageLayouts <<<< ($true)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException"

    $pageLayouts = $publishingSite.GetPageLayouts($false)

    I am using sharepoint2010. and created a ps1 script for this code.
    Any Help on this would be highly appreciated.

    ReplyDelete
  2. Try this approach:

    $objSite = Get-SPSite "http://sharepointfix.com/sites/mehul"
    [Microsoft.Sharepoint.Publishing.PublishingSite]$publishingSite = New-Object Microsoft.SharePoint.Publishing.PublishingSite($objSite)

    ##Check whether the site is a Publishing site in your code:
    if ([Microsoft.SharePoint.Publishing.PublishingWeb]::IsPublishingWeb($publishingSite.RootWeb) -eq $true)
    {
    ## - If the excludeObsolete parameter is set to false, then obsolete PageLayout objects are included in the collection.
    $pageLayouts = $publishingSite.GetPageLayouts($false)
    foreach($layout in $pageLayouts)
    {
    ##Iteration logic here
    }
    }

    ReplyDelete
  3. really amazing blog and I was thankful to you for sharing such a useful information.

    Sharepoint Development | Sharepoint Developers

    ReplyDelete