Skip to content

Instantly share code, notes, and snippets.

@timw255
Last active June 28, 2020 18:14
Show Gist options
  • Save timw255/529867a01c080dda811f30c18eb6d256 to your computer and use it in GitHub Desktop.
Save timw255/529867a01c080dda811f30c18eb6d256 to your computer and use it in GitHub Desktop.
Model, Views, and Controller to return a Sitefinity Blog Post as valid AMP content. (Uses timw255.AMP)
@using System.Web.Mvc;
@using Telerik.Sitefinity.Frontend.Mvc.Helpers;
@using Telerik.Sitefinity.Modules.Pages;
@using Telerik.Sitefinity.UI.MVC;
@using Telerik.Sitefinity.Services;
<!doctype html>
<html amp @Html.RenderLangAttribute()>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
@RenderSection("meta", false)
<title>@ViewBag.Title</title>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Open+Sans:300,400'>
<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>
@RenderSection("components", false)
<script async src="https://cdn.ampproject.org/v0.js"></script>
<style amp-custom>
.logo,footer nav li,footer nav ul{display:inline-block}.by,.logo span,footer nav li a{text-transform:uppercase}.clearfix:after,.content p{clear:both}body{font:400 14px/24px 'Open Sans',Arial,sans-serif}body>header{border-bottom:1px solid #eee;padding:12px 25px 6px}.container{padding:30px;max-width:900px;margin:0 auto}header .container{padding:0}.logo{font-size:20px;line-height:20px;color:#000;padding:0 0 0 50px;background:url(/static/img/logo.png) left center no-repeat;background-size:40px 40px;letter-spacing:-1px;text-decoration:none}.logo span{letter-spacing:0;line-height:10px;color:#888;font-size:10px}.content h1{font-weight:300;font-size:30px;line-height:38px;margin:0 0 20px;letter-spacing:-2px}.by{font-size:12px}.social-share{margin-top:10px}.summary{padding:10px 20px;border:1px solid #eee;font-size:13px;font-style:italic;background:#f4f4f4;margin:10px 0}.content p{font-size:14px;color:#666;line-height:24px}.copyright{text-align:center;font-size:11px}footer{padding:10px}footer nav{text-align:center;margin:10px 0 0}footer nav ul{padding:0;margin:0;list-style:none}footer nav li a{display:block;padding:0 5px;font-size:12px;color:#00b4c9;text-decoration:none}.clearfix:after,.clearfix:before{content:" ";display:table}amp-social-share span{height:40px;width:40px}amp-social-share a{padding:8px}amp-video,amp-youtube{margin:20px 0}
</style>
</head>
<body>
<header>
<div class="container"><a class="logo" href="/">Quantum<br><span>More than Computers</span></a></div>
</header>
<div class="container">
<div class="content main">
@RenderBody()
</div>
</div>
<footer>
<nav>
<ul class="clearfix">
<li><a href="/products">Products</a></li>
<li><a href="/online-shop">Online Shop</a></li>
<li><a href="/inside-quantum">Inside Quantum</a></li>
<li><a href="/about-us">About Us</a></li>
<li><a href="/store-locator">Store Locator</a></li>
</ul>
</nav>
<p class="copyright">Copyright © 2002-2013 Quantum. All rights reserved.</p>
</footer>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Sitefinity.News.Model;
using timw255.AMP;
namespace SitefinityWebApp.Mvc.Models
{
public class BlogPostDetailViewModel
{
public NewsItem Item { get; set; }
public string Content { get; set; }
public string ThumbnailUrl { get; set; }
public int ThumbnailHeight { get; set; }
public int ThumbnailWidth { get; set; }
public string CanonicalUrl { get; set; }
public List<Component> RequiredComponents { get; set; }
}
}
using HtmlAgilityPack;
using SitefinityWebApp.Mvc.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Telerik.Sitefinity.Frontend.Mvc.Models;
using Telerik.Sitefinity.Frontend.News.Mvc.Models;
using Telerik.Sitefinity.GenericContent.Model;
using Telerik.Sitefinity.Libraries.Model;
using Telerik.Sitefinity.Modules.GenericContent;
using Telerik.Sitefinity.Modules.News;
using Telerik.Sitefinity.News.Model;
using Telerik.Sitefinity.RelatedData;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Web.Utilities;
using timw255.AMP;
namespace SitefinityWebApp.Mvc.Controllers
{
public class BlogPostsController : Controller
{
public ActionResult Index(string id)
{
var item = GetNewsItem(id);
if (item == null)
{
return HttpNotFound();
}
var model = new NewsItemDetailViewModel();
model.Item = item;
DynamicLinksParser parser = new DynamicLinksParser(true, false);
var content = parser.Apply(item.Content);
AMP amp = new AMP();
amp.LoadHtml(content);
model.Content = amp.ConvertToAmpHtml();
model.RequiredComponents = amp.RequiredComponents;
var thumbnail = item.GetRelatedItems<Image>("Thumbnail").FirstOrDefault();
if (thumbnail != null)
{
model.ThumbnailUrl = thumbnail.ThumbnailUrl;
model.ThumbnailHeight = thumbnail.Thumbnail.Height;
model.ThumbnailWidth = thumbnail.Thumbnail.Width;
}
model.CanonicalUrl = GetItemDefaultLocationById(item.Id);
return View(model);
}
private NewsItem GetNewsItem(string urlName)
{
NewsManager newsManager = NewsManager.GetManager();
NewsItem item = newsManager.GetNewsItems().Where(newsItem => newsItem.UrlName == urlName && newsItem.Status == ContentLifecycleStatus.Live && newsItem.Visible).FirstOrDefault();
return item;
}
public static string GetItemDefaultLocationById(Guid itemId, string itemProvider = null)
{
var clService = SystemManager.GetContentLocationService();
var location = clService.GetItemDefaultLocation(typeof(NewsItem), itemProvider, itemId);
if (location != null)
{
return location.ItemAbsoluteUrl;
}
return string.Empty;
}
}
}
@model SitefinityWebApp.Mvc.Models.NewsItemDetailViewModel
@using timw255.AMP;
@using Telerik.Sitefinity.Frontend.Mvc.Helpers;
@using Telerik.Sitefinity.Web.DataResolving;
@{
Layout = "~/Mvc/Views/Shared/_AMP.cshtml";
ViewBag.Title = Model.Item.Title;
}
@section meta {
<meta name="description" content="@Model.Item.Description" />
<link rel="canonical" href="@Model.CanonicalUrl">
<!-- Schema.org markup for Google+ -->
<meta itemprop="name" content="@Model.Item.Title">
<meta itemprop="description" content="@Model.Item.Description">
<meta itemprop="image" content="@Model.ThumbnailUrl">
<!-- Twitter Card data -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="Quantum">
<meta name="twitter:creator" content="@DataResolver.Resolve(@Model.Item, "Author", null)">
<meta name="twitter:title" content="@Model.Item.Title">
<meta name="twitter:description" content="@Model.Item.Description">
<meta name="twitter:image" content="@Model.ThumbnailUrl">
<!-- Open Graph data -->
<meta property="og:title" content="@Model.Item.Title" />
<meta property="og:type" content="article" />
<meta property="og:image" content="@Model.ThumbnailUrl" />
<meta property="og:url" content="@Model.CanonicalUrl" />
<meta property="og:description" content="@Model.Item.Description" />
<meta property="og:site_name" content="Quantum" />
<meta property="article:published_time" content="@Model.Item.PublicationDate.ToUniversalTime()" />
<meta property="article:modified_time" content="@Model.Item.LastModified.ToUniversalTime()" />
<!-- Metadata required by Google for AMP files -->
<script type="application/ld+json">
{
"@@context": "http://schema.org",
"@@type": "NewsArticle",
"mainEntityOfPage": "@Model.CanonicalUrl",
"headline": "@Model.Item.Title",
"datePublished": "@Model.Item.PublicationDate.ToUniversalTime()",
"dateModified": "@Model.Item.LastModified.ToUniversalTime()",
"description": "@Model.Item.Description",
"author": {
"@@type": "Person",
"name": "@DataResolver.Resolve(@Model.Item, "Author", null)"
},
"publisher": {
"@@type": "Organization",
"name": "Quantum",
"logo": {
"@@type": "ImageObject",
"url": "http://amp.sitefinity.com/static/img/logo.png",
"width": "57",
"height": "64"
}
},
"image": {
"@@type": "ImageObject",
"url": "@Model.ThumbnailUrl",
"height": "@Model.ThumbnailHeight",
"width": "@Model.ThumbnailWidth"
}
}
</script>
}
@section components {
@foreach (Component c in Model.RequiredComponents)
{
<script async custom-element="@c.ElementName" src="@c.ScriptPath"></script>
}
<script async custom-element="amp-social-share" src="https://cdn.ampproject.org/v0/amp-social-share-0.1.js"></script>
}
<h1>@Model.Item.Title</h1>
<div class="by">
@Html.Resource("By") @DataResolver.Resolve(@Model.Item, "Author", null) | @Model.Item.PublicationDate.ToUniversalTime()
</div>
<div class="social-share">
<amp-social-share type="twitter" width="40" height="40" data-text="@Model.Item.Title"></amp-social-share>
<amp-social-share type="facebook" width="40" height="40" data-text="@Model.Item.Title" data-attribution="AMPHtml"></amp-social-share>
<amp-social-share type="pinterest" width="40" height="40" data-text="@Model.Item.Title"></amp-social-share>
<amp-social-share type="linkedin" width="40" height="40" data-text="@Model.Item.Title"></amp-social-share>
<amp-social-share type="gplus" width="40" height="40" data-text="@Model.Item.Title"></amp-social-share>
<amp-social-share type="email" width="40" height="40"></amp-social-share>
</div>
<article>
<div class="summary">
@Html.Raw(Model.Item.Summary)
</div>
<div class="body">
@Html.Raw(Model.Content)
</div>
</article>
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "AMP",
url: "amp/{controller}/{id}",
defaults: new { action = "Index", id = (string)null },
namespaces: new[] { "SitefinityWebApp.Mvc.Controllers" }
);
}
@timw255
Copy link
Author

timw255 commented Jun 14, 2016

This is a plain old MVC controller (not a widget) that returns a blog post (identified by the title) and returns it as an AMP document. Example route is in snippet_for_Global.asax.cs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment