Skip to content

Instantly share code, notes, and snippets.

@LucGosso
Last active March 13, 2017 12:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LucGosso/7c09334a2c1e6fab329cbc8ff97c5348 to your computer and use it in GitHub Desktop.
Save LucGosso/7c09334a2c1e6fab329cbc8ff97c5348 to your computer and use it in GitHub Desktop.
Content Providers in EPiServer is a way of publishing external data as pages/blocks etc. Here is en example of an RSS Content Provider. Read more: http://devblog.gosso.se/?p=70
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.Security;
using EPiServer.Web;
using EPiServer.Web.Routing;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Xml.Linq;
namespace Gosso.EPiStuff.ContentProviders
{
public class RSSContentProvider : EPiServer.Core.ContentProvider
{
private string _filePath;
private string _contentType;
private readonly IdentityMappingService _identityMappingService;
private readonly IContentTypeRepository _contentTypeRepository;
private readonly IContentFactory _contentFactory;
private readonly ServiceLocationHelper locator;
private readonly IUrlSegmentGenerator _urlSegment;
private readonly CategoryRepository categoryRepository;
private readonly IContentCacheKeyCreator _cacheCreator;
public RSSContentProvider(IdentityMappingService identityMappingService,
IContentTypeRepository _ContentTypeRepository,
IContentFactory _ContentFactory,
ServiceLocationHelper _Locator,
IUrlSegmentGenerator _UrlSegment,
IContentCacheKeyCreator _CacheCreator)
{
_identityMappingService = identityMappingService;
_contentTypeRepository = _ContentTypeRepository;
_contentFactory = _ContentFactory;
locator = _Locator;
_urlSegment = _UrlSegment;
_cacheCreator = _CacheCreator;
}
#region overrides from base class
/// <summary>
/// Initializes the provider from configuration settings.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="configParams">The config params.</param>
public override void Initialize(string key, NameValueCollection configParams)
{
//Let base classes do their initialization
base.Initialize(key, configParams);
//Make sure a path to the backing xml file is given in configuration of page provider
if (configParams["filePath"] == null)
{
throw new ConfigurationErrorsException("RSSContentProvider requires configuration attribute filePath that should be a application relative path");
}
if (configParams["contentType"] == null)
{
throw new ConfigurationErrorsException("RSSContentProvider requires configuration attribute contentType that should be ContentType");
}
_filePath = HttpUtility.UrlEncode(configParams["filePath"]);
_contentType = HttpUtility.UrlEncode(configParams["contentType"]);
}
protected override IContent LoadContent(ContentReference contentLink, ILanguageSelector languageSelector)
{
try
{
MappedIdentity mappedIdentity = _identityMappingService.Get(contentLink);
if (mappedIdentity != null && mappedIdentity.ExternalIdentifier != null)
{
// SOMETIME PARENT IS CALLED FIRST!! breaking change CMS 8 : Possible to set EntryPoint for content provider that have exactly 1 child
string rssGuid = mappedIdentity.ExternalIdentifier.PathAndQuery.TrimStart('/');
RSSPage rsspage = (from page in XmlPages.Descendants("item")
where string.Equals(page.GetValue<string>("guid"), rssGuid)
select new RSSPage
{
Guid = page.GetValue<string>("guid"),
Title = page.GetValue<string>("title"),
Description = page.GetValue<string>("description"),
Content = page.GetValue<string>("{http://purl.org/rss/1.0/modules/content/}encoded"),
PubDate = page.GetValue<DateTime>("pubDate"),
Language = page.Parent.Element("language").Value,
DocumentUrl = page.GetAttributeFromElement<string>("enclosure", "url")
}).FirstOrDefault<RSSPage>();
if (rsspage != null)
{
return CreateContent(mappedIdentity, rsspage);
}
else
{
//handle
return null;
}
}
}
catch (Exception)
{
//handle exception
}
return null;
}
public IContent CreateContent(MappedIdentity mappedIdentity, RSSPage rsspage)
{
ContentType type = _contentTypeRepository.Load(Type.GetType(_contentType));
var mirrored = _contentFactory.CreateContent(type);
mirrored.ContentTypeID = type.ID;
mirrored.ParentLink = new PageReference(EntryPoint.ID);
mirrored.ContentLink = mappedIdentity.ContentLink;
mirrored.ContentGuid = mappedIdentity.ContentGuid;
// not this one! May cause loop
//mirrored.Property["PageLinkURL"].Value = this.ConstructContentUri(contentTypeId, content.ContentLink, content.ContentGuid);
ILocalizable localizable = mirrored as ILocalizable;
var langs = new List<CultureInfo>();
langs.Add(new CultureInfo(rsspage.Language));
localizable.ExistingLanguages = langs;
localizable.Language = new CultureInfo(rsspage.Language);
//add date to url segment
(mirrored as IRoutable).RouteSegment = _urlSegment.Create(mirrored.Name + "-" + rsspage.PubDate.ToString("yyyy-MM-dd"));
//add role everyone
(mirrored as IContentSecurable).GetContentSecurityDescriptor().AddEntry(new AccessControlEntry(EveryoneRole.RoleName, AccessLevel.Read));
var versionable = mirrored as IVersionable;
if (versionable != null)
{
versionable.Status = VersionStatus.Published;
}
PageData pd = new PageData();
pd = (mirrored as PageData);
pd.Name = rsspage.Title;
pd.LinkType = PageShortcutType.Normal;
string description = HttpUtility.HtmlDecode(rsspage.Description);
if (!string.IsNullOrWhiteSpace(description))
{
pd["IntroText"] = description;
}
string content = HttpUtility.HtmlDecode(rsspage.Content);
if (!string.IsNullOrWhiteSpace(content))
{
pd["MainBody"] = content;
}
pd.StartPublish = rsspage.PubDate;
pd["PageChanged"] = rsspage.PubDate;
pd["PageCreated"] = rsspage.PubDate;
pd["DocumentUrl"] = rsspage.DocumentUrl;
//pd["Categories"] = "23,34,233"; //list of categories if you want
pd.LanguageID = rsspage.Language;
pd.MakeReadOnly();
AddContentToCache(mirrored);
return mirrored;
}
protected override IList<GetChildrenReferenceResult> LoadChildrenReferencesAndTypes(ContentReference contentLink, string languageID, out bool languageSpecific)
{
languageSpecific = false;
if (contentLink == this.EntryPoint)
{
IList<GetChildrenReferenceResult> list = new List<GetChildrenReferenceResult>();
try
{
List<string> pageLanguages = new List<string>();
foreach (var page in XmlPages.Descendants("item"))
{
var rsspage = new RSSPage
{
Guid = page.GetValue<string>("guid"),
Title = page.GetValue<string>("title"),
Description = page.GetValue<string>("description"),
Content = page.GetValue<string>("{http://purl.org/rss/1.0/modules/content/}encoded"),
PubDate = page.GetValue<DateTime>("pubDate"),
Language = page.Parent.Element("language").Value,
DocumentUrl = page.GetAttributeFromElement<string>("enclosure", "url")
};
try
{
Uri externalID = MappedIdentity.ConstructExternalIdentifier(ProviderKey, rsspage.Guid);
var mappedIdentity = _identityMappingService.Get(externalID, true);
if (rsspage != null)
{
Type modelType = Type.GetType(_contentType);
GetChildrenReferenceResult result = new GetChildrenReferenceResult
{
ContentLink = mappedIdentity.ContentLink,
ModelType = modelType,
IsLeafNode = true
};
list.Add(result);
}
}
catch (Exception)
{
//todo: Logging
}
}
return list;
}
catch (Exception)
{
//todo: Logging
}
return null;
}
return null;
}
protected override IList<MatchingSegmentResult> ListMatchingSegments(ContentReference parentLink, string urlSegment)
{
var list = new List<MatchingSegmentResult>();
foreach (var child in LoadChildren<IContent>(parentLink, LanguageSelector.Fallback("en", true), -1, -1))
{
var routable = child as Holmen.Externweb.Web.EpiPageTypes.HolmenGroup.Press.NewsPressReleaseReportsBase;
var isMatch = routable != null && urlSegment.Equals(routable.URLSegment, StringComparison.OrdinalIgnoreCase);
if (isMatch)
{
list.Add(new MatchingSegmentResult
{
ContentLink = child.ContentLink
});
}
}
return list;
}
#endregion
#region Private methods
private string CacheKey
{
get
{
return this.Name;
}
}
#endregion
#region properties
public XDocument XmlPages
{
get
{
var pages = CacheManager.Get(CacheKey) as XDocument;
if (pages == null)
{
try
{
pages = XDocument.Load(HttpUtility.UrlDecode(_filePath), LoadOptions.PreserveWhitespace);
}
catch (Exception)
{
//todo handle
}
CacheManager.Insert(
CacheKey,
pages);
}
return pages;
}
}
#endregion
#region cache
/// <summary>
/// The set cache settings.
/// </summary>
/// <param name="content">
/// The content.
/// </param>
/// <param name="cacheSettings">
/// The cache settings.
/// </param>
protected override void SetCacheSettings(IContent content, CacheSettings cacheSettings)
{
if (content == null || cacheSettings == null)
{
return;
}
// Make the cache of this content provider depend on the original content
cacheSettings.CacheKeys.Add(_cacheCreator.CreateCommonCacheKey(new ContentReference(content.ContentLink.ID)));
}
protected override void SetCacheSettings(ContentReference contentReference, IEnumerable<GetChildrenReferenceResult> children, CacheSettings cacheSettings)
{
// Set a low cache setting so new items are fetched from data source, but keep the
// items already fetched for a long time in the cache.
cacheSettings.SlidingExpiration = TimeSpan.FromSeconds(120);
base.SetCacheSettings(contentReference, children, cacheSettings);
}
#endregion
}
public class RSSPage
{
public string Guid { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Content { get; set; }
public DateTime PubDate { get; set; }
public string Language { get; set; }
public string DocumentUrl { get; set; }
}
public static class Extensions
{
public static T GetValue<T>(this XElement element, string name)
{
if (element.Element(name) == null)
{
return default(T);
}
try
{
return (T)Convert.ChangeType(element.Element(name).Value, typeof(T));
}
catch (Exception)
{
return default(T);
}
}
public static T GetAttributeFromElement<T>(this XElement element, string elementName, string attributeName)
{
if (element.Element(elementName) == null)
{
return default(T);
}
try
{
if (element.Element(elementName).HasAttributes && element.Element(elementName).Attribute(attributeName) != null)
{
}
return (T)Convert.ChangeType(element.Element(elementName).Attribute(attributeName).Value, typeof(T));
}
catch (Exception)
{
return default(T);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment