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