Wednesday, April 6, 2016

Install a SharePoint Publishing Design Package with PowerShell

Microsoft didn't provide a PowerShell cmdlet to install publishing design packages, and the one in the design gallery is just kinda overkill.  So I took it upon myself to make a simpler version, albeit it did require calling a couple of "internal" methods from the Microsoft.SharePoint.Publishing library.  Well, here goes have at it (you'll also see some reflection examples for calling internal constructors and methods).

Oh, here's an important note.  NOTE: MAKE SURE PUBLISHING INFRASTRUCTURE IS ENABLED FIRST.  Otherwise it won't turn on, and you won't be able to access the Master page settings page even if you go directly to ChangeSiteMasterPage.aspx.



#########################################################################
#
#  Install-PublishingDesignPackage -Site <site url> -Path <package path>
#
#########################################################################
param(
    [Parameter(Mandatory=$true)][string]$Site,
    [Parameter(Mandatory=$true)][string]$Path
)

$DesignPackage = [Microsoft.SharePoint.Publishing.DesignPackage]
$DesignPackageInfo = [Microsoft.SharePoint.Publishing.DesignPackageInfo]
$BindingFlags = [System.Reflection.BindingFlags]

function _Using {
    param (
        [System.IDisposable] $inputObject = $(throw `
                  "The parameter -inputObject is required."),
        [ScriptBlock] $scriptBlock = $(throw "The parameter -scriptBlock is required.")
    )
    
    Try {
        &$scriptBlock
    } Finally {
        if ($inputObject -ne $null) {
            if ($inputObject.psbase -eq $null) {
                $inputObject.Dispose()
            } else {
                $inputObject.psbase.Dispose()
            }
        }
    }
}

function Call-InternalConstructor([System.Type]$Type, [Object]$Parms) {
    $ParmTypes = @()
    foreach($parm in $Parms) { $ParmTypes += $parm.GetType() }

    return $Type.GetConstructor($BindingFlags::NonPublic -bor $BindingFlags::Instance,`
                $null, $ParmTypes, $null).Invoke($Parms)
}

function Call-InternalStaticMethod([System.Type]$Type, [string]$MethodName, [Object]$Parms) {
    $ParmTypes = @()
    foreach($parm in $Parms) { $ParmTypes += $parm.GetType() }
    return $Type.GetMethod($MethodName, $BindingFlags::NonPublic -bor`
                   $BIndingFlags::Static, $null, $ParmTypes, $null).Invoke($null, $Parms)
}


_using($spsite = New-Object Microsoft.SharePoint.SPSite($Site)) {
    if ($spsite -ne $null) {
        $Path = [System.Io.Path]::Combine((pwd).path,$Path)
        $finfo = Dir $Path
        $pkg = Get-SPUserSolution -Site $Site $finfo.Name -ErrorAction SilentlyContinue
        if ( ($pkg -eq $null) ) {
            try {
                $pkgInfo = Call-InternalConstructor $DesignPackageInfo ($finfo.Name)
                _Using($fileStream = New-Object System.IO.FileStream($finfo.FullName,`
                           [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)) {
                    if ($fileStream -ne $null) {
                        try {
                            Call-InternalStaticMethod $DesignPackage "Install" `
                                          ([Microsoft.SharePoint.SPSite]$spsite, `
                                              $pkgInfo, [System.IO.Stream]$fileStream)
                            $DesignPackage::Apply($spsite,$pkgInfo)
                        } catch [Exception] {
                            Write-host -ForegroundColor Red $_.Exception.ToString()
                        }
                    } else {
                        Write-Host -ForegroundColor Red Unable to open $finfo.FullName
                    }
                }
            } catch [Exception] {
                Write-host -ForegroundColor Red $_.Exception.ToString()
            }
        } else {
            Write-Host -ForegroundColor Red $finfo.Name `
                               is already in the solution gallery of $Site
        }
    } else {
        Write-Host -ForegroundColor Red "Unable to open site: " $Site
    }
}