/// <summary> /// When the page/content cache is refreshed, we'll check if any articulate root nodes were included in the refresh, if so we'll set a flag /// on the current request to rebuild the routes at the end of the request /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <remarks> /// This will also work for load balanced scenarios since this event executes on all servers /// </remarks> private void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs e) { switch (e.MessageType) { case MessageType.RefreshById: case MessageType.RemoveById: var item = _umbracoContextAccessor?.UmbracoContext?.ContentCache.GetById((int)e.MessageObject); if (item != null && item.ContentType.Alias.InvariantEquals("Articulate")) { //ensure routes are rebuilt _appCaches.RequestCache.GetCacheItem("articulate-refresh-routes", () => true); } break; case MessageType.RefreshByInstance: case MessageType.RemoveByInstance: var content = e.MessageObject as IContent; if (content == null) { return; } //TODO: There is a case when there are URL conflicts with Articulate data that when the other data is unpublished // we'd want to rebuild articulate routes, but not sure how to handle that and we don't want to rebuild everytime // something is unpublished if (content.ContentType.Alias.InvariantEquals("Articulate")) { //ensure routes are rebuilt _appCaches.RequestCache.GetCacheItem("articulate-refresh-routes", () => true); } break; } }
public override void Refresh(JsonPayload[] payloads) { // TODO: refactor // we should NOT directly clear caches here, but instead ask whatever class // is managing the cache to please clear that cache properly _contentTypeCommonRepository.ClearCache(); // always if (payloads.Any(x => x.ItemType == typeof(IContentType).Name)) { ClearAllIsolatedCacheByEntityType <IContent>(); ClearAllIsolatedCacheByEntityType <IContentType>(); } if (payloads.Any(x => x.ItemType == typeof(IMediaType).Name)) { ClearAllIsolatedCacheByEntityType <IMedia>(); ClearAllIsolatedCacheByEntityType <IMediaType>(); } if (payloads.Any(x => x.ItemType == typeof(IMemberType).Name)) { ClearAllIsolatedCacheByEntityType <IMember>(); ClearAllIsolatedCacheByEntityType <IMemberType>(); } foreach (var id in payloads.Select(x => x.Id)) { _idKeyMap.ClearCache(id); } if (payloads.Any(x => x.ItemType == typeof(IContentType).Name)) { // don't try to be clever - refresh all ContentCacheRefresher.RefreshContentTypes(AppCaches); } if (payloads.Any(x => x.ItemType == typeof(IMediaType).Name)) { // don't try to be clever - refresh all MediaCacheRefresher.RefreshMediaTypes(AppCaches); } if (payloads.Any(x => x.ItemType == typeof(IMemberType).Name)) { // don't try to be clever - refresh all MemberCacheRefresher.RefreshMemberTypes(AppCaches); } // refresh the models and cache _publishedModelFactory.WithSafeLiveFactoryReset(() => _publishedSnapshotService.Notify(payloads)); // now we can trigger the event base.Refresh(payloads); }
private void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (args.MessageType != MessageType.RefreshByPayload) { return; } if (!_fullTextConfig.IsFullTextIndexingEnabled()) { _logger.Debug <UpdateCacheOnPublish>("FullTextIndexing is not enabled"); return; } if (!_examineManager.TryGetIndex("ExternalIndex", out IIndex index)) { _logger.Error <UpdateCacheOnPublish>(new InvalidOperationException("No index found by name ExternalIndex")); return; } foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) { if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) { _cacheService.DeleteFromCache(payload.Id); } else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) { // just ignore that payload (Umbracos examine implementation does the same) } else // RefreshNode or RefreshBranch (maybe trashed) { _cacheService.AddCacheTask(payload.Id); // branch if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { const int pageSize = 500; var page = 0; var total = long.MaxValue; while (page * pageSize < total) { var descendants = _contentService.GetPagedDescendants(payload.Id, page++, pageSize, out total, //order by shallowest to deepest, this allows us to check it's published state without checking every item ordering: Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { _cacheService.AddCacheTask(descendant.Id); } } } } } }
private void HandleCacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (args.MessageType != MessageType.RefreshByPayload) { return; } var payloads = (ContentCacheRefresher.JsonPayload[])args.MessageObject; var hubContextInstance = _hubContext.Value; foreach (var payload in payloads) { var id = payload.Id; // keep it simple for now, ignore ChangeTypes hubContextInstance.Clients.All.refreshed(id); } }
/// <summary> /// Content cache was updated. /// </summary> private void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher contentCacheRefresher, CacheRefresherEventArgs e) { var kind = e.MessageType; if (kind == MessageType.RefreshById || kind == MessageType.RemoveById) { var id = e.MessageObject as int?; if (id.HasValue) { var node = ContentService.GetById(id.Value); if (node != null) { HandleChangedContent(new[] { node }); } } } }
/// <summary> /// When the page/content cache is refreshed, we'll check if any articulate root nodes were included in the refresh, if so we'll set a flag /// on the current request to rebuild the routes at the end of the request /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <remarks> /// This will also work for load balanced scenarios since this event executes on all servers /// </remarks> private void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs e) { switch (e.MessageType) { case MessageType.RefreshByPayload: //This is the standard case for content cache refresher foreach (var payload in (ContentCacheRefresher.JsonPayload[])e.MessageObject) { if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.Remove | TreeChangeTypes.RefreshBranch | TreeChangeTypes.RefreshNode)) { RefreshById(payload.Id); } } break; case MessageType.RefreshById: case MessageType.RemoveById: RefreshById((int)e.MessageObject); break; case MessageType.RefreshByInstance: case MessageType.RemoveByInstance: var content = e.MessageObject as IContent; if (content == null) { return; } //TODO: There is a case when there are URL conflicts with Articulate data that when the other data is unpublished // we'd want to rebuild articulate routes, but not sure how to handle that and we don't want to rebuild everytime // something is unpublished if (content.ContentType.Alias.InvariantEquals("Articulate")) { //ensure routes are rebuilt _appCaches.RequestCache.GetCacheItem("articulate-refresh-routes", () => true); } break; } }
/// <summary> /// When the page/content cache is refreshed, we'll check if any articulate root nodes were included in the refresh, if so we'll set a flag /// on the current request to rebuild the routes at the end of the request /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <remarks> /// This will also work for load balanced scenarios since this event executes on all servers /// </remarks> private void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs e) { switch (e.MessageType) { case MessageType.RefreshByPayload: //This is the standard case for content cache refresher foreach (var payload in (ContentCacheRefresher.JsonPayload[])e.MessageObject) { if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.Remove | TreeChangeTypes.RefreshBranch | TreeChangeTypes.RefreshNode)) { RefreshById(payload.Id, payload.ChangeTypes); } } break; case MessageType.RefreshById: case MessageType.RemoveById: RefreshById((int)e.MessageObject, TreeChangeTypes.Remove); break; case MessageType.RefreshByInstance: case MessageType.RemoveByInstance: var content = e.MessageObject as IContent; if (content == null) { return; } if (content.ContentType.Alias.InvariantEquals(ArticulateContentTypeAlias)) { //ensure routes are rebuilt _appCaches.RequestCache.GetCacheItem(RefreshRoutesToken, () => true); } break; } }
/// <summary> /// Updates indexes based on content changes /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) { return; } if (args.MessageType != MessageType.RefreshByPayload) { throw new NotSupportedException(); } var contentService = _services.ContentService; foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) { if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) { // delete content entirely (with descendants) // false: remove entirely from all indexes DeleteIndexForEntity(payload.Id, false); } else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) { // ExamineEvents does not support RefreshAll // just ignore that payload // so what?! // TODO: Rebuild the index at this point? } else // RefreshNode or RefreshBranch (maybe trashed) { // don't try to be too clever - refresh entirely // there has to be race conditions in there ;-( var content = contentService.GetById(payload.Id); if (content == null) { // gone fishing, remove entirely from all indexes (with descendants) DeleteIndexForEntity(payload.Id, false); continue; } IContent published = null; if (content.Published && contentService.IsPathPublished(content)) { published = content; } if (published == null) { DeleteIndexForEntity(payload.Id, true); } // just that content ReIndexForContent(content, published != null); // branch if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { var masked = published == null ? null : new List <int>(); const int pageSize = 500; var page = 0; var total = long.MaxValue; while (page * pageSize < total) { var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, //order by shallowest to deepest, this allows us to check it's published state without checking every item ordering: Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { published = null; if (masked != null) // else everything is masked { if (masked.Contains(descendant.ParentId) || !descendant.Published) { masked.Add(descendant.Id); } else { published = descendant; } } ReIndexForContent(descendant, published != null); } } } } // NOTE // // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes // care of also deleting the descendants // // ReIndexForContent is NOT taking care of descendants so we have to reload everything // again in order to process the branch - we COULD improve that by just reloading the // XML from database instead of reloading content & re-serializing! // // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" } }
private void ContentCacheUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs e) { if (!IsConfigured()) { return; } var jsonPayloads = e.MessageObject as ContentCacheRefresher.JsonPayload[]; if (jsonPayloads == null || !jsonPayloads.Any()) { return; } var jobs = new List <EnterspeedJob>(); using (var context = _umbracoContextFactory.EnsureUmbracoContext()) { var umb = context.UmbracoContext; foreach (var payload in jsonPayloads) { var node = umb.Content.GetById(payload.Id); var savedNode = umb.Content.GetById(true, payload.Id); if (node == null || savedNode == null) { continue; } var cultures = node.ContentType.VariesByCulture() ? node.Cultures.Keys : new List <string> { GetDefaultCulture(context) }; List <IPublishedContent> descendants = null; foreach (var culture in cultures) { var publishedUpdateDate = node.CultureDate(culture); var savedUpdateDate = savedNode.CultureDate(culture); if (savedUpdateDate > publishedUpdateDate) { // This means that the nodes was only saved, so we skip creating any jobs for this node and culture continue; } var now = DateTime.UtcNow; jobs.Add(new EnterspeedJob { ContentId = node.Id, Culture = culture, JobType = EnterspeedJobType.Publish, State = EnterspeedJobState.Pending, CreatedAt = now, UpdatedAt = now, }); if (payload.ChangeTypes == TreeChangeTypes.RefreshBranch) { if (descendants == null) { descendants = node.Descendants("*").ToList(); } foreach (var descendant in descendants) { var descendantCultures = descendant.ContentType.VariesByCulture() ? descendant.Cultures.Keys : new List <string> { GetDefaultCulture(context) }; foreach (var descendantCulture in descendantCultures) { jobs.Add(new EnterspeedJob { ContentId = descendant.Id, Culture = descendantCulture, JobType = EnterspeedJobType.Publish, State = EnterspeedJobState.Pending, CreatedAt = now, UpdatedAt = now, }); } } } } } } EnqueueJobs(jobs); }
private void SyncZeroValuePaymentProviderContinueUrl(ContentCacheRefresher sender, CacheRefresherEventArgs e) { var payloads = e.MessageObject as ContentCacheRefresher.JsonPayload[]; if (payloads == null) { return; } using (var umbNew = _umbracoContextFactory.EnsureUmbracoContext()) { foreach (var payload in payloads) { if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode)) { // Single node refresh var node = umbNew.UmbracoContext.Content.GetById(payload.Id); if (node != null && IsConfirmationPageType(node)) { SyncZeroValuePaymentProviderContinueUrl(node); } } else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) { // Branch refresh var rootNode = umbNew.UmbracoContext.Content.GetById(payload.Id); if (rootNode != null) { var nodeType = umbNew.UmbracoContext.Content.GetContentType(VendrCheckoutConstants.ContentTypes.Aliases.CheckoutStepPage); if (nodeType == null) { continue; } var nodes = umbNew.UmbracoContext.Content.GetByContentType(nodeType); foreach (var node in nodes?.Where(x => IsConfirmationPageType(x) && x.Path.StartsWith(rootNode.Path))) { SyncZeroValuePaymentProviderContinueUrl(node); } } } else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) { // All refresh var nodeType = umbNew.UmbracoContext.Content.GetContentType(VendrCheckoutConstants.ContentTypes.Aliases.CheckoutStepPage); if (nodeType == null) { continue; } var nodes = umbNew.UmbracoContext.Content.GetByContentType(nodeType); foreach (var node in nodes?.Where(x => IsConfirmationPageType(x))) { SyncZeroValuePaymentProviderContinueUrl(node); } } } } }
private void ContentCacheRefresher_CacheUpdated(ContentCacheRefresher sender, Umbraco.Core.Cache.CacheRefresherEventArgs e) { Cache.Instance.RemoveByPrefix(typeof(CmsServiceCachedProxy).ToString()); }