Category Archives: Silverlight

User Profiles in a Silverlight PivotViewer

After seeing the PivotViewer used for Cricket World Cup and SharePoint sites, I thought it would be great way of viewing user profiles in SharePoint. As always, I had a great idea without the time or in depth technical knowledge of PivotViewer, in this case, to implement it until now.

My aim was to great a Pivot View that could be updated daily or weekly for users to see people information in a graphical form that can be used for both identifying groups of individuals with similar characteristics such as location and/or skills as well as being able to display in histograms the number of people that fitted in the selected filters i.e. how many people in each department.

Here’s the result with how I did it to follow:

Zooming in on an individual shows the picture in full and the profile properties I mapped.

The process involves creating the image collection and xml definitions that the Pivot Viewer uses to display the content and filtering, sorting and grouping controls.

For my first run, I’m keeping it simple and just including the person’s Name, description (About Me), department and link to My Site. This meant I could focus on the solution rather than working out how to get each property and format it.

To start with I need to get an export of the User Profile pictures (presuming this is the main source, you may have another), onto the file system. As I intend to automate the build of these files to support scheduling, I also needed to use the command line tools to generate the deep zoom images. I decided to use powershell, as it’s my new best friend and quick to prototype.

In my first run, I created one powershell file to export the images and CXML file but discovered that the CXML must reference the correct image ID created by the command line tool DZCollection.exe so I split the file in two. The process is now as follows:

  1. Use Powershell to export images – Get Enumerator over the UserProfileManager
  2. Export each profile image if exists to the file system
  3. Execute the command line tools to generate the deep zoom images and collections
  4. Use Power Shell to export properties – Enumerator again over the profiles looking up the image ID from the XML file created in step 3
  5. Create the CXML file using a template

Powershell script for exporting images:

$ver = $host | select version

if ($ver.Version.Major -gt 1) {$Host.Runspace.ThreadOptions = “ReuseThread”}

Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

Set-location $home

Start-SPAssignment -Global

$LocalPath = “C:\Development\UserProfilePivot\ProfilePictures”

$MySiteUrl = “http://intranet.contoso.com/my/”

$site = Get-SPSite $MySiteUrl

$siteWeb = Get-SPWeb –site $site

Write-Host “My Site retrieved:” + $site

$context = Get-SPServiceContext($site)

$pm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)

$AllProfiles = $pm.GetEnumerator()

foreach($profile in $AllProfiles)

{

$photourl = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PictureUrl].Value

if($photourl){

$personName = $profile.DisplayName

Write-Host “Exporting: ” $personName

#Download file

$photoFile = $siteWeb.GetFile($photourl)

$binary = $photoFile.OpenBinary()

$stream = New-Object System.IO.FileStream($LocalPath + “/” + $personName + “.jpg”), Create

$writer = New-Object System.IO.BinaryWriter($stream)

$writer.write($binary)

$writer.Close()

}

}

Stop-SPAssignment -Global

If you have a large collection of profile properties, I would use the paging function overload of the GetEnumerator to specify the start row and page size

Create the deep zoom images and collection

I create a command line file to this piece that calls the 2 tools available in the SDK:

cd “C:\Program Files (x86)\Microsoft Expression\Deep Zoom Composer\DZSamples”

DZConvert.exe C:\Development\UserProfilePivot\ProfilePictures\*.jpg C:\Development\UserProfilePivot\ProfilePictures\dzimages

DZCollection.exe C:\Development\UserProfilePivot\ProfilePictures\dzimages C:\Development\UserProfilePivot\UserPicturecollection.xml

Export the properties to create the CXML file

To keep it simple, I created the CXML file first by hand (You could use the Excel tool) and then just clone the XML nodes in the power shell (get this tip from here http://powershell.com/cs/blogs/tobias/archive/2009/02/02/xml-part-2-write-add-and-change-xml-data.aspx)

The template looked like this:

<?xml version=”1.0″ encoding=”utf-8″?>

<Collection Name=”User Profiles” SchemaVersion=”1.0″ xmlns=”http://schemas.microsoft.com/collection/metadata/2009&#8243; xmlns:p=”http://schemas.microsoft.com/livelabs/pivot/collection/2009&#8243; xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt;

<FacetCategories>

<FacetCategory Name=”Department” Type=”String” />

</FacetCategories>

<Items ImgBase=”ProfilePictures/UserPicturecollection.xml”>

<Item Img=”#51″ Id=”51″ Href=”http://intranet.contoso.com&#8221; Name=”Person Name”>

<Description>About Me</Description>

<Facets>

<Facet Name=”Department”>

<String Value=”Executive” />

</Facet>

</Facets>

</Item>

</Items>

</Collection>

Here’s the powershell script that creates that generates the updated file:

$ver = $host | select version

if ($ver.Version.Major -gt 1) {$Host.Runspace.ThreadOptions = “ReuseThread”}

Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

Set-location $home

Start-SPAssignment -Global

$LocalPath = “C:\Development\UserProfilePivot”

$MySiteUrl = “http://intranet.contoso.com/my/&#8221;

$XMLFileLoc= “C:\Development\UserProfilePivot\UserProfilePivot.xml”

$PicCollFileLoc= “C:\Development\UserProfilePivot\UserPicturecollection.xml”

Write-Host “Load existing XML file: ” $XMLFileLoc

$UserProfileXML = New-Object XML

$UserProfileXML.Load($XMLFileLoc)

Write-Host “XML file Loaded”

$cloneItem = @($UserProfileXML.Collection.Items.Item)[0]

Write-Host “Load collection XML file: ” $PicCollFileLoc

$PicCollXML = New-Object XML

$PicCollXML.Load($PicCollFileLoc)

Write-Host “XML file Loaded”

$site = Get-SPSite $MySiteUrl

$siteWeb = Get-SPWeb –site $site

Write-Host “My Site retrieved:” + $site

$context = Get-SPServiceContext($site)

$pm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)

$AllProfiles = $pm.GetEnumerator()

foreach($profile in $AllProfiles)

{

$photourl = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PictureUrl].Value

if($photourl){

$personName = $profile.DisplayName

$personDept = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::Department].Value

$personAbout = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::AboutMe].Value

$personURL = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PersonalSpace].Value

Write-Host “Exporting: ” $personName

#Find users image reference in collections file

$ImagePath = “ProfilePictures/dzimages/” + $personName.Replace(” “,”%20”) + “.xml”

$ImageID = $PicCollXML.Collection.Items.I | Where-Object { $_.Source -eq $ImagePath }

if($ImageID){

$ProfileCount = $ImageID.Id

$newperson = $cloneItem.Clone()

$newperson.Img = “#” + $ProfileCount

$newperson.Id = $ProfileCount

if($personURL){

$newperson.Href = “http://intranet.contoso.com&#8221; + $personURL

} else {

$newperson.Href = “http://intranet.contoso.com&#8221;

}

$newperson.Name = $personName

$newperson.Description = $personAbout

#Assume we know the order of the Facets

$FacetDept = @($newperson.Facets.Facet)[0]

$FacetDept.String.Value = $personDept

$UserProfileXML.Collection.Items.AppendChild($newperson) > $null

}

}

}

Write-Host “Save XML file back”

$UserProfileXML.Save($XMLFileLoc)

Stop-SPAssignment –Global

In brief, I open the template CXML file and create a clone of the Item element, I then loop through all the profiles and if they have a picture, find the element in the collection XML file using the Where-Object clause on the Items node. If we find a corresponding image, then get the user properties and start updating the clone node. The Department value was tricky to reference as you need to get the sub element first in this case Facets.Facet[0] rather than just reference through the XML tree. Last of all, remember to append your cloned node and when you’ve reached the last profile, write it all back to file.

Some improvements are needed as follows:

  • Only process users who’s profiles were updated since the routine last ran
  • Add some more facets such as skills, past projects etc
  • Re-use the same CXML file and lookup the users entry by Name and update
  • Enumerate in batches using the overload
  • Include users without an image and create a dummy profile picture instead
  • Bigger images to start with would work better.
  • Remove the Bing search result link as it’s probably irrelevant in a corporate environment
  • My script could always be improved I expect and it may be possible to use FIM to do the export somehow but this works for now

The final step is to upload all the files into a SharePoint document library and a the Silverlight Pivot Viewer control to the page and reference the CXML file. Unfortunately my VM gave me the “Your client does not support opening this list with Windows Explorer” and I gave up trying to fix it.

You can find out more about the Pivot Viewer here along with all the downloadable examples and tools:

http://www.silverlight.net/learn/data-networking/pivot-viewer/pivotviewer-control

As always, use the code as-is.

Leave a comment

Filed under PowerShell, SharePoint 2007, SharePoint 2010, Silverlight, User Profiles