Showing posts with label migration. Show all posts
Showing posts with label migration. Show all posts

Thursday, August 18, 2011

Fun with SharePoint and PowerShell: Part 1 of ?

I gave a session at SharePoint Saturday the Conference in DC last weekend called “Applied PowerShell: PowerShell for the SharePoint Server Admin”. (I know it is an annoyingly long name, but I’m not really that creative.) The session basically consisted of me talking about and demonstrating a bunch of really cool things I had learned to do with PowerShell as it pertained to SharePoint.

I’ve been meaning to write a blog post with all of the slides and scripts that I showed during the session but it is an overwhelming large amount of content and I keep putting it off. So, I have decided to meet you (my imaginary reader) half way and do it over the course of several blog posts.

This is not a planned series and I have no idea how may parts there will be. But hey, I know this is part 1. Smile

Script 1: Applying a Theme to One or More Sites/Webs

Disclaimer for those who actually attended fore mentioned session: No, this wasn’t actually covered. Deal with it. Smile with tongue out

Let me start by saying that I don’t really like themes in SharePoint. I work for a company (http://www.northridge.com) that has a design team in house and makes really cool master pages and so I prefer not to resort to themes. However, sometimes you are stuck with OOTB and I get that. If you happen to be stuck with OOTB, themes got much cooler with SharePoint 2010. You can actually take a PowerShell theme and upload it to the Theme Gallery and apply it to your site.

This is because SharePoint 2010 now uses .thmx files for it’s themes. To prove my point see screen shots below:

image image

SharePoint 2010

PowerPoint 2010

I know what you’re thinking, “But Craig, I already knew all of that where is the script?!?!”. Well, it is important to understand that if you are going to do a custom them (not one of the ones that come OOTB) then you will need a THMX file to apply. So lets fast forward and assume you already have a THMX file and you want to apply that to your site(s).

There are two basic steps: add the THMX file to the Theme Gallery, and set the web to use said theme.

Step 1: Add the Theme to the Theme Gallery

This step is pretty straight forward. We just need to add a file to the gallery. I’ve written a simple function that is generic and adds a file from the file system to a specified document library within a given web. Let’s take a look:

function Add-SPFileToLibrary
{
param
(
[String] $file,
[Microsoft.SharePoint.SPWeb] $web,
[String] $LibraryName
)
$lib = $web.GetFolder($LibraryName)

# Prepare to upload
$replaceExistingFiles = $true
$fileName = [System.IO.Path]::GetFileName($file)
$fileStream = [System.IO.File]::OpenRead($file)

# Upload document
$spfile = $lib.Files.Add($fileName, $fileStream, $replaceExistingFiles);

# Commit
$lib.Update();
}




Cool! So, I just need to know what the library’s name is and then I should be good to go. Let’s take a look at the library’s that are provisioned by default in a web.







image


Hmm… that doesn’t seem to have what I need. Nothing that says theme at least. Lets take a look at all of the lists that get provisioned by default.








image



Ah ha! “Theme Gallery” looks like what I want. Lets investigate further!







image


Now we are making progress! From this I can see that the folder I am looking for is “_catalogs/theme”. Ok, let’s put it all together.




$site = Get-SPSite "http://nr01-crtru-lt01/"
Add-SPFileToLibrary -file "C:\Themes\MyNewTheme.thmx" -LibraryName "_catalogs/theme" -web $site.RootWeb
$site.Dispose()



A couple things to note about this. 1) The gallery only exists at the RootWeb of a site collection. 2) Always dispose of instantiated SPSite and SPWeb objects!



Ok, now the theme has been uploaded but it is still not applied to the site. Let’s do something about that.



Step 2: Apply the Theme


In the UI this is really easy. You go to Site Settings, click on Site Theme, choose your theme, and apply. In PowerShell the answer is not so obvious. If you do a little exploring with the Get-Member you may come across $web.Theme and think this is your answer. But alas, this is not the case. The theme property on SPWeb is actually a leftover from v3 themes from 2007. Definitely NOT what we want.



After some digging around on MSDN I ran across Microsoft.SharePoint.Utilities.ThmxTheme. This little utility class gives us access to serveral functions that when put together give us what we are looking for. I’ll spare you the painful iterations of getting to where we want to be and just give you the script.




# Get a collection of available themes
$managedThemes = [Microsoft.SharePoint.Utilities.ThmxTheme]::GetManagedThemes($site)

# Get my new theme
$theme = $managedThemes | Where-Object {$_.Name -eq "My New Theme"}

# Apply theme to root web
$theme.ApplyTo($site.RootWeb,$true)



Awesome! That applies it to the RootWeb but what about all my lonely sub webs that didn’t get it. Well, you can use this nifty trick to set them all to “inherit” the theme from their parent site.




# Loop through each web in the site
foreach ($web in $site.AllWebs)
{
# Make sure it is not the root web
if (-not $web.isRootWeb)
{
# Set sub webs to inherit from root
[Microsoft.SharePoint.Utilities.ThmxTheme]::SetThemeUrlForWeb($web,[Microsoft.SharePoint.Utilities.ThmxTheme]::GetThemeUrlForWeb($site.RootWeb))
}
$web.Dispose()
}
$site.Dispose()



Ok, that does it. Let’s put it all together so we can use it over and over again!




# Adds a file to given library
function Add-SPFileToLibrary
{
param
(
[String] $file,
[Microsoft.SharePoint.SPWeb] $web,
[String] $LibraryName
)
$lib = $web.GetFolder($LibraryName)

# Prepare to upload
$replaceExistingFiles = $true
$fileName = [System.IO.Path]::GetFileName($file)
$fileStream = [System.IO.File]::OpenRead($file)

# Upload document
$spfile = $lib.Files.Add($fileName, $fileStream, $replaceExistingFiles);

# Commit
$lib.Update();
}

# Applys a theme to an entire site collection
function Update-SPSiteTheme
{
param
(
$Path,
$Name,
$Site
)
# Add the theme to the Theme Gallery
Add-SPFileToLibrary -file $Path -LibraryName "_catalogs/theme" -web $site.RootWeb
# Get a collection of available themes
$managedThemes = [Microsoft.SharePoint.Utilities.ThmxTheme]::GetManagedThemes($site)
# Get my new theme
$theme = $managedThemes | Where-Object {$_.Name -eq $Name}
# Apply theme to root web
$theme.ApplyTo($site.RootWeb,$true)
# Loop through each web in the site
foreach ($web in $site.AllWebs)
{
# Make sure it is not the root web
if (-not $web.isRootWeb)
{
# Set sub webs to inherit from root
[Microsoft.SharePoint.Utilities.ThmxTheme]::SetThemeUrlForWeb($web,[Microsoft.SharePoint.Utilities.ThmxTheme]::GetThemeUrlForWeb($site.RootWeb),$true)
}
$web.Dispose()
}
$site.Dispose()
}



Obviously the function needs some work (error handling, comment based help, etc.) but it should get the job done. Give it a try and let me know what you think.



Enjoy!

Saturday, May 14, 2011

PowerShell to remove web parts with errors from SharePoint 2010

One of the things that I run into whenever I do an upgrade from an older version of SharePoint to SharePoint 2010 is that not all of the custom functionality previously implemented still works. This is especially true when moving from WSS 2.0 to SharePoint 2010.

When a client has 50+ sites and these web parts are on every landing page the process of removing them manually is just out of the questions. So how do we solve this?

Well, in SharePoint 2007 I would have said that you should go bug a developer and have them write you a console application to loop through and remove any and all erroring web parts. However, in SharePoint 2010 the story is slightly different.

PowerShell to the rescue!!!

function Remove-SPErroringWebParts
{
[CmdletBinding()]

param
(
[parameter(ValueFromPipeline=$true,Mandatory=$true,Position=0)]
$Web
)

begin
{
Out-Host -InputObject "Beginning Remove-SPErroringWebParts..."
}

process
{
if ($Web -is [string]) {Out-Host -InputObject " This is a string.";$Web = Get-SPWeb $Web}
if ($Web -is [Microsoft.SharePoint.Administration.SPWebApplication]) {Out-Host -InputObject " This is a web app.";$Web = Get-SPSite -WebApplication $Web -Limit All -Confirm:$false}
if ($Web -is [Microsoft.SharePoint.SPSite[]]) {Out-Host -InputObject " This is an array of sites.";$Web | %{Set-AllBackToSiteDefinition -Web $_}}
if ($Web -is [Microsoft.SharePoint.SPSite] ) {Out-Host -InputObject " This is a site.";$Web = (Get-SPWeb -Site $Web -Limit All -Confirm:$false)}
if ($Web -is [array]) {" This is an array."; $Web | %{ Remove-SPErroringWebParts $_ } }
if ($Web -is [microsoft.SharePoint.SPWeb])
{
Out-Host -InputObject " This is a web."
$webpartmanager = $Web.GetLimitedWebPartManager(($Web.Url + "/default.aspx"), [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)

#Create a separate list of webparts to be deleted to avoid enumeration error
$webpartsToDelete = $webpartmanager.WebParts | ?{$_.ErrorMessage -ne $null}

foreach ($webpart in $webpartsToDelete)
{
if ($webpart.ErrorMessage -ne $null)
{
Out-Host -InputObject " Deleting webpart.."
$webpartmanager.DeleteWebPart($webpart)
}
}
$Web.Dispose()
}
}

end
{
Out-Host -InputObject "Finished Remove-SPErroringWebParts."
}
}



The function above looks far more complicated than it really is. I wrote it so that I could save it off and use it for later projects. To be honest, this function isn’t really done as I need to add a great deal of error handling, it only checks the “/default.aspx” page so if you have Publishing sites or even sites that use the Wiki Home Page it will not work, and there is some Out-Host commands that will most likely be changed to Out-Debug. However, I digress. Writing PowerShell for reusability is a completely different discussion that I will probably write about at a later date. Until then there are a couple really great blog posts you can check out if you are interested:




For now, lets take a look at what a more minimalistic approach would be.




$Web = Get-SPWeb "http://site/web"
$webpartmanager = $Web.GetLimitedWebPartManager(($Web.Url + "/default.aspx"), [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$webpartsToDelete = $webpartmanager.WebParts | ?{$_.ErrorMessage -ne $null}
foreach ($webpart in $webpartsToDelete)
{
if ($webpart.ErrorMessage -ne $null)
{
Out-Host -InputObject " Deleting webpart.."
$webpartmanager.DeleteWebPart($webpart)
}
}
$Web.Dispose()



Much less code there but less power as well. This will obviously only work on an SPWeb object and the code would need to be adjusted for any other scope. Let’s take a look at the usage of the original function.

On an SPWeb object:




Get-SPWeb "http://server/site/web" | Remove-SPErroringWebParts


On an SPSite object:


Get-SPSite "http://server/site" | Remove-SPErroringWebParts


On an SPWebApplication object:


Get-SPWebApplication "http://server" | Remove-SPErroringWebParts


Or on an string:


"http://server/site/web" | Remove-SPErroringWebParts





Either way the function will loop through all of the “/default.aspx” pages on the SPWeb objects of the given scope and remove any web parts that have errors.

I’m sure I don’t have to tell you that this can be just as dangerous as it is helpful if you have web parts that are erroring that you are not aware of. As always use caution and I’m not responsible for anything you break while using anything you find here. Smile


I hope this helps! Any and all feedback is welcomed and appreciated. Let me know what you think!