Loading...

The Umbraco Approach

This site was constructed using a generic approach that could be adopted by any cms or dynamic content engine. Having attempted this solution and reworked it to utilise a masterpage switching approach, we were surprised with the overall elegance and ease.

 

In considering how this approach could work, we immediately came up with 3 umbraco structures which could support a neat fallback:

  1. Use a standard Umbraco masterpage structure and make use of IIS rewrites to allow for indexing/backwards compatibility.
  2. Use Umbraco's alternate template functionality to serve up alternative content for the ajax-based solution.
  3. Use a combination of both.

The draw back to the first approach was that a small amount of excess content is delivered when making our jQuery requests. An example of this would be that the header/footer layouts are transferred as part of the response. It was for this reason that we re-worked the first approach and ended up implementing the second approach using a combination of hash bang (#!), pushstate, and alternative master pages.

 

Please Read Me Google!

To make Google happy we need to ensure that any content delivered via #! needs to be able to be served in the form of
/?_escaped_fragment_=PATH_TO/OUR_CONTENT/

i.e. say for example we have a page at the url: #!/owls/barn-owl

in order for google to effectively "read" and index that page we need to do two things:

  1. Make sure it can be served using the _escaped_fragment_ querystring argument.
  2. Make sure our sitemap is generated using the #! convention.

The reason we'd like our sitemap to utilise the #! convention is so that when Google indexes our pages it does so in such a way that users will come in to the site with our jQuery/SammyJS enabled user experience as opposed to the fallback content.

To achieve this we'll need to add the following rewrite rule to IIS:

 

<rewrite>
      <rules>
          <rule name="Google Tracking Rewrite Rule" stopProcessing="true">
              <match url=".*" />
              <conditions>
                  <add input="{QUERY_STRING}" pattern="^(?:^|&amp;)_escaped_fragment_=(.*)?$" />
              </conditions>
              <action type="Rewrite" url="{C:1}" appendQueryString="false" />
          </rule>
          <rule name="Sammy Template Rewrite" stopProcessing="true">
              <match url="^template/(.*)\.hb$" />
              <action type="Rewrite" url="/{R:1}/?alttemplate={R:1}" appendQueryString="false" />
          </rule>
      </rules>
  </rewrite>

 

The first rule takes anything after the _escaped_fragment_ querystring pattern and rewrites the url to our fallback content.

i.e. it will take:

/?_escaped_fragment_=/owls/barn-owl

and turn it in to:

/owls/barn-owl

The great part about using the rewrites is that they'll fall back to the default umbraco url structure and Google will index the page contents. At the same time the url that will be indexed is the #! version of the url as we have followed google's ajax crawling convention.

The other advantage to this approach is that is allows us to provide a somewhat rich user experience even if the user has javascript disabled (please don't get me started on this one! You know who you are IE6 and co people)

The nice part about that, is due to pushstate, google will nicely index our site using conventional URL's and when a user visits the site without pushstate enabled SammyJS will enabled #! (well in our case we had to) - see the pushstate and gotchas section for more on this

So What Does Our Chosen Approach Look Like In Terms Of Masterpages?

Masterpages
Nothing special here about our structure

As you can see the master page structure is very simple. There is nothing elaborate required to execute the chosen approach. The downside of the approach is that for instances where differing layouts are required, we needed to implement a separate "Ajax" template. The upside to using alternate templates is that it allows us to reduce load overheads by loading in simple versions of the required content.

SO! what IS different? you might ask.

Within our Ajax masterpage we have the following ContentPlaceHolder setup:

<div id="content-wrap">
<%@ Master Language="C#" MasterPageFile="/umbraco/masterpages/default.master" AutoEventWireup="true" %>

<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server"><!doctype html>
  <html>
  <head>
     <title>
<asp:ContentPlaceHolder Id="TitlePlaceHolder" runat="server"><umbraco:Macro runat="server" language="cshtml">
        @{
            var titleSuffix = " | Hash Bang";
            var title = Current.Title();
            @(title + (!title.EndsWith(titleSuffix) && !titleSuffix.EndsWith(title) ? titleSuffix : ""));
        }
    </umbraco:Macro></asp:ContentPlaceHolder></title>
  </head>
  <body>
    <div id="content-body">
      <asp:ContentPlaceHolder ID="BodyContentPlaceHolder" runat="server">
      <umbraco:Macro runat="server" language="cshtml">
        <section>
          <h1>@Model.pageTitle</h1>
          @Model.bodyText
        </section>
      </umbraco:Macro>
      </asp:ContentPlaceHolder>
    </div>
  </body>
  </html>
</asp:Content>

The div labelled "content-body" is the white rounded area that we inject our content in to. By default it is set up to simply take a page's title and the contents of it's rich text editor. An example of this master page template in action is the page you are reading now!

Each sub-template which uses the Ajax masterpage basically just overrides the default BodyContentPlaceHolder implementation to alter the inner layout with things like <section> tags and <figure> tags for the "Owls" section of the site. Here is the contents of the Owls masterpage (please ignore the indenting!):

<%@ Master Language="C#" MasterPageFile="/masterpages/Ajax.master" AutoEventWireup="true" %>

<asp:content ContentPlaceHolderId="BodyContentPlaceHolder" runat="server">
  <umbraco:Macro runat="server" language="cshtml">
    @RenderPage("~/macroScripts/OwlImages.cshtml")
  </umbraco:Macro>
</asp:content>

The wonderful thing about using the recent version of umbraco is the ability to share the content between our AjaxOwl masterpage and our Owl masterpage.

SO! what is this Ajax<insert masterpage name here> master structure all about?

Read more about the approach in our Alternate Templates section

This demo is powered by

blog comments powered by Disqus