diff --git a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
index 6cb60e2df0..eda1b94e37 100644
--- a/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
+++ b/src/Umbraco.Core/ObjectResolution/ApplicationEventsResolver.cs
@@ -11,7 +11,13 @@ namespace Umbraco.Core.ObjectResolution
/// A resolver to return all IApplicationEvents objects
///
///
- /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.
+ /// This is disposable because after the app has started it should be disposed to release any memory being occupied by instances.
+ /// Ordering handlers: handlers are ordered by (ascending) weight. By default, handlers from the Umbraco.* or Concorde.*
+ /// assemblies have a -100 weight whereas any other handler has a weight of +100. A custom weight can be assigned to a handler
+ /// by marking the class with the WeightAttribute. For example, the CacheRefresherEventHandler is marked with [Weight(int.MinValue)]
+ /// because its events need to run before anything else. Positive weights are considered "user-space" while negative weights are
+ /// "core". Finally, users can register a filter to process the list (after it has been ordered) and re-order it, or remove handlers.
+ /// BEWARE! handlers order is an important thing, and removing handlers or reordering handlers can have unexpected consequences.
///
public sealed class ApplicationEventsResolver : ManyObjectsResolverBase, IDisposable
{
@@ -25,9 +31,9 @@ namespace Umbraco.Core.ObjectResolution
///
internal ApplicationEventsResolver(IServiceProvider serviceProvider, ILogger logger, IEnumerable applicationEventHandlers)
: base(serviceProvider, logger, applicationEventHandlers)
- {
+ {
//create the legacy resolver and only include the legacy types
- _legacyResolver = new LegacyStartupHandlerResolver(
+ _legacyResolver = new LegacyStartupHandlerResolver(
serviceProvider, logger,
applicationEventHandlers.Where(x => TypeHelper.IsTypeAssignableFrom(x) == false));
}
@@ -52,26 +58,29 @@ namespace Umbraco.Core.ObjectResolution
{
if (_orderedAndFiltered == null)
{
- _resolved = true;
- _orderedAndFiltered = GetSortedValues().ToList();
- OnCollectionResolved(_orderedAndFiltered);
+ _orderedAndFiltered = GetSortedValues().ToList();
+ if (FilterCollection != null)
+ FilterCollection(_orderedAndFiltered);
}
return _orderedAndFiltered;
}
}
- ///
- /// A delegate that can be set in the pre-boot phase in order to filter or re-order the event handler collection
- ///
- ///
- /// This can be set on startup in the pre-boot process in either a custom boot manager or global.asax (UmbracoApplication)
- ///
- public Action> FilterCollection
+ ///
+ /// Gets or sets a delegate to filter the event handler list (EXPERT!).
+ ///
+ ///
+ /// This can be set on startup in the pre-boot process in either a custom boot manager or global.asax (UmbracoApplication).
+ /// Allows custom logic to execute in order to filter and/or re-order the event handlers prior to executing.
+ /// To be used by custom boot sequences where the boot loader needs to remove some handlers, or raise their priority.
+ /// Filtering the event handler collection can have ugly consequences. Use with care.
+ ///
+ public Action> FilterCollection
{
get { return _filterCollection; }
set
{
- if (_resolved)
+ if (_orderedAndFiltered != null)
throw new InvalidOperationException("Cannot set the FilterCollection delegate once the ApplicationEventHandlers are resolved");
if (_filterCollection != null)
throw new InvalidOperationException("Cannot set the FilterCollection delegate once it's already been specified");
@@ -80,40 +89,16 @@ namespace Umbraco.Core.ObjectResolution
}
}
- ///
- /// Allow any filters to be applied to the event handler list
- ///
- ///
- ///
- /// This allows custom logic to execute in order to filter or re-order the event handlers prior to executing,
- /// however this also ensures that any core handlers are executed first to ensure the stabiliy of Umbraco.
- ///
- private void OnCollectionResolved(List handlers)
- {
- if (FilterCollection != null)
- {
- FilterCollection(handlers);
- }
+ protected override int GetObjectWeight(object o)
+ {
+ var type = o.GetType();
+ var attr = type.GetCustomAttribute(true);
+ if (attr != null) return attr.Weight;
+ var name = type.Assembly.FullName;
- //find all of the core handlers and their weight, remove them from the main list
- var coreItems = new List>();
- foreach (var handler in handlers.ToArray())
- {
- //Yuck, but not sure what else we can do
- if (
- handler.GetType().Assembly.FullName.StartsWith("Umbraco.", StringComparison.OrdinalIgnoreCase)
- || handler.GetType().Assembly.FullName.StartsWith("Concorde."))
- {
- coreItems.Add(new Tuple(handler, GetObjectWeight(handler)));
- handlers.Remove(handler);
- }
- }
-
- //re-add the core handlers to the beginning of the list ordered by their weight
- foreach (var coreHandler in coreItems.OrderBy(x => x.Item2))
- {
- handlers.Insert(0, coreHandler.Item1);
- }
+ // we should really attribute all our Core handlers, so this is temp
+ var core = name.InvariantStartsWith("Umbraco.") || name.InvariantStartsWith("Concorde.");
+ return core ? -DefaultPluginWeight : DefaultPluginWeight;
}
///
@@ -157,7 +142,6 @@ namespace Umbraco.Core.ObjectResolution
private bool _disposed;
private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
private Action> _filterCollection;
- private bool _resolved = false;
///
/// Gets a value indicating whether this instance is disposed.
@@ -215,7 +199,7 @@ namespace Umbraco.Core.ObjectResolution
_legacyResolver.Dispose();
ResetCollections();
_orderedAndFiltered.Clear();
- _orderedAndFiltered = null;
+ _orderedAndFiltered = null;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectResolution/WeightAttribute.cs b/src/Umbraco.Core/ObjectResolution/WeightAttribute.cs
index 35f10a4111..16d18fb7b7 100644
--- a/src/Umbraco.Core/ObjectResolution/WeightAttribute.cs
+++ b/src/Umbraco.Core/ObjectResolution/WeightAttribute.cs
@@ -12,26 +12,11 @@ namespace Umbraco.Core.ObjectResolution
/// Initializes a new instance of the class with a weight.
///
/// The object type weight.
- ///
- /// This internal constructor allows for internal Umbraco products to set a negative number weight
- ///
- internal WeightAttribute(int weight)
+ public WeightAttribute(int weight)
{
Weight = weight;
}
- ///
- /// Initializes a new instance of the class with a weight.
- ///
- /// The object type weight.
- ///
- /// The weight must be a positive number
- ///
- public WeightAttribute(uint weight)
- {
- Weight = Convert.ToInt32(weight);
- }
-
///
/// Gets or sets the weight of the object type.
///
diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
index 791318d8ab..78b211ee7c 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
@@ -11,6 +11,7 @@ using umbraco.cms.businesslogic;
using System.Linq;
using umbraco.cms.businesslogic.web;
using Umbraco.Core.Logging;
+using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Publishing;
using Content = Umbraco.Core.Models.Content;
using ApplicationTree = Umbraco.Core.Models.ApplicationTree;
@@ -21,6 +22,7 @@ namespace Umbraco.Web.Cache
///
/// Class which listens to events on business level objects in order to invalidate the cache amongst servers when data changes
///
+ [Weight(int.MinValue)]
public class CacheRefresherEventHandler : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
@@ -104,14 +106,14 @@ namespace Umbraco.Web.Cache
//Bind to media events
- MediaService.Saved += MediaServiceSaved;
+ MediaService.Saved += MediaServiceSaved;
MediaService.Deleted += MediaServiceDeleted;
MediaService.Moved += MediaServiceMoved;
MediaService.Trashed += MediaServiceTrashed;
MediaService.EmptiedRecycleBin += MediaServiceEmptiedRecycleBin;
//Bind to content events - this is for unpublished content syncing across servers (primarily for examine)
-
+
ContentService.Saved += ContentServiceSaved;
ContentService.Deleted += ContentServiceDeleted;
ContentService.Copied += ContentServiceCopied;
@@ -231,7 +233,7 @@ namespace Umbraco.Web.Cache
DistributedCache.Instance.RemoveUnpublishedCachePermanently(e.Ids.ToArray());
}
}
-
+
///
/// Handles cache refreshing for when content is trashed
///
@@ -253,7 +255,7 @@ namespace Umbraco.Web.Cache
///
///
///
- /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the
+ /// 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)
@@ -285,10 +287,10 @@ namespace Umbraco.Web.Cache
///
///
///
- /// When an entity is saved we need to notify other servers about the change in order for the Examine indexes to
+ /// 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
+ ///
+ /// 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)
@@ -303,8 +305,8 @@ namespace Umbraco.Web.Cache
var permissionsChanged = ((Content)x).WasPropertyDirty("PermissionsChanged");
if (permissionsChanged)
{
- clearUserPermissions = true;
- }
+ clearUserPermissions = true;
+ }
}
});
@@ -337,7 +339,7 @@ namespace Umbraco.Web.Cache
static void ApplicationTreeDeleted(ApplicationTree sender, EventArgs e)
{
DistributedCache.Instance.RefreshAllApplicationTreeCache();
- }
+ }
#endregion
#region Application event handlers
@@ -349,7 +351,7 @@ namespace Umbraco.Web.Cache
static void ApplicationDeleted(Section sender, EventArgs e)
{
DistributedCache.Instance.RefreshAllApplicationCache();
- }
+ }
#endregion
#region UserType event handlers
@@ -362,9 +364,9 @@ namespace Umbraco.Web.Cache
{
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserTypeCache(x.Id));
}
-
+
#endregion
-
+
#region Dictionary event handlers
static void LocalizationServiceSavedDictionaryItem(ILocalizationService sender, SaveEventArgs e)
@@ -390,11 +392,11 @@ namespace Umbraco.Web.Cache
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveDataTypeCache(x));
}
-
+
#endregion
#region Stylesheet and stylesheet property event handlers
-
+
static void FileServiceDeletedStylesheet(IFileService sender, DeleteEventArgs e)
{
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveStylesheetCache(x));
@@ -441,7 +443,7 @@ namespace Umbraco.Web.Cache
{
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshLanguageCache(x));
}
-
+
#endregion
#region Content/media/member Type event handlers
@@ -505,9 +507,9 @@ namespace Umbraco.Web.Cache
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshMemberTypeCache(x));
}
-
+
#endregion
-
+
#region User/permissions event handlers
static void CacheRefresherEventHandler_AssignedPermissions(PermissionRepository sender, SaveEventArgs e)
@@ -540,7 +542,7 @@ namespace Umbraco.Web.Cache
{
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserCache(x.Id));
}
-
+
private static void InvalidateCacheForPermissionsChange(UserPermission sender)
{
if (sender.User != null)
@@ -580,7 +582,7 @@ namespace Umbraco.Web.Cache
{
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshTemplateCache(x.Id));
}
-
+
#endregion
#region Macro event handlers
@@ -600,7 +602,7 @@ namespace Umbraco.Web.Cache
DistributedCache.Instance.RefreshMacroCache(entity);
}
}
-
+
#endregion
#region Media event handlers
@@ -631,14 +633,14 @@ namespace Umbraco.Web.Cache
static void MediaServiceSaved(IMediaService sender, SaveEventArgs e)
{
DistributedCache.Instance.RefreshMediaCache(e.SavedEntities.ToArray());
- }
+ }
#endregion
#region Member event handlers
static void MemberServiceDeleted(IMemberService sender, DeleteEventArgs e)
{
- DistributedCache.Instance.RemoveMemberCache(e.DeletedEntities.ToArray());
+ DistributedCache.Instance.RemoveMemberCache(e.DeletedEntities.ToArray());
}
static void MemberServiceSaved(IMemberService sender, SaveEventArgs e)