Skip to content

Commit

Permalink
Merge pull request #4 from bielu/feat/reference-refresh
Browse files Browse the repository at this point in the history
Feat/reference refresh
  • Loading branch information
bielu authored Jan 16, 2024
2 parents 02d203b + d6724b7 commit 0a91d36
Show file tree
Hide file tree
Showing 27 changed files with 169 additions and 475 deletions.
10 changes: 7 additions & 3 deletions Writerside/topics/Configuration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Configuration

Start typing here...
This section of documentation is dedicated to configuration of providers. Each provider has it's own configuration options and this section will only shared configuration options between all providers.
## Configuration
### Json Options Schema
```json
Expand All @@ -11,5 +10,10 @@ Start typing here...
This flag is responsible by enabling / disabling adding logs to Umbraco Audit log. Default value is true.
#### Preview Flag
This flag is responsible by enabling / disabling refreshment of preview in cases when CDN is enabled on preview Urls(not recommended). Default value is false.
#### ReferencePurge
This flag is responsible by enabling / disabling refreshment of content referencing current page(not recommended). Default value is false. This is not recommended as it can cause performance issues.
#### Development Mode Flag
This flag is responsible by enabling / disabling development mode for provider and logging more details to Umbraco log. Default value is false.
This flag is responsible by enabling / disabling development mode for provider and should be use only when working with changes in UI as allows to bootstrap Vite compiler and do live reloads. Default value is false.

## Umbraco Configuration
When using provider, it is highly recommend to use Culture and hostnames for each language. If you don't have hostnames for each language, languages will be send to provider based on umbraco url(https://github.com/umbraco/Umbraco-CMS/blob/2e61d6449ae8e0c837dafa1e93ac950eda36c4f2/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs#L68) and this will cause issues in load balancing scenarios.
2 changes: 1 addition & 1 deletion Writerside/topics/api-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Introduction

This is document in progress
This project was started as there was lack of good CDN integration providers for umbraco. Previous providers were not working as expected or were not maintained anymore.

## What you can do using CDN integration provider for umbraco

Expand Down
Binary file modified docs/webHelpADOC2-all.zip
Binary file not shown.
2 changes: 2 additions & 0 deletions src/bielu.Umbraco.Cdn.Core/Configuration/BieluCdnOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public class BieluCdnOptions
[SchemaPrefix]
[JsonIgnore]
public static string SectionName { get; set; } = $"{CdnConstants.CdnConfigSectionName}";

public bool ReferencePurge { get; set; } = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using bielu.Umbraco.Cdn.Core.Configuration;
using bielu.Umbraco.Cdn.Core.Services;
using bielu.Umbraco.Cdn.Services;
using Lucene.Net.Util;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -51,13 +52,7 @@ public async Task HandleAsync(ContentMovingNotification notification, Cancellati
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
foreach (var content in notification.MoveInfoCollection)
{
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Entity.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsById(content.Entity));
pages.AddRange(GetPages(content.Entity));
}

//todo: optimize as now we dont valide which domains is valid for either of cdns
Expand Down Expand Up @@ -91,21 +86,31 @@ public async Task HandleAsync(ContentMovingNotification notification, Cancellati
}
}

private List<string> GetPages(IContent content)
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
var pages = new List<string>();
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsByIContent(content));
if (_configuration.ReferencePurge)
{
pages.AddRange(_umbracoUrlDeliveryService.GetUrlsByReferences(content));
}

return pages;
}
public async Task HandleAsync(ContentDeletingNotification notification, CancellationToken cancellationToken)
{
var pages = new List<string>();
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;

foreach (var content in notification.DeletedEntities)
{
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsById(content));
pages.AddRange(GetPages(content));
}

//todo: optimize as now we dont valide which domains is valid for either of cdns
Expand Down Expand Up @@ -208,13 +213,7 @@ public async Task HandleAsync(ContentPublishedNotification notification, Cancell
var pages = new List<string>();
foreach (var content in notification.PublishedEntities)
{
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsById(content));
pages.AddRange(GetPages(content));
}

try
Expand Down Expand Up @@ -259,19 +258,10 @@ public async Task HandleAsync(ContentPublishedNotification notification, Cancell

public async Task HandleAsync(ContentUnpublishingNotification notification, CancellationToken cancellationToken)
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
var pages = new List<string>();


foreach (var content in notification.UnpublishedEntities)
{
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsById(content));
pages.AddRange(GetPages(content));
}

notification.State["purgedUrls"] = pages;
Expand All @@ -289,10 +279,7 @@ public async Task HandleAsync(ContentPublishingNotification notification, Cancel
{
if (content.Published)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"Cloudflare cache was purged", $"Clouflare cache purged");

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsById(content));
pages.AddRange(GetPages(content));
}
}

Expand Down Expand Up @@ -320,13 +307,7 @@ public async Task HandleAsync(ContentSavedNotification notification, Cancellatio
{
if (content.Published)
{
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsById(content));
pages.AddRange(GetPages(content));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace bielu.Umbraco.Cdn.Core.Services
{
public interface IUmbracoUrlDeliveryService
{
public List<string> GetUrlsById(IContent content, bool includeDescendants = false);
public List<string> GetUrlsByIContent(IContent content, bool includeDescendants = false);
public List<string> GetUrlsByReferences(IContent content, bool includeDescendants = false);
}
}
127 changes: 108 additions & 19 deletions src/bielu.Umbraco.Cdn.Core/Services/UmbracoUrlDeliveryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Web.Common.AspNetCore;
using Umbraco.Extensions;

namespace bielu.Umbraco.Cdn.Core.Services
Expand All @@ -13,38 +14,95 @@ public class UmbracoUrlDeliveryService : IUmbracoUrlDeliveryService
{
private readonly IDomainService _domainService;
private readonly IUmbracoContextFactory _contextFactory;
private readonly ITrackedReferencesService _trackedReferencesService;
private readonly IRequestAccessor _requestAccessor;

public UmbracoUrlDeliveryService(IDomainService domainService, IUmbracoContextFactory contextFactory)
public UmbracoUrlDeliveryService(IDomainService domainService, IUmbracoContextFactory contextFactory,
ITrackedReferencesService trackedReferencesService,IRequestAccessor requestAccessor)
{
_domainService = domainService;
_contextFactory = contextFactory;
_trackedReferencesService = trackedReferencesService;
_requestAccessor = requestAccessor;
}

public List<string> GetUrlsById(IContent content, bool includeDescendants = false)
public List<string> GetUrlsByIContent(IContent content, bool includeDescendants = false)
{
List<string> urls = new List<string>();
if (content == null)
{
return urls;
}

foreach (var url in GetUrl(content))
{
urls.AddRange(BuildDomainUrls(new List<string>() { url }, GetDomains(content)));
}

return urls;
}

public List<string> GetUrlsByReferences(IContent content, bool includeDescendants = false)
{
var urls = new List<string>();
var references = _trackedReferencesService.GetPagedItemsWithRelations(new[] { content.Id }, 0, 1000, false);
urls.AddRange(GetUrlFromReferencePage(references.Items));
for (int i = 1; i < references.TotalPages; i++)
{
references = _trackedReferencesService.GetPagedItemsWithRelations(new[] { content.Id }, i, 1000, false);
urls.AddRange(GetUrlFromReferencePage(references.Items));
}

return urls;
}

private List<string> GetUrlFromReferencePage(IEnumerable<RelationItem>? referencesItems)
{
List<string> urls = new List<string>();
using (var contextReference = _contextFactory.EnsureUmbracoContext())
{
foreach (var relationItem in referencesItems)

Check warning on line 64 in src/bielu.Umbraco.Cdn.Core/Services/UmbracoUrlDeliveryService.cs

View workflow job for this annotation

GitHub Actions / buildBeta / build / build

Dereference of a possibly null reference.
{
IPublishedContent item = null;

Check warning on line 66 in src/bielu.Umbraco.Cdn.Core/Services/UmbracoUrlDeliveryService.cs

View workflow job for this annotation

GitHub Actions / buildBeta / build / build

Converting null literal or possible null value to non-nullable type.
switch (relationItem.NodeUdi.EntityType)
{
case global::Umbraco.Cms.Core.Constants.UdiEntityType.DocumentType:
item = contextReference.UmbracoContext.Content.GetById(relationItem.NodeId);

break;
case global::Umbraco.Cms.Core.Constants.UdiEntityType.Media:
item = contextReference.UmbracoContext.Media.GetById(relationItem.NodeId);

break;
}

if (item == null) continue;
urls.AddRange(BuildDomainUrls(new List<string>() { item.Url(mode: UrlMode.Absolute) },
GetDomains(item)));
}
}

return urls;
}

private IEnumerable<string> BuildDomainUrls(List<string> urls, List<IDomain> assignedDomains)
{
var list = new List<string>();
if (urls == null || urls.All(x=>string.IsNullOrWhiteSpace(x))) return list;
if(assignedDomains == null || !assignedDomains.Any()) return list;
foreach (var url in urls. Where(x=>!string.IsNullOrWhiteSpace(x)))
if (urls == null || urls.All(x => string.IsNullOrWhiteSpace(x))) return list;
if (assignedDomains == null || !assignedDomains.Any())
{
var domain = (_requestAccessor as AspNetCoreRequestAccessor).GetApplicationUrl().GetLeftPart(UriPartial.Authority);
foreach (var url in urls.Where(x => !string.IsNullOrWhiteSpace(x)))
{

list.Add(CombinePaths(domain, url));
}
}
foreach (var url in urls.Where(x => !string.IsNullOrWhiteSpace(x)))
{
foreach (var domain in assignedDomains)
{
list.Add(CombinePaths(domain.DomainName,url.Replace(domain.RootContentId.ToString(),"")));
list.Add(CombinePaths(domain.DomainName, url.Replace(domain.RootContentId.ToString(), "")));
}
}

Expand All @@ -55,12 +113,22 @@ private List<IDomain> GetDomains(IContent content)
{
var list = new List<IDomain>();
//Termination case
if (content == null )
if (content == null)
{
return list;
}

foreach (var id in content.Path.Split(','))
list.AddRange(GetDomainByPath(content.Path));


return list;
}

private List<IDomain> GetDomainByPath(string path)
{
var list = new List<IDomain>();

foreach (var id in path.Split(','))
{
var numericId = int.Parse(id);
if (numericId < 0)
Expand All @@ -73,47 +141,68 @@ private List<IDomain> GetDomains(IContent content)
list.AddRange(validDomain);
}


return list;
}

private List<IDomain> GetDomains(IPublishedContent content)
{
var list = new List<IDomain>();
//Termination case
if (content == null)
{
return list;
}

list.AddRange(GetDomainByPath(content.Path));


return list;
}

private List<string> GetUrl(IContent content)
{
using (var contextReference = _contextFactory.EnsureUmbracoContext())
{
var urls = new List<string>();
var route = contextReference.UmbracoContext.Content.GetRouteById(content.Id);

if (!string.IsNullOrWhiteSpace(route))
{
urls.Add(route);
urls.Add(route);
}

urls.Add(content.Id.ToString());
IPublishedContent publishedContent = contextReference.UmbracoContext.Content.GetById(content.Id);
foreach (var culture in content.EditedCultures)
{
urls.Add(contextReference.UmbracoContext.Content.GetRouteById(false, content.Id, culture));
}
return urls;
IPublishedContent publishedContent = contextReference.UmbracoContext.Content.GetById(content.Id);
foreach (var culture in content.EditedCultures)
{
urls.Add(contextReference.UmbracoContext.Content.GetRouteById(false, content.Id, culture));
}

return urls;
}
}

public string CombinePaths(string domain, string url)
{
if (url.Contains(domain))
{
return url;
}
if(domain.EndsWith("/") && url.StartsWith("/"))

if (domain.EndsWith("/") && url.StartsWith("/"))
{
//strip the first / so they aren't doubled up when we combine them.
domain = domain.TrimEnd('/');
}
else if(!domain.EndsWith("/") && !url.StartsWith("/"))
else if (!domain.EndsWith("/") && !url.StartsWith("/"))
{
//neight of them had a / so we have to add one.
domain = domain + "/";
}

//on purpose we support only https
return (domain.Contains("https://")? "": "https://")+ domain + url;
return (domain.Contains("https://") ? "" : "https://") + domain + url;
}
}
}
Loading

0 comments on commit 0a91d36

Please sign in to comment.