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.

You can download the source code for the web part and the required Feature from here: Source Code: ContactDetailsWebPart Article
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:

contactdetailswebpart

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.

You can download the source code for the web part and the required Feature from here: Source Code: ContactDetailsWebPart Article
Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.






12 Responses to “Rolling Your Own Contact Details Web Part”

It looks like you have a mix of the old and new way of doing it. With WSS V2, you have the imnrc method, but with WSS V3, you say sip=”email@company.com” and leave the id as you have it without calling the IMNRC. The ShowOfflinePawn=1 (which is case sensitive), shows the presence indicator even if the person is offline instead of having to hover over it to find it. I am actually trying to find out how to use that ShowOfflinePawn with the WSS V3 method.

Chris Cottrell added these pithy words on Jan 30 09 at 21:46

Hi,

I tried to install your solution but I have a lot of problem.

Can you explain the steps to follow ?

Thanks

lattelis added these pithy words on Feb 05 09 at 12:09

Lattelis,

The archive contains a VS 2008 solution with two projects: Deployment and SG.Global.SharePoint. The first allows for easy deployment using either STSADM.EXE or WSPBuilder by simply modifying INSTALL.BAT, the latter contains the source code.

The easiest way to deploy the web part is to add the assembly in Deployment/GAC to the Global Assembly Cache and the contents in Deployment/12 to the 12 Hive of your MOSS instance and to use STSADM.EXE to activate the feature:

stsadm -o installFeature -name SG.Global.SharePoint
stsadm -o activateFeature -name SG.Global.SharePoint -url [yourSite]

That’s it. If you want make enhancements to the code I highly recommend downloading WSPBuilder for easy SharePoint Solution deployment.

Steve Graegert added these pithy words on Feb 05 09 at 13:21

If you haven’t heard yet the ShowOffLinePawn = 1 setting is a configurable setting to show the presence icon if the user sets their presence to “off line.”

If you don’t want the icon to show up when a user is off line, set ShowOffLinePawn=0, or simply remove the setting, the default is 0.

Matt Meyer added these pithy words on Apr 16 09 at 15:33

[...] http://graegert.com/?p=748 (Solución un elemento web personalizado que incorporar esta funcionalidad basado en el profile del usuario.) [...]

Tip del día: Agrega al administrador del Sitio como tu primer contacto en SharePoint - Manolo Herrera added these pithy words on Jul 24 09 at 05:04

[...] Considerations for using the SharePoint UserProfileManager UserProfileManager.GetUserProfile Method Rolling Your Own Contact Details Web Part Filed under: Uncategorized No Comments Comments (0) Trackbacks (0) ( subscribe to comments on [...]

Working with User data « n0rpa added these pithy words on Dec 01 09 at 15:39

First, let me say this is exactly what I have been looking for.

However, I get the following error when trying to use it: “A Web Part or Web Form Control on this Web Part Page cannot be displayed or imported because it is not registered on this site as safe”.

I’ve tried most of the stuff I found when I googled the error, but am not having any success.

Can you help?

Vic added these pithy words on Dec 30 09 at 16:46

Vic – apologies for the late response but I’ve been on vacation for quite some time. You most likely forgot to add the assembly to the SafeControls section of the web app’s web.config file. It resembles the following entry:

<SafeControl Assembly="MyWebPartLibrary, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=theToken" Namespace="MyWebPartLibrary.ContactDetailsWebPart"
TypeName="*" Safe="True" AllowRemoteDesigner="True"/>

I do not recall the assembly name and namespace right now but that should give you a hint. Happy coding!

Steve Graegert added these pithy words on Jan 08 10 at 17:33

Thanks a lot. You have no idea how much this article helped me.

Anonymous Coward added these pithy words on Jan 10 10 at 03:02

Nice article. So helpful. I have a problem here. You know at presence indicator. when you click on presence it will show dropdown menu. But it is show 1st colleague only. Not showing for other colleagues.

Srikanth added these pithy words on Feb 15 10 at 02:01

Hello i have tried this code. when i deployed this solution to sharepoint the webpart is appear but it gave no output. I have pass account name i.e. domain\\myname. but it show nothing in my webpart. Can i miss any steps? Please help on this.

Bhavin Sankhat added these pithy words on Jul 21 10 at 07:13

Bhavin – I am afraid I will need more information. Did you check the ULS log in the 12 hive for errors? Also, have you tried debugging the web part by? Please get back to me if you have checked the log and any debug information.

Steve Graegert added these pithy words on Jul 21 10 at 12:31

Leave a Reply

Bad Behavior has blocked 1665 access attempts in the last 7 days.