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 ;
using Umbraco.Core ;
using Umbraco.Core.Cache ;
using Umbraco.Core.Configuration ;
using Umbraco.Core.Models ;
using Umbraco.Core.Persistence.Repositories ;
2017-12-07 16:45:25 +01:00
using Umbraco.Core.Persistence.Repositories.Implement ;
2018-03-29 20:01:14 +11:00
using Umbraco.Core.Services ;
2016-05-26 17:12:04 +02:00
using Umbraco.Core.Services.Changes ;
2017-05-30 18:13:11 +02:00
using Umbraco.Web.Composing ;
2016-05-26 17:12:04 +02:00
using Umbraco.Web.PublishedCache ;
namespace Umbraco.Web.Cache
{
public sealed class ContentCacheRefresher : PayloadCacheRefresherBase < ContentCacheRefresher , ContentCacheRefresher . JsonPayload >
{
2017-10-31 12:48:24 +01:00
private readonly IPublishedSnapshotService _publishedSnapshotService ;
2018-03-29 20:01:14 +11:00
private readonly IdkMap _idkMap ;
2018-05-02 14:52:00 +10:00
private readonly IDomainService _domainService ;
2016-05-26 17:12:04 +02:00
2018-05-02 14:52:00 +10:00
public ContentCacheRefresher ( CacheHelper cacheHelper , IPublishedSnapshotService publishedSnapshotService , IdkMap idkMap , IDomainService domainService )
2016-05-26 17:12:04 +02:00
: base ( cacheHelper )
{
2017-10-31 12:48:24 +01:00
_publishedSnapshotService = publishedSnapshotService ;
2018-03-29 20:01:14 +11:00
_idkMap = idkMap ;
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-15 18:41:02 +01:00
CacheHelper . RuntimeCache . ClearCacheObjectTypes < PublicAccessEntry > ( ) ;
2016-11-03 10:31:44 +01:00
2018-05-02 14:52:00 +10:00
var idsRemoved = new HashSet < int > ( ) ;
2019-01-15 18:41:02 +01:00
var isolatedCache = CacheHelper . IsolatedRuntimeCache . GetOrCreateCache < IContent > ( ) ;
2018-05-02 14:52:00 +10:00
2016-05-26 17:12:04 +02:00
foreach ( var payload in payloads )
{
2019-01-15 18:41:02 +01:00
isolatedCache . ClearCacheItem ( RepositoryCacheKeys . GetKey < IContent > ( payload . Id ) ) ;
2016-05-26 17:12:04 +02:00
2018-03-29 20:01:14 +11:00
_idkMap . ClearCache ( payload . Id ) ;
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-15 18:41:02 +01:00
isolatedCache . ClearCacheObjectTypes < 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 )
{
//fixme - this is duplicating the logic in DomainCacheRefresher BUT we cannot inject that into this because it it not registered explicitly in the container,
// 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...
2017-09-19 15:51:47 +02:00
// fixme - what about this?
// should rename it, and then, this is only for Deploy, and then, ???
//if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache)
// ...
2017-10-31 12:48:24 +01:00
_publishedSnapshotService . Notify ( payloads , out _ , out var publishedChanged ) ;
2016-05-26 17:12:04 +02:00
if ( payloads . Any ( x = > x . ChangeTypes . HasType ( TreeChangeTypes . RefreshAll ) ) | | publishedChanged )
{
// when a public version changes
2016-09-01 19:06:08 +02:00
Current . ApplicationCache . ClearPartialViewCache ( ) ;
2016-05-26 17:12:04 +02:00
MacroCacheRefresher . ClearMacroContentCache ( CacheHelper ) ; // just the content
}
base . Refresh ( payloads ) ;
}
// these events should never trigger
// everything should be PAYLOAD/JSON
public override void RefreshAll ( )
{
throw new NotSupportedException ( ) ;
}
public override void Refresh ( int id )
{
throw new NotSupportedException ( ) ;
}
public override void Refresh ( Guid id )
{
throw new NotSupportedException ( ) ;
}
public override void Remove ( int id )
{
throw new NotSupportedException ( ) ;
}
#endregion
#region Json
public class JsonPayload
{
public JsonPayload ( int id , TreeChangeTypes changeTypes )
{
Id = id ;
ChangeTypes = changeTypes ;
}
public int Id { get ; }
public TreeChangeTypes ChangeTypes { get ; }
}
#endregion
#region Indirect
public static void RefreshContentTypes ( CacheHelper cacheHelper )
{
// 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
cacheHelper . ClearPartialViewCache ( ) ;
MacroCacheRefresher . ClearMacroContentCache ( cacheHelper ) ; // just the content
cacheHelper . IsolatedRuntimeCache . ClearCache < PublicAccessEntry > ( ) ;
cacheHelper . IsolatedRuntimeCache . ClearCache < IContent > ( ) ;
}
#endregion
}
}