Skip to content

Instantly share code, notes, and snippets.

@AThraen
Created March 5, 2019 21:45
Show Gist options
  • Select an option

  • Save AThraen/d5acf1f59bb486d82889eb6107283de0 to your computer and use it in GitHub Desktop.

Select an option

Save AThraen/d5acf1f59bb486d82889eb6107283de0 to your computer and use it in GitHub Desktop.
Contentful Content Provider for Episerver
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAnnotations;
using EPiServer.Web;
namespace ContentfulDemo.Models.Blocks
{
[ContentType(DisplayName = "Contentful Blog Block", GUID = "f28b3914-509d-41e4-bc8c-da0f63065fd7", Description = "")]
public class CFBlogBlock : BlockData
{
[Editable(false)]
public virtual string Name { get; set; }
[Editable(false)]
public virtual string HeroImage { get; set; }
[Editable(false)]
[UIHint(UIHint.Textarea)]
public virtual string Description { get; set; }
[Editable(false)]
[UIHint(UIHint.Textarea)]
public virtual string Body { get; set; }
}
}
@using EPiServer.Core
@using EPiServer.Web.Mvc.Html
@model ContentfulDemo.Models.Blocks.CFBlogBlock
<div class="row">
<div class="span4 hidden-tablet hidden-phone">
<img src="@Model.HeroImage"/>
</div>
<div class="span8">
<h1 class="jumbotron">@Model.Name</h1>
<p class="subHeader">@Model.Description</p>
<p>@Html.Raw(Markdig.Markdown.ToHtml(Model.Body))</p>
</div>
</div>
using Contentful.Core;
using ContentfulDemo.Contentful;
using ContentfulDemo.Models.Blocks;
using EPiServer;
using EPiServer.Construction;
using EPiServer.Core;
using EPiServer.Data.Entity;
using EPiServer.DataAbstraction;
using EPiServer.ServiceLocation;
using EPiServer.Web;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Configuration;
namespace ContentfulDemo.ContentfulTools
{
public class ContentfulContentProvider : ContentProvider
{
protected IdentityMappingService IdentityMappingService { get; set; }
protected ExternalTypeMapper TypeMapper { get; set; }
protected IContentTypeRepository TypeRepo { get; set; }
protected IContentFactory _ContentFactory { get; set; }
protected IContentRepository ContentRepo { get; set; }
public const string KEY = "CF";
private const string BASEURI = "CF://"+KEY+"/";
private ContentfulClient _Client { get; set; }
public ContentfulContentProvider(IdentityMappingService mappingService,ExternalTypeMapper mapper, IContentTypeRepository typerepo, IContentFactory contentfactory, IContentRepository contentrepo) : base()
{
IdentityMappingService = mappingService;
TypeMapper = mapper;
TypeRepo = typerepo;
_ContentFactory = contentfactory;
ContentRepo = contentrepo;
}
protected override IList<GetChildrenReferenceResult> LoadChildrenReferencesAndTypes(ContentReference contentLink, string languageID, out bool languageSpecific)
{
languageSpecific = false;
if (contentLink.CompareToIgnoreWorkID(EntryPoint))
{
//Root, list mapped types
//Get list of all mapped items
var results=_Client.GetEntries<dynamic>().Result;
List<GetChildrenReferenceResult> rt = new List<GetChildrenReferenceResult>();
foreach(var itm in results.Items)
{
string ctid = itm.sys.contentType.sys.id;
if (TypeMapper.TypeMappings.ContainsKey(ctid))
{
string entryid = itm.sys.id;
var id=IdentityMappingService.Get(new Uri(new Uri(BASEURI), entryid), true);
rt.Add(new GetChildrenReferenceResult() { ContentLink = id.ContentLink, IsLeafNode = true, ModelType = TypeMapper.TypeMappings[ctid].ContentType });
}
}
//Return those that we can map
return rt;
}
return base.LoadChildrenReferencesAndTypes(contentLink, languageID, out languageSpecific);
}
/// <summary>
/// Loads content items
/// </summary>
/// <param name="contentLink"></param>
/// <param name="languageSelector"></param>
/// <returns></returns>
protected override IContent LoadContent(ContentReference contentLink, ILanguageSelector languageSelector)
{
var lnk = IdentityMappingService.Get(contentLink);
string entryid = lnk.ExternalIdentifier.AbsolutePath.TrimStart('/');
//Future improvement: Handle languages, load linked content and assets as well
var entry=_Client.GetEntry<dynamic>(entryid).Result;
return GenerateContentFromEntry(entry);
}
/// <summary>
/// Generates the IContent entries
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
protected IContent GenerateContentFromEntry(dynamic entry)
{
string ctid = entry.sys.contentType.sys.id;
var map = TypeMapper.TypeMappings[ctid];
string entryid = entry.sys.id;
var id = IdentityMappingService.Get(new Uri(new Uri(BASEURI), entryid), true);
ContentType type = TypeRepo.Load(map.ContentType); //Update
var fc = _ContentFactory.CreateContent(type, new EPiServer.Construction.BuildingContext(type)
{
Parent = ContentRepo.Get<ContentFolder>(EntryPoint), ContentLink=id.ContentLink
}) as IContent;
fc.ContentGuid = id.ContentGuid;
map.MapAllProperties(fc, entry);
(fc as IVersionable).Status = VersionStatus.Published;
(fc as IVersionable).IsPendingPublish = false;
(fc as ILocalizable).Language = CultureInfo.GetCultureInfo("en");
(fc as ILocalizable).MasterLanguage = CultureInfo.InvariantCulture;
(fc as ILocalizable).ExistingLanguages = new List<CultureInfo>();
(fc as IReadOnly).MakeReadOnly();
return fc as IContent;
}
//Avoid caching for now. Future: Cache, but cancel caching based on webhooks
protected override void SetCacheSettings(IContent content, CacheSettings cacheSettings)
{
base.SetCacheSettings(content, cacheSettings);
cacheSettings.CancelCaching = true;
}
protected override void SetCacheSettings(ContentReference contentReference, IEnumerable<GetChildrenReferenceResult> children, CacheSettings cacheSettings)
{
base.SetCacheSettings(contentReference, children, cacheSettings);
cacheSettings.CancelCaching = true;
}
protected override void SetCacheSettings(ContentReference parentLink, string urlSegment, IEnumerable<MatchingSegmentResult> childrenMatches, CacheSettings cacheSettings)
{
base.SetCacheSettings(parentLink, urlSegment, childrenMatches, cacheSettings);
cacheSettings.CancelCaching = true;
}
protected override ContentResolveResult ResolveContent(ContentReference contentLink)
{
ContentResolveResult crr = new ContentResolveResult();
crr.ContentLink = contentLink;
var id=IdentityMappingService.Get(contentLink);
if (id == null) return null;
crr.UniqueID = id.ContentGuid;
return crr;
}
protected override ContentResolveResult ResolveContent(Guid contentGuid)
{
ContentResolveResult crr = new ContentResolveResult();
crr.UniqueID = contentGuid;
var id = IdentityMappingService.Get(contentGuid);
if (id == null) return null;
crr.ContentLink = id.ContentLink;
return crr;
}
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
var httpClient = new HttpClient();
_Client = new ContentfulClient(httpClient, WebConfigurationManager.AppSettings["Contentful:ApiKey"], WebConfigurationManager.AppSettings["Contentful:PreviewKey"], WebConfigurationManager.AppSettings["Contentful:SpaceId"]);
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using ContentfulDemo.Contentful;
using ContentfulDemo.Models.Blocks;
using EPiServer;
using EPiServer.Configuration;
using EPiServer.Core;
using EPiServer.DataAccess;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Security;
using EPiServer.ServiceLocation;
namespace ContentfulDemo.ContentfulTools
{
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class ContentfulInit : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
//Setup mapping
var mapper = ServiceLocator.Current.GetInstance<ExternalTypeMapper>();
mapper.MapType<CFBlogBlock>("blogPost").Map((a, b) =>
{
a.Name = Convert.ToString(b.title);
(a as CFBlogBlock).Description = Convert.ToString(b.description);
(a as CFBlogBlock).Body = Convert.ToString(b.body);
} );
}
public void Uninitialize(InitializationEngine context)
{
//Add uninitialization logic
}
}
}
using Contentful.Core.Models;
using EPiServer.Core;
using EPiServer.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
namespace ContentfulDemo.Contentful
{
[ServiceConfiguration(Lifecycle = ServiceInstanceScope.Singleton, ServiceType = typeof(ExternalTypeMapper))]
public class ExternalTypeMapper
{
public Dictionary<string, ExternalTypeMapping> TypeMappings { get; set; }
public ExternalTypeMapping MapType<G>(string Identifier)
{
//Return a type mapping object?
var rt = new ExternalTypeMapping();
rt.Identifier = Identifier;
rt.ContentType = typeof(G);
TypeMappings.Add(Identifier, rt);
return rt;
}
public ExternalTypeMapper()
{
TypeMappings = new Dictionary<string, ExternalTypeMapping>();
}
}
public class ExternalTypeMapping
{
public Action<EPiServer.Core.IContent, dynamic> MapAllProperties { get; set; }
public ExternalTypeMapping Map(Action<EPiServer.Core.IContent, dynamic> map)
{
this.MapAllProperties = map;
return this;
}
public string Identifier { get; set; }
public Type ContentType { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment