Quantcast
Channel: Second Life of a Hungarian SharePoint Geek
Viewing all articles
Browse latest Browse all 206

Copy an XsltListViewWebPart from another SharePoint Site via PowerShell – The client-side solution

$
0
0

In my recent post I’ve illustrated, how to display SharePoint lists from other sites via PowerShell. As I told you, that solution does work only if you have direct access to the SharePoint server. Based on my experience that is not always the case. In the current post I introduce you a solution that should work even in such cases. We built this solution on the Managed Client-Object Modell of SharePoint.

Since I knew, that the CreateWebPartFromList method of the Microsoft.SharePoint.WebPartPages.SPWebPartManager is not accessible via the client object model, I first planned to apply the another approach: export the source web part via a LimitedWebPartManager instance (the client-side equivalent of SPLimitedWebPartManager), then use another LimitedWebPartManager instance to import it onto the target page. BUT (there is almost always a but…) it turned out, that although LimitedWebPartManager supports the ImportWebPart method, the ExportWebPart method is not available on the client-side (Note: as Waldek Mastykarz reported, the ExportWebPart method should be available since the March 2016 SharePoint Online CSOM update). So I came up with a fall-back plan and exported the web part by calling the exportwp.aspx page as described here by Anatoly Mironov.

As we export and import the web part from / to another sites, we create to different context objects to access them.

We read the response from the exportwp.aspx page as XML, and set the WebId property according to the ID of the source web site. There is apparently an issue with the ViewGuid property (more about them here), so we have to append it, for example, by cloning an existing XML node, like the one for the WebId property. A bit dirty workaround, but seems to work at me…

Finally, we import the web part to the target page and add it to the web part zone / position we wish.

  1. $sourceWebUrl = "http://YourSharePoint/Site1/Site2"
  2. $listTitle = "YourList"
  3. $viewTitle = "YourView" # name of the view, like "All Items"
  4.  
  5. # set the path according the location of the assemblies
  6. Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
  7. Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
  8.  
  9. $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($sourceWebUrl)
  10.  
  11. $sourceWeb = $clientContext.Web
  12. $list = $sourceWeb.Lists.GetByTitle($listTitle)
  13. $views = $list.Views
  14.  
  15. $clientContext.Load($sourceWeb)
  16. $clientContext.Load($list)
  17. $clientContext.Load($views)
  18. $clientContext.ExecuteQuery()
  19.  
  20. $sourceWebId = $sourceWeb.Id
  21.  
  22. $view = $views | ? { $_.Title -eq $viewTitle }
  23.  
  24. $targetWebUrl = "http://YourSharePoint/Site1"
  25. # This path should be the site relative URL of the page. If you have the page sub webs, include them in the path
  26. $targetPageSiteRelUrl = "/Site1/SitePages/SubSiteLisTest.aspx"
  27. $targetWebPartZoneId = "Bottom" # change the ID of the web part zone to match your needs
  28. $targetWebPartIndex = 0 # the intended position of the web part in the zone
  29.  
  30.  
  31. if (!$view.ServerObjectIsNull)
  32. {
  33.     Write-Host View found, exporting WebPart…
  34.  
  35.     $file = $list.RootFolder.Files.GetByUrl($view.ServerRelativeUrl)
  36.     $clientContext.Load($file)
  37.  
  38.     $webPartManager = $file.GetLimitedWebPartManager([Microsoft.SharePoint.Client.WebParts.PersonalizationScope]::Shared)
  39.     $webParts = $webPartManager.WebParts
  40.     $clientContext.Load($webParts)
  41.     $clientContext.ExecuteQuery()
  42.  
  43.     # I assume there is a single web part on the view page
  44.     # if this assumption is false, you should filter the web parts first
  45.     $webPart = $webParts[0]
  46.     # https://www.red-gate.com/simple-talk/blogs/getting-the-absolute-url-of-a-file-in-csom/
  47.     $viewAbsoluteUrl = (New-Object System.Uri($clientContext.Url)).GetLeftPart([System.UriPartial]::Authority) + $view.ServerRelativeUrl
  48.  
  49.     $exportWPUrl = $sourceWebUrl + "/_vti_bin/exportwp.aspx?pageurl=" + [System.Web.HttpUtility]::UrlEncode($viewAbsoluteUrl) + "&guidstring=" + $webPart.Id
  50.  
  51.     $request = [System.Net.WebRequest]::Create($exportWPUrl)
  52.     $request.UseDefaultCredentials = $true
  53.  
  54.     $response = $request.GetResponse()
  55.     $reader = New-Object System.IO.StreamReader $response.GetResponseStream()
  56.  
  57.     $wpXml = [Xml]$reader.ReadToEnd()
  58.     $properties = $wpXml.webParts.webPart.data.properties
  59.     $webIdProp = $properties.property | ? { $_.name -eq "WebId" }
  60.     $webIdProp.InnerText = $sourceWebId
  61.  
  62.     # "For example, setting the ViewGuid or the Toolbar properties doesn't do anything!" see:
  63.     # http://blog.bonzai-intranet.com/analysthq/2014/10/adding-an-xsltlistviewwebpart-with-a-custom-view-using-javascript/
  64.     # as a workaround for the missing ViewGuid property, clone the WebId property and change its name / type / value
  65.     $viewIdProp = $webIdProp.Clone()
  66.     $viewIdProp.name = "ViewGuid"
  67.     $viewIdProp.type = "string"    
  68.     $viewIdProp.InnerText = $view.ID.ToString("B").ToUpper() # "convert to a format like {8C1D2A1A-5BE8-469D-806E-2112965D2C1C}"
  69.  
  70.     [Void]$properties.AppendChild($viewIdProp)  
  71.  
  72.     $wpText = $wpXml.OuterXml
  73.  
  74.     Write-Host Export completed
  75.  
  76.     Write-Host Importing WebPart…
  77.     
  78.     $targetClientContext = New-Object Microsoft.SharePoint.Client.ClientContext($targetWebUrl)
  79.     $targetFile = $targetClientContext.Web.GetFileByServerRelativeUrl($targetPageSiteRelUrl)
  80.  
  81.     $targetWebPartManager = $targetFile.GetLimitedWebPartManager([Microsoft.SharePoint.Client.WebParts.PersonalizationScope]::Shared)
  82.     # note the difference:
  83.     # the server-side version of ImportWebPart returns a WebPart
  84.     # the client-side equvalent of ImportWebPart returns a WebPartDefinition
  85.     $targetWebPartDef = $targetWebPartManager.ImportWebPart($wpText)
  86.     # you could set optionally the WebId and ViewGuid properties at this point as well, but be aware of the issue with the ViewGuid property I mentioned above..
  87.     #$targetWebPartDef.WebPart.Properties["WebId"] = $sourceWebId
  88.     #$targetWebPartDef.WebPart.Properties["ViewGuid"] = $view.ID
  89.     # note the difference:
  90.     # the server-side version of AddWebPart returns void
  91.     # the client-side equvalent of AddWebPart returns a WebPartDefinition
  92.     [Void]$targetWebPartManager.AddWebPart($targetWebPartDef.WebPart, $targetWebPartZoneId, $targetWebPartIndex)
  93.     # or if you need the WebPartDefinition later, you can use these lines
  94.     #$targetWebPartDef = $targetWebPartManager.AddWebPart($targetWebPartDef.WebPart, $targetWebPartZoneId, $targetWebPartIndex)
  95.     #$targetClientContext.Load($targetFile)
  96.     $targetClientContext.ExecuteQuery()
  97.     
  98.     # write code to check in / publish the page here as required
  99.  
  100.     Write-Host Import completed
  101.  
  102. }
  103. else
  104. {
  105.     Write-Host View $viewTitle not found
  106. }
  107.  
  108.  
  109. # http://wvg-epm01e.sv-services.at/Test-Site2/_vti_bin/exportwp.aspx?pageurl=/Test-Site2/Lists/TestList/test.aspx&guidstring=8c1d2a1a-5be8-469d-806e-2112965d2c1c

That’s it, you should now be able to copy list views (XsltListViewWebPart web parts) from one SharePoint site to another from client-side via PowerShell. Of course, that is limited to a site collection scope, and there are still known issues with copying list views (generally, not limited to the PowerShell solutions), for example, copying views for a document library with a folder structure seems not to work if  you copy it from a parent site to a sub site. More about that eventually later, as soon as I collect a bit more background information about the problem.


Viewing all articles
Browse latest Browse all 206

Trending Articles