Fix SharePoint Quick Launch Links using PowerShell


A lot of times when I’m working on an environment for a client, I come across a situation where I’m in need of a way to quickly update links – usually because people have entered them as an absolute URL (e.g. http://intranet/pages/somepage.aspx) instead of a relative URL (e.g. /pages/somepage.aspx).

Usually when I run across a problem like this, I actually get excited – because these are great opportunities to use our buddy PowerShell! Unfortunately, there aren’t many examples out there of how to use PowerShell to manipulate SharePoint Navigation objects. Hopefully this blog post will be helpful for someone looking to do the same thing I needed to do…

Here’s how it all comes together – and if you don’t want to understand how it works just skip to the end and copy/paste the code :).

Getting the Web Object

The first thing we have to do in order to even begin to work on SharePoint Navigation is to retrieve an SPWeb object. To do this, it’s pretty simple – just run the following code, which will store the Microsoft.SharePoint.SPWeb into the $SPWeb variable:

$SPWeb = Get-SPWeb $Web

Getting the Web.Navigation.QuickLaunch Object

After we’ve retrieved that object, if you use Get-Member to view the Methods and Properties, you’ll quickly notice that the Navigation Property has a QuickLaunch property within it. This is exactly what we want. We can now start to use the ForEach-Object cmdlet to actually show us everything beneath the SPWeb.Navigation.QuickLaunch. Or more specifically, we can use an IF statement to make sure we only grab the appropriate URLs – that is, only ones matching the string we want to replace. Here’s the code snippet:

$SPWeb.Navigation.QuickLaunch | ForEach-Object {
    if($_.Url -match $FindString){
        $linkUrl = $_.Url
        Write-Host "Updating $linkUrl with new URL"
        $_.Url = $_.Url.Replace($FindString,$ReplaceString)
        $_.Update()
    }

Pretty cool, so essentially we just say if the URL matches the string we’re looking for, then go ahead and tell us it’s working on it – then update it, replacing the value of the FindString variable with the value of the ReplaceString variable. Both of these variables are actually mandatory parameters, you’ll see that part at the end… Finally, run the Update() method on each one to actually commit the changes to the server.

All done right? No, we can’t forget about the children!

Fixing the Child links

So far the code we’ve seen will only touch the parent links, but each link also has a Children node. We can simply use the same code as before, except this time we’ll run it against the $_.Children property of each link node.

Here’s how that looks:

$_.Children | ForEach-Object {
        if($_.Url -match $FindString){
            $linkUrl = $_.Url
            Write-Host "Updating $linkUrl with new URL"
            $_.Url = $_.Url.Replace($FindString,$ReplaceString)
            $_.Update()
        }
    }

And now, if we put it all together, you’ll see that we have a nice, clean function to do a quick find and replace of navigation links on the quick launch of a single SharePoint Web. I’ve called the function Repair-SPLeftNavigation, and it has 3 required parameters – Web, FindString and ReplaceString:

function Repair-SPLeftNavigation {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][System.String]$Web,
[Parameter(Mandatory=$true)][System.String]$FindString,
[Parameter(Mandatory=$true)][System.String]$ReplaceString
)
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
$SPWeb = Get-SPWeb $Web
$SPWeb.Navigation.QuickLaunch | ForEach-Object {
    if($_.Url -match $FindString){
        $linkUrl = $_.Url
        Write-Host "Updating $linkUrl with new URL"
        $_.Url = $_.Url.Replace($FindString,$ReplaceString)
        $_.Update()
    }
    $_.Children | ForEach-Object {
        if($_.Url -match $FindString){
            $linkUrl = $_.Url
            Write-Host "Updating $linkUrl with new URL"
            $_.Url = $_.Url.Replace($FindString,$ReplaceString)
            $_.Update()
        }
    }
}
$SPWeb.Dispose()
}#endFunction

To run this bad boy, simply dot-source the function and then call it like a cmdlet – for example:

. .\Repair-SPLeftNavigation.ps1
Repair-SPLeftNavigation -Web http://sp2013 -FindString 'http://sp2010' -ReplaceString 'http://sp2013'

If you needed to run this on multiple site collections or even web applications, you could simply use the native ForEach-Object cmdlet in conjunction with this function to accomplish that goal.

Cheers!
RD

Advertisements

Convert SharePoint List Items to Lowercase using PowerShell


Sorry for the lack of blogging lately, it’s not that I haven’t had PLENTY of material – I just frankly haven’t had time to post anything.

However, I thought I’d share a recent function I threw together – if for no other reason than to make sure I don’t forget how I did this…

I’m currently working on a big migration from a legacy HTML website to SharePoint 2010 FIS-E. During this migration, I’ve used PowerShell a LOT to automate tasks and cleanup things. An example of something I needed to clean up is links used for top navigation. We are using a 3rd party web part for our top navigation – it is list driven.

The links in the list all work, but some were entered as lowercase, some camel-case, some with no rhyme or reason at all. I wanted consistency, and I’m sure my client would too…

I wrote a simple helper function to walk through the list and convert every item in a specified column to lowercase. Pretty easy, not much to it… Also, this could easily be modified to do other things like convert to uppercase. I just wanted lowercase. Here it is!

function Convert-SPListItemStringsToLowercase {
Param(
[string]$WebUrl,
[string]$ListName,
[string]$ColumnName
)
$web = Get-SPWeb $WebUrl                                                                    
$list = $web.Lists[$ListName]                                                                                             
$items = $list.Items                                                                                                   
	foreach ($item in $items){
		$item[$ColumnName] = $item[$ColumnName].toLower()
		$item.Update()
		$list.Update()
		}#end Foreach
	$web.Update()
	$web.Dispose()
}#end function

Configuring Custom 404 Pages using PowerShell


I came across an interesting challenge while working on my current project; which is a migration from a classic HTML-based website to SharePoint 2010 For Internet Sites, Enterprise.  Custom error pages…

Out of the box, SharePoint likes to give the user a useless 404 error like this:

image

While there is out-of-the-box support for custom error pages in SharePoint 2010, it’s not completely obvious how you set the custom error pages for a web application.  First of all, SharePoint includes an sps404.html page in the /_layouts/{language} folder which by default includes an STSNavigate reference to handle the redirect.  That’s all well and good, but I want my own!

Here’s how I got what I wanted:

Copy the sps404.html page and rename it to sps404custom.html – leave the new file in the same location.  Open the file in your favorite HTML editor and look for the STSNavigate line – change the link to an appropriate page, example /Pages/Page-Not-Found.aspx.

This would replace the OOTB 404 behavior with an automatic redirect to a Publishing page under the root Pages library of my web application.  Cool!

Now I just have to create a page at /Pages/Page-Not-Found.aspx and add some text.  The page will inherit whatever branding elements I’ve applied to the rest of my site, leaving the user with a much cleaner and consistent experience.  Once I’ve done those two things, there’s a little PowerShell that must be done to actually set the custom 404 page:

$WebApp = Get-SPWebApplication http://url
$WebApp.FileNotFoundPage = "sps404custom.html"
$WebApp.Update() 

Once that’s done, try going to a bogus page in your site and you should be taken to your custom 404 page!

image

Copy SharePoint Navigation across Site Collections


Have you every been tasked with consistent top-level navigation across Site Collections? Sure you have, we all have! Have you heard about the Gary Lapointe STSADM Extensions? I just recently found out about them, and I’m glad I did…

Without Gary’s extensions, you must manually enter the navigation on each site collection; no fun. With Gary’s extensions, you can copy the navigation from a source site collection to as many destination site collections as your heart desires.

This recently saved me a TON of time and since it’s an STSADM command, it can easily be scripted, scheduled, etc. The possibilities are truly endless.

I only used one of Gary’s 144 commands (gl-copynavigation), so I’m sure there is a ton of stuff that these can do that I don’t know of. Have you used these before? Tell me your success stories!

Gary LaPointe:
http://stsadm.blogspot.com/2007/08/stsadm-commands_09.html