When software attacks!

Thoughts and musings on anything that comes to mind

4. April 2011 13:57
by Rik Hepworth
0 Comments

Content Types programmatically added to SharePoint libraries not appearing on New menu

4. April 2011 13:57 by Rik Hepworth | 0 Comments

This one caused some consternation, I can tell you. As usual, the solution could be found on the great wide web, but it took some digging, so as usual I am repeating it here.

As part of a SharePoint migration we did recently, we replaced a SharePoint 2007 feature that the client was using (which added content types to libraries from a central list) with a mix of content type replication and PowerShell to add the content types to the libraries.

The code below scans through any site collections whose url begins with our $hostheader variable, then adds the list of content types to the Shared Document library in every web in the site collection. It’s a simple modification of some code on Phil Childs’ Get-SPScripts.com site and I take no credit for it – it’s all Phil’s work.

# Specify name of the library to look for in each site
#
$hostheader = "http://mywebapp*"
$lookForList = "Shared Documents"
#find our site collections and run through each in turn
# get all the site collections, then step into each site in each site collection
# find the list with the name in the var lookforlist
# then change content types on the list specified
#
# note - this will only find site collections where the url starts with the host header var
#
get-spsite where {$_.url -like $hostheader}| Get-SPWeb -Limit all | ForEach-Object {
    write-host "Checking site:"$_.Title
    #Make sure content types are allowed on the list specified
    $docLibrary = $_.Lists[$lookForList]
    if ($docLibrary -ne $null)
    {
        $docLibrary.ContentTypesEnabled = $true
        $docLibrary.Update()
        # Add site content types to the list
                # change the name in the quotes to the name of your content type
                #
        $ctToAdd = $site.RootWeb.ContentTypes["Word Document"]
        $ct = $docLibrary.ContentTypes.Add($ctToAdd)
        write-host "Content type" $ct.Name "added to list" $docLibrary.Title
                # 
        # Add second site content types to the list
                # change the name in the quotes to the name of your content type
                #
        $ctToAdd = $site.RootWeb.ContentTypes["Excel Spreadsheet"]
        $ct = $docLibrary.ContentTypes.Add($ctToAdd)
        write-host "Content type" $ct.Name "added to list" $docLibrary.Title
                # 
        # Add third site content types to the list
                # change the name in the quotes to the name of your content type
                #
        $ctToAdd = $site.RootWeb.ContentTypes["PowerPoint Presentation"]
        $ct = $docLibrary.ContentTypes.Add($ctToAdd)
        write-host "Content type" $ct.Name "added to list" $docLibrary.Title
                # 
        # Update the library object to commit changes
                #
        $docLibrary.Update()
    }
    else
    {
        write-host "The list" $lookForList "does not exist in site" $_.Title
    }
}

When we ran this through, however, whilst the content types were added correctly to the libraries, the New menu failed to list them.

Much (and I mean much!) digging revealed the cause to be down to a property in the library (spList.RootFolder.UniqueContentTypeOrder) that isn’t automatically set when we add the content types using code (which makes sense when you think about it…). However, all our fiddling with PowerShell failed to work. Adding a content type to the property (which is an array of content types) steadfastly refused to work.

We then found a post on the TechNet forums which appeared to give the answer in the form of C# code. We spent a long time on this, so to cut it short: You can’t simply add an item to the UniqueContentTypeOrder property – you have to set it to Null and rebuild it. The trouble is that in order to do that you have to stuff in an object that has an iList interface. Try as we might, we couldn’t create a PowerShell object that would allow us to store an array of ContentTypes and present the iList interface to the UniqueContentTypeOrder property to set the New menu values. Many people said that ArrayList or SortedList should do it, but they didn’t.

In the end, then I got our devs to knock up a rough and ready command line tool based on the code in the TechNet post. It’s really rough and ready, so I won’t post it here. Follow the posting and get your own tame devs to do the same. I only hope that this article becomes easier to find on the web to save you guys some time.

19. November 2010 17:06
by Rik Hepworth
0 Comments

Powershell to find missing features in SharePoint 2010

19. November 2010 17:06 by Rik Hepworth | 0 Comments

When migrating from SharePoint 2007 to 2010, no matter how hard you try there’s always the chance the the content database upgrade process will throw out errors about features being referenced that are not present in the farm. We have used Stefan Goßner’s WssAnalyzeFeatures and WSSRemoveFeatureFromSite (see his original article) to track down the references and exterminate them. It’s not the fastest thing on two legs though, and I have a fondness for having my SharePoint 2010 tooling in PowerShell because of the flexibility it gives me.

Here then, with a big hat-tip to Stefan, is the PowerShell to replicate his functionality. We differ slightly, in that I wanted one command to purge both site- and web-referenced features in one shot rather than calling a command with a scope switch. I have two functions – get-spmissingfeatures takes a Site Collection url as a parameter and scans through site and web feature collections, listing any features with a null definition property. The second function, remove-spmissingfeatures takes the same url and this time finds and removes the errant features in one pass.

Get-SpMissingFeatures:

function get-spmissingfeatures([string]$siteurl)
{
  $site = get-spsite $siteurl


  foreach ($feature in $site.features) {
    if ($feature.definition -eq $null) {
       write-host "Missing site feature:"
       write-host $feature.DefinitionId
       write-host $feature.parent
#      $site.features.remove($featureid)
    }
  }

  $webs = $site | get-spweb -limit all
  foreach ($web in $webs) {
  foreach ($feature in $web.features) {
    if ($feature.definition -eq $null) {
       write-host "Missing web feature:"
       write-host $web.url
       write-host $feature.DefinitionId
       write-host $feature.parent
#      $site.features.remove($featureid)
    }
  }

  }

}

Remove-SPMissingFeatures:

function remove-spmissingfeatures([string]$siteurl)
{
  $site = get-spsite $siteurl


  foreach ($feature in $site.features) {
    if ($feature.definition -eq $null) {
       write-host "Missing site feature:"
       write-host $feature.DefinitionId
       write-host $feature.parent
      $site.features.remove($feature.DefinitionId)
    }
  }

  $webs = $site | get-spweb -limit all
  foreach ($web in $webs) {
  foreach ($feature in $web.features) {
    if ($feature.definition -eq $null) {
       write-host "Missing web feature:"
       write-host $web.url
       write-host $feature.DefinitionId
       write-host $feature.parent
      $web.features.remove($feature.DefinitionId)
    }
  }

  }
}

29. July 2010 09:55
by Rik Hepworth
0 Comments

Powershell script to rename files for use as SharePoint 2010 User Profile thumbnails

29. July 2010 09:55 by Rik Hepworth | 0 Comments

User profile photos have changed in SharePoint 2010 in that they are now stored in a single image library in the MySite Host root site collection. They have also changed in that when you change the profile photo, SharePoint takes the file and creates three new images at specific sizes, then discards the file you gave it. These files have specific names to link them to the user account and come in small, medium and large flavours.

We’ve just delivered a solution to a customer that involved heavy customisation of the Profile page for users. This also involved replacing the large thumbnail version of the profile picture with one which met our size requirements.

The customer had a large group of image files all named in the pattern <firstname surname>.<extension> which had been loaded in to SharePoint as profile pictures. We wanted a quick way to replace the large thumbnail with our own version.

Enter Powershell, stage left. The script below is a little rough and ready but works great. It gets a directory listing, splits the filename and then looks up in AD to see if there’s a user that matches the filename (sans extension). If it finds a match it renames the file to match the pattern <domain>_<SamAccountName>_LThumb.<extension> to match the profile picture naming convention.

As I said, it’s a little rough and ready but I place here for the greater good. You need the ActiveDirectory powershell module to use this. It’s available on Server 2008 and above, and Windows 7 if you install the remote management tools.

The Active Directory Powershell Blog is a great resource for this stuff!

#import-module ActiveDirectory
if (-not (get-module -name activedirectory)) {
    write-host "This script requires the ActiveDirectory powershell modules to run"
    exit
}
$domain="mydomain"
$filesuffix = "LThumb"
$files = get-childitem
foreach ($file in $files) {
    $filesplit = $file.Name.split(".")
    $fullname = $filesplit[0]
    $fileext = $filesplit[1]
    write-host "Searching for:" $fullname
    $user = get-aduser -Filter { Name -eq $fullname }
    if ($user.SamAccountName -eq $Null) {
        write-host "Not Found!"
    } else {
        $newfilename = $domain+"_"+$user.SamAccountName+"_"+$filesuffix+"."+$fileext
        write-host "Renaming:" $file.name "New name:" $newfilename
        rename-item $file.Name $newfilename
    }
}