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.