Friday, October 12, 2012

Using Powershell and CSV to Automate Your Work, Add Webparts to SharePoint Pages Automagically - Part 2

In the first part of this two part post, Using Powershell and CSV to Automate Your Work - Part 1, we looked at how easy it was to use a CSV file to automate some tasks in  Powershell.  In that post we simply opened a CSV with Powershell, then looped through the contents and displayed them.  Now, we'll use the exact same method, but instead use it to do some SharePoint work for us!

Part 1 of this demo is here. Also, the full code including CSVs and Scripts can be downloaded from the Microsoft TechNet Gallery.

Recently a client requested a custom webpart be built and placed on a number of pages (100+) through-out the site.  The webpart had many custom properties and our initial estimate to manually place these webparts on the pages was somewhere around 16 hours, two days!  Now that's not absolutely terrible, but we can do better than that.  With Powershell and a CSV, we knocked that 16 hours down to less than a minute, of course not including the time to write the script!

So, let's jump in and add some webparts using Powershell!  Please note, all these scripts and CSVs are placed in the path "C:\Powershell" and are run from that location.

First, we'll create a new site collection in our development environment, and create two new lists.  This is simply to give me a few aspx pages with which to work with and place webparts on:


So now we have a new site collection, Powershell CSV (awesome name), and two new lists, List 1 and List 2.

Now we'll create our CSV file with the appropriate columns (excuse the tiny image, but I needed to zoom out for all the data to show):



In this demo we are going to be placing the PageViewerWebPart onto our pages.  I chose this only because you can set a two properties and it will work just fine, Title and ContentLink property.  Based on that, the CSV has an appropriate Title and ContentLink columns.

Here is a break down of the columns:
  • FullWebUrl - This is the full URL to the site where the aspx page lives.
  • FullPageUrl - This is the full URL to the page where we will place the webpart.
  • WebPartZone - This is the name of the webpart zone on the page where we will place the webpart.
  • Namespace - This is the full namespace of the webpart to place on the page.
  • Title - This is the title property of the webpart.
  • ContentLink - This is the link property of the webpart.
  • Position - This is the index position within the WebPart Zone.
 As you can see in the CSV, we've added our site URLs and page URLs, so we'll be adding webparts to both lists AllItems.aspx pages, List2's NewForm.aspx, and the home.aspx in the main site.  These will all get different webpages to display so we can see the difference in them.

Now perform a "Save As" and make sure you choose "CSV (Comma delimited)", we'll call it "Powershell-CSV-Demo-Part-2-Adding":

Now!  On to the script!

Write-Host "Starting..."
$addCsv = Import-Csv -path c:\PowerShell\Powershell-CSV-Demo-Part-2-Adding.csv
$addCsv | ForEach-Object {
          #Set variables from CSV
          $varWebUrl = $_.'FullWebUrl'
          $varPageUrl = $_.'FullPageUrl'
          $varWebPartZone = $_.'WebPartZone'
          $varNamespace = $_.'Namespace'
          $varTitle = $_.'Title'
          $varContentLink = $_.'ContentLink'
          $varPosition = [int]$_.'Position'


          #Add 10 to position to ensure they move to bottom of form
          $varPosition = $varPosition + 10


          #Open the web
          $web = Get-SPWeb -Identity $varWebUrl


          #Open the page and get the webpart manager
          $webpartmanager = $web.GetLimitedWebPartManager($varPageUrl,[System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)


           #Create webpart object
           $webpart = new-object $varNamespace


           #Set the webpart properties
           $webpart.Title = $varTitle
           $webpart.ContentLink = $varContentLink


          Write-Host "Adding Webpart in page: $varPageUrl" -foregroundcolor green

          #Add the webpart to the page
          $webpartmanager.AddWebPart($webpart, $varWebPartZone, $varPosition);


          #Dispose of all the objects
          $webpartmanager.Dispose();
          $web.Dispose();
}
Write-Host "Finished processing!"


Here's a break down of what the script is doing:
  1. Importing the CSV, then performing a loop on each line in the CSV
  2. For each column, we set a variable to use and pull out the column value
  3. We add 10 to the index position.
    • We're doing this mainly because there may already be webparts on the page and we want to make sure they go below the other content.  This may not be what your requirements need, and if not, just remove it, but be aware that there may be other webparts there.
  4. Create a web varibale and open the site.
  5. Using the web variable we get the Webpartmanager from our selected page in that web.
  6. Create a webpart variable using the Namespace from our CSV
  7. Set the appropriate properties on our new webpart object, again using the CSV values
  8. Using the Webpartmanager, we add the webpart to the zone specified in the CSV and the position.
  9. Finally we dispose of our objects
Easy peasy!  Now let's look at our pages before we run the script:





Ok, lets run the script and see the output, then the resulting pages:


Bam!  Script ran and outputted that it added webparts to the pages from our CSV.  Now let's look at the pages:





As you can see, all the pages now have a PageViewerWebPart added and are displaying the URLs from our CSV!  Also, note on List 2, I provided the SharePoint URL before the MSDN URL, but gave the MSDN URL a higher index position.  So even though the line in the CSV was before it, the index positions went in correctly.  So if you have a hugh CSV you can tack things onto the end and the script will still position correctly based on the index Postion column.

NOTE: In my environment it took a few seconds for these webparts to show and display correctly.  This was most likely due to my development environment, and its lower CPU/RAM and network.  Once the pages were fully rendered (and cached!), it worked fine.

Pretty cool eh?!  But let's say your CSV has a thousand items on it, and all of sudden you realize that every webpart you just deployed had a major issue!  You're looking at manually retracting all of those.  Well, fear not, we've got a script for that!

Following the same pattern, we can have a stripped down CSV and modified script to remove all those webparts.

First, let's look at the CSV:



Here is a break down of the columns:
  • FullWebUrl - This is the full URL to the site where the aspx page lives.
  • FullPageUrl - This is the full URL to the page where we will place the webpart.
  • Namespace - This is the full namespace of the webpart to remove from the page.
This CSV is much simpler as all we need to do is open the web, open the page, and remove the webparts we are looking for.

NOTE! Be careful with this script!  Your page may already have other webparts of the same type, that you do not want to remove!  This works best when you have your own custom webparts, this way there is no risk of removing something from SharePoint that you do not want to have gone!

Now perform a "Save As" and make sure you choose "CSV (Comma delimited)", we'll call it "Powershell-CSV-Demo-Part-2-Removing":

Now! On to the script!

Write-Host "Starting..."
$addCsv = Import-Csv -path c:\PowerShell\Powershell-CSV-Demo-Part-2-Removing.csv
$addCsv | ForEach-Object {
     #Set variables from CSV
     $varWebUrl = $_.'FullWebUrl'
     $varPageUrl = $_.'FullPageUrl'
     $varNamespace = $_.'Namespace'


     #Open the web
     $web = Get-SPWeb -Identity $varWebUrl


     #Open the list display page and get the webpart manager
     $webpartmanager = $web.GetLimitedWebPartManager($varPageUrl, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)


     #Get all the webparts
     $wps = $webpartmanager.webparts


     #Create an empty array to hold webparts we find
     $wpArray = @()


     #Loop throuhg the webparts and save off any webparts we find
     Write-Host "Looking for $varNamespace webparts in page: $varListUrl" -foregroundcolor green
     foreach($wp in $wps)
    {
         if ($wp.GetType().ToString() -eq $varNamespace)
        {
               $wpArray = $wpArray + $wp
         }
    }


   #Loop through our array and remove the webparts
   Write-Host "Found" $wpArray.length "$varNamespace webparts..." -foregroundcolor green
   foreach($wpA in $wpArray)
   {
         Write-Host "Deleting data view..." -foregroundcolor green
          $webpartmanager.DeleteWebPart($wpA)
   }


   #Dispose of our objects
   $webpartmanager.Dispose();
   $web.Dispose();


   #Add space for output
   Write-Host " "
}
Write-Host "Finished processing!"



Here's a break down of what the script is doing:
  1. Importing the CSV, then performing a loop on each line in the CSV
  2. For each column, we set a variable to use and pull out the column value
  3. Create a web varibale and open the site.
  4. Using the web variable we get the Webpartmanager from our selected page in that web and pull all the webparts into an array.
  5. Create an empty array to hold any webparts we are looking for.
  6. Loop through the current page's webparts and if we find any that match our namespace we save them off into our new array.
  7. Loop through our new array and delete any webparts we found.
  8. Finally we dispose of our objects
 Its pretty straight forward.  We have to pull the webparts out into our own array as like any other programming array, we can not delete from an object we're iterating through.

So, let's run this script and see the output:


Bam!  And now all those webparts we added are gone!  I won't bore you with 4 more screen shots, but they're history!  Our script let us know what it found and what it was removing.

Part 1 of this demo is here. Also, the full code including CSVs and Scripts can be downloaded from the Microsoft TechNet Gallery.

Enjoy!




No comments:

Post a Comment