Skip to content

Instantly share code, notes, and snippets.

@adnanzameer
Last active May 20, 2025 16:08
Show Gist options
  • Save adnanzameer/18c957059645752cdd4e2f178fa0ebd2 to your computer and use it in GitHub Desktop.
Save adnanzameer/18c957059645752cdd4e2f178fa0ebd2 to your computer and use it in GitHub Desktop.
Removing a Segment from the URL in Optimizely CMS 12 Using Partial Routing
using System.Globalization;
using System.Web;
using EPiServer.Core.Routing;
using EPiServer.Core.Routing.Pipeline;
using EPiServer.Filters;
using EPiServer.Framework.Cache;
public class SitePageDataPartialRouter(
IContentLoader contentLoader,
ISynchronizedObjectInstanceCache cache)
: IPartialRouter<PageData, SitePageData>
{
private static readonly FilterPublished FilterPublishedInstance = new();
public object RoutePartial(PageData content, UrlResolverContext segmentContext)
{
if (segmentContext.RemainingSegments.IsEmpty)
return content;
var remainingPath = HttpUtility.UrlDecode(segmentContext.RemainingSegments.ToString()).Trim('/');
if (string.IsNullOrEmpty(remainingPath) || remainingPath.Contains('.'))
{
return content;
}
var parent = segmentContext.Content.ContentLink;
var language = content.Language;
var resolvedContent = ResolveCategoryPageByHierarchicalPath(parent, remainingPath, language);
if (resolvedContent != null)
{
var containerPage = GetContainerPage(resolvedContent.ContentLink);
if (containerPage != null)
{
segmentContext.RemainingSegments = new ReadOnlyMemory<char>();
return resolvedContent;
}
}
return content;
}
public PartialRouteData GetPartialVirtualPath(SitePageData content, UrlGeneratorContext urlGeneratorContext)
{
if (ViewContextExtension.IsInEditMode())
{
return null;
}
var containerPage = GetContainerPage(content.ContentLink);
if (containerPage != null)
{
var ancestors = GetAncestorPathExcludingContainers(content.ContentLink);
var segments = ancestors
.OfType<PageData>()
.Select(x => x.URLSegment)
.Append(content.URLSegment);
var path = string.Join("/", segments);
return new PartialRouteData
{
BasePathRoot = new PageReference(ContentReference.StartPage),
PartialVirtualPath = path
};
}
return null;
}
private ContainerPage GetContainerPage(ContentReference page)
{
var ancestors = contentLoader
.GetAncestors(page)
.Where(x => !FilterPublishedInstance.ShouldFilter(x))
.ToArray();
return ancestors.LastOrDefault(x => x is ContainerPage) as ContainerPage;
}
private IEnumerable<IContent> GetAncestorPathExcludingContainers(ContentReference contentLink)
{
return contentLoader
.GetAncestors(contentLink)
.Where(x =>
x is not ContainerPage &&
x.ContentLink != ContentReference.StartPage &&
x.ContentLink != ContentReference.RootPage &&
!FilterPublishedInstance.ShouldFilter(x))
.Reverse(); // top-down
}
private PageData ResolveCategoryPageByHierarchicalPath(ContentReference root, string path, CultureInfo culture)
{
if (string.IsNullOrWhiteSpace(path))
return null;
var cacheKey = $"ResolvedCategoryPath:{root.ID}:{path}:{culture.Name}";
if (cache.Get(cacheKey) is PageData cachedPage)
return cachedPage;
var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (segments.Length == 0)
return null;
PageData current = null;
ContentReference currentParent = root;
LoaderOptions loadingOptions = [LanguageLoaderOption.FallbackWithMaster(culture)];
foreach (var segment in segments)
{
//Use Search & Navigation
// var result = client
// .Search<PageData>()
// .Filter(x => x.Ancestors().Match(currentParent))
// .GetContentResultAsync().ConfigureAwait(false).GetAwaiter().GetResult();
// OR
var descendantRefs = contentLoader.GetDescendents(currentParent);
var descendants = contentLoader
.GetItems(descendantRefs, loadingOptions)
.Where(x => !FilterPublishedInstance.ShouldFilter(x))
.OfType<PageData>()
.ToList();
var match = descendants.Find(x =>
string.Equals(x.URLSegment, segment, StringComparison.OrdinalIgnoreCase));
if (match == null)
{
return null;
}
current = match;
currentParent = current.ContentLink;
}
cache.Insert(cacheKey, current, new CacheEvictionPolicy(
TimeSpan.FromMinutes(30),
CacheTimeoutType.Absolute,
null, new List<string> { "masterKey" }));
return current;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment