In my current project one of the requirements was to add navigation to SharePoint wiki’s. We defined a number of rules that drive the navigation and that are not important for this post. In this post I will describe how I added the navigation controls to the page. Before I started I thought it was pretty easy. I started off creating a a custom navigation provider. I added the navigation controls to the out of the template page, which is wkpstd.aspx in the folder ‘12HIVE\TEMPLATE\DocumentTemplates’. I did the last step just on my development machine to test the navigation. Do not do this in your environment, because it is not supported! But for testing purposes of the navigation provider, it worked very nice.
After I finished this, I planned on creating my custom version of wkpstd.aspx and changing the reference to this template page from code. Turns out that this is not possible. And of course I had just done the demo. Everybody liked it and I just said: ‘it is almost finished. I just need to move the custom navigation controls to a custom template and then we can use it’.
The reference to the template page is stored in the property bag of the SPListItem, in a property called ‘vti_setuppath’. As Gary LaPointe documented in this blog post, you cannot update this property. This was also mentioned in the comments for this post by Mart Muller. So there is no way to change the template for existing wiki pages. I then started to look into the alternative that Mart mentions in his post. This option creates a new custom page to create a new wiki page. This custom page then needs to set the reference to our custom wiki template page. But while researching this option in Reflector, I found that the out of the box page is using some internal methods to add ghosted pages. And I think it is still very hard to get around the out of the box template wkpstd.aspx. It is hardcoded at such a deep level that I found it too difficult to change this to a custom aspx page.
I decided to take a different approach and use a custom usercontrol and add that to the page using the AdditionalPageHead control template. This control loads a custom ASCX file and adds that to the placeholder in the wiki page that is loaded. This control template is activated by a web scoped feature. The elements manifest for the feature looks like this:
<?xml version="1.0" encoding="utf-8" ?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Control Id="AdditionalPageHead" ControlSrc="~/_controltemplates/Custom/CustomWiki.ascx" /> </Elements>
The CustomWiki.ascx file is a custom UserControl that is stored in the CONTROLTEMPLATES folder. It does not contain other controls, it just contains the Control element that registers the inherited class in the code behind. In this class, I override the CreateChildControls:
public class CustomWiki: UserControl { protected override void CreateChildControls() { base.CreateChildControls(); if (SPContext.Current.ListItem != null && SPContext.Current.ListItem.Properties.ContainsKey("vti_setuppath") && SPContext.Current.ListItem.Properties["vti_setuppath"].ToString() == "DocumentTemplates\\wkpstd.aspx") { Control leftActions = GetControl(this.Page, "PlaceHolderLeftActions"); if (leftActions != null) { Control nav = LoadControl("wikinavigation.ascx"); leftActions.Controls.Add(nav); Control recentChanges = GetControl(leftActions, "RecentChanges"); if (recentChanges != null) { leftActions.Controls.Remove(recentChanges); } } } } private Control GetControl(Control ctrl, string controlName) { Control result = null; if (string.Compare(ctrl.ID, controlName) == 0) { return ctrl; } foreach (Control c in ctrl.Controls) { result = GetControl(c, controlName); if (result!=null) { return result; } } return null; } }
The CreateChildControls checks if we are in a wiki page, by checking value of the vti_setuppath property of the current listitem. If this equals wkpst.aspx, we know we are in a wiki page. In that case, we use the function GetControl to find the placeholder to which we would like to add our navigation controls. In our case, this is called ‘PlaceHolderLeftActions’. If that placeholder is found, we load a new user control called ‘wikinavigation.ascx’ and add that to the controls collection of the placeholder. Our custom controls also hides the out of the box ‘Recent Changes’ control in the wiki page template.
I would prefer to have done it in a different way. But I still think it is a nice way to change the out of the box, hard coded, wiki template page. And the advantage of this technique is that when you de-activate the feature, the control template is no longer used, and the wiki pages behave link an out of the box SharePoint wiki page.