Sunday, November 25, 2012

Performance Increase for Multi-Lingual sites

Improve Performance of Variations

Speed up custom SharePoint sites by caching often used objects.


In a project I was involved with we developed multiple public sites that were multi-lingual. We used the SharePoint Variation system to create an identical site structure in multiple languages.
Because we were developing multiple multi-lingual sites with custom webparts in this project we would often have to check in which Variation the webpart was, so we could find a list in the top level variation web, or create the correct link to a page in a variation.

After a while we found out this code was being called around 20x per page and each call would take approx. 150ms!
The object we were using frequently was Microsoft.SharePoint.Publishing.Variations.Current
We found that by caching this object in HttpContext.Current.Cache, performance increased quite significantly across all 5 live multi-lingual sites (approx. 3 secs in every page load). We also found out (the hard way) that it matters in which context you cache the object compared to the current context you're in when you get the object from the cache (e.g. http vs https), so be wary of that when you implement caching on this object.

Great results for such an easy change!


private static Microsoft.SharePoint.Publishing.Variations GetCurrentVariation()
        {
            Microsoft.SharePoint.Publishing.Variations aCachedCurrentvariation = null;

            try
            {
                bool secureConnection = HttpContext.Current.Request.IsSecureConnection;

                string aCurrentCacheKey;

                // set the cache key to ensure we're using a different cache for HTTP and HTTPS
                if (secureConnection)
                {
                    aCurrentCacheKey = Variations._cacheKeySecure;
                }
                else
                {
                    aCurrentCacheKey = Variations._cacheKeyUnsecure;
                }

                if (HttpContext.Current.Cache[aCurrentCacheKey] != null)
                {
                    aCachedCurrentvariation = HttpContext.Current.Cache[aCurrentCacheKey] as Microsoft.SharePoint.Publishing.Variations;
                }
                else
                {
                    aCachedCurrentvariation = Microsoft.SharePoint.Publishing.Variations.Current;

                    if ((aCachedCurrentvariation == null) || (aCachedCurrentvariation.UserAccessibleLabels == null) || (aCachedCurrentvariation.UserAccessibleLabels.Count == 0))
                    {
                        // do not cache when current variation is null
                    }
                    else
                    {
                        HttpContext.Current.Cache.Add(aCurrentCacheKey, aCachedCurrentvariation, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Caching error in ToolBox.Variations: " + ex.Message);
            }

            return aCachedCurrentvariation;
        }


Sunday, August 26, 2012

Minimize page size for Public Website


In order to minimize the page load times and page sizes for anonymous users that do not require all the SharePoint scripts (because they can't use the ribbon anyway) can be done by combining the ideas of my previous posts.

Here's how:
1) Deploy different masterpage for the anonymous/public side and the authenticated/CMS side
In the anonymous one you can get rid of fall the stuff that's only useful for the CMS side, like the ribbon and all the styling required for it. The CMS side is a normal SharePoint masterpage.
2) Switch between the masterpage when the page is loading based on whether the Page is in edit mode or whether the current zone is the public one or the authenticated one. Here's a code example of doing this by extending the SharePoint Pages class. This should also by possible by using a HTTPModule, when I have a code example I'll post that one too. 
3) That's it!

Look at this example of a working masterpage that is quite minimal. It does still output some gunk that still needs removing, but the page size already dropped by 2/3's. When I get it completely right I think it will drop by another 50%. (NB: It's an HTML5 masterpage so you should change some things, like the DOCTYPE, before you can use it as a normal XHTML masterpage for anonymous users)

Minimal Anonymous Master Page


<%@ Master Language="C#" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="Wiersema" Namespace="CapGemini.Microsoft.Wiersema.Utilities" Assembly="CapGemini.Microsoft.Wiersema,Version=1.0.0.0,Culture=neutral,PublicKeyToken=f9b45bece6cd4a03" %>
<%@ Register TagPrefix="WiersemaControls" TagName="SimpleSearchBox" Src="~/_controltemplates/CapGemini.Microsoft.Wiersema/SimpleSearchControl.ascx" %>
<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<!DOCTYPE html>
<html id="Html1" lang="<%$Resources:wss,language_value%>" dir="<%$Resources:wss,multipages_direction_dir_value%>" runat="server" >
<head id="Head1" runat="server">
    <meta charset="UTF-8" />  

    <title id="onetidTitle">
        <asp:ContentPlaceHolder ID="PlaceHolderPageTitle" runat="server">
            <SharePointWebControls:ListProperty ID="ListProperty1" Property="Title" runat="server"/> - <SharePointWebControls:FieldValue ID="FieldValue1" FieldName="Title" runat="server"/>
        </asp:ContentPlaceHolder>
    </title>
   
    <SharePoint:DelegateControl ID="DelegateControl1" runat="server" ControlId="AdditionalPageHead" AllowMultipleControls="true" />
    <!-- changed icon url -->
    <SharePoint:SPShortcutIcon runat="server" IconUrl="/_layouts/images/kpidefault-0.gif"/>
    <asp:ContentPlaceHolder ID="PlaceHolderBodyAreaClass" runat="server" />
    <asp:ContentPlaceHolder ID="PlaceHolderTitleAreaClass" runat="server" />
    <SharePoint:SPPageManager ID="SPPageManager1" runat="server" />
    <SharePoint:SPHelpPageComponent ID="SPHelpPageComponent1" Visible="false" runat="server" />
   
    <!-- html 5 styles -->
    <link rel="stylesheet" href="/_layouts/CapGemini.Microsoft.Wiersema/html5/css/style.css">

    <asp:ContentPlaceHolder ID="PlaceHolderAdditionalPageHead" runat="server" />

    <PublishingWebControls:AuthoringContainer runat="server" id="AuthoringContainer1" DisplayAudience="ReadersOnly">
        <Wiersema:StopJSPrefetchControl ID="StopPrefetch" runat="server" />
    </PublishingWebControls:AuthoringContainer>
 
     
</head>
<body id="body" runat="server">
    <form id="Form1" runat="server">
     
    <WebPartPages:SPWebPartManager ID="m" runat="Server" />
   
    <SharePoint:SPNoScript ID="SPNoScript1" runat="server" />
            <div id="MSO_ContentDiv" runat="server">
                <div id="s4-mainarea" class="s4-pr s4-widecontentarea">
                    <div class="site">
                        <!-- HEADER BLOCK Start -->
                        <!-- <div class="NutriciaUpHeader"></div> --><!-- end of NutriciaUpHeader -->
                        <div class="html5master">
                        </div>
                   <div class="WiersemaWrapper">
                            <div class="WiersemaBodyWrapper">
                       <Wiersema:HTMLSwitchPanel CurrentTagType="header" runat="server">
                                    <div class="WiersemaLogo">                                      
                                    </div>
                                    <div class="DivsearchFrm">
                                        <fieldset class="searchFrm">
                                            <div id="s4-searcharea" class="s4-search s4-rp">
                                                <asp:ContentPlaceHolder ID="PlaceHolderSearchArea" runat="server">
                                                    <WiersemaControls:SimpleSearchBox runat="server"></WiersemaControls:SimpleSearchBox>
                                                </asp:ContentPlaceHolder>
                                            </div>
                                        </fieldset>
                                    </div>
                                    <Wiersema:HTMLSwitchPanel CurrentTagType="nav" runat="server">
                                    </Wiersema:HTMLSwitchPanel>
                                    <!-- Top Menu Navigation -->
                       </Wiersema:HTMLSwitchPanel> <!-- end of header -->
                                                                                   
                       <div class="WiersemaMainContent">
                                    <asp:ContentPlaceHolder ID="WiersemaBreadCrumb" runat="server">
                                        <!-- breadcrumb navigation -->
                                        <Wiersema:HTMLSwitchPanel CurrentTagType="nav" runat="server">                                          
                                            <div class="blockBreadCrumb">
                                                   
                                            </div>                                                                                      
                                        </Wiersema:HTMLSwitchPanel>
                                    </asp:ContentPlaceHolder>
                                   
                                    <!-- main  section -->                                                                          
                                    <asp:ContentPlaceHolder ID="PlaceHolderMain" runat="server"></asp:ContentPlaceHolder>                              
                                    <!-- mian  section -->                                                                
                                </div> <!-- end WiersemaMainContent -->
                                <Wiersema:HTMLSwitchPanel CurrentTagType="footer" runat="server">
                                    <div class="WiersemaSiteMapContainer">
                                           
                                    </div><!-- WiersemaSiteMapContainer -->
                                </Wiersema:HTMLSwitchPanel> <!-- end of footer -->                                          
                            </div> <!-- end of WiersemaBodyWrapper -->
                        </div> <!-- end of WiersemaWrapper -->
                    </div> <!-- end of site -->
                </div>
            </div>
    </form>
</body>
</html>

Tuesday, June 5, 2012

Remove core.js for anonymous users the right way


Optimize page speed

Not loading unnecessary JavaScript (e.g. core.js) in anonymous mode

When developing public sites often a requirement is to make the site perform as fast as possible. A quick win would seem to stop loading the SharePoint Javascripts like core.js. However, it is not so easy to do this without creating JavaScript errors on the page.


I reproduce the solution here along with the statistics since it is such a hard to find solution.

The Goal

Stop having JavaScripts for the ribbon and site action menu being loaded for anonymous viewers of the site without creating JavaScript error or having other detrimental effects for the CMS users.

The Method 

Stop the pre-fetching of the SharePoint javascripts for anonymous users. Here are the implemention details:

Add the following controls to your masterpage. The Authoring container takes care of loading its contents only to it's DisplayAudience. We're targeting at the anonymous users which is the ReadersOnly group. In this way the editing experience is not affected. Should you ever want to target control to content editors, use the AuthorsOnly group. Don't forget to register the PublishingWebControls namespace on the masterpage!
Inside the Authoring container we add a reference to our custom control that will stop the prefetching of the JavaScript.

<PublishingWebControls:AuthoringContainer runat="server" id="AuthoringContainer1" DisplayAudience="ReadersOnly">
        <Wiersema:StopJSPrefetchControl ID="StopPrefetch" runat="server" />
    </PublishingWebControls:AuthoringContainer>

This is the control that stops the pre-fetching of, for example, core.js. You can simulate the behaviour by adding ?prefetch=0 to the url of a SharePoint page.

public class StopJSPrefetchControl : Control
    {
        protected override void OnPreRender(EventArgs e)
        {
            Page.ClientScript.RegisterStartupScript(this.GetType(), "TrimJs""<script type='text/javascript'>SP.SOD.set_prefetch(0);</script>");
            base.OnPreRender(e);
        }
    }


The Results

Here are the results on a local machine, using on almost empty page.




                                                              First Request               Second Request
Pre-fetch scripts (22 requests)           503,9 KB (2,3 secs)      503,9KB (456,5 cached in 1,3 secs)


No pre-fetch of scripts (13 requests)   154,9KB in 1,38 secs    154,9KB (107,5 cached in 0,7 secs)


As you can see all stats are greatly improved. The improvement is of course an absolute one, it will save 9 requests and approx. 350KB in page size. The page load time will decrease depending on the client's bandwidth and latency so could be far greater than the speed increase measured here of approx. 1 sec.
Server load will also have diminished because of the lower number of requests.


Below is shown what scripts are loaded with prefetch=0 or the default behaviour


Prefech=0
Default, every standard SP scripts is prefetched




Sunday, May 6, 2012


SharePoint and HTML5 Investigation Overview


This blog is meant to showcase my investigation on using SharePoint for HTML5 publishing solutions. It explains a visual studio project in which I tried to create a solution that allows you to create sites that utilize HTML5 for displaying engaging, interactive websites to visitors with new browsers while allowing content editors and legacy brower users to retain the standard SharePoint functionality.

There are three parts so far in the investigation:

Part 1 is about the background for the investigation on this subject, why HTML  5 + SharePoint.
Part 2 is about the work that was done, it contains links to the code and solution overview.
Part 3 is about the results that were found, and should get a discussion going on what can be improved and other possible paths to pursue.

Continue to Part 1


Saturday, May 5, 2012

SharePoint and HTML5 Technical Implementation


Publishing Definitions



public static class PublishingDefinitions
    {
        public static string HTML5SectionOpen = "<section>";
        public static string HTML5SectionClose = "</section>";

        public static string XHTMLSectionOpen = "<div class=\"section\">";
       
        public static string HTML5ArticleOpen = "<article>";
        public static string HTML5ArticleClose = "</article>";

        public static string XHTMLArticleOpen = "<div class=\"article\">";
       
        public static string HTML5ASideOpen = "<aside>";
        public static string HTML5ASideClose = "</aside>";

        public static string XHTMLASideOpen = "<div class=\"aside\">";

        public static string HTML5FooterOpen = "<footer>";
        public static string HTML5FooterClose = "</footer>";

        public static string XHTMLFooterOpen = "<div class=\"footer\">";

        public static string HTML5HeaderOpen = "<header>";
        public static string HTML5HeaderClose = "</header>";

        public static string XHTMLHeaderOpen = "<div class=\"header\">";

        public static string HTML5NavOpen = "<nav>";
        public static string HTML5NavClose = "</nav>";

        public static string XHTMLNavOpen = "<div class=\"nav\">";

        public static string XHTMLDivTagClose = "</div>";

        public static string GeneralMasterPageSuffix = ".master";
        public static string HTML5MasterPageSuffix = "_html5";
        public static string XHTMLMasterPageSuffix = "_xhtml";
        public static string DefaultMasterPageFileName = "WiersemaMaster_xhtml.master";

        public static string MasterPageDirectory = "/_catalogs/masterpage/";

        public enum TagTypes
        {
            header, section, article, aside, footer, nav
        }
    }

SharePoint and HTML 5 Technical Implementation


Extended Publishing Page



public class ExtendedPublishingPage : PublishingLayoutPage
    {
        // variable holds whether this page is html 5 or not (xhtml)
        public bool IsHTML5Page = false;
       
        /// <summary>
        /// In PreInit, get the context of the page which is getting rendered,
        /// containing the master page name and set that as the master page.
        /// read the column
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);

            if ((MSSharePoint.SPContext.Current.FormContext.FormMode == SPControlMode.Display) && (BrowserCheck.IsHTML5CompliantBrowser()))
            {              
                SwitchHTML(true);              
            }
            else
            {
                SwitchHTML(false);
            }
        }

        public void SwitchHTML(bool ToHTML5)
        {
            if (ToHTML5)
            {
                if (!(PublishingUtilities.IsHTML5Master(this.MasterPageFile)))
                {
                    this.MasterPageFile = PublishingUtilities.GetHTML5Master(this.MasterPageFile);
                }

                this.IsHTML5Page = true;
            }
            else
            {
                if (PublishingUtilities.IsHTML5Master(this.MasterPageFile))
                {
                    this.MasterPageFile = PublishingUtilities.GetXHTMLMaster(this.MasterPageFile);
                }
                this.IsHTML5Page = false;
            }
       
        }
    }

SharePoint and HTML5 Technical Implementation


Publishing Utilities


public static class PublishingUtilities
    {
        public static string GetHTML5Master(string aMasterPageFile)
        {
            string aReturnString = "";

            if (aMasterPageFile.EndsWith(PublishingDefinitions.GeneralMasterPageSuffix))
            {
                aReturnString = aMasterPageFile.Replace(PublishingDefinitions.XHTMLMasterPageSuffix, PublishingDefinitions.HTML5MasterPageSuffix);         
            }

            if (String.IsNullOrEmpty(aReturnString))
            {
                aReturnString = PublishingDefinitions.DefaultMasterPageFileName;
            }

            return aReturnString;
        }

        public static string GetXHTMLMaster(string aMasterPageFile)
        {
            string aReturnString = "";

            if (aMasterPageFile.EndsWith(PublishingDefinitions.GeneralMasterPageSuffix))
            {
                aReturnString = aMasterPageFile.Replace(PublishingDefinitions.HTML5MasterPageSuffix, PublishingDefinitions.XHTMLMasterPageSuffix);
            }

            if (String.IsNullOrEmpty(aReturnString))
            {
                aReturnString = PublishingDefinitions.DefaultMasterPageFileName;
            }

            return aReturnString;
        }

        internal static bool IsHTML5Master(string aMasterPageFile)
        {
            bool aReturn = false;

            if (aMasterPageFile.Contains(PublishingDefinitions.HTML5MasterPageSuffix))
            {
                aReturn = true;
            }

            return aReturn;
        }

        public static string GetHTMLOpenTag(PublishingDefinitions.TagTypes aTagType, bool InEditMode)
        {
            string aTemp = "";

            if ((BrowserCheck.IsHTML5CompliantBrowser()) && !InEditMode)
            {
                switch (aTagType)
                {
                    case PublishingDefinitions.TagTypes.section: aTemp = PublishingDefinitions.HTML5SectionOpen; break;
                    case PublishingDefinitions.TagTypes.article: aTemp = PublishingDefinitions.HTML5ArticleOpen; break;
                    case PublishingDefinitions.TagTypes.aside: aTemp = PublishingDefinitions.HTML5ASideOpen; break;
                    case PublishingDefinitions.TagTypes.footer: aTemp = PublishingDefinitions.HTML5FooterOpen; break;
                    case PublishingDefinitions.TagTypes.header: aTemp = PublishingDefinitions.HTML5HeaderOpen; break;
                    case PublishingDefinitions.TagTypes.nav: aTemp = PublishingDefinitions.HTML5NavOpen; break;
                    default: aTemp = PublishingDefinitions.XHTMLSectionOpen; break;
                }
            }
            else
            {
                switch (aTagType)
                {
                    case PublishingDefinitions.TagTypes.section: aTemp = PublishingDefinitions.XHTMLSectionOpen; break;
                    case PublishingDefinitions.TagTypes.article: aTemp = PublishingDefinitions.XHTMLArticleOpen; break;
                    case PublishingDefinitions.TagTypes.aside: aTemp = PublishingDefinitions.XHTMLASideOpen; break;
                    case PublishingDefinitions.TagTypes.footer: aTemp = PublishingDefinitions.XHTMLFooterOpen; break;
                    case PublishingDefinitions.TagTypes.header: aTemp = PublishingDefinitions.XHTMLHeaderOpen; break;
                    case PublishingDefinitions.TagTypes.nav: aTemp = PublishingDefinitions.XHTMLNavOpen; break;
                    default: aTemp = PublishingDefinitions.XHTMLSectionOpen; break;
                }
            }

            return aTemp;
        }

        public static string GetHTMLCloseTag(PublishingDefinitions.TagTypes aTagType, bool IsEditMode)
        {
            string aTemp = "";

            if ((BrowserCheck.IsHTML5CompliantBrowser()) && !IsEditMode)
            {
                switch (aTagType)
                {
                    case PublishingDefinitions.TagTypes.section: aTemp = PublishingDefinitions.HTML5SectionClose; break;
                    case PublishingDefinitions.TagTypes.article: aTemp = PublishingDefinitions.HTML5ArticleClose; break;
                    case PublishingDefinitions.TagTypes.aside: aTemp = PublishingDefinitions.HTML5ASideClose; break;
                    case PublishingDefinitions.TagTypes.footer: aTemp = PublishingDefinitions.HTML5FooterClose; break;
                    case PublishingDefinitions.TagTypes.header: aTemp = PublishingDefinitions.HTML5HeaderClose; break;
                    case PublishingDefinitions.TagTypes.nav: aTemp = PublishingDefinitions.HTML5NavClose; break;
                    default: aTemp = PublishingDefinitions.HTML5SectionClose; break;
                }
            }
            else
            {
                switch (aTagType)
                {
                    case PublishingDefinitions.TagTypes.section: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                    case PublishingDefinitions.TagTypes.article: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                    case PublishingDefinitions.TagTypes.aside: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                    case PublishingDefinitions.TagTypes.footer: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                    case PublishingDefinitions.TagTypes.header: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                    case PublishingDefinitions.TagTypes.nav: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                    default: aTemp = PublishingDefinitions.XHTMLDivTagClose; break;
                }
            }

            return aTemp;
        }
    }

SharePoint and HTML 5 Technical Implementation


HTML Panel

// Does two things:
// 1. Adds a html5 or a div tag around it contents depending on page mode, e.g. <section></section> or <div class="section"></div>
// 2. Does or does not show it's child webpartzones when the page is in HTML5 mode based on an attribute of the webpart zone. 


public class HTMLSwitchPanel : Panel
    {
        public CapGemini.Microsoft.Wiersema.Constants.PublishingDefinitions.TagTypes CurrentTagType { get; set; }

        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            bool InEditMode = (MSSharePoint.SPContext.Current.FormContext.FormMode != SPControlMode.Display);

            string aTemp = PublishingUtilities.GetHTMLOpenTag(CurrentTagType, InEditMode);

            writer.Write(aTemp);
        }

        public override void RenderEndTag(HtmlTextWriter writer)
        {
            bool InEditMode = (MSSharePoint.SPContext.Current.FormContext.FormMode != SPControlMode.Display);

            string aTemp = PublishingUtilities.GetHTMLCloseTag(CurrentTagType, InEditMode);        

            writer.Write(aTemp);
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

                // iterate through all child controls of this panel
                // Thanks for the code fragment to Cristain Librado , see http://stackoverflow.com/questions/277646/finding-all-controls-in-an-asp-net-panel
                foreach (Control c in EnumerateControlsRecursive(this))
                {
                    // if the control is a webpartzone
                    if (c is MSWPP.WebPartZone)
                    {
                        MSWPP.WebPartZone aTempZone = (MSWPP.WebPartZone)c;

                        // when not in display mode make all webpart zones visible
                        if (MSSharePoint.SPContext.Current.FormContext.FormMode != SPControlMode.Display)
                        {
                            aTempZone.Visible = true;
                        }
                        else
                        {
                            // when in display mode
                            // if the attribute exist on the webpartzone, continue
                            if (aTempZone.Attributes["IsHTML5"] != null)
                            {
                                // if the page is in html5 mode
                                if (((this.Page as ExtendedPublishingPage).IsHTML5Page))
                                {
                                    // if the page is in html5 mode and the current part zone ISHTML5 that show it
                                    if (aTempZone.Attributes["IsHTML5"] == "true")
                                    {
                                        aTempZone.Visible = true;
                                    }
                                    else
                                    {
                                        // if the page is in html5 mode but the currentwebpart zone is not, don't show it
                                        aTempZone.Visible = false;
                                    }
                                }
                                else
                                {
                                    // if the page is not in html5 mode and the webpartzone isnt either, show the webpart
                                    if (aTempZone.Attributes["IsHTML5"] == "false")
                                    {
                                        aTempZone.Visible = true;
                                    }
                                    else
                                    {
                                        // if the page is not in html5 mode but the webpartzone is, don't show it
                                        aTempZone.Visible = false;
                                    }
                                }
                            }
                        }
                    }
                }
        }

        // enumerate through all first level children of the panel
       // Thanks for the code fragment to Cristain Librado , see http://stackoverflow.com/questions/277646/finding-all-controls-in-an-asp-net-panel
        IEnumerable<Control> EnumerateControlsRecursive(Control parent)
        {
            foreach (Control child in parent.Controls)
            {
                yield return child;
             
                /* don't need to return grandchildren because we're only interested in child webpartzones
                /*
                foreach (Control descendant in EnumerateControlsRecursive(child))
                    yield return descendant;
                 * */
            }
        }
    }

// USAGE in a page layout:


<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">          
 
                                     
            <Wiersema:HTMLSwitchPanel CurrentTagType="article" runat="server">
                <Wiersema:HTMLSwitchPanel CurrentTagType="header" runat="server">
                    <h2><SharePointWebControls:FieldValue ID="ArticleTitle" FieldName="Title" runat="server"/></h2>  
                </Wiersema:HTMLSwitchPanel> <!-- end article header -->
                <WebPartPages:WebPartZone runat="server" ID="MainArticle_html5" Title="MainArticle_html5" FrameType="None" tableless="true" IsHTML5="true">
                    <ZoneTemplate></ZoneTemplate>
                </WebPartPages:WebPartZone>
                <WebPartPages:WebPartZone runat="server" ID="MainArticle_xhtml" Title="MainArticle_xhtml" FrameType="None" tableless="true" IsHTML5="false">
                    <ZoneTemplate></ZoneTemplate>
                </WebPartPages:WebPartZone>
            </Wiersema:HTMLSwitchPanel> <!-- end article -->
                         
</asp:Content>




SharePoint and HTML 5 Technical Implementation


Browser Check

// Browser check class. Should be greatly improved to check for features and versions instead of browser type


public static bool IsHTML5CompliantBrowser()
        {
            bool aReturnBool = true;

            System.Web.HttpBrowserCapabilities browser = HttpContext.Current.Request.Browser;

            // best way is to check the following string for 'chrome','safari', etc?
            string useragent = HttpContext.Current.Request.UserAgent;
            
            // Internet Explorer = "IE"
            // Firefox = "Firefox"
            // Chrome = "AppleMAC-Safari"

            switch (browser.Browser)
            {
                case "AppleMAC-Safari": 
                    { 
                        aReturnBool = true; 
                    } break;
                case "IE": 
                    { 
                        aReturnBool = false; 
                    }
                    break;
                case "Firefox":
                    {
                        aReturnBool = true;
                    } break;
                default:
                    {
                        aReturnBool = false;
                    } break; 
            }
            
            return aReturnBool;
        }

SharePoint and HTML5 investigation (Part 3)


Results & Discussion

I will discuss what can be achieved with my solution on utilizing SharePoint for HTML5 development.

Valid html5 and xhtml pages

Through this method you can create valid html5 pages but still fully support legacy browsers and the edit mode. By creating or extending webparts you can utilize the sharepoint platform fully and create a beautiful interactive website that is also backwards compatible with older software.

Possible to create pages suitable for all platforms

Using multiple masterpages you can deploy one application to many platforms within one project. Within this project you can unlock back-end systems and make them available on phones, tablets, desktops and whatnot.

Edit mode stays XHTML, works always with older browsers

The most usable feature of SharePoint, Publishing stays in tact and you can still efficiently create pages through the standard CMS, while still creating a state-of-the-art html 5 site for the public visitors.

Simplified development of great designs

Web designers can implement designs separately for different platforms, so they could work in parallel on each platform, or designers with different skill sets can work effectively on their own favorite platform.

Page overhead of JavaScript diminishes

Because a specific page of valid html or xhtml5 is sent to the user, there's a lot less overhead on the page of javascript. This makes the page load faster and makes development easier and more maintainable.


What do you think about my solution especially compared to modernzr or other solutions?

Future Work


Extend basic sharepoint webparts for HTML5 compatibility. 
                
Improve browser feature detection with UserAgentInfo
                

SharePoint and HTML5 investigation (Part 2)


Method

The main MO is to create a masterpage for use on a specific platform/browser. I’ve created two but you could create any number to deliver the best experience for each platform. The two platforms I consider are:
  • Browsers that (theoretically) support all the HTML5 functionality I’d like to use
  • Browsers that don’t, but need to be supported. For example for the standard SharePoint edit page experience.

My solution consist of the following components (See image). The main idea is that, based on the browser's capabilities, the masterpage and the control on the page (layout) are switched to html5 or xhtml mode.




Masterpage switcher (Extended Publishing Page)

I’m using two masterpages: one for showing a fancy HTML5 page to browsers capable of handling it, and another standard XHTML masterpage for use with legacy browsers and for the edit mode. The switch between them needs to be done in the OnPreInit method of an extended PublishingPage that checks the browser's capabilities in order to decide which masterpage to use.

Browser check

Currently simply checks the browser from the Request.Browser property. This is done for simplicities' sake, the check can be very advanced. It can also set multiple properties like IsMobile, IsHTML5, etc. The UserAgentInfo class can also be used which gives advanced and up to date info on the browser used, so you can do serverside feature detection. See the article on JS libraries for feature detection as well.

HTML panel

This panel simply checks whether the page is in HTML5 mode or not and creates a html5 or div tag around it’s contents.
If the page is in HTML5 mode, it'll render for example <section></section>, if the page isn't it'll render <div class="section"></div>.


Next to this it has another feature. The panel will hide or show it's child webpartzones based on the page's mode (HTML5 or not) and an attribute of the webpartzone (IsHTML5). If the page is in edit mode it will show all webpartzones, but in display mode it shows only the webpartzones that are in the same HTML mode as the page. In this way you can add HTML5 content to one webpartzone and older version HTML to another zone in the panel and then it will only show the contents of the appropriate webpartzone. In this way it is unnecessary to create two display modes for each webpart, but you can target specific content at certain browsers/devices.


JS and CSS resources for specific platforms

There are different css+js resources used for the two masterpages. This improves simplicity of the solution and therefore makes it easier for web developers to create the styling and realize their interaction designs. Too many different masterpages would complicate this again of course, so keep the amount as limited as possible for example one for mobile, one for legacy and one for new browsers (just a thought).


Utilities and Definitions

Used for creating the right tags and other stuff.

MVC webpart (TO DO)

Because there are two different masterpages, one for legacy and one for HTML5 capable browsers, the webparts themselves (possibly) also need to render in two modes. Using a Model-View-Controller pattern the extra webpart development is minimized because only multiple views need to be created, while the controller and the model can be developed once. Also, since adding the feature to the HTML panel that it can display one webpartzone depending on the HTML state of the page, you could use an older, non-html5 webpart to display to legacy browsers, while showing a new html5 webpart to new browsers.



Continue to Part 3





Part 1 is about the background for the investigation on this subject, why HTML  5 + SharePoint.
Part 2 is about the work that was done, it contains links to the code and solution overview.
Part 3 is about the results that were found, and should get a discussion going on what can be improved and other possible paths to pursue.

SharePoint and HTML5 investigation (Part 1)


Background


If you’re a web developer you will have heard about the new version of HTML, version 5. It has been lauded as the next step in standardization of online functionality. But the standard will also not be final until 2022. And unless you’re doing a state-of-the-art website or mobile application, you’ll probably won’t have a lot of professional experience with it.
It’s a fact that HTML5 is up and coming, but older browsers are lagging behind and will be heavily used for quite some time. So the question arises, is HTML 5 already a valid platform for development?
Browser statistics from March 2012 say that Internet Explorer ‘s (that has limited HTML5 support) share is still around 35-40%. That’s a lot of users you’d exclude if you’d use a HTML5 solution without support for older browsers.
By observing the following statistics on HTML tags and doctype usage we can see the percentage of sites using html5 is not large yet, but it’s definitely growing. The widespread belief among designers and developers that html5 is the future will of course probably be a decisive factor in its adoption.
With the increased use of mobile devices like smartphones and tablets html5 seems the only possible way forward in creating solutions for multiple devices.
And even if it’s not very useful right now, researching a good way to implement HTML5 as well as support older browsers is simply a fun exercise!

Modernzr or HTML5shiv

Right now the most used method to create HTML5 that is also supported in older browsers is by using JavaScript libraries like html5shiv and Modernizr to trick older browsers into thinking they understand HTML5. See another blog of mine with more info on those methods and why I think I didn’t waste time doing it in another way J

SharePoint as platform (for HTML5)

SharePoint, when it’s used without customization as a standard package will sometimes evoke quite some unloving responses. However, it is very widely used with reason.

When used as a development platform and utilizing the built-in Publishing framework or other parts of the Object Model you can create a very strong, custom experience for internet, intranet or extranet.
The extensive web application platform capabilities make it possible to integrate with backend systems like SAP or web services fairly easily for a professional developer, while allowing a good administration experience for content creators.

It is a complete CMS, Publishing, ECM, Collaboration platform/solution. Both a plus as well as a minus is that it does a lot of things moderately well. Customization will make it do what you want very well.
A negative regarding using SharePoint for creating a HTML5 site is that it contains a lot of useful webparts, a lot of which might not be useable out of the box in an HTML5 site and would need to be extended. However, for a publishing system the standard webparts will not often be used bar a few of them, the rest would be extended anyway or custom functionality would be created.

The most useful webparts for Publishing: the Content Editor and the Content Query could still be utilized in my solution (since they can create their own HTML output).

The general question I’ll try to answer as best as possible is the following:
How can we best utilize the SharePoint 2010 platform to function with new browser capabilities and display devices using HTML5?


Continue to Part 2







Part 1 is about the background for the investigation on this subject, why HTML  5 + SharePoint.
Part 2 is about the work that was done, it contains links to the code and solution overview.
Part 3 is about the results that were found, and should get a discussion going on what can be improved and other possible paths to pursue.