How to enable AMP in Umbraco 7 Websites

Posted written by Paul Seal on January 10, 2020 Umbraco

Accelerated Mobile Pages (AMP)

Google and Twitter collaborated to develop AMP, an open source framework for creating faster experiences on the mobile web. In a nutshell, it is a simplified set of html and css for mobile.

For this codeshare website I wanted to enable AMP. The first version of this site was built in Umbraco v7, so here is how I did it.

Premise

The basic premise is to have a new template called Amp.cshtml, have a true/false property on your page to enable AMP or not and if it is true provide the link in the head for the AMP version of the page.

Template

Here is the code for the template, it's a bit rough and naive but it might help someone:

@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
    Layout = null;

    string pageTitle = Model.GetPropertyValue<string>("pageTitle");

    string imgUrl = "";
    if (Model.HasValue("pageBannerImage"))
    {
        imgUrl = Umbraco.AssignedContentItem.GetPropertyValue<IPublishedContent>("pageBannerImage").Url;
    }
}


<!doctype html>
<html amp>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
    <title>@(Model.HasValue("pageTitle") ? pageTitle : Model.Name)</title>
    <link rel="canonical" href="@Model.Url" />
    <script src="https://cdn.ampproject.org/v0.js" async></script>
    <style amp-boilerplate>
        body {
            -webkit-animation: -amp-start 8s steps(1,end) 0s 1 normal both;
            -moz-animation: -amp-start 8s steps(1,end) 0s 1 normal both;
            -ms-animation: -amp-start 8s steps(1,end) 0s 1 normal both;
            animation: -amp-start 8s steps(1,end) 0s 1 normal both
        }

        @@-webkit-keyframes -amp-start {
            from {
                visibility: hidden
            }

            to {
                visibility: visible
            }
        }

        @@-moz-keyframes -amp-start {
            from {
                visibility: hidden
            }

            to {
                visibility: visible
            }
        }

        @@-ms-keyframes -amp-start {
            from {
                visibility: hidden
            }

            to {
                visibility: visible
            }
        }

        @@-o-keyframes -amp-start {
            from {
                visibility: hidden
            }

            to {
                visibility: visible
            }
        }

        @@keyframes -amp-start {
            from {
                visibility: hidden
            }

            to {
                visibility: visible
            }
        }
    </style>
    <noscript>
        <style amp-boilerplate>
            body {
                -webkit-animation: none;
                -moz-animation: none;
                -ms-animation: none;
                animation: none
            }
        </style>
    </noscript>

    <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
</head>
<body>
    <amp-analytics type="googleanalytics" id="analytics1">
        <script type="application/json">
            {
               "vars": {
                    "account": "UA-12345678-1"
                },
                "triggers": {
                    "trackPageview": {
                        "on": "visible",
                        "request": "pageview"
                    }
                }
            }
        </script>
    </amp-analytics>
    <nav class="amp-wp-title-bar">
        <div>
            <a href="https://mydomain.com">
                mydomain.com
            </a>
        </div>
    </nav>
    <div class="amp-wp-content">
        <h1 class="amp-wp-title">@(Model.HasValue("pageTitle") ? pageTitle : Model.Name)</h1>
        <ul class="amp-wp-meta">
            <li class="amp-wp-byline">
                <span class="amp-wp-author">Paul Seal</span>
            </li>
            <li class="amp-wp-posted-on">
                <time datetime="@(Model.GetPropertyValue<DateTime>("blogPostDate").ToString("yyyy-MM-ddTHH:mm:sszzz", System.Globalization.CultureInfo.InvariantCulture))">
                    @CodeShare.Library.Utility.Social.General.GetRelativeTime(Model.GetPropertyValue<DateTime>("blogPostDate"))
                </time>
            </li>
        </ul>
        <a href="http://www.facebook.com/sharer/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="790a11180b1c0b57091109460c443934161d1c15572c0b15">[email&#160;protected]</a>" rel="nofollow">
            <amp-img src="https://codeshare.co.uk/media/1127/fbamp-1.png" alt="FB-Share" width="105" height="39" class="amp-wp-enforced-sizes"></amp-img>
        </a>
        <a href="http://twitter.com/intent/tweet?text=@(Model.HasValue("pageTitle") ? pageTitle : Model.Name)&amp;url=@(Model.Url)&amp;via=prjseal" rel="nofollow">
            <amp-img src="https://codeshare.co.uk/media/1128/twamp-1.png" alt="Tw-Share" width="105" height="39" class="amp-wp-enforced-sizes"></amp-img>
        </a>
        <div class="media-credit-container alignnone"><amp-img class="size-full wp-image-264208 amp-wp-enforced-sizes" src="@imgUrl" alt="@Model.Name" width="1200" srcset="@(imgUrl)?width=1200&amp;mode=crop&amp;anchor=center 1200w, @(imgUrl)?width=622&amp;height=350&amp;mode=crop&amp;anchor=center 622w" sizes="(min-width: 800px) 800px, 100vw" height="675"></amp-img></div>
        @Html.GetGridHtml(Model, "contentGrid", "Amp")
    </div>
</body>
</html>

The Grid Partial

As you can see in my example, I am using the grid instead of just a rich text for my content.

If you are using the grid too, you want to create a partial in the folder Views > Partials > Grid and call it Amp.cshtml

Here is the code for it.

@inherits UmbracoViewPage<dynamic>

@if (Model != null &amp;&amp; Model.sections != null)
{
    foreach (var section in Model.sections) {            
        foreach (var row in section.rows) {
            @renderRow(row);
        }
    }
}

@helper renderRow(dynamic row){
    foreach ( var area in row.areas ) {
        foreach (var control in area.controls) {
            if (control !=null &amp;&amp; control.editor != null &amp;&amp; control.editor.view != null ) {
                <text>@Html.Partial("grid/editors/base", (object)control)</text>
            }
        }
    }
}

Rendering the link in the head

In the head of your master template you want to add this:

@if(Model.Content.HasValue("enableAmp") 
    &amp;&amp; (bool)Model.Content.GetPropertyValue<bool>("enableAmp"))
{
    <link rel="amphtml" href="@(Model.Content.Url)amp/" />
}

Then when the main page gets rendered, it has the amphtml link in it.

Enable SearchForTemplate

The last thing left to do is to make sure you have alternate templates enabled. To do this you need to have the SearchForTemplate line set in the 404handlers.config like this:

<?xml version="1.0" encoding="utf-8" ?>
<NotFoundHandlers>
    <notFound assembly="umbraco" type="SearchForAlias" />
    <notFound assembly="umbraco" type="SearchForTemplate"/>
    <notFound assembly="umbraco" type="SearchForProfile"/>
    <notFound assembly="umbraco" type="handle404"/>
</NotFoundHandlers>

That should work for you.