2020-12-09 22:43:49 +11:00
|
|
|
using System;
|
2021-04-20 12:17:11 +02:00
|
|
|
using System.Collections.Generic;
|
2020-12-09 22:43:49 +11:00
|
|
|
using System.Linq;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core;
|
|
|
|
|
using Umbraco.Cms.Core.Events;
|
|
|
|
|
using Umbraco.Cms.Core.Models;
|
|
|
|
|
using Umbraco.Cms.Core.PublishedCache;
|
|
|
|
|
using Umbraco.Cms.Core.Services;
|
|
|
|
|
using Umbraco.Cms.Core.Services.Changes;
|
2021-04-09 13:43:39 +02:00
|
|
|
using Umbraco.Cms.Core.Services.Notifications;
|
2021-02-10 10:46:52 +01:00
|
|
|
using Umbraco.Cms.Infrastructure.PublishedCache.Persistence;
|
2021-02-09 13:32:34 +01:00
|
|
|
using Umbraco.Extensions;
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2021-02-10 10:46:52 +01:00
|
|
|
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
2020-12-21 16:44:50 +11:00
|
|
|
/// <summary>
|
|
|
|
|
/// Subscribes to Umbraco events to ensure nucache remains consistent with the source data
|
|
|
|
|
/// </summary>
|
2021-04-20 12:17:11 +02:00
|
|
|
public class PublishedSnapshotServiceEventHandler :
|
|
|
|
|
IDisposable,
|
|
|
|
|
INotificationHandler<LanguageSavedNotification>,
|
|
|
|
|
INotificationHandler<MemberDeletingNotification>,
|
|
|
|
|
INotificationHandler<ContentRefreshNotification>,
|
|
|
|
|
INotificationHandler<MediaRefreshNotification>,
|
|
|
|
|
INotificationHandler<MemberRefreshNotification>,
|
|
|
|
|
INotificationHandler<ContentTypeRefreshedNotification>,
|
|
|
|
|
INotificationHandler<MediaTypeRefreshedNotification>,
|
|
|
|
|
INotificationHandler<MemberTypeRefreshedNotification>,
|
|
|
|
|
INotificationHandler<ScopedEntityRemoveNotification>
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
private readonly IRuntimeState _runtime;
|
|
|
|
|
private bool _disposedValue;
|
|
|
|
|
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
|
|
|
|
private readonly INuCacheContentService _publishedContentService;
|
2021-04-20 12:17:11 +02:00
|
|
|
private readonly IContentService _contentService;
|
|
|
|
|
private readonly IMediaService _mediaService;
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2020-12-21 16:44:50 +11:00
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="PublishedSnapshotServiceEventHandler"/> class.
|
|
|
|
|
/// </summary>
|
2020-12-09 22:43:49 +11:00
|
|
|
public PublishedSnapshotServiceEventHandler(
|
|
|
|
|
IRuntimeState runtime,
|
|
|
|
|
IPublishedSnapshotService publishedSnapshotService,
|
2021-04-20 12:17:11 +02:00
|
|
|
INuCacheContentService publishedContentService,
|
|
|
|
|
IContentService contentService,
|
|
|
|
|
IMediaService mediaService)
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
_runtime = runtime;
|
|
|
|
|
_publishedSnapshotService = publishedSnapshotService;
|
|
|
|
|
_publishedContentService = publishedContentService;
|
2021-04-20 12:17:11 +02:00
|
|
|
_contentService = contentService;
|
|
|
|
|
_mediaService = mediaService;
|
2020-12-09 22:43:49 +11:00
|
|
|
}
|
|
|
|
|
|
2020-12-21 16:44:50 +11:00
|
|
|
/// <summary>
|
|
|
|
|
/// Binds to the Umbraco events
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Returns true if binding occurred</returns>
|
2020-12-22 12:33:00 +11:00
|
|
|
public bool Initialize()
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
// however, the cache is NOT available until we are configured, because loading
|
|
|
|
|
// content (and content types) from database cannot be consistent (see notes in "Handle
|
|
|
|
|
// Notifications" region), so
|
|
|
|
|
// - notifications will be ignored
|
|
|
|
|
// - trying to obtain a published snapshot from the service will throw
|
|
|
|
|
if (_runtime.Level != RuntimeLevel.Run)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// note: if the service is not ready, ie _isReady is false, then we still handle repository events,
|
|
|
|
|
// because we can, we do not need a working published snapshot to do it - the only reason why it could cause an
|
|
|
|
|
// issue is if the database table is not ready, but that should be prevented by migrations.
|
|
|
|
|
|
|
|
|
|
// we need them to be "repository" events ie to trigger from within the repository transaction,
|
|
|
|
|
// because they need to be consistent with the content that is being refreshed/removed - and that
|
|
|
|
|
// should be guaranteed by a DB transaction
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(ScopedEntityRemoveNotification notification) =>
|
|
|
|
|
_publishedContentService.DeleteContentItem(notification.Entity);
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(MemberDeletingNotification notification) => _publishedContentService.DeleteContentItems(notification.DeletedEntities);
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(MemberRefreshNotification notification) => _publishedContentService.RefreshEntity(notification.Entity);
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(MediaRefreshNotification notification) => _publishedContentService.RefreshEntity(notification.Entity);
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(ContentRefreshNotification notification) => _publishedContentService.RefreshContent(notification.Entity);
|
2020-12-09 22:43:49 +11:00
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(ContentTypeRefreshedNotification notification)
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
const ContentTypeChangeTypes types // only for those that have been refreshed
|
|
|
|
|
= ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther;
|
2021-04-20 12:17:11 +02:00
|
|
|
var contentTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray();
|
2020-12-09 22:43:49 +11:00
|
|
|
if (contentTypeIds.Any())
|
|
|
|
|
{
|
|
|
|
|
_publishedSnapshotService.Rebuild(contentTypeIds: contentTypeIds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(MediaTypeRefreshedNotification notification)
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
const ContentTypeChangeTypes types // only for those that have been refreshed
|
|
|
|
|
= ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther;
|
2021-04-20 12:17:11 +02:00
|
|
|
var mediaTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray();
|
2020-12-09 22:43:49 +11:00
|
|
|
if (mediaTypeIds.Any())
|
|
|
|
|
{
|
|
|
|
|
_publishedSnapshotService.Rebuild(mediaTypeIds: mediaTypeIds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-20 12:17:11 +02:00
|
|
|
public void Handle(MemberTypeRefreshedNotification notification)
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
const ContentTypeChangeTypes types // only for those that have been refreshed
|
|
|
|
|
= ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther;
|
2021-04-20 12:17:11 +02:00
|
|
|
var memberTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray();
|
2020-12-09 22:43:49 +11:00
|
|
|
if (memberTypeIds.Any())
|
|
|
|
|
{
|
|
|
|
|
_publishedSnapshotService.Rebuild(memberTypeIds: memberTypeIds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 14:51:00 +01:00
|
|
|
// TODO: This should be a cache refresher call!
|
2020-12-09 22:43:49 +11:00
|
|
|
/// <summary>
|
2020-12-22 13:49:26 +11:00
|
|
|
/// If a <see cref="ILanguage"/> is ever saved with a different culture, we need to rebuild all of the content nucache database table
|
2020-12-09 22:43:49 +11:00
|
|
|
/// </summary>
|
2021-03-25 14:51:00 +01:00
|
|
|
public void Handle(LanguageSavedNotification notification)
|
2020-12-09 22:43:49 +11:00
|
|
|
{
|
|
|
|
|
// culture changed on an existing language
|
2021-03-25 14:51:00 +01:00
|
|
|
var cultureChanged = notification.SavedEntities.Any(x => !x.WasPropertyDirty(nameof(ILanguage.Id)) && x.WasPropertyDirty(nameof(ILanguage.IsoCode)));
|
2020-12-09 22:43:49 +11:00
|
|
|
if (cultureChanged)
|
|
|
|
|
{
|
2020-12-22 13:49:26 +11:00
|
|
|
// Rebuild all content for all content types
|
2020-12-09 22:43:49 +11:00
|
|
|
_publishedSnapshotService.Rebuild(contentTypeIds: Array.Empty<int>());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
if (!_disposedValue)
|
|
|
|
|
{
|
|
|
|
|
_disposedValue = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
|
|
|
Dispose(disposing: true);
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|