using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; using Umbraco.Web.Services; namespace Umbraco.Web.Cache { /// /// Default implementation. /// public partial class DistributedCacheBinder { 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.Info("Initializing Umbraco internal event handlers for cache refreshing."); //// bind to application tree events //Bind(() => ApplicationTreeService.Deleted += ApplicationTreeService_Deleted, // () => ApplicationTreeService.Deleted -= ApplicationTreeService_Deleted); //Bind(() => ApplicationTreeService.Updated += ApplicationTreeService_Updated, // () => ApplicationTreeService.Updated -= ApplicationTreeService_Updated); //Bind(() => ApplicationTreeService.New += ApplicationTreeService_New, // () => ApplicationTreeService.New -= ApplicationTreeService_New); //// bind to application events //Bind(() => SectionService.Deleted += SectionService_Deleted, // () => SectionService.Deleted -= SectionService_Deleted); //Bind(() => SectionService.New += SectionService_New, // () => SectionService.New -= SectionService_New); // bind to user and user group events Bind(() => UserService.SavedUserGroup += UserService_SavedUserGroup, () => UserService.SavedUserGroup -= UserService_SavedUserGroup); Bind(() => UserService.DeletedUserGroup += UserService_DeletedUserGroup, () => UserService.DeletedUserGroup -= UserService_DeletedUserGroup); Bind(() => UserService.SavedUser += UserService_SavedUser, () => UserService.SavedUser -= UserService_SavedUser); Bind(() => UserService.DeletedUser += UserService_DeletedUser, () => UserService.DeletedUser -= UserService_DeletedUser); Bind(() => UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned, () => UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned); // bind to dictionary events Bind(() => LocalizationService.DeletedDictionaryItem += LocalizationService_DeletedDictionaryItem, () => LocalizationService.DeletedDictionaryItem -= LocalizationService_DeletedDictionaryItem); Bind(() => LocalizationService.SavedDictionaryItem += LocalizationService_SavedDictionaryItem, () => LocalizationService.SavedDictionaryItem -= LocalizationService_SavedDictionaryItem); // bind to data type events Bind(() => DataTypeService.Deleted += DataTypeService_Deleted, () => DataTypeService.Deleted -= DataTypeService_Deleted); Bind(() => DataTypeService.Saved += DataTypeService_Saved, () => DataTypeService.Saved -= DataTypeService_Saved); // bind to stylesheet events // fixme why not in v8? Bind(() => FileService.SavedStylesheet += FileService_SavedStylesheet, () => FileService.SavedStylesheet -= FileService_SavedStylesheet); Bind(() => FileService.DeletedStylesheet += FileService_DeletedStylesheet, () => FileService.DeletedStylesheet -= FileService_DeletedStylesheet); // bind to domain events Bind(() => DomainService.Saved += DomainService_Saved, () => DomainService.Saved -= DomainService_Saved); Bind(() => DomainService.Deleted += DomainService_Deleted, () => DomainService.Deleted -= DomainService_Deleted); // bind to language events Bind(() => LocalizationService.SavedLanguage += LocalizationService_SavedLanguage, () => LocalizationService.SavedLanguage -= LocalizationService_SavedLanguage); Bind(() => LocalizationService.DeletedLanguage += LocalizationService_DeletedLanguage, () => LocalizationService.DeletedLanguage -= LocalizationService_DeletedLanguage); // 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 member events Bind(() => MemberService.Saved += MemberService_Saved, () => MemberService.Saved -= MemberService_Saved); Bind(() => MemberService.Deleted += MemberService_Deleted, () => MemberService.Deleted -= MemberService_Deleted); Bind(() => MemberGroupService.Saved += MemberGroupService_Saved, () => MemberGroupService.Saved -= MemberGroupService_Saved); Bind(() => MemberGroupService.Deleted += MemberGroupService_Deleted, () => MemberGroupService.Deleted -= MemberGroupService_Deleted); // bind to media events - handles all media changes Bind(() => MediaService.TreeChanged += MediaService_Changed, () => MediaService.TreeChanged -= MediaService_Changed); // bind to content events Bind(() => ContentService.Saved += ContentService_Saved, // needed for permissions () => ContentService.Saved -= ContentService_Saved); Bind(() => ContentService.Copied += ContentService_Copied, // needed for permissions () => ContentService.Copied -= ContentService_Copied); 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 public access events Bind(() => PublicAccessService.Saved += PublicAccessService_Saved, () => PublicAccessService.Saved -= PublicAccessService_Saved); Bind(() => PublicAccessService.Deleted += PublicAccessService_Deleted, () => PublicAccessService.Deleted -= PublicAccessService_Deleted); // 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 private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs e) { _distributedCache.RefreshPublicAccess(); } private void PublicAccessService_Deleted(IPublicAccessService sender, DeleteEventArgs e) { _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) { } /// /// Handles cache refreshing for when content is saved (not published) /// /// /// /// /// When an entity is saved we need to notify other servers about the change in order for the Examine indexes to /// stay up-to-date for unpublished content. /// private void ContentService_Saved(IContentService sender, SaveEventArgs e) { } private void ContentService_TreeChanged(IContentService sender, TreeChange.EventArgs args) { _distributedCache.RefreshContentCache(args.Changes.ToArray()); } // fixme our weird events handling wants this for now private void ContentService_Deleted(IContentService sender, DeleteEventArgs e) { } private void ContentService_Moved(IContentService sender, MoveEventArgs e) { } private void ContentService_Trashed(IContentService sender, MoveEventArgs e) { } private void ContentService_EmptiedRecycleBin(IContentService sender, RecycleBinEventArgs e) { } private void ContentService_Published(IContentService sender, PublishEventArgs e) { } private void ContentService_Unpublished(IContentService sender, PublishEventArgs e) { } //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 ApplicationTreeService //private void ApplicationTreeService_New(ApplicationTree sender, EventArgs e) //{ // _distributedCache.RefreshAllApplicationTreeCache(); //} //private void ApplicationTreeService_Updated(ApplicationTree sender, EventArgs e) //{ // _distributedCache.RefreshAllApplicationTreeCache(); //} //private void ApplicationTreeService_Deleted(ApplicationTree sender, EventArgs e) //{ // _distributedCache.RefreshAllApplicationTreeCache(); //} //#endregion //#region Application event handlers //private void SectionService_New(ISectionService sender, EventArgs e) //{ // _distributedCache.RefreshAllApplicationCache(); //} //private void SectionService_Deleted(ISectionService sender, EventArgs e) //{ // _distributedCache.RefreshAllApplicationCache(); //} //#endregion #region LocalizationService / Dictionary private void LocalizationService_SavedDictionaryItem(ILocalizationService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshDictionaryCache(entity.Id); } private void LocalizationService_DeletedDictionaryItem(ILocalizationService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveDictionaryCache(entity.Id); } #endregion #region DataTypeService private void DataTypeService_Saved(IDataTypeService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshDataTypeCache(entity); } private void DataTypeService_Deleted(IDataTypeService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveDataTypeCache(entity); } #endregion #region DomainService private void DomainService_Saved(IDomainService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshDomainCache(entity); } private void DomainService_Deleted(IDomainService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveDomainCache(entity); } #endregion #region LocalizationService / Language /// /// Fires when a langauge is deleted /// /// /// private void LocalizationService_DeletedLanguage(ILocalizationService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveLanguageCache(entity); } /// /// Fires when a langauge is saved /// /// /// private void LocalizationService_SavedLanguage(ILocalizationService sender, SaveEventArgs e) { foreach (var entity in e.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()); } // fixme 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 // fixme STATIC?? private void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs e) { //TODO: Not sure if we need this yet depends if we start caching permissions //var groupIds = e.SavedEntities.Select(x => x.UserGroupId).Distinct(); //foreach (var groupId in groupIds) //{ // DistributedCache.Instance.RefreshUserGroupPermissionsCache(groupId); //} } private void UserService_SavedUser(IUserService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshUserCache(entity.Id); } private void UserService_DeletedUser(IUserService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) _distributedCache.RemoveUserCache(entity.Id); } private void UserService_SavedUserGroup(IUserService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) _distributedCache.RefreshUserGroupCache(entity.UserGroup.Id); } private void UserService_DeletedUserGroup(IUserService sender, DeleteEventArgs e) { foreach (var entity in e.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); } // fixme 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_Changed(IMediaService sender, TreeChange.EventArgs args) { _distributedCache.RefreshMediaCache(args.Changes.ToArray()); } // fixme our weird events handling wants this for now private void MediaService_Saved(IMediaService sender, SaveEventArgs e) { } private void MediaService_Deleted(IMediaService sender, DeleteEventArgs e) { } private void MediaService_Moved(IMediaService sender, MoveEventArgs e) { } private void MediaService_Trashed(IMediaService sender, MoveEventArgs e) { } private void MediaService_EmptiedRecycleBin(IMediaService sender, RecycleBinEventArgs e) { } #endregion #region MemberService private void MemberService_Deleted(IMemberService sender, DeleteEventArgs e) { _distributedCache.RemoveMemberCache(e.DeletedEntities.ToArray()); } private void MemberService_Saved(IMemberService sender, SaveEventArgs e) { _distributedCache.RefreshMemberCache(e.SavedEntities.ToArray()); } #endregion #region MemberGroupService private void MemberGroupService_Deleted(IMemberGroupService sender, DeleteEventArgs e) { foreach (var m in e.DeletedEntities.ToArray()) { _distributedCache.RemoveMemberGroupCache(m.Id); } } private void MemberGroupService_Saved(IMemberGroupService sender, SaveEventArgs e) { foreach (var m in e.SavedEntities.ToArray()) { _distributedCache.RemoveMemberGroupCache(m.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 } }