Rolling Your Own Contact Details Web Part
The OOB Contact Details web Part suffers from lack of functionality as it is incapable of displaying basic user profile data except for the name and the department along with the photo if available. In this post I will demonstrate how we can roll our own contact details web part that contains additional information like a phone number, position, email and supports presence indication.
Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.
Introduction: What are we going to get?
When finished we will have accomplished the following:
- Accessed user profile properties facilitating the UserProfileManager from the Microsoft.Office.Server.UserProfiles namespace.
- Created a web part displaying some user profile data and supporting presence indication
- The ability to display more than one contact in the web part which could be attractive for virtual teams.
The following Screenshot summarizes the points:
CSS classes are specified in the code and would resemble the following:
.mbContactDetails { padding-right: 15px; padding-left: 0px; margin-bottom: 15px; padding-bottom: 0px; padding-top: 10px; } .mbUserImage { float: left; margin-right: 10px; }
Add this style sheet wherever you find it appropriate.
Basic Web Part Properties
For sake of simplicity I have dropped initial plans to include user selection based on a People Picker control which would have required utilizing an EditorPart to host this control. Instead, user account names must be specified in a semicolon-separated list. This list is stored in the web part’s AccountNames property:
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Account names"), WebDescription("Enter ';'-separated account names (DOMAIN\\...)")] public string AccountNames { get { return this.accountNames; } set { this.accountNames = value; } }
Additionally, users will have the opportunity to decide whether photos should be displayed or not. This might be advantageous in cases where all account information is to be displayed more compact. The DisplayImage property is stores this user setting: (photos are displayed by default)
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Display contact image?"), WebDescription("Display contact image?")] public bool DisplayImage { get { return this.displayImage; } set { this.displayImage = value; } }
That’s all about the properties, let’s take a look at accessing user profiles.
Accessing User Profile Information
Before we dive into the UserProfileManager class (located in Microsoft.Office.Server.UserProfiles) it should be pointed out that this is a MOSS 2007 feature and not available with WSS 3.0 installations. Anyway, in this post we will take a look at it.
The UserProfileManager allows access to user profiles via GetUserProfile which has 6 overloads:
public UserProfile GetUserProfile(bool bCreateIfNotExist); public UserProfile GetUserProfile(byte[] rgbySID); public UserProfile GetUserProfile(Guid guid); public UserProfile GetUserProfile(long recordId); public UserProfile GetUserProfile(string strAccountName); public UserProfile GetUserProfile(string strAccountName, bool doNotResolveToMasterAccount);
While the first 4 overloads shown above are not useful for us both the two last are what we are looking for. One returns a UserProfile object for the given account name (in domain name notation) and the other tries to resolve to the master account which is useful when dealing with exchange mailboxes from within SharePoint. In this example we will use the previous method.
In order to retrieve user profiles for all specified account names we call Initialize from CreateChildControls in order to get a handle to the UserProfileManager instance. The GetUserProfiles method returns a typed list of UserProfile objects associated with the account names. This list is the base of our web part.
private void Initialize() { using (SPSite site = new SPSite(SPContext.Current.Site.Url)) { if (site == null) return; this.serverContext = ServerContext.GetContext(site); this.userProfileManager = new UserProfileManager(serverContext); this.customValidator = new CustomValidator(); } } private List<UserProfile> GetUserProfiles() { List<UserProfile> list = new List<UserProfile>(); string[] split = this.AccountNames.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string accountName in split) { if (!ExistsProfile(accountName)) continue; try { list.Add(this.userProfileManager.GetUserProfile(accountName)); } catch (Exception exception) { customValidator.ErrorMessage = String.Format("Could not get user profile for '{0}'. Reason: {1}", accountName, exception.Message); } } return list; } private bool ExistsProfile(string accountName) { bool result = false; try { result = userProfileManager.UserExists(accountName); } catch { return result; } return result; }
In this web part I have created a Helper class that turns user profile information into HTML. The UserProfile implements an indexer of type string to access various elements in the UserProfileValueCollection. Luckily all keys are encapsulated in the PropertyConstants class allowing for simple access to the values:
UserProfile p = GetUserProfile("DOMAIN/steve"); string pictureUrl = p[PropertyConstants.PictureUrl].Value.ToString();
Now that’s simple. So, the helper class simply gets the information and puts them nicely together on a panel added to the web part’s control collection:
private static Panel GetUserInfo(UserProfile profile) { Panel html = new Panel(); Literal position = new Literal(); position.Text = profile[PropertyConstants.Title].Value.ToString(); Literal department = new Literal(); department.Text = profile[PropertyConstants.Department].Value.ToString(); HyperLink email = new HyperLink(); email.NavigateUrl = String.Format("mailto:{0}", profile[PropertyConstants.WorkEmail].Value.ToString()); email.Text = String.Format("{0}", profile[PropertyConstants.WorkEmail].Value.ToString()); Literal phone = new Literal(); phone.Text = String.Format("P: {0}", profile[PropertyConstants.WorkPhone].Value.ToString()); LiteralControl imnrc = new LiteralControl(); imnrc.Text = "<span style=\"padding: 0 5px 0 5px;\"><img border=\"0\" valign=\"middle\" height=\"12\" width=\"12\" src=\"/_layouts/images/imnhdr.gif\" onload=\"IMNRC('" + profile[PropertyConstants.WorkEmail].Value.ToString() + "')\" name=\"imnmark\" ShowOfflinePawn=1 id=\"contact_im,type=sip\" /></span>"; html.Controls.Add(GetNameControl(profile)); html.Controls.Add(imnrc); html.Controls.Add(new LiteralControl("<br>")); html.Controls.Add(position); html.Controls.Add(new LiteralControl("<br>")); html.Controls.Add(department); html.Controls.Add(new LiteralControl("<br>")); html.Controls.Add(new LiteralControl("E: ")); html.Controls.Add(email); html.Controls.Add(new LiteralControl("<br>")); html.Controls.Add(phone); html.CssClass = "ms-vb"; return html; }
I know there is room for improvements regarding the coding of this piece, yet I want you to remind that this is actually a proof of concept and serves as a starting point for more sophisticated solutions.
Implementing the presence indicator
The default Contact Details web part places a neat little icon next to the user name allowing for sending email quickly among things. All the magic comes from a single line of code:
LiteralControl imnrc = new LiteralControl(); imnrc.Text = "<span style=\"padding: 0 5px 0 5px;\"> <img border=\"0\" valign=\"middle\" height=\"12\" width=\"12\" src=\"/_layouts/images/imnhdr.gif\" onload=\"IMNRC('" + profile[PropertyConstants.WorkEmail].Value.ToString() + "')\" name=\"imnmark\" ShowOfflinePawn=1 id=\"contact_im,type=sip\" /></span>";
The JavaScript function IMNRC does all the work. It takes a single: the email address to use (which apparently is the one associated with the current user profile). Additionally, add both the name and ShowOfflinePawn properties as shown and don’t ask what they do (yet, I’d appreciate some enlightenment here).
That’s it. Once all the stuff has been put together we get the nice web part layout shown in the screenshot at the top of the page.
Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.

