diff --git a/src/Umbraco.Core/Events/CancellableEventArgs.cs b/src/Umbraco.Core/Events/CancellableEventArgs.cs index 72cef19f7a..a102ea66ef 100644 --- a/src/Umbraco.Core/Events/CancellableEventArgs.cs +++ b/src/Umbraco.Core/Events/CancellableEventArgs.cs @@ -10,8 +10,8 @@ namespace Umbraco.Core.Events /// Event args for that can support cancellation /// [HostProtection(SecurityAction.LinkDemand, SharedState = true)] - public class CancellableEventArgs : EventArgs - { + public class CancellableEventArgs : EventArgs, IEquatable + { private bool _cancel; public CancellableEventArgs(bool canCancel, EventMessages messages, IDictionary additionalData) @@ -98,6 +98,36 @@ namespace Umbraco.Core.Events /// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility /// so we cannot change the strongly typed nature for some events. /// - public ReadOnlyDictionary AdditionalData { get; private set; } - } + public ReadOnlyDictionary AdditionalData { get; private set; } + + public bool Equals(CancellableEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(AdditionalData, other.AdditionalData); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CancellableEventArgs) obj); + } + + public override int GetHashCode() + { + return (AdditionalData != null ? AdditionalData.GetHashCode() : 0); + } + + public static bool operator ==(CancellableEventArgs left, CancellableEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(CancellableEventArgs left, CancellableEventArgs right) + { + return !Equals(left, right); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs index a05f09ece5..7815747f18 100644 --- a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs +++ b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Security.Permissions; using Umbraco.Core.Models; @@ -10,7 +11,7 @@ namespace Umbraco.Core.Events /// /// [HostProtection(SecurityAction.LinkDemand, SharedState = true)] - public class CancellableObjectEventArgs : CancellableEventArgs + public class CancellableObjectEventArgs : CancellableEventArgs, IEquatable> { public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary additionalData) : base(canCancel, messages, additionalData) @@ -48,5 +49,37 @@ namespace Umbraco.Core.Events /// protected T EventObject { get; set; } + public bool Equals(CancellableObjectEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && EqualityComparer.Default.Equals(EventObject, other.EventObject); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CancellableObjectEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ EqualityComparer.Default.GetHashCode(EventObject); + } + } + + public static bool operator ==(CancellableObjectEventArgs left, CancellableObjectEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(CancellableObjectEventArgs left, CancellableObjectEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/CopyEventArgs.cs b/src/Umbraco.Core/Events/CopyEventArgs.cs index 16f4fae982..65dc54cc88 100644 --- a/src/Umbraco.Core/Events/CopyEventArgs.cs +++ b/src/Umbraco.Core/Events/CopyEventArgs.cs @@ -1,6 +1,9 @@ +using System; +using System.Collections.Generic; + namespace Umbraco.Core.Events { - public class CopyEventArgs : CancellableObjectEventArgs + public class CopyEventArgs : CancellableObjectEventArgs, IEquatable> { public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId) : base(original, canCancel) @@ -43,5 +46,42 @@ namespace Umbraco.Core.Events public int ParentId { get; private set; } public bool RelateToOriginal { get; set; } + + public bool Equals(CopyEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && EqualityComparer.Default.Equals(Copy, other.Copy) && ParentId == other.ParentId && RelateToOriginal == other.RelateToOriginal; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CopyEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(Copy); + hashCode = (hashCode * 397) ^ ParentId; + hashCode = (hashCode * 397) ^ RelateToOriginal.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(CopyEventArgs left, CopyEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(CopyEventArgs left, CopyEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/DeleteEventArgs.cs b/src/Umbraco.Core/Events/DeleteEventArgs.cs index 1025066bcc..8a0fdaf290 100644 --- a/src/Umbraco.Core/Events/DeleteEventArgs.cs +++ b/src/Umbraco.Core/Events/DeleteEventArgs.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; namespace Umbraco.Core.Events { - public class DeleteEventArgs : CancellableObjectEventArgs> + public class DeleteEventArgs : CancellableObjectEventArgs>, IEquatable> { /// /// Constructor accepting multiple entities that are used in the delete operation @@ -99,10 +100,43 @@ namespace Umbraco.Core.Events /// /// A list of media files that can be added to during a deleted operation for which Umbraco will ensure are removed /// - public List MediaFilesToDelete { get; private set; } + public List MediaFilesToDelete { get; private set; } + + public bool Equals(DeleteEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && MediaFilesToDelete.Equals(other.MediaFilesToDelete); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((DeleteEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ MediaFilesToDelete.GetHashCode(); + } + } + + public static bool operator ==(DeleteEventArgs left, DeleteEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(DeleteEventArgs left, DeleteEventArgs right) + { + return !Equals(left, right); + } } - public class DeleteEventArgs : CancellableEventArgs + public class DeleteEventArgs : CancellableEventArgs, IEquatable { public DeleteEventArgs(int id, bool canCancel, EventMessages eventMessages) : base(canCancel, eventMessages) @@ -125,5 +159,38 @@ namespace Umbraco.Core.Events /// Gets the Id of the object being deleted. /// public int Id { get; private set; } + + public bool Equals(DeleteEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Id == other.Id; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((DeleteEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ Id; + } + } + + public static bool operator ==(DeleteEventArgs left, DeleteEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(DeleteEventArgs left, DeleteEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs b/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs index 4d53270608..1db1296640 100644 --- a/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs +++ b/src/Umbraco.Core/Events/DeleteRevisionsEventArgs.cs @@ -2,7 +2,7 @@ using System; namespace Umbraco.Core.Events { - public class DeleteRevisionsEventArgs : DeleteEventArgs + public class DeleteRevisionsEventArgs : DeleteEventArgs, IEquatable { public DeleteRevisionsEventArgs(int id, bool canCancel, Guid specificVersion = default(Guid), bool deletePriorVersions = false, DateTime dateToRetain = default(DateTime)) : base(id, canCancel) @@ -31,5 +31,42 @@ namespace Umbraco.Core.Events { get { return SpecificVersion != default(Guid); } } + + public bool Equals(DeleteRevisionsEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && DateToRetain.Equals(other.DateToRetain) && DeletePriorVersions == other.DeletePriorVersions && SpecificVersion.Equals(other.SpecificVersion); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((DeleteRevisionsEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ DateToRetain.GetHashCode(); + hashCode = (hashCode * 397) ^ DeletePriorVersions.GetHashCode(); + hashCode = (hashCode * 397) ^ SpecificVersion.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventDefinitionBase.cs b/src/Umbraco.Core/Events/EventDefinitionBase.cs index 46e57306a5..c0a061f80f 100644 --- a/src/Umbraco.Core/Events/EventDefinitionBase.cs +++ b/src/Umbraco.Core/Events/EventDefinitionBase.cs @@ -15,7 +15,10 @@ namespace Umbraco.Core.Events if (EventName.IsNullOrWhiteSpace()) { - var findResult = EventNameExtractor.FindEvent(sender, args, EventNameExtractor.MatchIngNames); + var findResult = EventNameExtractor.FindEvent(sender, args, + //don't match "Ing" suffixed names + exclude:EventNameExtractor.MatchIngNames); + if (findResult.Success == false) throw new AmbiguousMatchException("Could not automatically find the event name, the event name will need to be explicitly registered for this event definition. Error: " + findResult.Result.Error); EventName = findResult.Result.Name; @@ -32,7 +35,7 @@ namespace Umbraco.Core.Events { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Sender.Equals(other.Sender) && string.Equals(EventName, other.EventName); + return Args.Equals(other.Args) && string.Equals(EventName, other.EventName) && Sender.Equals(other.Sender); } public override bool Equals(object obj) @@ -47,7 +50,10 @@ namespace Umbraco.Core.Events { unchecked { - return (Sender.GetHashCode() * 397) ^ EventName.GetHashCode(); + var hashCode = Args.GetHashCode(); + hashCode = (hashCode * 397) ^ EventName.GetHashCode(); + hashCode = (hashCode * 397) ^ Sender.GetHashCode(); + return hashCode; } } @@ -58,7 +64,7 @@ namespace Umbraco.Core.Events public static bool operator !=(EventDefinitionBase left, EventDefinitionBase right) { - return !Equals(left, right); + return Equals(left, right) == false; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventDefinitionFilter.cs b/src/Umbraco.Core/Events/EventDefinitionFilter.cs new file mode 100644 index 0000000000..4bbe75d10b --- /dev/null +++ b/src/Umbraco.Core/Events/EventDefinitionFilter.cs @@ -0,0 +1,24 @@ +namespace Umbraco.Core.Events +{ + /// + /// The filter used in the GetEvents method which determines + /// how the result list is filtered + /// + public enum EventDefinitionFilter + { + /// + /// Returns all events tracked + /// + All, + + /// + /// Deduplicates events and only returns the first duplicate instance tracked + /// + FirstIn, + + /// + /// Deduplicates events and only returns the last duplicate instance tracked + /// + LastIn + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventNameExtractor.cs b/src/Umbraco.Core/Events/EventNameExtractor.cs index 3630763405..33137770d4 100644 --- a/src/Umbraco.Core/Events/EventNameExtractor.cs +++ b/src/Umbraco.Core/Events/EventNameExtractor.cs @@ -22,23 +22,20 @@ namespace Umbraco.Core.Events /// internal class EventNameExtractor { + /// /// Finds the event name on the sender that matches the args type /// - /// - /// + /// + /// /// /// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched /// /// /// null if not found or an ambiguous match /// - public static Attempt FindEvent(object sender, object args, Func exclude) - { - var argsType = args.GetType(); - - var senderType = sender.GetType(); - + public static Attempt FindEvent(Type senderType, Type argsType, Func exclude) + { var found = MatchedEventNames.GetOrAdd(new Tuple(senderType, argsType), tuple => { var events = CandidateEvents.GetOrAdd(senderType, t => @@ -70,14 +67,14 @@ namespace Umbraco.Core.Events return true; //special case for our own TypedEventHandler - if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>) + if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>) && x.GenericArgs.Length == 2 && x.GenericArgs[1] == tuple.Item2) { return true; } - return false; + return false; }).Select(x => x.EventInfo.Name).ToArray(); }); @@ -93,6 +90,22 @@ namespace Umbraco.Core.Events return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.Ambiguous)); } + /// + /// Finds the event name on the sender that matches the args type + /// + /// + /// + /// + /// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched + /// + /// + /// null if not found or an ambiguous match + /// + public static Attempt FindEvent(object sender, object args, Func exclude) + { + return FindEvent(sender.GetType(), args.GetType(), exclude); + } + /// /// Return true if the event is named with an ING name such as "Saving" or "RollingBack" /// diff --git a/src/Umbraco.Core/Events/ExportEventArgs.cs b/src/Umbraco.Core/Events/ExportEventArgs.cs index 161a073615..acbf920636 100644 --- a/src/Umbraco.Core/Events/ExportEventArgs.cs +++ b/src/Umbraco.Core/Events/ExportEventArgs.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Xml.Linq; namespace Umbraco.Core.Events { - public class ExportEventArgs : CancellableObjectEventArgs> + public class ExportEventArgs : CancellableObjectEventArgs>, IEquatable> { /// /// Constructor accepting a single entity instance @@ -48,5 +49,38 @@ namespace Umbraco.Core.Events /// Returns the xml relating to the export event /// public XElement Xml { get; private set; } + + public bool Equals(ExportEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Equals(Xml, other.Xml); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ExportEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0); + } + } + + public static bool operator ==(ExportEventArgs left, ExportEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(ExportEventArgs left, ExportEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/IEventDispatcher.cs b/src/Umbraco.Core/Events/IEventDispatcher.cs index c5daf6b2f0..78f8ed3a4a 100644 --- a/src/Umbraco.Core/Events/IEventDispatcher.cs +++ b/src/Umbraco.Core/Events/IEventDispatcher.cs @@ -93,6 +93,6 @@ namespace Umbraco.Core.Events /// Gets the collected events. /// /// The collected events. - IEnumerable GetEvents(); + IEnumerable GetEvents(EventDefinitionFilter filter); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/ImportEventArgs.cs b/src/Umbraco.Core/Events/ImportEventArgs.cs index 3bdd6d6fcf..dcecf5c36b 100644 --- a/src/Umbraco.Core/Events/ImportEventArgs.cs +++ b/src/Umbraco.Core/Events/ImportEventArgs.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Xml.Linq; namespace Umbraco.Core.Events { - public class ImportEventArgs : CancellableObjectEventArgs> + public class ImportEventArgs : CancellableObjectEventArgs>, IEquatable> { /// /// Constructor accepting an XElement with the xml being imported @@ -46,5 +47,38 @@ namespace Umbraco.Core.Events /// Returns the xml relating to the import event /// public XElement Xml { get; private set; } + + public bool Equals(ImportEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && Equals(Xml, other.Xml); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ImportEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0); + } + } + + public static bool operator ==(ImportEventArgs left, ImportEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(ImportEventArgs left, ImportEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs index 8596629731..506481a79e 100644 --- a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Packaging.Models; namespace Umbraco.Core.Events { - internal class ImportPackageEventArgs : CancellableObjectEventArgs> + internal class ImportPackageEventArgs : CancellableObjectEventArgs>, IEquatable> { private readonly MetaData _packageMetaData; @@ -22,5 +23,38 @@ namespace Umbraco.Core.Events { get { return _packageMetaData; } } + + public bool Equals(ImportPackageEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ImportPackageEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ _packageMetaData.GetHashCode(); + } + } + + public static bool operator ==(ImportPackageEventArgs left, ImportPackageEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(ImportPackageEventArgs left, ImportPackageEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/MigrationEventArgs.cs b/src/Umbraco.Core/Events/MigrationEventArgs.cs index 6afe9bd754..8b8898e7d4 100644 --- a/src/Umbraco.Core/Events/MigrationEventArgs.cs +++ b/src/Umbraco.Core/Events/MigrationEventArgs.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.Migrations; namespace Umbraco.Core.Events { - public class MigrationEventArgs : CancellableObjectEventArgs> + public class MigrationEventArgs : CancellableObjectEventArgs>, IEquatable { /// /// Constructor accepting multiple migrations that are used in the migration runner @@ -141,5 +141,43 @@ namespace Umbraco.Core.Events public string ProductName { get; private set; } internal MigrationContext MigrationContext { get; private set; } + + public bool Equals(MigrationEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && ConfiguredSemVersion.Equals(other.ConfiguredSemVersion) && MigrationContext.Equals(other.MigrationContext) && string.Equals(ProductName, other.ProductName) && TargetSemVersion.Equals(other.TargetSemVersion); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((MigrationEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ ConfiguredSemVersion.GetHashCode(); + hashCode = (hashCode * 397) ^ MigrationContext.GetHashCode(); + hashCode = (hashCode * 397) ^ ProductName.GetHashCode(); + hashCode = (hashCode * 397) ^ TargetSemVersion.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(MigrationEventArgs left, MigrationEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(MigrationEventArgs left, MigrationEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/MoveEventArgs.cs b/src/Umbraco.Core/Events/MoveEventArgs.cs index 0f0a5183a9..228e1ca2f7 100644 --- a/src/Umbraco.Core/Events/MoveEventArgs.cs +++ b/src/Umbraco.Core/Events/MoveEventArgs.cs @@ -4,7 +4,7 @@ using System.Linq; namespace Umbraco.Core.Events { - public class MoveEventArgs : CancellableObjectEventArgs + public class MoveEventArgs : CancellableObjectEventArgs, IEquatable> { /// /// Constructor accepting a collection of MoveEventInfo objects @@ -123,5 +123,38 @@ namespace Umbraco.Core.Events /// [Obsolete("Retrieve the ParentId from the MoveInfoCollection property instead")] public int ParentId { get; private set; } + + public bool Equals(MoveEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && MoveInfoCollection.Equals(other.MoveInfoCollection); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((MoveEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ MoveInfoCollection.GetHashCode(); + } + } + + public static bool operator ==(MoveEventArgs left, MoveEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(MoveEventArgs left, MoveEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/MoveEventInfo.cs b/src/Umbraco.Core/Events/MoveEventInfo.cs index a74db7f36e..9e77971837 100644 --- a/src/Umbraco.Core/Events/MoveEventInfo.cs +++ b/src/Umbraco.Core/Events/MoveEventInfo.cs @@ -1,6 +1,9 @@ +using System; +using System.Collections.Generic; + namespace Umbraco.Core.Events { - public class MoveEventInfo + public class MoveEventInfo : IEquatable> { public MoveEventInfo(TEntity entity, string originalPath, int newParentId) { @@ -12,5 +15,41 @@ namespace Umbraco.Core.Events public TEntity Entity { get; set; } public string OriginalPath { get; set; } public int NewParentId { get; set; } + + public bool Equals(MoveEventInfo other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return EqualityComparer.Default.Equals(Entity, other.Entity) && NewParentId == other.NewParentId && string.Equals(OriginalPath, other.OriginalPath); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((MoveEventInfo) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = EqualityComparer.Default.GetHashCode(Entity); + hashCode = (hashCode * 397) ^ NewParentId; + hashCode = (hashCode * 397) ^ OriginalPath.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(MoveEventInfo left, MoveEventInfo right) + { + return Equals(left, right); + } + + public static bool operator !=(MoveEventInfo left, MoveEventInfo right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/NewEventArgs.cs b/src/Umbraco.Core/Events/NewEventArgs.cs index acfd64e60d..415d82b90e 100644 --- a/src/Umbraco.Core/Events/NewEventArgs.cs +++ b/src/Umbraco.Core/Events/NewEventArgs.cs @@ -1,8 +1,10 @@ +using System; +using System.Collections.Generic; using Umbraco.Core.Models; namespace Umbraco.Core.Events { - public class NewEventArgs : CancellableObjectEventArgs + public class NewEventArgs : CancellableObjectEventArgs, IEquatable> { @@ -84,5 +86,42 @@ namespace Umbraco.Core.Events /// Gets or Sets the parent IContent object. /// public TEntity Parent { get; private set; } + + public bool Equals(NewEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && string.Equals(Alias, other.Alias) && EqualityComparer.Default.Equals(Parent, other.Parent) && ParentId == other.ParentId; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((NewEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ Alias.GetHashCode(); + hashCode = (hashCode * 397) ^ EqualityComparer.Default.GetHashCode(Parent); + hashCode = (hashCode * 397) ^ ParentId; + return hashCode; + } + } + + public static bool operator ==(NewEventArgs left, NewEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(NewEventArgs left, NewEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/PassThroughEventDispatcher.cs b/src/Umbraco.Core/Events/PassThroughEventDispatcher.cs index b2b51f9361..1e1a9bc6d1 100644 --- a/src/Umbraco.Core/Events/PassThroughEventDispatcher.cs +++ b/src/Umbraco.Core/Events/PassThroughEventDispatcher.cs @@ -50,7 +50,7 @@ namespace Umbraco.Core.Events eventHandler(sender, args); } - public IEnumerable GetEvents() + public IEnumerable GetEvents(EventDefinitionFilter filter) { return Enumerable.Empty(); } diff --git a/src/Umbraco.Core/Events/PublishEventArgs.cs b/src/Umbraco.Core/Events/PublishEventArgs.cs index a791781617..1aa7c2308c 100644 --- a/src/Umbraco.Core/Events/PublishEventArgs.cs +++ b/src/Umbraco.Core/Events/PublishEventArgs.cs @@ -1,8 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Umbraco.Core.Events { - public class PublishEventArgs : CancellableObjectEventArgs> + public class PublishEventArgs : CancellableObjectEventArgs>, IEquatable> { /// /// Constructor accepting multiple entities that are used in the publish operation @@ -101,5 +102,38 @@ namespace Umbraco.Core.Events } public bool IsAllRepublished { get; private set; } + + public bool Equals(PublishEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && IsAllRepublished == other.IsAllRepublished; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((PublishEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (base.GetHashCode() * 397) ^ IsAllRepublished.GetHashCode(); + } + } + + public static bool operator ==(PublishEventArgs left, PublishEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(PublishEventArgs left, PublishEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/RecycleBinEventArgs.cs b/src/Umbraco.Core/Events/RecycleBinEventArgs.cs index ca4bbd2719..c6049cb7a6 100644 --- a/src/Umbraco.Core/Events/RecycleBinEventArgs.cs +++ b/src/Umbraco.Core/Events/RecycleBinEventArgs.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Events { - public class RecycleBinEventArgs : CancellableEventArgs + public class RecycleBinEventArgs : CancellableEventArgs, IEquatable { public RecycleBinEventArgs(Guid nodeObjectType, Dictionary> allPropertyData, bool emptiedSuccessfully) : base(false) @@ -122,5 +122,44 @@ namespace Umbraco.Core.Events { get { return NodeObjectType == new Guid(Constants.ObjectTypes.Media); } } + + public bool Equals(RecycleBinEventArgs other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && AllPropertyData.Equals(other.AllPropertyData) && Files.Equals(other.Files) && Ids.Equals(other.Ids) && NodeObjectType.Equals(other.NodeObjectType) && RecycleBinEmptiedSuccessfully == other.RecycleBinEmptiedSuccessfully; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((RecycleBinEventArgs) obj); + } + + public override int GetHashCode() + { + unchecked + { + int hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ AllPropertyData.GetHashCode(); + hashCode = (hashCode * 397) ^ Files.GetHashCode(); + hashCode = (hashCode * 397) ^ Ids.GetHashCode(); + hashCode = (hashCode * 397) ^ NodeObjectType.GetHashCode(); + hashCode = (hashCode * 397) ^ RecycleBinEmptiedSuccessfully.GetHashCode(); + return hashCode; + } + } + + public static bool operator ==(RecycleBinEventArgs left, RecycleBinEventArgs right) + { + return Equals(left, right); + } + + public static bool operator !=(RecycleBinEventArgs left, RecycleBinEventArgs right) + { + return !Equals(left, right); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/RollbackEventArgs.cs b/src/Umbraco.Core/Events/RollbackEventArgs.cs index db9dded08c..cf2189e962 100644 --- a/src/Umbraco.Core/Events/RollbackEventArgs.cs +++ b/src/Umbraco.Core/Events/RollbackEventArgs.cs @@ -17,5 +17,7 @@ namespace Umbraco.Core.Events { get { return EventObject; } } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/SaveEventArgs.cs b/src/Umbraco.Core/Events/SaveEventArgs.cs index dafd326e1c..e816a8f8bd 100644 --- a/src/Umbraco.Core/Events/SaveEventArgs.cs +++ b/src/Umbraco.Core/Events/SaveEventArgs.cs @@ -116,5 +116,7 @@ namespace Umbraco.Core.Events { get { return EventObject; } } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/ScopeEventDispatcher.cs b/src/Umbraco.Core/Events/ScopeEventDispatcher.cs index 8073b9ac6e..6eb6ee3b85 100644 --- a/src/Umbraco.Core/Events/ScopeEventDispatcher.cs +++ b/src/Umbraco.Core/Events/ScopeEventDispatcher.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; namespace Umbraco.Core.Events -{ +{ + /// /// This event manager is created for each scope and is aware of if it is nested in an outer scope /// @@ -13,14 +15,14 @@ namespace Umbraco.Core.Events internal class ScopeEventDispatcher : IEventDispatcher { private readonly EventsDispatchMode _mode; - private List _events; + private List _events; public ScopeEventDispatcher(EventsDispatchMode mode) { _mode = mode; } - private List Events { get { return _events ?? (_events = new List()); } } + private List Events { get { return _events ?? (_events = new List()); } } private bool PassThroughCancelable { get { return _mode == EventsDispatchMode.PassThrough || _mode == EventsDispatchMode.Scope; } } @@ -81,9 +83,32 @@ namespace Umbraco.Core.Events Events.Add(new EventDefinition(eventHandler, sender, args, eventName)); } - public IEnumerable GetEvents() + public IEnumerable GetEvents(EventDefinitionFilter filter) { - return _events ?? Enumerable.Empty(); + if (_events == null) + return Enumerable.Empty(); + + switch (filter) + { + case EventDefinitionFilter.All: + return _events; + case EventDefinitionFilter.FirstIn: + var l1 = new OrderedHashSet(); + foreach (var e in _events) + { + l1.Add(e); + } + return l1; + case EventDefinitionFilter.LastIn: + var l2 = new OrderedHashSet(keepOldest:false); + foreach (var e in _events) + { + l2.Add(e); + } + return l2; + default: + throw new ArgumentOutOfRangeException("filter", filter, null); + } } public void ScopeExit(bool completed) diff --git a/src/Umbraco.Core/OrderedHashSet.cs b/src/Umbraco.Core/OrderedHashSet.cs new file mode 100644 index 0000000000..2fd545c915 --- /dev/null +++ b/src/Umbraco.Core/OrderedHashSet.cs @@ -0,0 +1,50 @@ +using System.Collections.ObjectModel; + +namespace Umbraco.Core +{ + /// + /// A custom collection similar to HashSet{T} which only contains unique items, however this collection keeps items in order + /// and is customizable to keep the newest or oldest equatable item + /// + /// + internal class OrderedHashSet : KeyedCollection + { + private readonly bool _keepOldest; + + public OrderedHashSet(bool keepOldest = true) + { + _keepOldest = keepOldest; + } + + protected override void InsertItem(int index, T item) + { + if (Dictionary == null) + { + base.InsertItem(index, item); + } + else + { + var exists = Dictionary.ContainsKey(item); + + //if we want to keep the newest, then we need to remove the old item and add the new one + if (exists == false) + { + base.InsertItem(index, item); + } + else if(_keepOldest == false) + { + if (Remove(item)) + { + index--; + } + base.InsertItem(index, item); + } + } + } + + protected override T GetKeyForItem(T item) + { + return item; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4b39c465bd..8ab094747e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -311,6 +311,8 @@ + + diff --git a/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs b/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs new file mode 100644 index 0000000000..3aa1db675f --- /dev/null +++ b/src/Umbraco.Tests/Cache/CacheRefresherEventHandlerTests.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Cache; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class CacheRefresherEventHandlerTests : BaseUmbracoApplicationTest + { + [Test] + public void Can_Find_All_Event_Handlers() + { + var definitions = new IEventDefinition[] + { + //I would test these but they are legacy events and we don't need them for deploy, when we migrate to new/better events we can wire up the check + //Permission.New += PermissionNew; + //Permission.Updated += PermissionUpdated; + //Permission.Deleted += PermissionDeleted; + //PermissionRepository.AssignedPermissions += CacheRefresherEventHandler_AssignedPermissions; + + new EventDefinition(null, ServiceContext.ApplicationTreeService, new EventArgs(), "Deleted"), + new EventDefinition(null, ServiceContext.ApplicationTreeService, new EventArgs(), "Updated"), + new EventDefinition(null, ServiceContext.ApplicationTreeService, new EventArgs(), "New"), + + new EventDefinition(null, ServiceContext.SectionService, new EventArgs(), "Deleted"), + new EventDefinition(null, ServiceContext.SectionService, new EventArgs(), "New"), + + new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs((IUserType) null)), + new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUserType) null)), + + new EventDefinition>(null, ServiceContext.UserService, new SaveEventArgs((IUser) null)), + new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUser) null)), + new EventDefinition>(null, ServiceContext.UserService, new DeleteEventArgs((IUser) null)), + + new EventDefinition>(null, ServiceContext.LocalizationService, new SaveEventArgs((IDictionaryItem) null)), + new EventDefinition>(null, ServiceContext.LocalizationService, new DeleteEventArgs((IDictionaryItem) null)), + + new EventDefinition>(null, ServiceContext.DataTypeService, new SaveEventArgs((IDataTypeDefinition) null)), + new EventDefinition>(null, ServiceContext.DataTypeService, new DeleteEventArgs((IDataTypeDefinition) null)), + + new EventDefinition>(null, ServiceContext.FileService, new SaveEventArgs((Stylesheet) null)), + new EventDefinition>(null, ServiceContext.FileService, new DeleteEventArgs((Stylesheet) null)), + + new EventDefinition>(null, ServiceContext.DomainService, new SaveEventArgs((IDomain) null)), + new EventDefinition>(null, ServiceContext.DomainService, new DeleteEventArgs((IDomain) null)), + + new EventDefinition>(null, ServiceContext.LocalizationService, new SaveEventArgs((ILanguage) null)), + new EventDefinition>(null, ServiceContext.LocalizationService, new DeleteEventArgs((ILanguage) null)), + + new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs((IContentType) null)), + new EventDefinition>(null, ServiceContext.ContentTypeService, new DeleteEventArgs((IContentType) null)), + new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs((IMediaType) null)), + new EventDefinition>(null, ServiceContext.ContentTypeService, new DeleteEventArgs((IMediaType) null)), + + new EventDefinition>(null, ServiceContext.MemberTypeService, new SaveEventArgs((IMemberType) null)), + new EventDefinition>(null, ServiceContext.MemberTypeService, new DeleteEventArgs((IMemberType) null)), + + new EventDefinition>(null, ServiceContext.FileService, new SaveEventArgs((ITemplate) null)), + new EventDefinition>(null, ServiceContext.FileService, new DeleteEventArgs((ITemplate) null)), + + new EventDefinition>(null, ServiceContext.MacroService, new SaveEventArgs((IMacro) null)), + new EventDefinition>(null, ServiceContext.MacroService, new DeleteEventArgs((IMacro) null)), + + new EventDefinition>(null, ServiceContext.MemberService, new SaveEventArgs((IMember) null)), + new EventDefinition>(null, ServiceContext.MemberService, new DeleteEventArgs((IMember) null)), + + new EventDefinition>(null, ServiceContext.MemberGroupService, new SaveEventArgs((IMemberGroup) null)), + new EventDefinition>(null, ServiceContext.MemberGroupService, new DeleteEventArgs((IMemberGroup) null)), + + new EventDefinition>(null, ServiceContext.MediaService, new SaveEventArgs((IMedia) null)), + new EventDefinition>(null, ServiceContext.MediaService, new DeleteEventArgs((IMedia) null)), + new EventDefinition>(null, ServiceContext.MediaService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Moved"), + new EventDefinition>(null, ServiceContext.MediaService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), + new EventDefinition(null, ServiceContext.MediaService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary>(), true)), + + new EventDefinition>(null, ServiceContext.ContentService, new SaveEventArgs((IContent) null)), + new EventDefinition>(null, ServiceContext.ContentService, new DeleteEventArgs((IContent) null)), + new EventDefinition>(null, ServiceContext.ContentService, new CopyEventArgs(null, null, -1)), + new EventDefinition>(null, ServiceContext.ContentService, new MoveEventArgs(new MoveEventInfo(null, "", -1)), "Trashed"), + new EventDefinition(null, ServiceContext.ContentService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary>(), true)), + new EventDefinition>(null, ServiceContext.ContentService, new PublishEventArgs((IContent) null), "Published"), + new EventDefinition>(null, ServiceContext.ContentService, new PublishEventArgs((IContent) null), "UnPublished"), + + new EventDefinition>(null, ServiceContext.PublicAccessService, new SaveEventArgs((PublicAccessEntry) null)), + new EventDefinition>(null, ServiceContext.PublicAccessService, new DeleteEventArgs((PublicAccessEntry) null)), + + new EventDefinition>(null, ServiceContext.RelationService, new SaveEventArgs((IRelationType) null)), + new EventDefinition>(null, ServiceContext.RelationService, new DeleteEventArgs((IRelationType) null)), + + new EventDefinition>(null, ServiceContext.RelationService, new SaveEventArgs((IRelationType) null)), + new EventDefinition>(null, ServiceContext.RelationService, new DeleteEventArgs((IRelationType) null)), + }; + + foreach (var definition in definitions) + { + var found = CacheRefresherEventHandler.FindHandler(definition); + Assert.IsNotNull(found, "Couldn't find method for " + definition.EventName + " on " + definition.Sender.GetType()); + } + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs b/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs index 55a7f2a893..2ec175933e 100644 --- a/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs +++ b/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Web.Caching; using Moq; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Models; diff --git a/src/Umbraco.Tests/Collections/OrderedHashSetTests.cs b/src/Umbraco.Tests/Collections/OrderedHashSetTests.cs new file mode 100644 index 0000000000..267e31506f --- /dev/null +++ b/src/Umbraco.Tests/Collections/OrderedHashSetTests.cs @@ -0,0 +1,81 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests.Collections +{ + [TestFixture] + public class OrderedHashSetTests + { + [Test] + public void Keeps_Last() + { + var list = new OrderedHashSet(keepOldest:false); + var items = new MyClass[] {new MyClass("test"), new MyClass("test"), new MyClass("test") }; + foreach (var item in items) + { + list.Add(item); + } + + Assert.AreEqual(1, list.Count); + Assert.AreEqual(items[2].Id, list[0].Id); + Assert.AreNotEqual(items[0].Id, list[0].Id); + } + + [Test] + public void Keeps_First() + { + var list = new OrderedHashSet(keepOldest: true); + var items = new MyClass[] { new MyClass("test"), new MyClass("test"), new MyClass("test") }; + foreach (var item in items) + { + list.Add(item); + } + + Assert.AreEqual(1, list.Count); + Assert.AreEqual(items[0].Id, list[0].Id); + } + + private class MyClass : IEquatable + { + public MyClass(string name) + { + Name = name; + Id = Guid.NewGuid(); + } + + public string Name { get; private set; } + public Guid Id { get; private set; } + + public bool Equals(MyClass other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Name, other.Name); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((MyClass) obj); + } + + public override int GetHashCode() + { + return Name.GetHashCode(); + } + + public static bool operator ==(MyClass left, MyClass right) + { + return Equals(left, right); + } + + public static bool operator !=(MyClass left, MyClass right) + { + return !Equals(left, right); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs index 6020d1f888..c8bb1137d3 100644 --- a/src/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs @@ -52,7 +52,7 @@ namespace Umbraco.Tests.Scoping events.Dispatch(DoThing2, this, new EventArgs()); events.Dispatch(DoThing3, this, new EventArgs()); - Assert.IsEmpty(events.GetEvents()); + Assert.IsEmpty(events.GetEvents(EventDefinitionFilter.All)); } public event EventHandler DoThing1; diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 23921182ad..7d5754529e 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -77,9 +77,9 @@ namespace Umbraco.Tests.Scoping scope.Events.Dispatch(DoThing3, this, new SaveEventArgs(0)); // events have been queued - Assert.AreEqual(3, scope.Events.GetEvents().Count()); + Assert.AreEqual(3, scope.Events.GetEvents(EventDefinitionFilter.All).Count()); - var events = scope.Events.GetEvents().ToArray(); + var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray(); var knownNames = new[] { "DoThing1", "DoThing2", "DoThing3" }; var knownArgTypes = new[] { typeof (SaveEventArgs), typeof (SaveEventArgs), typeof (SaveEventArgs) }; @@ -110,7 +110,7 @@ namespace Umbraco.Tests.Scoping scope.Events.Dispatch(DoThing3, this, new SaveEventArgs(0)); // events have not been queued - Assert.IsEmpty(scope.Events.GetEvents()); + Assert.IsEmpty(scope.Events.GetEvents(EventDefinitionFilter.All)); // events have been raised Assert.AreEqual(3, counter); @@ -139,7 +139,7 @@ namespace Umbraco.Tests.Scoping scope.Events.Dispatch(DoThing3, this, new SaveEventArgs(0)); // events have been queued - Assert.AreEqual(3, scope.Events.GetEvents().Count()); + Assert.AreEqual(3, scope.Events.GetEvents(EventDefinitionFilter.All).Count()); if (complete) scope.Complete(); @@ -180,7 +180,7 @@ namespace Umbraco.Tests.Scoping scope.Events.Dispatch(DoThing3, this, new SaveEventArgs(0)); // events have been queued - Assert.AreEqual(3, scope.Events.GetEvents().Count()); + Assert.AreEqual(3, scope.Events.GetEvents(EventDefinitionFilter.All).Count()); if (complete) scope.Complete(); diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 57595578da..70f48b12fb 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -352,12 +352,7 @@ namespace Umbraco.Tests.TestHelpers onFail(ex); } } - - protected ServiceContext ServiceContext - { - get { return ApplicationContext.Services; } - } - + protected DatabaseContext DatabaseContext { get { return ApplicationContext.DatabaseContext; } diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 98f7b36e81..43b6171b94 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -208,6 +208,11 @@ namespace Umbraco.Tests.TestHelpers Resolution.Freeze(); } + protected ServiceContext ServiceContext + { + get { return ApplicationContext.Services; } + } + protected ApplicationContext ApplicationContext { get { return ApplicationContext.Current; } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 10810b0a1f..e99dfaf454 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -156,7 +156,9 @@ + + diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs index 23e064ed9c..59084f13c4 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs +++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using Umbraco.Core; using Umbraco.Core.Events; @@ -9,6 +10,7 @@ using Umbraco.Core.Services; using umbraco.BusinessLogic; using umbraco.cms.businesslogic; using System.Linq; +using System.Reflection; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; using Umbraco.Core.Publishing; @@ -29,35 +31,35 @@ namespace Umbraco.Web.Cache LogHelper.Info("Initializing Umbraco internal event handlers for cache refreshing"); //bind to application tree events - ApplicationTreeService.Deleted += ApplicationTreeDeleted; - ApplicationTreeService.Updated += ApplicationTreeUpdated; - ApplicationTreeService.New += ApplicationTreeNew; + ApplicationTreeService.Deleted += ApplicationTreeService_Deleted; + ApplicationTreeService.Updated += ApplicationTreeService_Updated; + ApplicationTreeService.New += ApplicationTreeService_New; //bind to application events - SectionService.Deleted += ApplicationDeleted; - SectionService.New += ApplicationNew; + SectionService.Deleted += SectionService_Deleted; + SectionService.New += SectionService_New; //bind to user / user type events - UserService.SavedUserType += UserServiceSavedUserType; - UserService.DeletedUserType += UserServiceDeletedUserType; - UserService.SavedUser += UserServiceSavedUser; - UserService.DeletedUser += UserServiceDeletedUser; + UserService.SavedUserType += UserService_SavedUserType; + UserService.DeletedUserType += UserService_DeletedUserType; + UserService.SavedUser += UserService_SavedUser; + UserService.DeletedUser += UserService_DeletedUser; //Bind to dictionary events - LocalizationService.DeletedDictionaryItem += LocalizationServiceDeletedDictionaryItem; - LocalizationService.SavedDictionaryItem += LocalizationServiceSavedDictionaryItem; + LocalizationService.DeletedDictionaryItem += LocalizationService_DeletedDictionaryItem; + LocalizationService.SavedDictionaryItem += LocalizationService_SavedDictionaryItem; //Bind to data type events //NOTE: we need to bind to legacy and new API events currently: http://issues.umbraco.org/issue/U4-1979 - DataTypeService.Deleted += DataTypeServiceDeleted; - DataTypeService.Saved += DataTypeServiceSaved; + DataTypeService.Deleted += DataTypeService_Deleted; + DataTypeService.Saved += DataTypeService_Saved; //Bind to stylesheet events - FileService.SavedStylesheet += FileServiceSavedStylesheet; - FileService.DeletedStylesheet += FileServiceDeletedStylesheet; + FileService.SavedStylesheet += FileService_SavedStylesheet; + FileService.DeletedStylesheet += FileService_DeletedStylesheet; //Bind to domain events @@ -66,17 +68,17 @@ namespace Umbraco.Web.Cache //Bind to language events - LocalizationService.SavedLanguage += LocalizationServiceSavedLanguage; - LocalizationService.DeletedLanguage += LocalizationServiceDeletedLanguage; + LocalizationService.SavedLanguage += LocalizationService_SavedLanguage; + LocalizationService.DeletedLanguage += LocalizationService_DeletedLanguage; //Bind to content type events - ContentTypeService.SavedContentType += ContentTypeServiceSavedContentType; - ContentTypeService.SavedMediaType += ContentTypeServiceSavedMediaType; - ContentTypeService.DeletedContentType += ContentTypeServiceDeletedContentType; - ContentTypeService.DeletedMediaType += ContentTypeServiceDeletedMediaType; - MemberTypeService.Saved += MemberTypeServiceSaved; - MemberTypeService.Deleted += MemberTypeServiceDeleted; + ContentTypeService.SavedContentType += ContentTypeService_SavedContentType; + ContentTypeService.SavedMediaType += ContentTypeService_SavedMediaType; + ContentTypeService.DeletedContentType += ContentTypeService_DeletedContentType; + ContentTypeService.DeletedMediaType += ContentTypeService_DeletedMediaType; + MemberTypeService.Saved += MemberTypeService_Saved; + MemberTypeService.Deleted += MemberTypeService_Deleted; //Bind to permission events @@ -88,83 +90,83 @@ namespace Umbraco.Web.Cache //Bind to template events - FileService.SavedTemplate += FileServiceSavedTemplate; - FileService.DeletedTemplate += FileServiceDeletedTemplate; + FileService.SavedTemplate += FileService_SavedTemplate; + FileService.DeletedTemplate += FileService_DeletedTemplate; //Bind to macro events - MacroService.Saved += MacroServiceSaved; - MacroService.Deleted += MacroServiceDeleted; + MacroService.Saved += MacroService_Saved; + MacroService.Deleted += MacroService_Deleted; //Bind to member events - MemberService.Saved += MemberServiceSaved; - MemberService.Deleted += MemberServiceDeleted; + MemberService.Saved += MemberService_Saved; + MemberService.Deleted += MemberService_Deleted; MemberGroupService.Saved += MemberGroupService_Saved; MemberGroupService.Deleted += MemberGroupService_Deleted; //Bind to media events - MediaService.Saved += MediaServiceSaved; - MediaService.Deleted += MediaServiceDeleted; - MediaService.Moved += MediaServiceMoved; - MediaService.Trashed += MediaServiceTrashed; - MediaService.EmptiedRecycleBin += MediaServiceEmptiedRecycleBin; + MediaService.Saved += MediaService_Saved; + MediaService.Deleted += MediaService_Deleted; + MediaService.Moved += MediaService_Moved; + MediaService.Trashed += MediaService_Trashed; + MediaService.EmptiedRecycleBin += MediaService_EmptiedRecycleBin; //Bind to content events - this is for unpublished content syncing across servers (primarily for examine) - ContentService.Saved += ContentServiceSaved; - ContentService.Deleted += ContentServiceDeleted; - ContentService.Copied += ContentServiceCopied; + ContentService.Saved += ContentService_Saved; + ContentService.Deleted += ContentService_Deleted; + ContentService.Copied += ContentService_Copied; //TODO: The Move method of the content service fires Saved/Published events during its execution so we don't need to listen to moved //ContentService.Moved += ContentServiceMoved; - ContentService.Trashed += ContentServiceTrashed; - ContentService.EmptiedRecycleBin += ContentServiceEmptiedRecycleBin; + ContentService.Trashed += ContentService_Trashed; + ContentService.EmptiedRecycleBin += ContentService_EmptiedRecycleBin; - PublishingStrategy.Published += PublishingStrategy_Published; - PublishingStrategy.UnPublished += PublishingStrategy_UnPublished; + ContentService.Published += ContentService_Published; + ContentService.UnPublished += ContentService_UnPublished; //public access events PublicAccessService.Saved += PublicAccessService_Saved; PublicAccessService.Deleted += PublicAccessService_Deleted; - RelationService.SavedRelationType += RelationType_Saved; - RelationService.DeletedRelationType += RelationType_Deleted; + RelationService.SavedRelationType += RelationService_SavedRelationType; + RelationService.DeletedRelationType += RelationService_DeletedRelationType; } // for tests internal void Destroy() { //bind to application tree events - ApplicationTreeService.Deleted -= ApplicationTreeDeleted; - ApplicationTreeService.Updated -= ApplicationTreeUpdated; - ApplicationTreeService.New -= ApplicationTreeNew; + ApplicationTreeService.Deleted -= ApplicationTreeService_Deleted; + ApplicationTreeService.Updated -= ApplicationTreeService_Updated; + ApplicationTreeService.New -= ApplicationTreeService_New; //bind to application events - SectionService.Deleted -= ApplicationDeleted; - SectionService.New -= ApplicationNew; + SectionService.Deleted -= SectionService_Deleted; + SectionService.New -= SectionService_New; //bind to user / user type events - UserService.SavedUserType -= UserServiceSavedUserType; - UserService.DeletedUserType -= UserServiceDeletedUserType; - UserService.SavedUser -= UserServiceSavedUser; - UserService.DeletedUser -= UserServiceDeletedUser; + UserService.SavedUserType -= UserService_SavedUserType; + UserService.DeletedUserType -= UserService_DeletedUserType; + UserService.SavedUser -= UserService_SavedUser; + UserService.DeletedUser -= UserService_DeletedUser; //Bind to dictionary events - LocalizationService.DeletedDictionaryItem -= LocalizationServiceDeletedDictionaryItem; - LocalizationService.SavedDictionaryItem -= LocalizationServiceSavedDictionaryItem; + LocalizationService.DeletedDictionaryItem -= LocalizationService_DeletedDictionaryItem; + LocalizationService.SavedDictionaryItem -= LocalizationService_SavedDictionaryItem; //Bind to data type events //NOTE: we need to bind to legacy and new API events currently: http://issues.umbraco.org/issue/U4-1979 - DataTypeService.Deleted -= DataTypeServiceDeleted; - DataTypeService.Saved -= DataTypeServiceSaved; + DataTypeService.Deleted -= DataTypeService_Deleted; + DataTypeService.Saved -= DataTypeService_Saved; //Bind to stylesheet events - FileService.SavedStylesheet -= FileServiceSavedStylesheet; - FileService.DeletedStylesheet -= FileServiceDeletedStylesheet; + FileService.SavedStylesheet -= FileService_SavedStylesheet; + FileService.DeletedStylesheet -= FileService_DeletedStylesheet; //Bind to domain events @@ -173,17 +175,17 @@ namespace Umbraco.Web.Cache //Bind to language events - LocalizationService.SavedLanguage -= LocalizationServiceSavedLanguage; - LocalizationService.DeletedLanguage -= LocalizationServiceDeletedLanguage; + LocalizationService.SavedLanguage -= LocalizationService_SavedLanguage; + LocalizationService.DeletedLanguage -= LocalizationService_DeletedLanguage; //Bind to content type events - ContentTypeService.SavedContentType -= ContentTypeServiceSavedContentType; - ContentTypeService.SavedMediaType -= ContentTypeServiceSavedMediaType; - ContentTypeService.DeletedContentType -= ContentTypeServiceDeletedContentType; - ContentTypeService.DeletedMediaType -= ContentTypeServiceDeletedMediaType; - MemberTypeService.Saved -= MemberTypeServiceSaved; - MemberTypeService.Deleted -= MemberTypeServiceDeleted; + ContentTypeService.SavedContentType -= ContentTypeService_SavedContentType; + ContentTypeService.SavedMediaType -= ContentTypeService_SavedMediaType; + ContentTypeService.DeletedContentType -= ContentTypeService_DeletedContentType; + ContentTypeService.DeletedMediaType -= ContentTypeService_DeletedMediaType; + MemberTypeService.Saved -= MemberTypeService_Saved; + MemberTypeService.Deleted -= MemberTypeService_Deleted; //Bind to permission events @@ -195,53 +197,53 @@ namespace Umbraco.Web.Cache //Bind to template events - FileService.SavedTemplate -= FileServiceSavedTemplate; - FileService.DeletedTemplate -= FileServiceDeletedTemplate; + FileService.SavedTemplate -= FileService_SavedTemplate; + FileService.DeletedTemplate -= FileService_DeletedTemplate; //Bind to macro events - MacroService.Saved -= MacroServiceSaved; - MacroService.Deleted -= MacroServiceDeleted; + MacroService.Saved -= MacroService_Saved; + MacroService.Deleted -= MacroService_Deleted; //Bind to member events - MemberService.Saved -= MemberServiceSaved; - MemberService.Deleted -= MemberServiceDeleted; + MemberService.Saved -= MemberService_Saved; + MemberService.Deleted -= MemberService_Deleted; MemberGroupService.Saved -= MemberGroupService_Saved; MemberGroupService.Deleted -= MemberGroupService_Deleted; //Bind to media events - MediaService.Saved -= MediaServiceSaved; - MediaService.Deleted -= MediaServiceDeleted; - MediaService.Moved -= MediaServiceMoved; - MediaService.Trashed -= MediaServiceTrashed; - MediaService.EmptiedRecycleBin -= MediaServiceEmptiedRecycleBin; + MediaService.Saved -= MediaService_Saved; + MediaService.Deleted -= MediaService_Deleted; + MediaService.Moved -= MediaService_Moved; + MediaService.Trashed -= MediaService_Trashed; + MediaService.EmptiedRecycleBin -= MediaService_EmptiedRecycleBin; //Bind to content events - this is for unpublished content syncing across servers (primarily for examine) - ContentService.Saved -= ContentServiceSaved; - ContentService.Deleted -= ContentServiceDeleted; - ContentService.Copied -= ContentServiceCopied; + ContentService.Saved -= ContentService_Saved; + ContentService.Deleted -= ContentService_Deleted; + ContentService.Copied -= ContentService_Copied; //TODO: The Move method of the content service fires Saved/Published events during its execution so we don't need to listen to moved //ContentService.Moved -= ContentServiceMoved; - ContentService.Trashed -= ContentServiceTrashed; - ContentService.EmptiedRecycleBin -= ContentServiceEmptiedRecycleBin; + ContentService.Trashed -= ContentService_Trashed; + ContentService.EmptiedRecycleBin -= ContentService_EmptiedRecycleBin; - PublishingStrategy.Published -= PublishingStrategy_Published; - PublishingStrategy.UnPublished -= PublishingStrategy_UnPublished; + ContentService.Published -= ContentService_Published; + ContentService.UnPublished -= ContentService_UnPublished; //public access events PublicAccessService.Saved -= PublicAccessService_Saved; PublicAccessService.Deleted -= PublicAccessService_Deleted; - RelationService.SavedRelationType -= RelationType_Saved; - RelationService.DeletedRelationType -= RelationType_Deleted; + RelationService.SavedRelationType -= RelationService_SavedRelationType; + RelationService.DeletedRelationType -= RelationService_DeletedRelationType; } #region Publishing - void PublishingStrategy_UnPublished(IPublishingStrategy sender, PublishEventArgs e) + static void ContentService_UnPublished(IPublishingStrategy sender, PublishEventArgs e) { if (e.PublishedEntities.Any()) { @@ -263,12 +265,12 @@ namespace Umbraco.Web.Cache /// /// Refreshes the xml cache for a single node by removing it /// - private void UnPublishSingle(IContent content) + private static void UnPublishSingle(IContent content) { DistributedCache.Instance.RemovePageCache(content); } - void PublishingStrategy_Published(IPublishingStrategy sender, PublishEventArgs e) + static void ContentService_Published(IPublishingStrategy sender, PublishEventArgs e) { if (e.PublishedEntities.Any()) { @@ -293,7 +295,7 @@ namespace Umbraco.Web.Cache /// /// Refreshes the xml cache for all nodes /// - private void UpdateEntireCache() + private static void UpdateEntireCache() { DistributedCache.Instance.RefreshAllPageCache(); } @@ -301,7 +303,7 @@ namespace Umbraco.Web.Cache /// /// Refreshes the xml cache for nodes in list /// - private void UpdateMultipleContentCache(IEnumerable content) + private static void UpdateMultipleContentCache(IEnumerable content) { DistributedCache.Instance.RefreshPageCache(content.ToArray()); } @@ -309,7 +311,7 @@ namespace Umbraco.Web.Cache /// /// Refreshes the xml cache for a single node /// - private void UpdateSingleContentCache(IContent content) + private static void UpdateSingleContentCache(IContent content) { DistributedCache.Instance.RefreshPageCache(content); } @@ -323,7 +325,7 @@ namespace Umbraco.Web.Cache DistributedCache.Instance.RefreshPublicAccess(); } - private void PublicAccessService_Deleted(IPublicAccessService sender, DeleteEventArgs e) + static void PublicAccessService_Deleted(IPublicAccessService sender, DeleteEventArgs e) { DistributedCache.Instance.RefreshPublicAccess(); } @@ -332,7 +334,7 @@ namespace Umbraco.Web.Cache #region Content service event handlers - static void ContentServiceEmptiedRecycleBin(IContentService sender, RecycleBinEventArgs e) + static void ContentService_EmptiedRecycleBin(IContentService sender, RecycleBinEventArgs e) { if (e.RecycleBinEmptiedSuccessfully && e.IsContentRecycleBin) { @@ -349,7 +351,7 @@ namespace Umbraco.Web.Cache /// This is for the unpublished page refresher - the entity will be unpublished before being moved to the trash /// and the unpublished event will take care of remove it from any published caches /// - static void ContentServiceTrashed(IContentService sender, MoveEventArgs e) + static void ContentService_Trashed(IContentService sender, MoveEventArgs e) { DistributedCache.Instance.RefreshUnpublishedPageCache( e.MoveInfoCollection.Select(x => x.Entity).ToArray()); @@ -364,7 +366,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 /// case then we need to clear all user permissions cache. /// - static void ContentServiceCopied(IContentService sender, CopyEventArgs e) + static void ContentService_Copied(IContentService sender, CopyEventArgs e) { //check if permissions have changed var permissionsChanged = ((Content)e.Copy).WasPropertyDirty("PermissionsChanged"); @@ -382,7 +384,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void ContentServiceDeleted(IContentService sender, DeleteEventArgs e) + static void ContentService_Deleted(IContentService sender, DeleteEventArgs e) { DistributedCache.Instance.RemoveUnpublishedPageCache(e.DeletedEntities.ToArray()); } @@ -399,7 +401,7 @@ namespace Umbraco.Web.Cache /// 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) + static void ContentService_Saved(IContentService sender, SaveEventArgs e) { var clearUserPermissions = false; e.SavedEntities.ForEach(x => @@ -432,41 +434,41 @@ namespace Umbraco.Web.Cache #endregion #region ApplicationTree event handlers - static void ApplicationTreeNew(ApplicationTree sender, EventArgs e) + static void ApplicationTreeService_New(ApplicationTree sender, EventArgs e) { DistributedCache.Instance.RefreshAllApplicationTreeCache(); } - static void ApplicationTreeUpdated(ApplicationTree sender, EventArgs e) + static void ApplicationTreeService_Updated(ApplicationTree sender, EventArgs e) { DistributedCache.Instance.RefreshAllApplicationTreeCache(); } - static void ApplicationTreeDeleted(ApplicationTree sender, EventArgs e) + static void ApplicationTreeService_Deleted(ApplicationTree sender, EventArgs e) { DistributedCache.Instance.RefreshAllApplicationTreeCache(); } #endregion #region Application event handlers - static void ApplicationNew(Section sender, EventArgs e) + static void SectionService_New(Section sender, EventArgs e) { DistributedCache.Instance.RefreshAllApplicationCache(); } - static void ApplicationDeleted(Section sender, EventArgs e) + static void SectionService_Deleted(Section sender, EventArgs e) { DistributedCache.Instance.RefreshAllApplicationCache(); } #endregion #region UserType event handlers - static void UserServiceDeletedUserType(IUserService sender, DeleteEventArgs e) + static void UserService_DeletedUserType(IUserService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserTypeCache(x.Id)); } - static void UserServiceSavedUserType(IUserService sender, SaveEventArgs e) + static void UserService_SavedUserType(IUserService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserTypeCache(x.Id)); } @@ -475,12 +477,12 @@ namespace Umbraco.Web.Cache #region Dictionary event handlers - static void LocalizationServiceSavedDictionaryItem(ILocalizationService sender, SaveEventArgs e) + static void LocalizationService_SavedDictionaryItem(ILocalizationService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshDictionaryCache(x.Id)); } - static void LocalizationServiceDeletedDictionaryItem(ILocalizationService sender, DeleteEventArgs e) + static void LocalizationService_DeletedDictionaryItem(ILocalizationService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveDictionaryCache(x.Id)); } @@ -488,12 +490,12 @@ namespace Umbraco.Web.Cache #endregion #region DataType event handlers - static void DataTypeServiceSaved(IDataTypeService sender, SaveEventArgs e) + static void DataTypeService_Saved(IDataTypeService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshDataTypeCache(x)); } - static void DataTypeServiceDeleted(IDataTypeService sender, DeleteEventArgs e) + static void DataTypeService_Deleted(IDataTypeService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveDataTypeCache(x)); } @@ -503,12 +505,12 @@ namespace Umbraco.Web.Cache #region Stylesheet and stylesheet property event handlers - static void FileServiceDeletedStylesheet(IFileService sender, DeleteEventArgs e) + static void FileService_DeletedStylesheet(IFileService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveStylesheetCache(x)); } - static void FileServiceSavedStylesheet(IFileService sender, SaveEventArgs e) + static void FileService_SavedStylesheet(IFileService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshStylesheetCache(x)); } @@ -535,7 +537,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void LocalizationServiceDeletedLanguage(ILocalizationService sender, DeleteEventArgs e) + static void LocalizationService_DeletedLanguage(ILocalizationService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveLanguageCache(x)); } @@ -545,7 +547,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void LocalizationServiceSavedLanguage(ILocalizationService sender, SaveEventArgs e) + static void LocalizationService_SavedLanguage(ILocalizationService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshLanguageCache(x)); } @@ -558,7 +560,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void ContentTypeServiceDeletedMediaType(IContentTypeService sender, DeleteEventArgs e) + static void ContentTypeService_DeletedMediaType(IContentTypeService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveMediaTypeCache(x)); } @@ -568,7 +570,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void ContentTypeServiceDeletedContentType(IContentTypeService sender, DeleteEventArgs e) + static void ContentTypeService_DeletedContentType(IContentTypeService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(contentType => DistributedCache.Instance.RemoveContentTypeCache(contentType)); } @@ -578,7 +580,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void MemberTypeServiceDeleted(IMemberTypeService sender, DeleteEventArgs e) + static void MemberTypeService_Deleted(IMemberTypeService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(contentType => DistributedCache.Instance.RemoveMemberTypeCache(contentType)); } @@ -588,7 +590,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void ContentTypeServiceSavedMediaType(IContentTypeService sender, SaveEventArgs e) + static void ContentTypeService_SavedMediaType(IContentTypeService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshMediaTypeCache(x)); } @@ -598,7 +600,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void ContentTypeServiceSavedContentType(IContentTypeService sender, SaveEventArgs e) + static void ContentTypeService_SavedContentType(IContentTypeService sender, SaveEventArgs e) { e.SavedEntities.ForEach(contentType => DistributedCache.Instance.RefreshContentTypeCache(contentType)); } @@ -608,7 +610,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void MemberTypeServiceSaved(IMemberTypeService sender, SaveEventArgs e) + static void MemberTypeService_Saved(IMemberTypeService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshMemberTypeCache(x)); } @@ -617,7 +619,8 @@ namespace Umbraco.Web.Cache #endregion #region User/permissions event handlers - + + //fixme: this isn't named correct static void CacheRefresherEventHandler_AssignedPermissions(PermissionRepository sender, SaveEventArgs e) { var userIds = e.SavedEntities.Select(x => x.UserId).Distinct(); @@ -639,12 +642,12 @@ namespace Umbraco.Web.Cache InvalidateCacheForPermissionsChange(sender); } - static void UserServiceSavedUser(IUserService sender, SaveEventArgs e) + static void UserService_SavedUser(IUserService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserCache(x.Id)); } - static void UserServiceDeletedUser(IUserService sender, DeleteEventArgs e) + static void UserService_DeletedUser(IUserService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserCache(x.Id)); } @@ -674,7 +677,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void FileServiceDeletedTemplate(IFileService sender, DeleteEventArgs e) + static void FileService_DeletedTemplate(IFileService sender, DeleteEventArgs e) { e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveTemplateCache(x.Id)); } @@ -684,7 +687,7 @@ namespace Umbraco.Web.Cache /// /// /// - static void FileServiceSavedTemplate(IFileService sender, SaveEventArgs e) + static void FileService_SavedTemplate(IFileService sender, SaveEventArgs e) { e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshTemplateCache(x.Id)); } @@ -693,7 +696,7 @@ namespace Umbraco.Web.Cache #region Macro event handlers - void MacroServiceDeleted(IMacroService sender, DeleteEventArgs e) + static void MacroService_Deleted(IMacroService sender, DeleteEventArgs e) { foreach (var entity in e.DeletedEntities) { @@ -701,7 +704,7 @@ namespace Umbraco.Web.Cache } } - void MacroServiceSaved(IMacroService sender, SaveEventArgs e) + static void MacroService_Saved(IMacroService sender, SaveEventArgs e) { foreach (var entity in e.SavedEntities) { @@ -713,7 +716,7 @@ namespace Umbraco.Web.Cache #region Media event handlers - static void MediaServiceEmptiedRecycleBin(IMediaService sender, RecycleBinEventArgs e) + static void MediaService_EmptiedRecycleBin(IMediaService sender, RecycleBinEventArgs e) { if (e.RecycleBinEmptiedSuccessfully && e.IsMediaRecycleBin) { @@ -721,22 +724,22 @@ namespace Umbraco.Web.Cache } } - static void MediaServiceTrashed(IMediaService sender, MoveEventArgs e) + static void MediaService_Trashed(IMediaService sender, MoveEventArgs e) { DistributedCache.Instance.RemoveMediaCacheAfterRecycling(e.MoveInfoCollection.ToArray()); } - static void MediaServiceMoved(IMediaService sender, MoveEventArgs e) + static void MediaService_Moved(IMediaService sender, MoveEventArgs e) { DistributedCache.Instance.RefreshMediaCacheAfterMoving(e.MoveInfoCollection.ToArray()); } - static void MediaServiceDeleted(IMediaService sender, DeleteEventArgs e) + static void MediaService_Deleted(IMediaService sender, DeleteEventArgs e) { DistributedCache.Instance.RemoveMediaCachePermanently(e.DeletedEntities.Select(x => x.Id).ToArray()); } - static void MediaServiceSaved(IMediaService sender, SaveEventArgs e) + static void MediaService_Saved(IMediaService sender, SaveEventArgs e) { DistributedCache.Instance.RefreshMediaCache(e.SavedEntities.ToArray()); } @@ -744,12 +747,12 @@ namespace Umbraco.Web.Cache #region Member event handlers - static void MemberServiceDeleted(IMemberService sender, DeleteEventArgs e) + static void MemberService_Deleted(IMemberService sender, DeleteEventArgs e) { DistributedCache.Instance.RemoveMemberCache(e.DeletedEntities.ToArray()); } - static void MemberServiceSaved(IMemberService sender, SaveEventArgs e) + static void MemberService_Saved(IMemberService sender, SaveEventArgs e) { DistributedCache.Instance.RefreshMemberCache(e.SavedEntities.ToArray()); } @@ -777,14 +780,14 @@ namespace Umbraco.Web.Cache #region Relation type event handlers - private static void RelationType_Saved(IRelationService sender, SaveEventArgs args) + static void RelationService_SavedRelationType(IRelationService sender, SaveEventArgs args) { var dc = DistributedCache.Instance; foreach (var e in args.SavedEntities) dc.RefreshRelationTypeCache(e.Id); } - private static void RelationType_Deleted(IRelationService sender, DeleteEventArgs args) + static void RelationService_DeletedRelationType(IRelationService sender, DeleteEventArgs args) { var dc = DistributedCache.Instance; foreach (var e in args.DeletedEntities) @@ -792,5 +795,60 @@ namespace Umbraco.Web.Cache } #endregion + + + /// + /// This will inspect the event metadata and execute it's affiliated handler if one is found + /// + /// + internal static void HandleEvents(IEnumerable events) + { + foreach (var e in events) + { + var handler = FindHandler(e); + if (handler == null) continue; + + handler.Invoke(null, new object[] { e.Sender, e.Args }); + } + } + + /// + /// Used to cache all candidate handlers + /// + private static readonly Lazy CandidateHandlers = new Lazy(() => + { + var candidates = + + typeof(CacheRefresherEventHandler).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + .Where(x => x.Name.Contains("_")) + .Select(x => new + { + method = x, + nameParts = x.Name.Split(new[] { "_" }, StringSplitOptions.RemoveEmptyEntries), + methodParams = x.GetParameters() + }) + .Where(x => x.nameParts.Length == 2 && x.methodParams.Length == 2 && typeof(EventArgs).IsAssignableFrom(x.methodParams[1].ParameterType)) + .Select(x => x.method) + .ToArray(); + + return candidates; + }); + + /// + /// Used to cache all found event handlers + /// + private static readonly ConcurrentDictionary FoundHandlers = new ConcurrentDictionary(); + + internal static MethodInfo FindHandler(IEventDefinition eventDefinition) + { + return FoundHandlers.GetOrAdd(eventDefinition, definition => + { + var candidates = CandidateHandlers.Value; + + var found = candidates.FirstOrDefault(x => x.Name == string.Format("{0}_{1}", eventDefinition.Sender.GetType().Name, eventDefinition.EventName)); + + return found; + }); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/SafeXmlReaderWriter.cs b/src/Umbraco.Web/umbraco.presentation/SafeXmlReaderWriter.cs index 581f0fec97..9ec95ae45b 100644 --- a/src/Umbraco.Web/umbraco.presentation/SafeXmlReaderWriter.cs +++ b/src/Umbraco.Web/umbraco.presentation/SafeXmlReaderWriter.cs @@ -69,7 +69,10 @@ namespace umbraco if (_isWriter) throw new InvalidOperationException("Already a writer."); _isWriter = true; - _xml = Clone(_xml); // cloning for writer is not an option anymore (see XmlIsImmutable) + + // cloning for writer is not an option anymore (see XmlIsImmutable) + //fixme: But XmlIsImmutable is not actually used! + _xml = Clone(_xml); } internal static Action Cloning { get; set; } diff --git a/src/Umbraco.Web/umbraco.presentation/content.cs b/src/Umbraco.Web/umbraco.presentation/content.cs index 8db4e9122f..fcbf4dfdf3 100644 --- a/src/Umbraco.Web/umbraco.presentation/content.cs +++ b/src/Umbraco.Web/umbraco.presentation/content.cs @@ -562,7 +562,8 @@ namespace umbraco { get { return XmlFileEnabled && UmbracoConfig.For.UmbracoSettings().Content.XmlContentCheckForDiskChanges; } } - + + //fixme: this is not used? // whether _xml is immutable or not (achieved by cloning before changing anything) private static bool XmlIsImmutable {