2016-05-26 17:12:04 +02:00
using System ;
2018-05-02 14:52:00 +10:00
using System.Collections.Generic ;
2016-05-26 17:12:04 +02:00
using System.Linq ;
2021-02-18 11:06:02 +01:00
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Persistence.Repositories ;
using Umbraco.Cms.Core.PublishedCache ;
using Umbraco.Cms.Core.Serialization ;
using Umbraco.Cms.Core.Services ;
using Umbraco.Cms.Core.Services.Changes ;
using Umbraco.Extensions ;
2016-05-26 17:12:04 +02:00
2021-02-18 11:06:02 +01:00
namespace Umbraco.Cms.Core.Cache
2016-05-26 17:12:04 +02:00
{
public sealed class ContentCacheRefresher : PayloadCacheRefresherBase < ContentCacheRefresher , ContentCacheRefresher . JsonPayload >
{
2017-10-31 12:48:24 +01:00
private readonly IPublishedSnapshotService _publishedSnapshotService ;
2020-01-22 14:09:20 +01:00
private readonly IIdKeyMap _idKeyMap ;
2018-05-02 14:52:00 +10:00
private readonly IDomainService _domainService ;
2016-05-26 17:12:04 +02:00
2020-01-22 14:09:20 +01:00
public ContentCacheRefresher ( AppCaches appCaches , IJsonSerializer serializer , IPublishedSnapshotService publishedSnapshotService , IIdKeyMap idKeyMap , IDomainService domainService )
2019-11-07 21:28:56 +11:00
: base ( appCaches , serializer )
2016-05-26 17:12:04 +02:00
{
2017-10-31 12:48:24 +01:00
_publishedSnapshotService = publishedSnapshotService ;
2020-01-22 14:09:20 +01:00
_idKeyMap = idKeyMap ;
2018-05-02 14:52:00 +10:00
_domainService = domainService ;
2016-05-26 17:12:04 +02:00
}
#region Define
2017-07-11 19:21:13 +02:00
protected override ContentCacheRefresher This = > this ;
2016-05-26 17:12:04 +02:00
public static readonly Guid UniqueId = Guid . Parse ( "900A4FBE-DF3C-41E6-BB77-BE896CD158EA" ) ;
public override Guid RefresherUniqueId = > UniqueId ;
public override string Name = > "ContentCacheRefresher" ;
#endregion
#region Refresher
public override void Refresh ( JsonPayload [ ] payloads )
{
2019-01-17 11:01:23 +01:00
AppCaches . RuntimeCache . ClearOfType < PublicAccessEntry > ( ) ;
2016-11-03 10:31:44 +01:00
2018-05-02 14:52:00 +10:00
var idsRemoved = new HashSet < int > ( ) ;
2019-01-17 11:01:23 +01:00
var isolatedCache = AppCaches . IsolatedCaches . GetOrCreate < IContent > ( ) ;
2018-05-02 14:52:00 +10:00
2019-06-21 15:48:10 +10:00
foreach ( var payload in payloads . Where ( x = > x . Id ! = default ) )
2016-05-26 17:12:04 +02:00
{
2019-09-27 10:49:48 +02:00
//By INT Id
2019-01-17 11:01:23 +01:00
isolatedCache . Clear ( RepositoryCacheKeys . GetKey < IContent > ( payload . Id ) ) ;
2019-09-27 10:49:48 +02:00
//By GUID Key
isolatedCache . Clear ( RepositoryCacheKeys . GetKey < IContent > ( payload . Key ) ) ;
2016-05-26 17:12:04 +02:00
2020-01-22 14:09:20 +01:00
_idKeyMap . ClearCache ( payload . Id ) ;
2018-03-29 20:01:14 +11:00
2016-05-26 17:12:04 +02:00
// remove those that are in the branch
if ( payload . ChangeTypes . HasTypesAny ( TreeChangeTypes . RefreshBranch | TreeChangeTypes . Remove ) )
{
var pathid = "," + payload . Id + "," ;
2019-01-17 11:01:23 +01:00
isolatedCache . ClearOfType < IContent > ( ( k , v ) = > v . Path . Contains ( pathid ) ) ;
2016-05-26 17:12:04 +02:00
}
2018-05-02 14:52:00 +10:00
//if the item is being completely removed, we need to refresh the domains cache if any domain was assigned to the content
if ( payload . ChangeTypes . HasTypesAny ( TreeChangeTypes . Remove ) )
{
idsRemoved . Add ( payload . Id ) ;
}
}
if ( idsRemoved . Count > 0 )
{
var assignedDomains = _domainService . GetAll ( true ) . Where ( x = > x . RootContentId . HasValue & & idsRemoved . Contains ( x . RootContentId . Value ) ) . ToList ( ) ;
if ( assignedDomains . Count > 0 )
{
2019-01-26 10:52:19 -05:00
// TODO: this is duplicating the logic in DomainCacheRefresher BUT we cannot inject that into this because it it not registered explicitly in the container,
2018-05-02 14:52:00 +10:00
// and we cannot inject the CacheRefresherCollection since that would be a circular reference, so what is the best way to call directly in to the
// DomainCacheRefresher?
ClearAllIsolatedCacheByEntityType < IDomain > ( ) ;
// note: must do what's above FIRST else the repositories still have the old cached
// content and when the PublishedCachesService is notified of changes it does not see
// the new content...
// notify
_publishedSnapshotService . Notify ( assignedDomains . Select ( x = > new DomainCacheRefresher . JsonPayload ( x . Id , DomainChangeTypes . Remove ) ) . ToArray ( ) ) ;
}
2016-05-26 17:12:04 +02:00
}
// note: must do what's above FIRST else the repositories still have the old cached
// content and when the PublishedCachesService is notified of changes it does not see
// the new content...
2019-01-27 01:17:32 -05:00
// TODO: what about this?
2017-09-19 15:51:47 +02:00
// should rename it, and then, this is only for Deploy, and then, ???
//if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache)
// ...
2019-10-14 15:21:00 +11:00
NotifyPublishedSnapshotService ( _publishedSnapshotService , AppCaches , payloads ) ;
2016-05-26 17:12:04 +02:00
base . Refresh ( payloads ) ;
}
// these events should never trigger
// everything should be PAYLOAD/JSON
2019-10-14 15:21:00 +11:00
public override void RefreshAll ( ) = > throw new NotSupportedException ( ) ;
2016-05-26 17:12:04 +02:00
2019-10-14 15:21:00 +11:00
public override void Refresh ( int id ) = > throw new NotSupportedException ( ) ;
2016-05-26 17:12:04 +02:00
2019-10-14 15:21:00 +11:00
public override void Refresh ( Guid id ) = > throw new NotSupportedException ( ) ;
2016-05-26 17:12:04 +02:00
2019-10-14 15:21:00 +11:00
public override void Remove ( int id ) = > throw new NotSupportedException ( ) ;
2016-05-26 17:12:04 +02:00
#endregion
#region Json
2019-10-14 15:21:00 +11:00
/// <summary>
/// Refreshes the publish snapshot service and if there are published changes ensures that partial view caches are refreshed too
/// </summary>
/// <param name="service"></param>
/// <param name="appCaches"></param>
/// <param name="payloads"></param>
internal static void NotifyPublishedSnapshotService ( IPublishedSnapshotService service , AppCaches appCaches , JsonPayload [ ] payloads )
{
service . Notify ( payloads , out _ , out var publishedChanged ) ;
if ( payloads . Any ( x = > x . ChangeTypes . HasType ( TreeChangeTypes . RefreshAll ) ) | | publishedChanged )
{
// when a public version changes
appCaches . ClearPartialViewCache ( ) ;
}
}
2016-05-26 17:12:04 +02:00
public class JsonPayload
{
2019-09-27 10:49:48 +02:00
public JsonPayload ( int id , Guid ? key , TreeChangeTypes changeTypes )
2016-05-26 17:12:04 +02:00
{
Id = id ;
2019-09-27 10:49:48 +02:00
Key = key ;
2016-05-26 17:12:04 +02:00
ChangeTypes = changeTypes ;
}
public int Id { get ; }
2019-09-27 10:49:48 +02:00
public Guid ? Key { get ; }
2016-05-26 17:12:04 +02:00
public TreeChangeTypes ChangeTypes { get ; }
}
#endregion
#region Indirect
2019-01-17 08:34:29 +01:00
public static void RefreshContentTypes ( AppCaches appCaches )
2016-05-26 17:12:04 +02:00
{
// we could try to have a mechanism to notify the PublishedCachesService
// and figure out whether published items were modified or not... keep it
// simple for now, just clear the whole thing
2019-01-17 08:34:29 +01:00
appCaches . ClearPartialViewCache ( ) ;
2016-05-26 17:12:04 +02:00
2019-01-17 11:01:23 +01:00
appCaches . IsolatedCaches . ClearCache < PublicAccessEntry > ( ) ;
appCaches . IsolatedCaches . ClearCache < IContent > ( ) ;
2016-05-26 17:12:04 +02:00
}
#endregion
}
}