// Copyright (c) Umbraco. // See LICENSE for more details. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; using Umbraco.Cms.Core.Services.Implement; using Umbraco.Cms.Infrastructure.Services.Notifications; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache { /// /// Default implementation. /// public partial class DistributedCacheBinder : INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler { private List _unbinders; private void Bind(Action binder, Action unbinder) { // bind now binder(); // and register unbinder for later, if needed _unbinders?.Add(unbinder); } /// public void UnbindEvents() { if (_unbinders == null) throw new NotSupportedException(); foreach (var unbinder in _unbinders) unbinder(); _unbinders = null; } /// public void BindEvents(bool supportUnbinding = false) { if (supportUnbinding) _unbinders = new List(); _logger.LogInformation("Initializing Umbraco internal event handlers for cache refreshing."); // bind to stylesheet events Bind(() => FileService.SavedStylesheet += FileService_SavedStylesheet, () => FileService.SavedStylesheet -= FileService_SavedStylesheet); Bind(() => FileService.DeletedStylesheet += FileService_DeletedStylesheet, () => FileService.DeletedStylesheet -= FileService_DeletedStylesheet); // bind to content type events Bind(() => ContentTypeService.Changed += ContentTypeService_Changed, () => ContentTypeService.Changed -= ContentTypeService_Changed); Bind(() => MediaTypeService.Changed += MediaTypeService_Changed, () => MediaTypeService.Changed -= MediaTypeService_Changed); Bind(() => MemberTypeService.Changed += MemberTypeService_Changed, () => MemberTypeService.Changed -= MemberTypeService_Changed); // bind to template events Bind(() => FileService.SavedTemplate += FileService_SavedTemplate, () => FileService.SavedTemplate -= FileService_SavedTemplate); Bind(() => FileService.DeletedTemplate += FileService_DeletedTemplate, () => FileService.DeletedTemplate -= FileService_DeletedTemplate); // bind to macro events Bind(() => MacroService.Saved += MacroService_Saved, () => MacroService.Saved -= MacroService_Saved); Bind(() => MacroService.Deleted += MacroService_Deleted, () => MacroService.Deleted -= MacroService_Deleted); // bind to media events - handles all media changes Bind(() => MediaService.TreeChanged += MediaService_TreeChanged, () => MediaService.TreeChanged -= MediaService_TreeChanged); // bind to content events Bind(() => ContentService.TreeChanged += ContentService_TreeChanged,// handles all content changes () => ContentService.TreeChanged -= ContentService_TreeChanged); // TreeChanged should also deal with this //Bind(() => ContentService.SavedBlueprint += ContentService_SavedBlueprint, // () => ContentService.SavedBlueprint -= ContentService_SavedBlueprint); //Bind(() => ContentService.DeletedBlueprint += ContentService_DeletedBlueprint, // () => ContentService.DeletedBlueprint -= ContentService_DeletedBlueprint); // bind to relation type events Bind(() => RelationService.SavedRelationType += RelationService_SavedRelationType, () => RelationService.SavedRelationType -= RelationService_SavedRelationType); Bind(() => RelationService.DeletedRelationType += RelationService_DeletedRelationType, () => RelationService.DeletedRelationType -= RelationService_DeletedRelationType); } #region PublicAccessService public void Handle(PublicAccessEntrySavedNotification notification) { _distributedCache.RefreshPublicAccess(); } public void Handle(PublicAccessEntryDeletedNotification notification) { _distributedCache.RefreshPublicAccess(); } #endregion #region ContentService /// /// Handles cache refreshing for when content is copied /// /// /// /// /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the /// case then we need to clear all user permissions cache. /// private void ContentService_Copied(IContentService sender, CopyEventArgs e) { } private void ContentService_TreeChanged(IContentService sender, TreeChange.EventArgs args) { _distributedCache.RefreshContentCache(args.Changes.ToArray()); } //private void ContentService_SavedBlueprint(IContentService sender, SaveEventArgs e) //{ // _distributedCache.RefreshUnpublishedPageCache(e.SavedEntities.ToArray()); //} //private void ContentService_DeletedBlueprint(IContentService sender, DeleteEventArgs e) //{ // _distributedCache.RemoveUnpublishedPageCache(e.DeletedEntities.ToArray()); //} #endregion #region LocalizationService / Dictionary public void Handle(DictionaryItemSavedNotification notification) { foreach (IDictionaryItem entity in notification.SavedEntities) { _distributedCache.RefreshDictionaryCache(entity.Id); } } public void Handle(DictionaryItemDeletedNotification notification) { foreach (IDictionaryItem entity in notification.DeletedEntities) { _distributedCache.RemoveDictionaryCache(entity.Id); } } #endregion #region DataTypeService public void Handle(DataTypeSavedNotification notification) { foreach (IDataType entity in notification.SavedEntities) { _distributedCache.RefreshDataTypeCache(entity); } } public void Handle(DataTypeDeletedNotification notification) { foreach (IDataType entity in notification.DeletedEntities) { _distributedCache.RemoveDataTypeCache(entity); } } #endregion #region DomainService public void Handle(DomainSavedNotification notification) { foreach (IDomain entity in notification.SavedEntities) { _distributedCache.RefreshDomainCache(entity); } } public void Handle(DomainDeletedNotification notification) { foreach (IDomain entity in notification.DeletedEntities) { _distributedCache.RemoveDomainCache(entity); } } #endregion #region LocalizationService / Language /// /// Fires when a language is deleted /// /// public void Handle(LanguageDeletedNotification notification) { foreach (ILanguage entity in notification.DeletedEntities) { _distributedCache.RemoveLanguageCache(entity); } } /// /// Fires when a language is saved /// /// public void Handle(LanguageSavedNotification notification) { foreach (ILanguage entity in notification.SavedEntities) { _distributedCache.RefreshLanguageCache(entity); } } #endregion #region Content|Media|MemberTypeService private void ContentTypeService_Changed(IContentTypeService sender, ContentTypeChange.EventArgs args) { _distributedCache.RefreshContentTypeCache(args.Changes.ToArray()); } private void MediaTypeService_Changed(IMediaTypeService sender, ContentTypeChange.EventArgs args) { _distributedCache.RefreshContentTypeCache(args.Changes.ToArray()); } private void MemberTypeService_Changed(IMemberTypeService sender, ContentTypeChange.EventArgs args) { _distributedCache.RefreshContentTypeCache(args.Changes.ToArray()); } // TODO: our weird events handling wants this for now private void ContentTypeService_Saved(IContentTypeService sender, SaveEventArgs args) { } private void MediaTypeService_Saved(IMediaTypeService sender, SaveEventArgs args) { } private void MemberTypeService_Saved(IMemberTypeService sender, SaveEventArgs args) { } private void ContentTypeService_Deleted(IContentTypeService sender, DeleteEventArgs args) { } private void MediaTypeService_Deleted(IMediaTypeService sender, DeleteEventArgs args) { } private void MemberTypeService_Deleted(IMemberTypeService sender, DeleteEventArgs args) { } #endregion #region UserService public void Handle(UserSavedNotification notification) { foreach (IUser entity in notification.SavedEntities) { _distributedCache.RefreshUserCache(entity.Id); } } public void Handle(UserDeletedNotification notification) { foreach (IUser entity in notification.DeletedEntities) { _distributedCache.RemoveUserCache(entity.Id); } } public void Handle(UserGroupWithUsersSavedNotification notification) { foreach (UserGroupWithUsers entity in notification.SavedEntities) { _distributedCache.RefreshUserGroupCache(entity.UserGroup.Id); } } public void Handle(UserGroupDeletedNotification notification) { foreach (IUserGroup entity in notification.DeletedEntities) { _distributedCache.RemoveUserGroupCache(entity.Id); } } #endregion #region FileService /// /// Removes cache for template /// /// /// private void FileService_DeletedTemplate(IFileService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveTemplateCache(entity.Id); } /// /// Refresh cache for template /// /// /// private void FileService_SavedTemplate(IFileService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshTemplateCache(entity.Id); } // TODO: our weird events handling wants this for now private void FileService_DeletedStylesheet(IFileService sender, DeleteEventArgs e) { } private void FileService_SavedStylesheet(IFileService sender, SaveEventArgs e) { } #endregion #region MacroService private void MacroService_Deleted(IMacroService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveMacroCache(entity); } private void MacroService_Saved(IMacroService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshMacroCache(entity); } #endregion #region MediaService private void MediaService_TreeChanged(IMediaService sender, TreeChange.EventArgs args) { _distributedCache.RefreshMediaCache(args.Changes.ToArray()); } #endregion #region MemberService public void Handle(MemberDeletedNotification notification) { _distributedCache.RemoveMemberCache(notification.DeletedEntities.ToArray()); } public void Handle(MemberSavedNotification notification) { _distributedCache.RefreshMemberCache(notification.SavedEntities.ToArray()); } #endregion #region MemberGroupService /// /// Fires when a member group is deleted /// /// public void Handle(MemberGroupDeletedNotification notification) { foreach (IMemberGroup entity in notification.DeletedEntities) { _distributedCache.RemoveMemberGroupCache(entity.Id); } } /// /// Fires when a member group is saved /// /// public void Handle(MemberGroupSavedNotification notification) { foreach (IMemberGroup entity in notification.SavedEntities) { _distributedCache.RemoveMemberGroupCache(entity.Id); } } #endregion #region RelationType private void RelationService_SavedRelationType(IRelationService sender, SaveEventArgs args) { var dc = _distributedCache; foreach (var e in args.SavedEntities) dc.RefreshRelationTypeCache(e.Id); } private void RelationService_DeletedRelationType(IRelationService sender, DeleteEventArgs args) { var dc = _distributedCache; foreach (var e in args.DeletedEntities) dc.RemoveRelationTypeCache(e.Id); } #endregion } }