Ian Morrish recently posted a solution to the problem that _layouts/userdisp.aspx, the default destination of user links in SharePoint, won’t redirect to the user’s MySite in certain situations. His code works great, but there’s a bit of a snag: it breaks supportability. However, SharePoint’s delegate control functionality provides an opportunity to reproduce his results without breaking the rules. But first, we need to make a few tweaks to let his code work as a delegate.
Since the UserListForm property is protected (sigh), we need to find the control ourselves. FindControl doesn’t search down the control tree, so we need a method that does:
public static T FindControl<T>(this Control root, string id) where T : Control
{
Control c = root;
Queue<Control> q = new Queue<Control>();
if (c == null || c.ID == id)
return c as T;
do
{
foreach (Control child in c.Controls)
{
if (child.ID == id)
return child as T;
if (child.HasControls())
q.Enqueue(child);
}
c = q.Dequeue();
} while (c != null);
return null;
}
This iterative implementation should perform better than its recursive alternative, particularly on a deep tree like that which SharePoint generates.
Next, we build our delegate control. Rather than reinvent the wheel, I used Zac Smith‘s example as a starting point:
public class ProfileMySiteRedirectControl : UserControl, IFormDelegateControlSource
{
public void OnFormInit(object objOfInterest)
{
Redirect();
}
public void OnFormSave(object objOfInterest) { }
protected void Redirect()
{
try
{
var s = Page.FindControl<FormComponent>("UserListForm");
var sc = ServerContext.Default;
if (s == null || sc == null)
return;
var account = s.Item["Account"] as string;
var mgr = new UserProfileManager(sc);
if (mgr != null && mgr.UserExists(account))
Page.Response.Redirect(mgr.MySiteHostUrl + "/Person.aspx?accountname=" + account);
}
catch (SPException) { }
}
}
Finally, we need a feature to activate the control. Delegate controls are attached through features scoped at either the Farm or the WebApp level, with an element manifest containing something like this:
<Control Id="ProfileRedirection"
Sequence="50"
ControlAssembly="Solutionizing.ProfileMySiteRedirect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f549a4d9093d696"
ControlClass="Solutionizing.ProfileMySiteRedirect.ProfileMySiteRedirectControl"
/>
The ID matches the ControlID set on the delegate control in userdisp.aspx:
<SharePoint:DelegateControl runat="server" id="DelctlProfileRedirection" ControlId="ProfileRedirection" Scope="Farm" />
Because the delegate has Scope=”Farm”, our feature needs to be Farm-scoped as well.
Not as simple as Ian’s fix, but once it’s built you can reuse it anywhere with all the advantages of a solutionized customization.
Full solution on CodePlex:
Update 12/23/2008: New version: User Profile MySite Redirect 0.2
Update 3/27/2008: New version: User Profile MySite Redirect 0.3

