Sunday, January 28, 2007

Now we have the portal setup I want to add a Contact WebPart to each page but before I can do that I have to add a picture for each user I have imported, in order to keep this post a little shorter I’m only going to concentrate on using the UserProfile API’s to set our picture for each user.
(The Contact WebPart is MOSS WebPart that shows a users name and description and optionally their picture)
The field that we are going to set programmatically is accessed on the Users edit profile page as Picture:

Once set this will display the picture on the user profile like this

To do this I need to revisit our User.csv import file and add an extra field that gives the name of the JPG file that holds the users picture.
I’m going to assume that the previous blog post Upload a directory of files in 4 lines has already uploaded the users picture to the SiteCollectionImages picture library, in real life you’d probably use a separate picture library.

Ideally a profile import has occurred after we have added the users to Active Directory and populated the SharePoint profile database.

Now to set the Users Picture property we know to know the Property Name of the Picture Field.
To make this easy to find out here’s the first function for our toolbox Get-SPUserProfileConfigManager.
This function returns a UserProfileConfigManager (, note this is the new class that resides in the Microsoft.Office.Server namespace not the v2 obsolete one that lives in Microsoft.sharePoint.Portal.UserProfiles.
Also ignore the sample currently given in the MSDN documentation above, it won’t work as it uses the old classes where you pass a PortalContext to the constructor whereas the new version of the classes take a ServerContext object.


# Function: Get-UserProfileConfigManager # Description: return a UserProfileConfigManager object which is used for management of MOSS User Profiles # Parameters: PortalURL URL for the Portal Site Collection # # function Get-UserProfileConfigManager([string]$PortalURL) { # Need to get a PortalContext object # as we do not have a HttpContext we need to source one the hard way $site=new-object Microsoft.SharePoint.SPSite($PortalURL) $servercontext=[Microsoft.Office.Server.ServerContext]::GetContext($site) $site.Dispose() # clean up # Return the UserProfileConfigManager new-object Microsoft.Office.Server.UserProfiles.UserProfileConfigmanager($servercontext) }


Once we get the UserProfileConfigManager we can call GetProperties and list the internal and display names for each profile property

$cm=get-userprofileconfigmanager "http://sps:2828" $cm.getproperties() | select name, displayname Name DisplayName ---- ----------- UserProfile_GUID Id SID SID ADGuid Active Directory Id AccountName Account name FirstName First name LastName Last name PreferredName Name WorkPhone Work phone Office Office Department Department Title Title Manager Manager AboutMe About me PersonalSpace Personal site PictureURL Picture UserName User name QuickLinks Quick links WebSite Web site PublicSiteRedirect Public site redirect SPS-Dotted-line Dotted-line Manager SPS-Peers Peers SPS-Responsibility Responsibilities SPS-Skills Skills SPS-PastProjects Past projects SPS-Interests Interests SPS-School Schools SPS-SipAddress SIP Address SPS-Birthday Birthday SPS-MySiteUpgrade My Site Upgrade SPS-DontSuggestList Don't Suggest List SPS-ProxyAddresses Proxy addresses SPS-HireDate Hire date SPS-LastColleagueAdded Last Colleague Added SPS-OWAUrl Outlook Web Access URL SPS-ResourceSID Resource Forest SID SPS-ResourceAccountName Resource Forest Account Name SPS-MasterAccountName Master Account Name Assistant Assistant WorkEmail Work e-mail CellPhone Mobile phone Fax Fax HomePhone Home phone

So from this list I see that I need to set the PictureURL property, to get a UserProfile we first need a UserProfileManager object:

# Function: Get-SPProfileManager # Description: Return a UserProfileManager object which is used for accessing MOSS User Profiles # Parameters: PortalURL URL for the Portal Site Collection # function Get-SPProfileManager([string]$PortalURL) { # Need to get a PortalContext object # as we do not have a HttpContext we need to source one the hard way $site=new-object Microsoft.SharePoint.SPSite($PortalURL) $servercontext=[Microsoft.Office.Server.ServerContext]::GetContext($site) $site.Dispose() # clean up # Return the UserProfileManager new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($servercontext) }

And then a helper function Get-SPUserProfile to obtain the UserProfile object itself:

# Function: Get-SPUserProfile # Description: Return a UserProfile object, this will be created if it does not exist # Parameters: PortalURL URL for the Portal Site Collection # DomainUser UserName in Domain\user format function Get-SPUserProfile([string]$PortalURL, [string] $DomainUser) { $upm= Get-SPProfileManager([string]$PortalURL) if ($upm.UserExists($DomainUser) -eq $false) { $upm.CreateUserProfile($DomainUser) } $upm.GetUserProfile($DomainUser) }

Note that this function will create the UserProfile if it does not already exist.

Now we just need a function that makes it easy to set a single UserProfile property, if you have multiple properties to set it would be best to do them all at once and then call commit.

# Function: Set-UserProfileProperty # Description: Sets a property on a User Profile # Parameters: UserName [optional] UserName in Domain\user format # PropertyName Property to set # PropertyValue Property Value to set # $UserProfile UserProfile object, if using this in a loop this should be set # function Set-UserProfileProperty([string]$UserName, [string] $PropertyName, [string] $PropertyValue, [Microsoft.Office.Server.UserProfiles.UserProfile] $UserProfile) { # If we are not passed a UserProfile object then create it # if ($UserProfile -eq $null) { $UserProfile = Get-SPUserProfile($UserName) } $UserProfile[$PropertyName].Value= $PropertyValue $UserProfile.Commit() }

Note this function can either be called with a pre-created userProfile object or a UserName.

Heres the updated Users.CSV with the Picture Field added at the end  

LoginName, DisplayName, FirstName, LastName, Email, Picture brianb, Brian Ballack, Brian, Ballack,, cowner10.jpg walterf, Walter French, Walter, French,, cowner12.jpg

Now a function to tie this all together, it imports the CSV files, locates the user profile by login name and updates the user’s Picture URL:

function Set-UserPictures([string] $PortalURL, [string] $UserFile, [string] $Domain ) { Import-Csv $UserFile | foreach-object { $name=$Domain + "\" + $_.LoginName; $fullURL=$PortalURL + "/" + $_.Picture; Set-UserProfileProperty $PortalURL $name "PictureURL" $fullURL } }

 And you can make use of all of the above code by running this command:

Set-UserPictures "http://sps:2828" "users.csv" "contoso"

Ok now we're all set to add the Contact WebPart to each publishing page in the next post.

Sunday, January 28, 2007 6:24:37 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, January 25, 2007

Made the UK PowerShell User Group meeting last night in Wokingham, just, the weather and people having accidents on the M3 and M4 kept me late. Got to try out my new Acer GPS unit based on Windows Mobile 5, worked beautifully until setting the return journey when a fatal exception dialog popped up on route calculation, I had to reboot the unit, having seen that dialog many times developing on WM I just had to laugh.

Nice to meet other PowerShell enthusiasts, kudos to PowerGadgets for supplying the beer and pizza and Global Knowledge for hosting.

The meeting was hosted by all round good guy Thomas Lee along with Richard Siddaway. Thomas gave an interesting presentation on PowerShell installation a lot of which I didn't know.

One point to come out of the discussion was the number of profiles that PowerShell will load when it first starts up. Here are the locations and order the profiles get loaded in, note these files won't exist unless you or your admin has created them

  1. %windir%\system32\WindowsPowerShell\v1.0\profile.ps1  (all users, all shells)
  2. %windir%\system32\WindowsPowerShell\v1.0\ Microsoft.PowerShell_profile.ps1 ( all users, the Windows Powershell shell)
  3. %userprofile%\My Documents\WindowsPowerShell\profile.ps1  (per user, all PowerShell versions)
  4. %userprofile%\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 ( per user and the default Windows Powershell shell)

Now you can put code or functions in each of these files but the last loaded file wins if there are any conflicts.

This could be a problem with some companies that would like to define a corporate wide profile for every user and put it in system32\WindowsPowerShell\v1.0, as a user I can override any of the company function definitions with my own code in my own profile. You could lock down the PowerShell %userprofile% files but that might cause other problems.

Someone pointed out at the meeting what was needed is a readonly profile that can be loaded globally and last for every user but hey presto there's a PowerShell team blog out today on making functions read-only , now that's what I call service.


As the meeting closed Thomas also gave out a USB Pen drive to everyone who turned up loaded with PowerShell related goodies, woo hoo free stuff, and oh yeah I'll be speaking at the next meeting about controlling SharePoint with PowerShell, should happen sometime in March.



Thursday, January 25, 2007 9:36:19 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, January 11, 2007

I just finished finalising the flights and hotels to attend the SharePoint 2007 European Conference in Berlin today when I realised I knew zip about the German language, yes I know the usual German words picked up from films, schnell, bitte etc. but I'd never spoken one single german phrase for real.

Whatever country I go to I try to speak a little of the local language, if nothing else it gives the waiters a laugh, so I needed a quick and easy starters course in German, a little Googling and I found this gem German Podcasts by Stephan Wiesner

This is fantastic, its a series of podcasts where Stephan takes you through a story teaching you German as he goes about a Hans a German progammer who flies into an airport, goes through customs and talks about a conference he's been at!

The story is nicely pitched at the complete beginner and Stephan has produced mp3's, pdfs to accompany and even a video.

The content is also posted at which lets you listen on the webpage itself via a Flash plugin.

I wonder can he do a podcast that teaches me how to say: 'What?? you're NOT using PowerShell to script SharePoint?'




Thursday, January 11, 2007 6:12:07 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [2]  |