As will soon be obvious, I’m a technical SharePoint virgin. It’s been in my life before, but I’ve never really developed with it.
…
I’ll let you decide what that means exactly.
Throughout the week I’ve been working with SharePoint 2010 to create a content managed, mobile-optimized website for a product line. I’ve actually been impressed with the scads of improvements over 2007, particularly in the IDE. No more WSPBuilder! A Right-click “deploy” and “retract” commands that actually work! And best of all, GUI-based feature, package, and web part management.
Even the site collections finally look nice-ish.
What is disconcerting, though, is the heavy integration of the Microsoft AJAX Toolkit (formerly ATLAS, now System.Web.Extensions). It is true that it’s an incredibly full-featured Javascript framework a-la jQuery or Prototype (in fact, more so in some of it’s abilities to mimic the ASP.NET lifecycles and event driven nature). For one, it’s gigantic. An out-of the box page weighs in at ~400 kilobytes when all is said and done.
If your page layouts contain the new Ribbon interface or do anything with authoring, this extra payload is simply required. But what if you didn’t need it? For example, on said mobile site.
Considering the scope of the site was small, the team decided it would be best to try to just hide the authoring pieces unless the user was logged in via Windows Auth. A different approach, but it seemed simpler (at the time) then extending the site and doing all kinds of extra page layouts. Acutally, it would be cool to hear how you other SharePoint masters would approach the issue.
Clearly, a 400 kilobyte page payload won’t cut it on mobile devices. And the vast majority of that (~350 kilobytes or so) was SharePoint’s authoring and Ajax Toolkit scripts. So we set out to strip it out.
To achieve this, we wrapped all the unnecessary (read: authoring) junk in the SPSecurityTrimmedControl, a slick shortcut which flips the “Visible” property to False on any controls contained within it’s tags. The ribbon, the ScriptLink, et cetera.
And such began the Javascript errors.
The first peculiar error consisted of the following:
_spBodyOnLoadFunctionNames is not defined : _spBodyOnLoadFunctionNames.push('loadMDN1');
A lot of Googling later, I was able to determine that this is related to the TreeViewAndDataSource functionality of the Ribbon. Which matched up nicely to the DelegateControl with ControlId=”TreeViewAndDataSource”.
<SharePoint:DelegateControl ID="treeViewAndDataSourceDelegateControl" runat="server" ControlId="TreeViewAndDataSource"></SharePoint:DelegateControl>
But there’s no words there. What does it mean TreeViewAndDataSource? This thing sucks!
Well, if you pull the reference into code and debug it to check out it’s Controls collection, you’ll be able to see it’s actually a built-in SharePoint user control, the markup for which lives in the 14 hive:
control.Controls[0]
{ASP._controltemplates_metadatanavtree_ascx}
[ASP._controltemplates_metadatanavtree_ascx]: {ASP._controltemplates_metadatanavtree_ascx}
AppRelativeTemplateSourceDirectory: "~/_controltemplates/"
BindingContainer: {ASP.PDHOMEPAGE_ASPX_73005077}
ClientID: "ctl00_ctl16"
Controls: {System.Web.UI.ControlCollection}
EnableTheming: true
EnableViewState: true
ID: "ctl16"
NamingContainer: {ASP.PURDUE_MASTER_1189259114}
Page: {ASP.PDHOMEPAGE_ASPX_73005077}
Parent: {Microsoft.SharePoint.WebControls.DelegateControl}
Site: null
SkinID: ""
TemplateControl: {ASP._controltemplates_metadatanavtree_ascx}
TemplateSourceDirectory: "/_controltemplates"
UniqueID: "ctl00$ctl16"
Visible: false
Opening up the 14 hive, looking in the TEMPLATE\CONTROLTEMPLATES\MetadataNavTree.ascx markup shows us it inherits from the MetaDataNavTree class in Microsoft.Office.DocumentManagement assembly in the GAC:
<%@ Control Language="C#" Inherits="Microsoft.Office.Server.WebControls.MetaDataNavTree,Microsoft.Office.DocumentManagement,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" compilationMode="Always" %>
Now we can call up our old friend .NET Reflector to figure out what exactly is happening here.
protected override void OnLoad(EventArgs e)
{
//...SNIP...
this.ResizeGrippyForKeyFilters();
this.CallOnTreeViewLoad();
}
private void ResizeGrippyForKeyFilters()
{
//...SNIP...
string script = "<style>BODY #s4-leftpanel {width:230px;} BODY #MSO_ContentTable{margin-left:230px;} .res-nav-l{width:230px;}</style>";
this.Page.ClientScript.RegisterClientScriptBlock(this.Page.GetType(), "overrideTreeWidthCSSForKeyFilters", script, false);
//...SNIP...
}
protected void CallOnTreeViewLoad()
{
//...SNIP...
MetadataNavigationContext.Current.OnTreeViewLoad(this.WebTreeView);
//...SNIP...
}
public void OnTreeViewLoad(SPTreeView spTreeView)
{
//...SNIP...
spTreeView.Page.ClientScript.RegisterStartupScript(typeof(MetadataNavigationContext), "prepareMDNScript", "function loadMDN2() { EnsureScript('MDN.js', typeof(loadFilterFn), null); }\r\nfunction loadMDN1() { ExecuteOrDelayUntilScriptLoaded(loadMDN2, 'sp.ribbon.js'); }\r\n_spBodyOnLoadFunctionNames.push('loadMDN1');", true);
//...SNIP...
}
That’s a lot of script getting registered. What’s weirder is that even though the control is set to Visible to false, it still renders out the data. I ended up having to go into the page where the control lived, and manually remove it from the control collection:
protected void Page_Init(object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated) return;
var control = this.FindControl("metaDataNavTreeDelegateControl");
this.Controls.Remove(control);
}
All well and good, right?
False.
Message: Object expected
Line: 117
Char: 77
Code: 0
URI: http://ebiz-comm-2010:1983/Pages/default.aspx
That line corresponds to:
document.onreadystatechange=fnRemoveAllStatus; function fnRemoveAllStatus(){removeAllStatus(true)};
And what’s worse, this error occurs ONLY in Internet Explorer. Firebug/Firefox, and even Chrome will ignore it (can someone try that out in Safari for me please?).
{ASP._controltemplates_publishingconsole_ascx}
[ASP._controltemplates_publishingconsole_ascx]: {ASP._controltemplates_publishingconsole_ascx}
AppRelativeTemplateSourceDirectory: "~/_controltemplates/"
BindingContainer: {ASP.PDHOMEPAGE_ASPX_73005077}
ClientID: "ctl00_SPNavigation_ctl00"
Controls: {System.Web.UI.ControlCollection}
EnableTheming: true
EnableViewState: true
ID: "ctl00"
NamingContainer: {System.Web.UI.WebControls.ContentPlaceHolder}
Page: {ASP.PDHOMEPAGE_ASPX_73005077}
Parent: {Microsoft.SharePoint.WebControls.DelegateControl}
Site: null
SkinID: ""
TemplateControl: {ASP._controltemplates_publishingconsole_ascx}
TemplateSourceDirectory: "/_controltemplates"
UniqueID: "ctl00$SPNavigation$ctl00"
Visible: false
The PublishingConsole.ascx seems be an amalgamation of other Publishing Web Controls, but seems to interact with the Ribbon which, of course, was earlier removed.
<%@ Control Language="C#" %>
<%@Assembly Name="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@Register TagPrefix="PublishingWebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.Publishing.WebControls"%>
<%@Register TagPrefix="PublishingInt" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.Publishing.Internal.WebControls"%>
<%@Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.WebControls"%>
<%@ Register TagPrefix="wssuc" TagName="Welcome" src="~/_controltemplates/Welcome.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="MUISelector" src="~/_controltemplates/MUISelector.ascx" %>
<SharePoint:UIVersionedContent id="publishingConsoleV4" UIVersion="4" runat="server"><ContentTemplate>
<PublishingInt:PublishingRibbon id="publishingRibbon" runat="server" />
</ContentTemplate></SharePoint:UIVersionedContent>
<SharePoint:UIVersionedContent id="publishingConsoleV3" UIVersion="3" runat="server"><ContentTemplate>
<!-- Console -->
<%--..SNIP... -- Check out the implementation in your 14 hive \TEMPLATE\CONTROLTEMPLATES\PublishingConsole.ascx file --%>
<!-- Console -->
</ContentTemplate></SharePoint:UIVersionedContent>
Somewhere there is something that I expect is trying to set the ribbon active or inactive with that Javascript snippet. With the ribbon being removed, clearly that won’t be happening.
Again, the solution seemed to be dropping the control out of the Controls collection:
protected void Page_Init(object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated) return;
var control = this.FindControl("publishingConsoleDelegateControl");
this.Controls.Remove(control);
}
Thus it seems, the DelegateControl seems to be a hopped up ContentPlaceHolder for injecting functionality into the page or SharePoint Ribbon. Which I guess is kinda cool, but it seems to have weird side effects, like “ignoring” control visibility.
Or, I may just be using them wrong…
No doubt there’s more items like this to watch for, so hopefully this process will help others track down strange issues when attempting to hide the authoring bits.