diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 8b3440d435..e89cb6e2af 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -25,6 +25,20 @@ namespace Umbraco.Core.Models
{
#region IContent
+ ///
+ /// Returns true if this entity was just published as part of a recent save operation (i.e. it wasn't previously published)
+ ///
+ ///
+ ///
+ ///
+ /// This is helpful for determining if the published event will execute during the saved event for a content item.
+ ///
+ internal static bool JustPublished(this IContent entity)
+ {
+ var dirty = (IRememberBeingDirty)entity;
+ return dirty.WasPropertyDirty("Published") && entity.Published;
+ }
+
///
/// Determines if a new version should be created
///
diff --git a/src/Umbraco.Core/Models/EntityExtensions.cs b/src/Umbraco.Core/Models/EntityExtensions.cs
index f461c4007c..6daf99a58d 100644
--- a/src/Umbraco.Core/Models/EntityExtensions.cs
+++ b/src/Umbraco.Core/Models/EntityExtensions.cs
@@ -23,5 +23,5 @@ namespace Umbraco.Core.Models
var dirty = (IRememberBeingDirty)entity;
return dirty.WasPropertyDirty("Id");
}
- }
+ }
}
diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
index a9b01fb6e5..6f8e708219 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
@@ -131,19 +131,30 @@ namespace Umbraco.Web.Cache
MediaService.Moving += MediaServiceMoving;
MediaService.Trashing += MediaServiceTrashing;
- ContentService.Created += ContentServiceCreated;
+ //Bind to content events - this is for unpublished content syncing across servers (primarily for examine)
+
+ ContentService.Saved += ContentServiceSaved;
+ ContentService.Deleted += ContentServiceDeleted;
ContentService.Copied += ContentServiceCopied;
+ //NOTE: we do not listen for the trashed event because there is no cache to update for content in that case since
+ // the unpublishing event handles that, and for examine with unpublished content indexes, we want to keep that data
+ // in the index, it's not until it's complete deleted that we want to remove it.
}
+
+
#region Content service event handlers
///
- /// 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.
+ /// Handles cache refreshgi for when content is copied
///
///
///
- static void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs e)
+ ///
+ /// 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.
+ ///
+ static void ContentServiceCopied(IContentService sender, CopyEventArgs e)
{
//check if permissions have changed
var permissionsChanged = ((Content)e.Copy).WasPropertyDirty("PermissionsChanged");
@@ -151,23 +162,63 @@ namespace Umbraco.Web.Cache
{
DistributedCache.Instance.RefreshAllUserPermissionsCache();
}
+
+ //run the un-published cache refresher
+ DistributedCache.Instance.RefreshUnpublishedPageCache(e.Copy);
}
///
- /// When an entity is created 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.
+ /// Handles cache refreshing for when content is deleted (not unpublished)
///
///
///
- static void ContentServiceCreated(IContentService sender, Core.Events.NewEventArgs e)
+ static void ContentServiceDeleted(IContentService sender, DeleteEventArgs e)
{
- //check if permissions have changed
- var permissionsChanged = ((Content)e.Entity).WasPropertyDirty("PermissionsChanged");
- if (permissionsChanged)
+ DistributedCache.Instance.RemoveUnpublishedPageCache(e.DeletedEntities.ToArray());
+ }
+
+ ///
+ /// 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.
+ ///
+ /// When an entity is created 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.
+ ///
+ static void ContentServiceSaved(IContentService sender, SaveEventArgs e)
+ {
+ var clearUserPermissions = false;
+ e.SavedEntities.ForEach(x =>
+ {
+ //check if it is new
+ if (x.IsNewEntity())
+ {
+ //check if permissions have changed
+ var permissionsChanged = ((Content)x).WasPropertyDirty("PermissionsChanged");
+ if (permissionsChanged)
+ {
+ clearUserPermissions = true;
+ }
+ }
+ });
+
+ if (clearUserPermissions)
{
DistributedCache.Instance.RefreshAllUserPermissionsCache();
}
- }
+
+ //filter out the entities that have only been saved (not newly published) since
+ // newly published ones will be synced with the published page cache refresher
+ var unpublished = e.SavedEntities.Where(x => x.JustPublished() == false);
+ //run the un-published cache refresher
+ DistributedCache.Instance.RefreshUnpublishedPageCache(unpublished.ToArray());
+ }
+
+
#endregion
#region ApplicationTree event handlers
@@ -451,12 +502,12 @@ namespace Umbraco.Web.Cache
InvalidateCacheForPermissionsChange(sender);
}
- void UserServiceSavedUser(IUserService sender, Core.Events.SaveEventArgs e)
+ static void UserServiceSavedUser(IUserService sender, SaveEventArgs e)
{
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserCache(x.Id));
}
- void UserServiceDeletedUser(IUserService sender, Core.Events.DeleteEventArgs e)
+ static void UserServiceDeletedUser(IUserService sender, DeleteEventArgs e)
{
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserCache(x.Id));
}
@@ -547,7 +598,7 @@ namespace Umbraco.Web.Cache
#region Media event handlers
static void MediaServiceTrashing(IMediaService sender, Core.Events.MoveEventArgs e)
{
- DistributedCache.Instance.RemoveMediaCache(e.Entity);
+ DistributedCache.Instance.RemoveMediaCache(false, e.Entity);
}
static void MediaServiceMoving(IMediaService sender, Core.Events.MoveEventArgs e)
@@ -557,7 +608,7 @@ namespace Umbraco.Web.Cache
static void MediaServiceDeleting(IMediaService sender, Core.Events.DeleteEventArgs e)
{
- DistributedCache.Instance.RemoveMediaCache(e.DeletedEntities.ToArray());
+ DistributedCache.Instance.RemoveMediaCache(true, e.DeletedEntities.ToArray());
}
static void MediaServiceSaved(IMediaService sender, Core.Events.SaveEventArgs e)
diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs
index b66dd2174a..8fa1dec3b3 100644
--- a/src/Umbraco.Web/Cache/DistributedCache.cs
+++ b/src/Umbraco.Web/Cache/DistributedCache.cs
@@ -39,6 +39,7 @@ namespace Umbraco.Web.Cache
public const string ApplicationCacheRefresherId = "B15F34A1-BC1D-4F8B-8369-3222728AB4C8";
public const string TemplateRefresherId = "DD12B6A0-14B9-46e8-8800-C154F74047C8";
public const string PageCacheRefresherId = "27AB3022-3DFA-47b6-9119-5945BC88FD66";
+ public const string UnpublishedPageCacheRefresherId = "55698352-DFC5-4DBE-96BD-A4A0F6F77145";
public const string MemberCacheRefresherId = "E285DF34-ACDC-4226-AE32-C0CB5CF388DA";
public const string MemberGroupCacheRefresherId = "187F236B-BD21-4C85-8A7C-29FBA3D6C00C";
public const string MediaCacheRefresherId = "B29286DD-2D40-4DDB-B325-681226589FEC";
diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
index faede0bffc..e218537ef2 100644
--- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
+++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
@@ -123,8 +123,7 @@ namespace Umbraco.Web.Cache
}
#endregion
-
-
+
#region Data type cache
///
/// Refreshes the cache amongst servers for a data type
@@ -232,7 +231,28 @@ namespace Umbraco.Web.Cache
public static void RemovePageCache(this DistributedCache dc, int documentId)
{
dc.Remove(new Guid(DistributedCache.PageCacheRefresherId), documentId);
- }
+ }
+
+ ///
+ /// invokes the unpublished page cache refresher
+ ///
+ ///
+ ///
+ public static void RefreshUnpublishedPageCache(this DistributedCache dc, params IContent[] content)
+ {
+ dc.Refresh(new Guid(DistributedCache.UnpublishedPageCacheRefresherId), x => x.Id, content);
+ }
+
+ ///
+ /// invokes the unpublished page cache refresher
+ ///
+ ///
+ ///
+ public static void RemoveUnpublishedPageCache(this DistributedCache dc, params IContent[] content)
+ {
+ dc.Remove(new Guid(DistributedCache.UnpublishedPageCacheRefresherId), x => x.Id, content);
+ }
+
#endregion
#region Member cache
@@ -291,7 +311,7 @@ namespace Umbraco.Web.Cache
public static void RefreshMediaCache(this DistributedCache dc, params IMedia[] media)
{
dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
- MediaCacheRefresher.SerializeToJsonPayload(media));
+ MediaCacheRefresher.SerializeToJsonPayload(MediaCacheRefresher.OperationType.Saved, media));
}
///
@@ -304,6 +324,7 @@ namespace Umbraco.Web.Cache
/// to clear all of the cache but the media item will be removed before the other servers can
/// look it up. Only here for legacy purposes.
///
+ [Obsolete("Ensure to clear with other RemoveMediaCache overload")]
public static void RemoveMediaCache(this DistributedCache dc, int mediaId)
{
dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), mediaId);
@@ -313,11 +334,14 @@ namespace Umbraco.Web.Cache
/// Removes the cache amongst servers for media items
///
///
+ ///
///
- public static void RemoveMediaCache(this DistributedCache dc, params IMedia[] media)
+ public static void RemoveMediaCache(this DistributedCache dc, bool isPermanentlyDeleted, params IMedia[] media)
{
- dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
- MediaCacheRefresher.SerializeToJsonPayload(media));
+ dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
+ MediaCacheRefresher.SerializeToJsonPayload(
+ isPermanentlyDeleted ? MediaCacheRefresher.OperationType.Deleted : MediaCacheRefresher.OperationType.Trashed,
+ media));
}
#endregion
diff --git a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs
index 53abacc7a5..72a541b720 100644
--- a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Core.Cache;
@@ -19,13 +20,13 @@ namespace Umbraco.Web.Cache
public class MediaCacheRefresher : JsonCacheRefresherBase
{
#region Static helpers
-
+
///
/// Converts the json to a JsonPayload object
///
///
///
- private static JsonPayload[] DeserializeFromJsonPayload(string json)
+ internal static JsonPayload[] DeserializeFromJsonPayload(string json)
{
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.Deserialize(json);
@@ -35,12 +36,13 @@ namespace Umbraco.Web.Cache
///
/// Creates the custom Json payload used to refresh cache amongst the servers
///
+ ///
///
///
- internal static string SerializeToJsonPayload(params IMedia[] media)
+ internal static string SerializeToJsonPayload(OperationType operation, params IMedia[] media)
{
var serializer = new JavaScriptSerializer();
- var items = media.Select(FromMedia).ToArray();
+ var items = media.Select(x => FromMedia(x, operation)).ToArray();
var json = serializer.Serialize(items);
return json;
}
@@ -49,15 +51,17 @@ namespace Umbraco.Web.Cache
/// Converts a macro to a jsonPayload object
///
///
+ ///
///
- private static JsonPayload FromMedia(IMedia media)
+ internal static JsonPayload FromMedia(IMedia media, OperationType operation)
{
if (media == null) return null;
var payload = new JsonPayload
{
Id = media.Id,
- Path = media.Path
+ Path = media.Path,
+ Operation = operation
};
return payload;
}
@@ -66,10 +70,18 @@ namespace Umbraco.Web.Cache
#region Sub classes
- private class JsonPayload
+ internal enum OperationType
+ {
+ Saved,
+ Trashed,
+ Deleted
+ }
+
+ internal class JsonPayload
{
public string Path { get; set; }
public int Id { get; set; }
+ public OperationType Operation { get; set; }
}
#endregion
@@ -97,13 +109,15 @@ namespace Umbraco.Web.Cache
public override void Refresh(int id)
{
- ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
+ ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), OperationType.Saved));
base.Refresh(id);
}
public override void Remove(int id)
- {
- ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
+ {
+ ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id),
+ //NOTE: we'll just default to trashed for this one.
+ OperationType.Trashed));
base.Remove(id);
}
@@ -121,7 +135,7 @@ namespace Umbraco.Web.Cache
string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart));
// Also clear calls that only query this specific item!
- if (idPart == payload.Id.ToString())
+ if (idPart == payload.Id.ToString(CultureInfo.InvariantCulture))
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id));
diff --git a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs
new file mode 100644
index 0000000000..4413a61e9b
--- /dev/null
+++ b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs
@@ -0,0 +1,33 @@
+using System;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web.Cache
+{
+ ///
+ /// A cache refresher used for non-published content, this is primarily to notify Examine indexes to update
+ ///
+ public sealed class UnpublishedPageCacheRefresher : TypedCacheRefresherBase
+ {
+ protected override UnpublishedPageCacheRefresher Instance
+ {
+ get { return this; }
+ }
+
+ public override Guid UniqueIdentifier
+ {
+ get { return new Guid(DistributedCache.UnpublishedPageCacheRefresherId); }
+ }
+
+ public override string Name
+ {
+ get { return "Unpublished Page Refresher"; }
+ }
+
+ //NOTE: There is no functionality for this cache refresher, it is here simply to emit events on each server for which examine
+ // binds to. We could put the Examine index functionality in here but we've kept it all in the ExamineEvents class so that all of
+ // the logic is in one place. In the future we may put the examine logic in a cache refresher instead (that would make sense) but we'd
+ // want to get this done before making more cache refreshers:
+ // http://issues.umbraco.org/issue/U4-2633
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs
index 102038a7c0..0d4cb9cdeb 100644
--- a/src/Umbraco.Web/Search/ExamineEvents.cs
+++ b/src/Umbraco.Web/Search/ExamineEvents.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.Linq;
using System.Security;
using System.Xml;
@@ -7,9 +8,13 @@ using Examine;
using Examine.LuceneEngine;
using Lucene.Net.Documents;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Services;
+using Umbraco.Core.Sync;
+using Umbraco.Web.Cache;
using UmbracoExamine;
using umbraco;
using umbraco.BusinessLogic;
@@ -50,20 +55,12 @@ namespace Umbraco.Web.Search
if (registeredProviders == 0)
return;
- MediaService.Saved += MediaServiceSaved;
- MediaService.Deleted += MediaServiceDeleted;
- MediaService.Moved += MediaServiceMoved;
- MediaService.Trashed += MediaServiceTrashed;
-
- ContentService.Saved += ContentServiceSaved;
- ContentService.Deleted += ContentServiceDeleted;
- ContentService.Moved += ContentServiceMoved;
- ContentService.Trashed += ContentServiceTrashed;
-
- //These should only fire for providers that DONT have SupportUnpublishedContent set to true
- content.AfterUpdateDocumentCache += ContentAfterUpdateDocumentCache;
- content.AfterClearDocumentCache += ContentAfterClearDocumentCache;
-
+ //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part
+ // in a load balanced environment.
+ CacheRefresherBase.CacheUpdated += UnpublishedPageCacheRefresherCacheUpdated;
+ CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated;
+ CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated;
+
Member.AfterSave += MemberAfterSave;
Member.AfterDelete += MemberAfterDelete;
@@ -79,60 +76,192 @@ namespace Umbraco.Web.Search
}
}
- [SecuritySafeCritical]
- static void ContentServiceTrashed(IContentService sender, Core.Events.MoveEventArgs e)
+ ///
+ /// Handles index management for all media events - basically handling saving/copying/trashing/deleting
+ ///
+ ///
+ ///
+ static void MediaCacheRefresherCacheUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs e)
{
- IndexConent(e.Entity);
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForMedia(c1, c1.Trashed == false);
+ }
+ break;
+ case MessageType.RemoveById:
+ var c2 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject);
+ if (c2 != null)
+ {
+ //This is triggered when the item has trashed.
+ // So we need to delete the index from all indexes not supporting unpublished content.
+
+ DeleteIndexForEntity(c2.Id, true);
+
+ //We then need to re-index this item for all indexes supporting unpublished content
+
+ ReIndexForMedia(c2, false);
+ }
+ break;
+ case MessageType.RefreshByJson:
+
+ var jsonPayloads = MediaCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject);
+ if (jsonPayloads.Any())
+ {
+ foreach (var payload in jsonPayloads)
+ {
+ switch (payload.Operation)
+ {
+ case MediaCacheRefresher.OperationType.Saved:
+ var media = ApplicationContext.Current.Services.MediaService.GetById(payload.Id);
+ if (media != null)
+ {
+ ReIndexForMedia(media, media.Trashed == false);
+ }
+ break;
+ case MediaCacheRefresher.OperationType.Trashed:
+ //keep if trashed for indexes supporting unpublished
+ DeleteIndexForEntity(payload.Id, true);
+ break;
+ case MediaCacheRefresher.OperationType.Deleted:
+ //permanently remove from all indexes
+ DeleteIndexForEntity(payload.Id, false);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ break;
+ case MessageType.RefreshByInstance:
+ case MessageType.RemoveByInstance:
+ case MessageType.RefreshAll:
+ default:
+ //We don't support these, these message types will not fire for media
+ break;
+ }
}
+ ///
+ /// Handles index management for all published content events - basically handling published/unpublished
+ ///
+ ///
+ ///
+ ///
+ /// This will execute on all servers taking part in load balancing
+ ///
[SecuritySafeCritical]
- static void MediaServiceTrashed(IMediaService sender, Core.Events.MoveEventArgs e)
+ static void PublishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e)
{
- IndexMedia(e.Entity);
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForContent(c1, true);
+ }
+ break;
+ case MessageType.RemoveById:
+
+ //This is triggered when the item has been unpublished or trashed (which also performs an unpublish).
+
+ var c2 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject);
+ if (c2 != null)
+ {
+ // So we need to delete the index from all indexes not supporting unpublished content.
+
+ DeleteIndexForEntity(c2.Id, true);
+
+ // We then need to re-index this item for all indexes supporting unpublished content
+
+ ReIndexForContent(c2, false);
+ }
+ break;
+ case MessageType.RefreshByInstance:
+ var c3 = e.MessageObject as IContent;
+ if (c3 != null)
+ {
+ ReIndexForContent(c3, true);
+ }
+ break;
+ case MessageType.RemoveByInstance:
+
+ //This is triggered when the item has been unpublished or trashed (which also performs an unpublish).
+
+ var c4 = e.MessageObject as IContent;
+ if (c4 != null)
+ {
+ // So we need to delete the index from all indexes not supporting unpublished content.
+
+ DeleteIndexForEntity(c4.Id, true);
+
+ // We then need to re-index this item for all indexes supporting unpublished content
+
+ ReIndexForContent(c4, false);
+ }
+ break;
+ case MessageType.RefreshAll:
+ case MessageType.RefreshByJson:
+ default:
+ //We don't support these for examine indexing
+ break;
+ }
}
+ ///
+ /// Handles index management for all unpublished content events - basically handling saving/copying/deleting
+ ///
+ ///
+ ///
+ ///
+ /// This will execute on all servers taking part in load balancing
+ ///
[SecuritySafeCritical]
- static void ContentServiceMoved(IContentService sender, Umbraco.Core.Events.MoveEventArgs e)
+ static void UnpublishedPageCacheRefresherCacheUpdated(UnpublishedPageCacheRefresher sender, CacheRefresherEventArgs e)
{
- IndexConent(e.Entity);
- }
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.ContentService.GetById((int) e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForContent(c1, false);
+ }
+ break;
+ case MessageType.RemoveById:
+
+ // This is triggered when the item is permanently deleted
+
+ DeleteIndexForEntity((int)e.MessageObject, false);
+ break;
+ case MessageType.RefreshByInstance:
+ var c3 = e.MessageObject as IContent;
+ if (c3 != null)
+ {
+ ReIndexForContent(c3, false);
+ }
+ break;
+ case MessageType.RemoveByInstance:
- [SecuritySafeCritical]
- static void ContentServiceDeleted(IContentService sender, Umbraco.Core.Events.DeleteEventArgs e)
- {
- e.DeletedEntities.ForEach(
- content =>
- ExamineManager.Instance.DeleteFromIndex(
- content.Id.ToString(),
- ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)));
- }
+ // This is triggered when the item is permanently deleted
- [SecuritySafeCritical]
- static void ContentServiceSaved(IContentService sender, Umbraco.Core.Events.SaveEventArgs e)
- {
- e.SavedEntities.ForEach(IndexConent);
- }
-
- [SecuritySafeCritical]
- static void MediaServiceMoved(IMediaService sender, Umbraco.Core.Events.MoveEventArgs e)
- {
- IndexMedia(e.Entity);
- }
-
- [SecuritySafeCritical]
- static void MediaServiceDeleted(IMediaService sender, Umbraco.Core.Events.DeleteEventArgs e)
- {
- e.DeletedEntities.ForEach(
- media =>
- ExamineManager.Instance.DeleteFromIndex(
- media.Id.ToString(),
- ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)));
- }
-
- [SecuritySafeCritical]
- static void MediaServiceSaved(IMediaService sender, Umbraco.Core.Events.SaveEventArgs e)
- {
- e.SavedEntities.ForEach(IndexMedia);
+ var c4 = e.MessageObject as IContent;
+ if (c4 != null)
+ {
+ DeleteIndexForEntity(c4.Id, false);
+ }
+ break;
+ case MessageType.RefreshAll:
+ case MessageType.RefreshByJson:
+ default:
+ //We don't support these, these message types will not fire for unpublished content
+ break;
+ }
}
[SecuritySafeCritical]
@@ -156,39 +285,6 @@ namespace Umbraco.Web.Search
.Where(x => x.EnableDefaultEventHandler));
}
- ///
- /// Only Update indexes for providers that dont SupportUnpublishedContent
- ///
- ///
- ///
- [SecuritySafeCritical]
- private static void ContentAfterUpdateDocumentCache(Document sender, DocumentCacheEventArgs e)
- {
- //ensure that only the providers that have DONT unpublishing support enabled
- //that are also flagged to listen
- ExamineManager.Instance.ReIndexNode(ToXDocument(sender, true).Root, IndexTypes.Content,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => !x.SupportUnpublishedContent
- && x.EnableDefaultEventHandler));
- }
-
- ///
- /// Only update indexes for providers that don't SupportUnpublishedContnet
- ///
- ///
- ///
- [SecuritySafeCritical]
- private static void ContentAfterClearDocumentCache(Document sender, DocumentCacheEventArgs e)
- {
- var nodeId = sender.Id.ToString();
- //ensure that only the providers that DONT have unpublishing support enabled
- //that are also flagged to listen
- ExamineManager.Instance.DeleteFromIndex(nodeId,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => !x.SupportUnpublishedContent
- && x.EnableDefaultEventHandler));
- }
-
///
/// Event handler to create a lower cased version of the node name, this is so we can support case-insensitive searching and still
/// use the Whitespace Analyzer
@@ -210,27 +306,61 @@ namespace Umbraco.Web.Search
}
}
-
- private static void IndexMedia(IMedia sender)
+ [SecuritySafeCritical]
+ private static void ReIndexForMedia(IMedia sender, bool isMediaPublished)
{
ExamineManager.Instance.ReIndexNode(
sender.ToXml(), "media",
- ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler));
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+
+ //Index this item for all indexers if the media is not trashed, otherwise if the item is trashed
+ // then only index this for indexers supporting unpublished media
+
+ .Where(x => isMediaPublished || (x.SupportUnpublishedContent))
+ .Where(x => x.EnableDefaultEventHandler));
}
- private static void IndexConent(IContent sender)
+ ///
+ /// Remove items from any index that doesn't support unpublished content
+ ///
+ ///
+ ///
+ /// If true, indicates that we will only delete this item from indexes that don't support unpublished content.
+ /// If false it will delete this from all indexes regardless.
+ ///
+ [SecuritySafeCritical]
+ private static void DeleteIndexForEntity(int entityId, bool keepIfUnpublished)
+ {
+ ExamineManager.Instance.DeleteFromIndex(
+ entityId.ToString(CultureInfo.InvariantCulture),
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+
+ //if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content,
+ // otherwise if keepIfUnpublished == false then remove from all indexes
+
+ .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false)
+ .Where(x => x.EnableDefaultEventHandler));
+ }
+
+ ///
+ /// Re-indexes a content item whether published or not but only indexes them for indexes supporting unpublished content
+ ///
+ ///
+ ///
+ /// Value indicating whether the item is published or not
+ ///
+ [SecuritySafeCritical]
+ private static void ReIndexForContent(IContent sender, bool isContentPublished)
{
- //only index this content if the indexer supports unpublished content. that is because the
- // content.AfterUpdateDocumentCache will handle anything being published and will only index against indexers
- // that only support published content.
- // NOTE: The events for publishing have changed slightly from 6.0 to 6.1 and are streamlined in 6.1. Before
- // this event would fire before publishing, then again after publishing. Now the save event fires once before
- // publishing and that is all.
-
ExamineManager.Instance.ReIndexNode(
sender.ToXml(), "content",
ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => x.SupportUnpublishedContent && x.EnableDefaultEventHandler));
+
+ //Index this item for all indexers if the content is published, otherwise if the item is not published
+ // then only index this for indexers supporting unpublished content
+
+ .Where(x => isContentPublished || (x.SupportUnpublishedContent))
+ .Where(x => x.EnableDefaultEventHandler));
}
///
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 944d51536a..bff6ea8189 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -288,6 +288,7 @@
+