Skip to content

Commit

Permalink
implements #7
Browse files Browse the repository at this point in the history
  • Loading branch information
kentico-ericd committed May 23, 2022
1 parent 2bcbac4 commit 627224d
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 36 deletions.
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ To override the properties registered during application startup, pass a new `Br
})
```

## Custom rendering
## Customizations

The final HTML of your breadcrumbs is determined by the `IBreadcrumbsRenderer` interface, which you can find the default code for in [DefaultBreadcrumbsRenderer](/Renderer/DefaultBreadcrumbsRenderer.cs). If you'd like to customize the HTML of the breadcrumbs, you can implement your own `IBreadcrumbsRenderer` and use the `RegisterImplementation` attribute to register your code with a higher priority:
### Breadcrumb rendering

The final HTML of your breadcrumbs is determined by the `IBreadcrumbsRenderer` interface, which you can find the default code for in [DefaultBreadcrumbsRenderer](/src/Xperience.Core.Breadcrumbs/Services/DefaultBreadcrumbsRenderer.cs). If you'd like to customize the HTML of the breadcrumbs, you can implement your own `IBreadcrumbsRenderer` and use the `RegisterImplementation` attribute to register your code with a higher priority:

```cs
[assembly: RegisterImplementation(typeof(IBreadcrumbsRenderer), typeof(CustomBreadcrumbsRenderer), Lifestyle = Lifestyle.Singleton, Priority = RegistrationPriority.Default)]
Expand All @@ -80,6 +82,20 @@ namespace MySite.Breadcrumbs {
public class CustomBreadcrumbsRenderer : IBreadcrumbsRenderer {
```

### Breadcrumb item generation

Breadcrumb items are provided by the [DefaultBreadcrumbItemMapper](/src/Xperience.Core.Breadcrumbs/Services/DefaultBreadcrumbItemMapper.cs). If you would like to modify how the breadcrumb names, URLs, etc. are generated, you can implement your own `IBreadcrumbItemMapper` and use the `RegisterImplementation` attribute to register your code with a higher priority:

```cs
[assembly: RegisterImplementation(typeof(IBreadcrumbItemMapper), typeof(CustomBreadcrumbItemMapper), Lifestyle = Lifestyle.Singleton, Priority = RegistrationPriority.Default)]
namespace MySite.Breadcrumbs
{
/// <summary>
/// Custom implementation of <see cref="IBreadcrumbItemMapper"/>.
/// </summary>
public class CustomBreadcrumbItemMapper : IBreadcrumbItemMapper {
```

## Compatibility

This code is only available for use on Kentico Xperience 13 websites using the [.NET Core development model](https://docs.xperience.io/developing-websites/developing-xperience-applications-using-asp-net-core). The website must be using the [content tree-based routing](https://docs.xperience.io/developing-websites/implementing-routing/content-tree-based-routing) model for the breadcrumbs to display properly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

using Kentico.PageBuilder.Web.Mvc;

using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewComponents;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static IServiceCollection AddBreadcrumbs(

// Register widget properties
var props = new BreadcrumbsWidgetProperties();
if (configure is object)
if (configure != null)
{
configure(props);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ public class BreadcrumbHelper
{
private const string CACHE_KEY_FORMAT = "breadcrumbswidget|document|{0}|{1}|{2}";
private readonly IPageDataContextRetriever pageDataContextRetriever;
private readonly IPageUrlRetriever pageUrlRetriever;
private readonly IBreadcrumbsRenderer breadcrumbsRenderer;
private readonly IBreadcrumbItemMapper breadcrumbItemMapper;
private readonly IPageRetriever pageRetriever;
private readonly BreadcrumbsWidgetProperties breadcrumbsWidgetProperties;


public BreadcrumbHelper(
IPageDataContextRetriever pageDataContextRetriever,
IPageUrlRetriever pageUrlRetriever,
IBreadcrumbsRenderer breadcrumbsRenderer,
IPageRetriever pageRetriever,
IBreadcrumbItemMapper breadcrumbItemMapper,
BreadcrumbsWidgetProperties breadcrumbsWidgetProperties)
{
this.pageDataContextRetriever = pageDataContextRetriever;
this.pageUrlRetriever = pageUrlRetriever;
this.breadcrumbsRenderer = breadcrumbsRenderer;
this.pageRetriever = pageRetriever;
this.breadcrumbItemMapper = breadcrumbItemMapper;
this.breadcrumbsWidgetProperties = breadcrumbsWidgetProperties;
}

Expand Down Expand Up @@ -76,7 +76,7 @@ public IReadOnlyList<BreadcrumbItem> GetHierarchy(BreadcrumbsWidgetProperties pr
}


private IHtmlContent GetBreadcrumbContent(BreadcrumbsWidgetProperties? props)
private IHtmlContent GetBreadcrumbContent(BreadcrumbsWidgetProperties props)
{
if (props == null)
{
Expand All @@ -98,7 +98,7 @@ private IHtmlContent GetBreadcrumbContent(BreadcrumbsWidgetProperties? props)
{
sb.Append(breadcrumbsRenderer.RenderSiteLink(bci, props.BreadcrumbItemClass));
}
else if (string.IsNullOrEmpty(bci.Url))
else if (String.IsNullOrEmpty(bci.Url))
{
sb.Append(breadcrumbsRenderer.RenderItemWithoutLink(bci, props.BreadcrumbItemClass));
}
Expand All @@ -114,9 +114,13 @@ private IHtmlContent GetBreadcrumbContent(BreadcrumbsWidgetProperties? props)
}


private string GetCacheKey(int docID, bool showSiteLink, bool showContainers)
private string GetCacheKey(int docID, BreadcrumbsWidgetProperties props)
{
return string.Format(CACHE_KEY_FORMAT, docID, showSiteLink, showContainers);
return string.Format(
CACHE_KEY_FORMAT,
docID,
props.ShowSiteLink,
props.ShowContainers);
}


Expand All @@ -131,26 +135,21 @@ private IReadOnlyList<BreadcrumbItem> GetHierarchyInternal(BreadcrumbsWidgetProp
var hierarchy = CacheHelper.Cache((cs) =>
{
ICollection<string> cacheDependencies = new List<string>();
var list = BuildHierarchyInternal(current, props.ShowSiteLink, props.ShowContainers, ref cacheDependencies);
var list = BuildHierarchyInternal(current, props, ref cacheDependencies);
cs.CacheDependency = CacheHelper.GetCacheDependency(cacheDependencies);
return list;
}, new CacheSettings(120, GetCacheKey(current.DocumentID, props.ShowSiteLink, props.ShowContainers)));
}, new CacheSettings(120, GetCacheKey(current.DocumentID, props)));

return hierarchy.ToList().AsReadOnly();
}


private IEnumerable<BreadcrumbItem> BuildHierarchyInternal(TreeNode current, bool addSiteLink, bool showContainers, ref ICollection<string> cacheDependencies)
private IEnumerable<BreadcrumbItem> BuildHierarchyInternal(TreeNode current, BreadcrumbsWidgetProperties props, ref ICollection<string> cacheDependencies)
{
// Add current page
var ret = new List<BreadcrumbItem>
{
new BreadcrumbItem
{
Name = current.DocumentName,
Url = null,
IsCurrentPage = true
}
breadcrumbItemMapper.MapPage(current, true)
};
cacheDependencies.Add($"documentid|{current.DocumentID}");

Expand All @@ -168,14 +167,9 @@ private IEnumerable<BreadcrumbItem> BuildHierarchyInternal(TreeNode current, boo
if (type != null)
{
if (type.ClassIsCoupledClass ||
!type.ClassIsCoupledClass && showContainers)
!type.ClassIsCoupledClass && props.ShowContainers)
{
var url = pageUrlRetriever.Retrieve(parent).AbsoluteUrl;
ret.Add(new BreadcrumbItem
{
Name = parent.DocumentName,
Url = url
});
ret.Add(breadcrumbItemMapper.MapPage(parent));
}
}
cacheDependencies.Add($"documentid|{current.DocumentID}");
Expand All @@ -184,14 +178,9 @@ private IEnumerable<BreadcrumbItem> BuildHierarchyInternal(TreeNode current, boo
}

// Add link to main domain if needed
if (addSiteLink)
if (props.ShowSiteLink)
{
ret.Add(new BreadcrumbItem
{
IsSiteLink = true,
Name = current.Site.DisplayName,
Url = current.Site.SitePresentationURL
});
ret.Add(breadcrumbItemMapper.MapSite(current.Site));
cacheDependencies.Add($"cms.site|byid|{current.Site.SiteID}");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using CMS;
using CMS.Core;
using CMS.DocumentEngine;
using CMS.SiteProvider;

using Kentico.Content.Web.Mvc;

using System;

using Xperience.Core.Breadcrumbs;

[assembly: RegisterImplementation(typeof(IBreadcrumbItemMapper), typeof(DefaultBreadcrumbItemMapper), Lifestyle = Lifestyle.Singleton, Priority = RegistrationPriority.SystemDefault)]
namespace Xperience.Core.Breadcrumbs
{
/// <summary>
/// Default implementation of <see cref="IBreadcrumbItemMapper"/>.
/// </summary>
internal class DefaultBreadcrumbItemMapper : IBreadcrumbItemMapper
{
private readonly IPageUrlRetriever pageUrlRetriever;


public DefaultBreadcrumbItemMapper(IPageUrlRetriever pageUrlRetriever)
{
this.pageUrlRetriever = pageUrlRetriever;
}


public BreadcrumbItem MapPage(TreeNode page, bool isCurrent = false)
{
var url = String.Empty;
if (!isCurrent)
{
url = pageUrlRetriever.Retrieve(page).AbsoluteUrl;
}

return new BreadcrumbItem
{
Name = page.DocumentName,
Url = url,
IsCurrentPage = isCurrent
};
}

public BreadcrumbItem MapSite(SiteInfo site)
{
return new BreadcrumbItem
{
IsSiteLink = true,
Name = site.DisplayName,
Url = site.SitePresentationURL
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Xperience.Core.Breadcrumbs
/// <summary>
/// Default implementation of <see cref="IBreadcrumbsRenderer"/>.
/// </summary>
public class DefaultBreadcrumbsRenderer : IBreadcrumbsRenderer
internal class DefaultBreadcrumbsRenderer : IBreadcrumbsRenderer
{
public string RenderClosingTag()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using CMS.DocumentEngine;
using CMS.SiteProvider;

namespace Xperience.Core.Breadcrumbs
{
/// <summary>
/// Converts Xperience objects into <see cref="BreadcrumbItem"/>s.
/// </summary>
public interface IBreadcrumbItemMapper
{
/// <summary>
/// Generates a <see cref="BreadcrumbItem"/> for an Xperience content tree page.
/// </summary>
/// <param name="page">The page to generate the breadcrumb for.</param>
/// <param name="isCurrent">If <c>true</c>, the <paramref name="page"/> is what the visitor is currently viewing.</param>
public BreadcrumbItem MapPage(TreeNode page, bool isCurrent = false);


/// <summary>
/// Generates a <see cref="BreadcrumbItem"/> for the current site.
/// </summary>
/// <param name="site">The current Xperience site.</param>
public BreadcrumbItem MapSite(SiteInfo site);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.0.0</Version>
<Version>3.0.0</Version>
<Authors>Eric Dugre</Authors>
<Company>Kentico</Company>
<Description>A breadcrumbs widget for Xperience .NET Core websites</Description>
Expand Down

0 comments on commit 627224d

Please sign in to comment.