I'm writing a powershell function to add custom webparts (i.e. not Out Of The Box webparts such as ContentEditor) to publishing pages on a SharePoint Server 2010 site. I encountered a frustrating issue when trying to add / delete webparts from the page whereby SPLimitedWebPartManager was throwing the following error:
--------------------------------------------------
-- Error information
--------------------------------------------------
Line Number: 198
Offset: 27
Command:
Line: $wpManager.AddWebPart($wp, $WebPartSettings.Zone, $WebPartSettings.ZoneIndex)
Error Details: Exception calling "AddWebPart" with "3" argument(s): "The file is not checked out. You must first check out this document before making changes."
Unbound args:
Exception: System.Management.Automation.MethodInvocationException: Exception calling "AddWebPart" with "3" argument(s): "The file is not checked out. You must first check out this document before making changes." ---> Microsoft.SharePoint.WebPartPages.WebPartPageUserException: The file is not checked out. You must first check out this document before making changes.
at Microsoft.SharePoint.WebPartPages.SPWebPartManager.SaveSomeChanges(SPLayoutProperties layoutProperties)
at Microsoft.SharePoint.WebPartPages.SPWebPartManager.MakeSpaceForWebPart(WebPart webPartToPlace, Int32 zoneIndex, String zoneID, Boolean storeInPendingChangesList)
at Microsoft.SharePoint.WebPartPages.SPWebPartManager.OnWebPartAddingCore(SPSupersetWebPart superset, Boolean throwIfLocked)
at Microsoft.SharePoint.WebPartPages.SPWebPartManager.AddWebPartInternal(SPSupersetWebPart superset, Boolean throwIfLocked)
at Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager.AddWebPartInternal(WebPart webPart, String zoneId, Int32 zoneIndex, Boolean throwIfLocked)
at Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager.AddWebPart(WebPart webPart, String zoneId, Int32 zoneIndex)
at AddWebPart(Object , Object[] )
at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] originalArguments)
I have Write-Host
statements on the line before calling $wpManager.AddWebPart
and $wpManager.DeleteWebPart
that print out the values of CheckedOutBy
, CheckOutStatus
, etc for the PublishingPage that show that the file is checked out but I still get an error stating that the file is not checked out.
I found this post:
Possible to checkout a file using PowerShell?
but their issue was the file wasn't actually checked out, whereas mine apparently is checked out.
Sample output from Write-Host
here:
CheckOutType: Online
CheckOutStatus: LongTerm
CheckedOutBy: MYDOMAIN\user.name
On another question, Anders Rask suggested the poster compare the userid of the PublishingPage.ListItem.File.CheckedOutBy
field with the userid of PublishingPage.Web.CurrentUser
but these returned the exact same value.
My PowerShell code is:
function Add-WebPartToPage
Param
(
[Microsoft.SharePoint.SPWeb]$SpWeb,
[Microsoft.SharePoint.Publishing.PublishingPage]$PublishingPage,
[System.Xml.XmlNode]$WebPartSettings,
[string]$LogName
)
Process
{
if ($PublishingPage -eq $null)
{
Write-Host "Cannot add webparts to page as page is null"
return
}
if ([string]::IsNullOrEmpty($PublishingPage.Url))
{
Write-Host "Unable to access webparts for page as page url is undefined"
return
}
Try
{
$serverRelativeUrl = $SpWeb.ServerRelativeUrl + "/" + $PublishingPage.Url
Write-Host "ServerRelativeUrl:" $serverRelativeUrl
[Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager]$wpManager = $SpWeb.GetLimitedWebPartManager($serverRelativeUrl, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
if ($wpManager -eq $null)
{
Write-Host "Failed to retrieve WPManager"
return
}
# Page must be checked out before making any changes
if ($PublishingPage.ListItem.File.CheckOutType -eq "None")
{
Write-Host "checking out file" $PublishingPage.Title
$PublishingPage.ListItem.File.CheckOut()
}
$pageWebparts = $wpManager.WebParts
if ($pageWebparts -eq $null -or $pageWebparts.Count -eq 0)
{
Write-Host "No webparts on page " $PublishingPage.Name
}
else
{
$pageWebparts | ForEach-Object {
Write-Host "Webpart:" $_.Name
}
$existingWebPart = $pageWebparts | Where-Object { $_.Name -eq $WebPartSettings.Name }
if ($existingWebPart -ne $null)
{
$file = $PublishingPage.ListItem.File
Write-Host "Checked out by" $file.CheckedOutBy
Write-Host "Current user:" $file.Web.CurrentUser
Write-Host "Locked by:" $file.LockedByUser
Write-Host "LockType:" $file.LockType
Write-Host "Removing webpart from page..."
#$wpManager.DeleteWebPart($existingWebPart)
}
}
Write-Host "CheckOutType:" $PublishingPage.ListItem.File.CheckOutType
Write-Host "CheckOutStatus:" $PublishingPage.ListItem.File.CheckOutStatus
Write-Host "CheckedOutBy:" $PublishingPage.ListItem.File.CheckedOutBy
Write-Host "Adding webpart"
[System.Xml.XmlTextReader]$oxmlReader = new-object System.Xml.XmlTextReader($WebPartSettings.FileLocation)
[string]$errorMsg = ""
$wp = $wpManager.ImportWebPart($oxmlReader, [ref]$errorMsg)
Write-Host "Import output:" $errorMsg
$wpManager.AddWebPart($wp, $WebPartSettings.Zone, $WebPartSettings.ZoneIndex)
$PublishingPage.CheckIn("Added webpart")
}
Catch
{
Write-Host $Error[0]
if ($PublishingPage -ne $null -and $PublishingPage.ListItem.File.CheckOutType -ne "none")
{
Write-Verbose "Error occurred whilst adding webparts to the page. Undoing checkout"
$PublishingPage.ListItem.File.UndoCheckOut()
}
throw $Error[0]
}
}
}
NOTE: The $WebPartSettings comes from an XML config file
<webpart name="MyWebPart.webpart" zone="Header" zoneIndex="0" FileLocation="C:\Releases\MyWebPart\MyWebPart.webpart">
</webpart>