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
Tagged: Delegate Control, MySite, User Profile
