Adding Web Part to Page with PowerShell

Adding Web Part to Page with PowerShell
7 votes, 4.14 avg. rating (83% score)

Introduction

In this post we will see a generic function to add a Web Part from local drive to a page in a particular Web Part zone using PowerShell script
The script include

  • Reading Web Part details from config file
  • Check out the page
  • Adds Web Part to the page
  • Check in the page
  • Approve the page (if EnableModeration is true)

Add Web Part Config file




Create a config xml file and name it as “WebPartToPageConfig.xml”. Add the following content into it

<?xml version="1.0"?>
<Config>
<SiteSettings url="http://www.adicodes.com/MySite"></SiteSettings>
<WebParts>
	<WebPart>
		<Title>NewsTagsWebpart</Title>
		<LocalSrc>D:\Adi\powershell\AddWebpartToPage\NewsTagsWebpart.webpart</LocalSrc>
		<Replace>true</Replace>
		<ZoneName>Main Zone</ZoneName>
		<ZoneIndex>1</ZoneIndex>
		<DestinationPagePath>http://www.adicodes.com/MySite/Pages/default.aspx</DestinationPagePath>
  </WebPart>
 </WebParts>
 </Config>

url : Url of the site
Title : Title of the Web Part
LocalSrc : Local path of the Web Part
Replace : true/false
true-deletes the existing Web Part in the page and adds the new one
false-adds the Web Part without deleting.
(if false is used we will get multiple Web Part if we run the script more than once)

ZoneName : The Zone name where the Web Part should be added
ZoneIndex : 0/1/2.. The order of the Web Part in the Zone
DestinationPagePath : The path of the page where the Web Part should be added

We are now ready to use the script

Add Web Part script

Create a powershell script file and name it as “WebPartToPage.ps”. Add the following script into it

Remove-PSSnapin Microsoft.SharePoint.Powershell -ErrorAction SilentlyContinue
Add-PSSnapin Microsoft.SharePoint.Powershell

function AddWebPartToPage([string]$siteUrl,[string]$pageRelativeUrl,[string]$localWebpartPath,[string]$ZoneName,[int]$ZoneIndex)
{

	try
	{

	#this reference is required here
	$clientContext= [Microsoft.SharePoint.Client.ClientContext,Microsoft.SharePoint.Client, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c]
	$context=New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
	write-host "Reading file " $pageRelativeUrl
	$oFile = $context.Web.GetFileByServerRelativeUrl($pageRelativeUrl);
	$limitedWebPartManager = $oFile.GetLimitedWebPartManager([Microsoft.Sharepoint.Client.WebParts.PersonalizationScope]::Shared);
	write-host "getting xml reader from file"
	$xtr = New-Object System.Xml.XmlTextReader($localWebpartPath)
	 [void] [Reflection.Assembly]::LoadWithPartialName("System.Text")
	$sb = new-object System.Text.StringBuilder
	  
		 while ($xtr.Read()) 
		 {
			$tmpObj = $sb.AppendLine($xtr.ReadOuterXml());  
		 }
		 $newXml =  $sb.ToString()
		 
	if ($xtr -ne $null)
	{
		$xtr.Close()
	}

	#Add Web Part to catalogs folder
	write-host "Adding Webpart....."
	$oWebPartDefinition = $limitedWebPartManager.ImportWebPart($newXml);
	$limitedWebPartManager.AddWebPart($oWebPartDefinition.WebPart, $ZoneName, $ZoneIndex);
	$context.ExecuteQuery();
	write-host "Adding Web Part Done"
	}
	catch
	{
	write-host "Error while 'AddWebPartToPage'" $_.exception
	}


}

#Checks out the page
function CheckOutPage ($SPFile)
{
	$x=$SPFile.ServerRelativeUrl    
    if($SPFile.Level -eq [Microsoft.SharePoint.SPFileLevel]::Checkout)
    {
        write-host " File already checked-out...doing undo checkout"
		
        $SPFile.UndoCheckOut()
    }
        
	if ($SPFile.Level -ne [Microsoft.SharePoint.SPFileLevel]::Checkout)
	{
        write-host " Checking-out page" $x
		
        $SPFile.CheckOut()
	}
	else
	{
        write-host " No Check-out needed page" $x
		
	}
} 

#check in the page
function CheckInPage ($SPFile)
{
    $x=$SPFile.ServerRelativeUrl
	write-host "file level" $SPFile.Level -ForegroundColor Green
	if ($SPFile.Level -eq [Microsoft.SharePoint.SPFileLevel]::Checkout)
	{
        write-host " Checking-in page" $x
		
        $SPFile.CheckIn("Checkin", [Microsoft.SharePoint.SPCheckInType]::MajorCheckin)
	}
	else
	{
        write-host " No Check-in needed page" $x
		
	}

} 

#Approve the page
function ApprovePage ($PageListItem)
{
    if($PageListItem.ListItems.List.EnableModeration)
    {
        #Check to ensure page requires approval, and if so, approve it
        try{
            write-host " Approving page" $PageListItem.File.ServerRelativeUrl
			
		    $PageListItem.File.Publish("")
            $PageListItem.File.Approve("Page approved automatically by PowerShell script")
		}
		catch
		{   
			write-host $_
			
		}
    }
    else
    {
        write-host " No approval on list"
		
    }
}

#Add Web Part to the page
function AddWebPart($wpTitle, $wpDestinationPageFullUrl, $wpLocalPath, $wpZoneName, $wpZoneIndex, $IsReplace)
{

[Microsoft.SharePoint.SPFile] $spFile = $Web.GetFile($wpDestinationPageFullUrl)
if($spFile.Exists)
    {
        try
		{
		if ($SPFile.CheckOutStatus -ne "None")
		{
		 write-host "doing undocheckout"
		 $SPFile.UndoCheckOut()
		}
		else
		{
			write-host "checkout the page" -ForeGround Green
			CheckOutPage -SPFile $spFile
		}
		
		####
		[Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager]$wpManager = $Web.GetLimitedWebPartManager($FullUrl,[System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared) 
    
		if ($null -eq [System.Web.HttpContext]::Current) 
		{     
			$sw = New-Object System.IO.StringWriter 
			$resp = New-Object System.Web.HttpResponse $sw
			$req = New-Object System.Web.HttpRequest "", $Web.Url, ""
			$htc = New-Object System.Web.HttpContext $req, $resp 
			#explicitly cast $web to spweb object else sharepoint will 
			#see it as a PSObject, and AddWebpart wil fail 
			$htc.Items["HttpHandlerSPWeb"] = $web  -as [Microsoft.SharePoint.SPweb]
			[System.Web.HttpContext]::Current = $htc
			if ($sw -ne $null)
			{
				$sw.Dispose()
			}
		}    
		$Web.AllowUnsafeUpdates = $true
		
		
		
		if ($IsReplace -eq $true)
		{
		
			$wpToDelete = $wpManager.WebParts | % { $_ }
			foreach($WebPart in $wpToDelete)
			{
				if($WebPart -ne $null)
				{
				    if($WebPart.Title -eq $WebPartTitle)
					{
					write-host "deleting existing Web Part" $WebPart.Title -ForegroundColor Green
					$wpManager.DeleteWebPart($WebPart)
					}
					
					
				}
			}
		}
		####
		
		$pageRelativeUrl = $SPFile.ServerRelativeUrl
		AddWebPartToPage $siteUrl $pageRelativeUrl $wpLocalPath $wpZoneName $wpZoneIndex
		}
		catch
		{
			write-host $_.exception 
		}
		finally
		{
			#check in
			CheckInPage -SPFile $spFile
			
			# Approve & Publish
			ApprovePage -PageListItem $spFile.Item
			
			
			if($wpManager -ne $null)
			{
				$wpManager.Dispose()
			}
			if($Web -ne $null)
			{
					$Web.AllowUnsafeUpdates = $false
					$Web.Dispose()
			}
		}
			
    }
}




## Main Programm ##
try
{
	$runningDir = resolve-Path .\  
	$configXmlPath = join-path -path $runningDir -childpath "WebPartToPageConfig.xml" 
	1$SiteConfig = get-content $configXmlPath 
	$siteUrl = $SiteConfig.Config.SiteSettings.Url
	$Web = Get-SPWeb -identity $siteUrl 
	
	foreach($WebPart in $SiteConfig.Config.WebParts.WebPart)
	{
		#if attributes are present to the node, use innerText as $WebPart.Title will not return value
		#$wpTitle = $WebPart.Title.InnerText
		$wpTitle = $WebPart.Title
		$FullUrl = $WebPart.DestinationPagePath
		$LocalWebPartPath = $WebPart.LocalSrc
		$ZoneName = $WebPart.ZoneName
		$ZoneIndex = $WebPart.ZoneIndex
		$Replace = $WebPart.Replace
				
		AddWebPart $wpTitle $FullUrl $LocalWebPartPath $ZoneName $ZoneIndex $Replace
		
	}
}
catch  
{  
write-host $error[0]  
} 
finally
{
	if($Web -ne $null)
	{
		$Web.Dispose()
	}
	
}

Pop-Location

The script will read the settings from the config file ‘WebPartToPageConfig.xml’ and uploads the Web Part to the page.
You can check how to use config file in PowerShell
If we have more than 1 Web Part, just add another ‘WebPart’ node in ‘WebParts’ with values.
Note: If the Web Part does not has title, the script will add duplicate Web Part to the zone though you set ‘Replace’ to ‘true’.
Its always good way to maintain the Title. The visibility can be managed from chrome type of the Web Part
Also I strongly prefer using write-log to log any error or status instead of just using write-host.
Check how to use log file in PowerShell

Conclusion

Hope this generic function will be helpful to add or replace Web Parts in any page. Now, we can just concentrate on maintaining the config file
and our work is so simple. If you are stuck up at any point let me know. Happy Coding :)

May 14, 2012 · Adi · 19 Comments
Tags: , , , , , , ,  · Posted in: Packaging and Deployment, Powershell, Uncategorized

19 Responses

  1. Naresh - May 31, 2012

    Excellent Article!!!

  2. Adi - May 31, 2012

    Thanks dude :)

  3. Robert - September 28, 2012

    I had a client requirement to do just this. Had not done this before… saved me… thanks!

  4. Rob - October 8, 2012

    It’s magic, magic I tell ya! Putting the power in powershell :-).

  5. Rob - October 9, 2012

    On a more serious note, I did some modifications to the script to be able to loop through a bunch of sites:

    #A siteUrl per webpart (in the config)
    $siteUrl = $WebPart.SiteSettings.Url
    $TopWeb = Get-SPWeb -identity $siteUrl
    foreach($subSite in $TopWeb.Webs)
    {
    $Web= Get-SPWeb -identity $subSite.Url
    #.. all the other stuff that’s under Webpart in the original script
    }

    And I also removed the HTTP context-part from the addwebpart, else it would not work. So this part was removed from my script (not sure if this will generate major issues, I couldn’t find any):

    if ($null -eq [System.Web.HttpContext]::Current)
    {
    $sw = New-Object System.IO.StringWriter
    $resp = New-Object System.Web.HttpResponse $sw
    $req = New-Object System.Web.HttpRequest “”, $Web.Url, “”
    $htc = New-Object System.Web.HttpContext $req, $resp
    #explicitly cast $web to spweb object else sharepoint will
    #see it as a PSObject, and AddWebpart wil fail
    $htc.Items[“HttpHandlerSPWeb”] = $web -as [Microsoft.SharePoint.SPweb]
    [System.Web.HttpContext]::Current = $htc
    if ($sw -ne $null)
    {
    $sw.Dispose()
    }
    }

  6. Shriram - September 2, 2013

    superb post…saved lots of hrs.

  7. Rohit - November 6, 2013

    hi
    found 1 issue, i used the same script over a colletion of sites.
    for 1st loop it went well and webpart is added, and for 2nd and further loops and SPFile object is is getting value of file but its ‘Exist’ property is coming ‘False’.. don’t know,, is it a problem with GetFile() method..
    Please help

  8. Adi - November 7, 2013

    Hi Rohit,

    This issue arises only when the user with which you are running the script does not have necessary permissions. You try with site admin or another user and it should resolve.

    Regards,
    Adi

  9. aditya - May 12, 2014

    Error while ‘AddWebPartToPage’ System.Management.Automation.MethodInvocationException: Exception calling “ExecuteQuery” with “0” argument(s): “The remote server returned an error: (403) Forbidden.” —> System.Net.WebException: The remote server returned an error: (403) Forbidden.
    at System.Net.HttpWebRequest.GetResponse()

  10. Adi - May 13, 2014

    Hi Aditya,

    I suspect the user you are executing powershell script does not have proper permission to do respective action. Try to add webpart to the the page with the same user from UI to test everything is fine. If that works without issues, then the script will be working like charm.
    Let me know if you are still facing issues.

    Cheers,
    Adi

  11. aditya - May 13, 2014

    Im executing the scripts with service account

  12. Adi - May 13, 2014

    Did you try adding webpart from the UI?

  13. aditya - May 13, 2014

    Ya its working fine

  14. Adi - May 13, 2014

    The error is related to the permission of the current user. May be you should try getting inner exception, or try adding credentials object to the web context with username and password.

    Regards,
    Adi

  15. Michael Pullen - July 9, 2015

    This is fantastic. Do you mind if I use this work to create a DSC (Desired State Configuration) Resource to add a webpart to a page? This will enable me to integrate independent web part development into my release management pipeline.

    Thanks for the good work 😉

  16. rachel marshall - November 23, 2015

    Thank you this really help me today.

  17. Adi - November 23, 2015

    Great, happy coding Rachel

    Regards,
    Adi

  18. João Costa - February 27, 2016

    Hi,

    Thanks for the code, but I’m having a problem:

    On function AddWebPartToPage, it never enters the while ($xtr.Read()), I don’t know whats going on.

  19. João Costa - March 1, 2016

    Ok, that problem I solved, now the ” $context=New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) ” isn’t receiving anything, causing an exeption for calling ExecuteQuery with 0 arguments.

Leave a Reply

What is 7 + 5 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)