Skip to content

Instantly share code, notes, and snippets.

@LucGosso
Last active March 13, 2017 12:39
Show Gist options
  • Save LucGosso/c60f1c0fccaac79d571a726b4d7f6945 to your computer and use it in GitHub Desktop.
Save LucGosso/c60f1c0fccaac79d571a726b4d7f6945 to your computer and use it in GitHub Desktop.
Content Providers in EPiServer is a way of publishing external data as pages/blocks etc... but also a way of publishing content in several places on your web. Due to the lack of publishing in several containers/parents, this is a way of cloning data and keeping friendly urls. Read more http://devblog.gosso.se/2016/09/contentprovider-example-for-…
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using EPiServer.Web.Routing;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Linq;
namespace Gosso.EPiStuff.ContentProviders
{
/**
* This replicates pages from one container to another
*/
public class ClonedByCategoryContentProvider : EPiServer.Core.ContentProvider
{
private int CATEGORYID = -1;
private int _sourceid = -1;
private const string languageBranch = "en";
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 ClonedCategoryContentProvider(IdentityMappingService identityMappingService,
IContentTypeRepository _ContentTypeRepository,
IContentFactory _ContentFactory,
ServiceLocationHelper _Locator,
IUrlSegmentGenerator _UrlSegment,
CategoryRepository _categoryRepository,
IContentCacheKeyCreator _CacheCreator)
{
_identityMappingService = identityMappingService;
_contentTypeRepository = _ContentTypeRepository;
_contentFactory = _ContentFactory;
locator = _Locator;
_urlSegment = _UrlSegment;
categoryRepository = _categoryRepository;
_cacheCreator = _CacheCreator;
}
/// <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);
if (string.IsNullOrEmpty(configParams["sourceid"]))
{
throw new ConfigurationErrorsException("ClonedCategoryContentProvider requires configuration attribute sourceid");
}
int id;
if (int.TryParse(configParams["sourceid"],out id))
{ _sourceid = id; }
if (!string.IsNullOrEmpty(configParams["categoryid"]))
{
int catid;
if (int.TryParse(configParams["categoryid"], out catid))
{ CATEGORYID = catid; }
}
}
protected override IContent LoadContent(ContentReference contentLink, ILanguageSelector languageSelector)
{
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 strId = mappedIdentity.ExternalIdentifier.Segments[1];
int intId;
var success = int.TryParse(strId, out intId);
if (success)
{
var reference = new PageReference(intId);
PageData page = DataFactory.Instance.GetPage(reference, LanguageSelector.Fallback(languageBranch, true));
if (page != null)
{
return CreateMirroredContent(mappedIdentity, page);
}
}
}
return null;
}
protected override IList<GetChildrenReferenceResult> LoadChildrenReferencesAndTypes(ContentReference contentLink, string languageID, out bool languageSpecific)
{
languageSpecific = false;
// Only the root node has children
if (contentLink == this.EntryPoint)
{
PageReference pr = new PageReference(_sourceid);
var entryPoint = repository.Get<PageData>(this.EntryPoint);
if (!PageReference.IsNullOrEmpty(pr))
{
System.Collections.Generic.IList<GetChildrenReferenceResult> list = new List<GetChildrenReferenceResult>();
var pages = DataFactory.Instance.GetChildren(pr, new LanguageSelector(entryPoint.LanguageBranch)).OrderByDescending(z => z.StartPublish).Where(s => s.CheckPublishedStatus(PagePublishedStatus.Published)); //.Where(x => x.Categories.IsEmpty);
foreach (PageData page in pages)
{
if (page.Category.MemberOf(CATEGORYID) || CATEGORYID<1)
{
Uri externalID = MappedIdentity.ConstructExternalIdentifier(ProviderKey, page.ContentLink.ToString());
var mappedIdentity = _identityMappingService.Get(externalID, true);
System.Type modelType = null;
modelType = typeof(PageData);
ContentReference contentRef = page.PageLink;
GetChildrenReferenceResult result = new GetChildrenReferenceResult
{
ContentLink = mappedIdentity.ContentLink,
ModelType = modelType,
IsLeafNode=true
};
list.Add(result);
}
}
return list.Reverse().ToList();
}
}
return null;
}
/// <summary>
/// Used for routing
/// </summary>
/// <param name="parentLink"></param>
/// <param name="urlSegment"></param>
/// <returns></returns>
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 IRoutable;
var isMatch = routable != null && urlSegment.Equals(routable.RouteSegment, StringComparison.OrdinalIgnoreCase);
if (isMatch)
{
list.Add(new MatchingSegmentResult
{
ContentLink = child.ContentLink
});
}
}
return list;
}
public IContent CreateMirroredContent(MappedIdentity mappedIdentity, PageData original)
{
PageData mirrored = original.CreateWritableClone();
mirrored.ParentLink = new PageReference(EntryPoint.ID);
mirrored.ContentLink = mappedIdentity.ContentLink;
mirrored.ContentGuid = mappedIdentity.ContentGuid;
(mirrored as PageData).LinkType = PageShortcutType.Normal;
(mirrored as IRoutable).RouteSegment = (original as IRoutable).RouteSegment;
mirrored.LinkURL = "";
return mirrored;
}
/// <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(30);
base.SetCacheSettings(contentReference, children, cacheSettings);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment