Merge pull request #1908 from umbraco/temp-scope-events-stale-entities
POC to normalize all event entities when we track events so that all …
This commit is contained in:
@@ -1,85 +1,177 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Permissions;
|
using System.Security.Permissions;
|
||||||
using Umbraco.Core.Models;
|
using Umbraco.Core.Models;
|
||||||
using Umbraco.Core.Models.EntityBase;
|
using Umbraco.Core.Models.EntityBase;
|
||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event args for a strongly typed object that can support cancellation
|
/// Used as a base class for the generic type CancellableObjectEventArgs{T} so that we can get direct 'object' access to the underlying EventObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
||||||
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
public abstract class CancellableObjectEventArgs : CancellableEventArgs
|
||||||
public class CancellableObjectEventArgs<T> : CancellableEventArgs, IEquatable<CancellableObjectEventArgs<T>>
|
{
|
||||||
{
|
protected CancellableObjectEventArgs(object eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||||
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
|
||||||
: base(canCancel, messages, additionalData)
|
: base(canCancel, messages, additionalData)
|
||||||
{
|
{
|
||||||
EventObject = eventObject;
|
EventObject = eventObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages)
|
protected CancellableObjectEventArgs(object eventObject, bool canCancel, EventMessages eventMessages)
|
||||||
: base(canCancel, eventMessages)
|
: base(canCancel, eventMessages)
|
||||||
{
|
{
|
||||||
EventObject = eventObject;
|
EventObject = eventObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages)
|
protected CancellableObjectEventArgs(object eventObject, EventMessages eventMessages)
|
||||||
: this(eventObject, true, eventMessages)
|
: this(eventObject, true, eventMessages)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected CancellableObjectEventArgs(object eventObject, bool canCancel)
|
||||||
|
: base(canCancel)
|
||||||
|
{
|
||||||
|
EventObject = eventObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CancellableObjectEventArgs(object eventObject)
|
||||||
|
: this(eventObject, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the object relating to the event
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is protected so that inheritors can expose it with their own name
|
||||||
|
/// </remarks>
|
||||||
|
internal object EventObject { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event args for a strongly typed object that can support cancellation
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
||||||
|
public class CancellableObjectEventArgs<T> : CancellableObjectEventArgs, IEquatable<CancellableObjectEventArgs<T>>
|
||||||
|
{
|
||||||
|
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||||
|
: base(eventObject, canCancel, messages, additionalData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages)
|
||||||
|
: base(eventObject, canCancel, eventMessages)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages)
|
||||||
|
: base(eventObject, eventMessages)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public CancellableObjectEventArgs(T eventObject, bool canCancel)
|
public CancellableObjectEventArgs(T eventObject, bool canCancel)
|
||||||
: base(canCancel)
|
: base(eventObject, canCancel)
|
||||||
{
|
{
|
||||||
EventObject = eventObject;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public CancellableObjectEventArgs(T eventObject)
|
public CancellableObjectEventArgs(T eventObject)
|
||||||
: this(eventObject, true)
|
: base(eventObject)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the object relating to the event
|
/// Returns the object relating to the event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This is protected so that inheritors can expose it with their own name
|
/// This is protected so that inheritors can expose it with their own name
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected T EventObject { get; set; }
|
protected new T EventObject
|
||||||
|
{
|
||||||
|
get { return (T) base.EventObject; }
|
||||||
|
set { base.EventObject = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public bool Equals(CancellableObjectEventArgs<T> other)
|
public bool Equals(CancellableObjectEventArgs<T> other)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, other)) return false;
|
if (ReferenceEquals(null, other)) return false;
|
||||||
if (ReferenceEquals(this, other)) return true;
|
if (ReferenceEquals(this, other)) return true;
|
||||||
return base.Equals(other) && EqualityComparer<T>.Default.Equals(EventObject, other.EventObject);
|
return base.Equals(other) && EqualityComparer<T>.Default.Equals(EventObject, other.EventObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
if (ReferenceEquals(this, obj)) return true;
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
if (obj.GetType() != this.GetType()) return false;
|
if (obj.GetType() != this.GetType()) return false;
|
||||||
return Equals((CancellableObjectEventArgs<T>) obj);
|
return Equals((CancellableObjectEventArgs<T>)obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
unchecked
|
unchecked
|
||||||
{
|
{
|
||||||
return (base.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(EventObject);
|
return (base.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(EventObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
public static bool operator ==(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||||
{
|
{
|
||||||
return Equals(left, right);
|
return Equals(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator !=(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
public static bool operator !=(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||||
{
|
{
|
||||||
return !Equals(left, right);
|
return !Equals(left, right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
||||||
|
public class CancellableEnumerableObjectEventArgs<T> : CancellableObjectEventArgs<IEnumerable<T>>, IEquatable<CancellableEnumerableObjectEventArgs<T>>
|
||||||
|
{
|
||||||
|
public CancellableEnumerableObjectEventArgs(IEnumerable<T> eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||||
|
: base(eventObject, canCancel, messages, additionalData)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public CancellableEnumerableObjectEventArgs(IEnumerable<T> eventObject, bool canCancel, EventMessages eventMessages)
|
||||||
|
: base(eventObject, canCancel, eventMessages)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public CancellableEnumerableObjectEventArgs(IEnumerable<T> eventObject, EventMessages eventMessages)
|
||||||
|
: base(eventObject, eventMessages)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public CancellableEnumerableObjectEventArgs(IEnumerable<T> eventObject, bool canCancel)
|
||||||
|
: base(eventObject, canCancel)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public CancellableEnumerableObjectEventArgs(IEnumerable<T> eventObject)
|
||||||
|
: base(eventObject)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public bool Equals(CancellableEnumerableObjectEventArgs<T> other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
|
||||||
|
return EventObject.SequenceEqual(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((CancellableEnumerableObjectEventArgs<T>)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCodeHelper.GetHashCode(EventObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>, IDeletingMediaFilesEventArgs
|
[SupersedeEvent(typeof(SaveEventArgs<>))]
|
||||||
|
[SupersedeEvent(typeof(PublishEventArgs<>))]
|
||||||
|
[SupersedeEvent(typeof(MoveEventArgs<>))]
|
||||||
|
[SupersedeEvent(typeof(CopyEventArgs<>))]
|
||||||
|
public class DeleteEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<DeleteEventArgs<TEntity>>, IDeletingMediaFilesEventArgs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor accepting multiple entities that are used in the delete operation
|
/// Constructor accepting multiple entities that are used in the delete operation
|
||||||
@@ -106,7 +111,7 @@ namespace Umbraco.Core.Events
|
|||||||
{
|
{
|
||||||
if (ReferenceEquals(null, other)) return false;
|
if (ReferenceEquals(null, other)) return false;
|
||||||
if (ReferenceEquals(this, other)) return true;
|
if (ReferenceEquals(this, other)) return true;
|
||||||
return base.Equals(other) && MediaFilesToDelete.Equals(other.MediaFilesToDelete);
|
return base.Equals(other) && MediaFilesToDelete.SequenceEqual(other.MediaFilesToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Xml.Linq;
|
|||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
public class ImportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportEventArgs<TEntity>>
|
public class ImportEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportEventArgs<TEntity>>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor accepting an XElement with the xml being imported
|
/// Constructor accepting an XElement with the xml being imported
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Packaging.Models;
|
|||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportPackageEventArgs<TEntity>>
|
internal class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
|
||||||
{
|
{
|
||||||
private readonly MetaData _packageMetaData;
|
private readonly MetaData _packageMetaData;
|
||||||
|
|
||||||
@@ -32,7 +32,8 @@ namespace Umbraco.Core.Events
|
|||||||
public bool Equals(ImportPackageEventArgs<TEntity> other)
|
public bool Equals(ImportPackageEventArgs<TEntity> other)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, other)) return false;
|
if (ReferenceEquals(null, other)) return false;
|
||||||
if (ReferenceEquals(this, other)) return true;
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
//TODO: MetaData for package metadata has no equality operators :/
|
||||||
return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData);
|
return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<PublishEventArgs<TEntity>>
|
public class PublishEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<PublishEventArgs<TEntity>>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor accepting multiple entities that are used in the publish operation
|
/// Constructor accepting multiple entities that are used in the publish operation
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
public class SaveEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
public class SaveEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor accepting multiple entities that are used in the saving operation
|
/// Constructor accepting multiple entities that are used in the saving operation
|
||||||
@@ -116,7 +117,5 @@ namespace Umbraco.Core.Events
|
|||||||
{
|
{
|
||||||
get { return EventObject; }
|
get { return EventObject; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Umbraco.Core.Models.EntityBase;
|
||||||
|
|
||||||
namespace Umbraco.Core.Events
|
namespace Umbraco.Core.Events
|
||||||
{
|
{
|
||||||
@@ -15,6 +17,7 @@ namespace Umbraco.Core.Events
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public abstract class ScopeEventDispatcherBase : IEventDispatcher
|
public abstract class ScopeEventDispatcherBase : IEventDispatcher
|
||||||
{
|
{
|
||||||
|
//events will be enlisted in the order they are raised
|
||||||
private List<IEventDefinition> _events;
|
private List<IEventDefinition> _events;
|
||||||
private readonly bool _raiseCancelable;
|
private readonly bool _raiseCancelable;
|
||||||
|
|
||||||
@@ -73,29 +76,263 @@ namespace Umbraco.Core.Events
|
|||||||
{
|
{
|
||||||
if (_events == null)
|
if (_events == null)
|
||||||
return Enumerable.Empty<IEventDefinition>();
|
return Enumerable.Empty<IEventDefinition>();
|
||||||
|
|
||||||
switch (filter)
|
switch (filter)
|
||||||
{
|
{
|
||||||
case EventDefinitionFilter.All:
|
case EventDefinitionFilter.All:
|
||||||
return _events;
|
return FilterSupersededAndUpdateToLatestEntity(_events);
|
||||||
case EventDefinitionFilter.FirstIn:
|
case EventDefinitionFilter.FirstIn:
|
||||||
var l1 = new OrderedHashSet<IEventDefinition>();
|
var l1 = new OrderedHashSet<IEventDefinition>();
|
||||||
foreach (var e in _events)
|
foreach (var e in _events)
|
||||||
{
|
{
|
||||||
l1.Add(e);
|
l1.Add(e);
|
||||||
}
|
}
|
||||||
return l1;
|
return FilterSupersededAndUpdateToLatestEntity(l1);
|
||||||
case EventDefinitionFilter.LastIn:
|
case EventDefinitionFilter.LastIn:
|
||||||
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
|
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
|
||||||
foreach (var e in _events)
|
foreach (var e in _events)
|
||||||
{
|
{
|
||||||
l2.Add(e);
|
l2.Add(e);
|
||||||
}
|
}
|
||||||
return l2;
|
return FilterSupersededAndUpdateToLatestEntity(l2);
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException("filter", filter, null);
|
throw new ArgumentOutOfRangeException("filter", filter, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class EventDefinitionTypeData
|
||||||
|
{
|
||||||
|
public IEventDefinition EventDefinition { get; set; }
|
||||||
|
public Type EventArgType { get; set; }
|
||||||
|
public SupersedeEventAttribute[] SupersedeAttributes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will iterate over the events (latest first) and filter out any events or entities in event args that are included
|
||||||
|
/// in more recent events that Supersede previous ones. For example, If an Entity has been Saved and then Deleted, we don't want
|
||||||
|
/// to raise the Saved event (well actually we just don't want to include it in the args for that saved event)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="events"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static IEnumerable<IEventDefinition> FilterSupersededAndUpdateToLatestEntity(IReadOnlyList<IEventDefinition> events)
|
||||||
|
{
|
||||||
|
//used to keep the 'latest' entity and associated event definition data
|
||||||
|
var allEntities = new List<Tuple<IEntity, EventDefinitionTypeData>>();
|
||||||
|
|
||||||
|
//tracks all CancellableObjectEventArgs instances in the events which is the only type of args we can work with
|
||||||
|
var cancelableArgs = new List<CancellableObjectEventArgs>();
|
||||||
|
|
||||||
|
var result = new List<IEventDefinition>();
|
||||||
|
|
||||||
|
//This will eagerly load all of the event arg types and their attributes so we don't have to continuously look this data up
|
||||||
|
var allArgTypesWithAttributes = events.Select(x => x.Args.GetType())
|
||||||
|
.Distinct()
|
||||||
|
.ToDictionary(x => x, x => x.GetCustomAttributes<SupersedeEventAttribute>(false).ToArray());
|
||||||
|
|
||||||
|
//Iterate all events and collect the actual entities in them and relates them to their corresponding EventDefinitionTypeData
|
||||||
|
//we'll process the list in reverse because events are added in the order they are raised and we want to filter out
|
||||||
|
//any entities from event args that are not longer relevant
|
||||||
|
//(i.e. if an item is Deleted after it's Saved, we won't include the item in the Saved args)
|
||||||
|
for (var index = events.Count - 1; index >= 0; index--)
|
||||||
|
{
|
||||||
|
var eventDefinition = events[index];
|
||||||
|
|
||||||
|
var argType = eventDefinition.Args.GetType();
|
||||||
|
var attributes = allArgTypesWithAttributes[eventDefinition.Args.GetType()];
|
||||||
|
|
||||||
|
var meta = new EventDefinitionTypeData
|
||||||
|
{
|
||||||
|
EventDefinition = eventDefinition,
|
||||||
|
EventArgType = argType,
|
||||||
|
SupersedeAttributes = attributes
|
||||||
|
};
|
||||||
|
|
||||||
|
var args = eventDefinition.Args as CancellableObjectEventArgs;
|
||||||
|
if (args != null)
|
||||||
|
{
|
||||||
|
var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
//extract the event object
|
||||||
|
var obj = args.EventObject as IEntity;
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
//Now check if this entity already exists in other event args that supersede this current event arg type
|
||||||
|
if (IsFiltered(obj, meta, allEntities) == false)
|
||||||
|
{
|
||||||
|
//if it's not filtered we can adde these args to the response
|
||||||
|
cancelableArgs.Add(args);
|
||||||
|
result.Add(eventDefinition);
|
||||||
|
//track the entity
|
||||||
|
allEntities.Add(Tuple.Create(obj, meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Can't retrieve the entity so cant' filter or inspect, just add to the output
|
||||||
|
result.Add(eventDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var toRemove = new List<IEntity>();
|
||||||
|
foreach (var entity in list)
|
||||||
|
{
|
||||||
|
//extract the event object
|
||||||
|
var obj = entity as IEntity;
|
||||||
|
if (obj != null)
|
||||||
|
{
|
||||||
|
//Now check if this entity already exists in other event args that supersede this current event arg type
|
||||||
|
if (IsFiltered(obj, meta, allEntities))
|
||||||
|
{
|
||||||
|
//track it to be removed
|
||||||
|
toRemove.Add(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//track the entity, it's not filtered
|
||||||
|
allEntities.Add(Tuple.Create(obj, meta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//we don't need to do anything here, we can't cast to IEntity so we cannot filter, so it will just remain in the list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove anything that has been filtered
|
||||||
|
foreach (var entity in toRemove)
|
||||||
|
{
|
||||||
|
list.Remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
//track the event and include in the response if there's still entities remaining in the list
|
||||||
|
if (list.Count > 0)
|
||||||
|
{
|
||||||
|
if (toRemove.Count > 0)
|
||||||
|
{
|
||||||
|
//re-assign if the items have changed
|
||||||
|
args.EventObject = list;
|
||||||
|
}
|
||||||
|
cancelableArgs.Add(args);
|
||||||
|
result.Add(eventDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//it's not a cancelable event arg so we just include it in the result
|
||||||
|
result.Add(eventDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args
|
||||||
|
UpdateToLatestEntities(allEntities, cancelableArgs);
|
||||||
|
|
||||||
|
//we need to reverse the result since we've been adding by latest added events first!
|
||||||
|
result.Reverse();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateToLatestEntities(IEnumerable<Tuple<IEntity, EventDefinitionTypeData>> allEntities, IEnumerable<CancellableObjectEventArgs> cancelableArgs)
|
||||||
|
{
|
||||||
|
//Now we'll deal with ensuring that only the latest(non stale) entities are used throughout all event args
|
||||||
|
|
||||||
|
var latestEntities = new OrderedHashSet<IEntity>(keepOldest: true);
|
||||||
|
foreach (var entity in allEntities.OrderByDescending(entity => entity.Item1.UpdateDate))
|
||||||
|
{
|
||||||
|
latestEntities.Add(entity.Item1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var args in cancelableArgs)
|
||||||
|
{
|
||||||
|
var list = TypeHelper.CreateGenericEnumerableFromObject(args.EventObject);
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
//try to find the args entity in the latest entity - based on the equality operators, this will
|
||||||
|
//match by Id since that is the default equality checker for IEntity. If one is found, than it is
|
||||||
|
//the most recent entity instance so update the args with that instance so we don't emit a stale instance.
|
||||||
|
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, args.EventObject));
|
||||||
|
if (foundEntity != null)
|
||||||
|
{
|
||||||
|
args.EventObject = foundEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var updated = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
//try to find the args entity in the latest entity - based on the equality operators, this will
|
||||||
|
//match by Id since that is the default equality checker for IEntity. If one is found, than it is
|
||||||
|
//the most recent entity instance so update the args with that instance so we don't emit a stale instance.
|
||||||
|
var foundEntity = latestEntities.FirstOrDefault(x => Equals(x, list[i]));
|
||||||
|
if (foundEntity != null)
|
||||||
|
{
|
||||||
|
list[i] = foundEntity;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated)
|
||||||
|
{
|
||||||
|
args.EventObject = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will check against all of the processed entity/events (allEntities) to see if this entity already exists in
|
||||||
|
/// event args that supersede the event args being passed in and if so returns true.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <param name="eventDef"></param>
|
||||||
|
/// <param name="allEntities"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static bool IsFiltered(
|
||||||
|
IEntity entity,
|
||||||
|
EventDefinitionTypeData eventDef,
|
||||||
|
List<Tuple<IEntity, EventDefinitionTypeData>> allEntities)
|
||||||
|
{
|
||||||
|
var argType = eventDef.EventDefinition.Args.GetType();
|
||||||
|
|
||||||
|
//check if the entity is found in any processed event data that could possible supersede this one
|
||||||
|
var foundByEntity = allEntities
|
||||||
|
.Where(x => x.Item2.SupersedeAttributes.Length > 0
|
||||||
|
//if it's the same arg type than it cannot supersede
|
||||||
|
&& x.Item2.EventArgType != argType
|
||||||
|
&& Equals(x.Item1, entity))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
//no args have been processed with this entity so it should not be filtered
|
||||||
|
if (foundByEntity.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (argType.IsGenericType)
|
||||||
|
{
|
||||||
|
var supercededBy = foundByEntity
|
||||||
|
.FirstOrDefault(x =>
|
||||||
|
x.Item2.SupersedeAttributes.Any(y =>
|
||||||
|
//if the attribute type is a generic type def then compare with the generic type def of the event arg
|
||||||
|
(y.SupersededEventArgsType.IsGenericTypeDefinition && y.SupersededEventArgsType == argType.GetGenericTypeDefinition())
|
||||||
|
//if the attribute type is not a generic type def then compare with the normal type of the event arg
|
||||||
|
|| (y.SupersededEventArgsType.IsGenericTypeDefinition == false && y.SupersededEventArgsType == argType)));
|
||||||
|
return supercededBy != null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var supercededBy = foundByEntity
|
||||||
|
.FirstOrDefault(x =>
|
||||||
|
x.Item2.SupersedeAttributes.Any(y =>
|
||||||
|
//since the event arg type is not a generic type, then we just compare type 1:1
|
||||||
|
y.SupersededEventArgsType == argType));
|
||||||
|
return supercededBy != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ScopeExit(bool completed)
|
public void ScopeExit(bool completed)
|
||||||
{
|
{
|
||||||
|
|||||||
20
src/Umbraco.Core/Events/SupersedeEventAttribute.cs
Normal file
20
src/Umbraco.Core/Events/SupersedeEventAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Events
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is used to know if the event arg attributed should supersede another event arg type when
|
||||||
|
/// tracking events for the same entity. If one event args supercedes another then the event args that have been superseded
|
||||||
|
/// will mean that the event will not be dispatched or the args will be filtered to exclude the entity.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||||
|
internal class SupersedeEventAttribute : Attribute
|
||||||
|
{
|
||||||
|
public Type SupersededEventArgsType { get; private set; }
|
||||||
|
|
||||||
|
public SupersedeEventAttribute(Type supersededEventArgsType)
|
||||||
|
{
|
||||||
|
SupersededEventArgsType = supersededEventArgsType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -7,32 +6,32 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Umbraco.Core
|
namespace Umbraco.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to create a hash code from multiple objects.
|
/// Used to create a hash code from multiple objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// .Net has a class the same as this: System.Web.Util.HashCodeCombiner and of course it works for all sorts of things
|
/// .Net has a class the same as this: System.Web.Util.HashCodeCombiner and of course it works for all sorts of things
|
||||||
/// which we've not included here as we just need a quick easy class for this in order to create a unique
|
/// which we've not included here as we just need a quick easy class for this in order to create a unique
|
||||||
/// hash of directories/files to see if they have changed.
|
/// hash of directories/files to see if they have changed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal class HashCodeCombiner
|
internal class HashCodeCombiner
|
||||||
{
|
{
|
||||||
private long _combinedHash = 5381L;
|
private long _combinedHash = 5381L;
|
||||||
|
|
||||||
internal void AddInt(int i)
|
internal void AddInt(int i)
|
||||||
{
|
{
|
||||||
_combinedHash = ((_combinedHash << 5) + _combinedHash) ^ i;
|
_combinedHash = ((_combinedHash << 5) + _combinedHash) ^ i;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddObject(object o)
|
internal void AddObject(object o)
|
||||||
{
|
{
|
||||||
AddInt(o.GetHashCode());
|
AddInt(o.GetHashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddDateTime(DateTime d)
|
internal void AddDateTime(DateTime d)
|
||||||
{
|
{
|
||||||
AddInt(d.GetHashCode());
|
AddInt(d.GetHashCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddString(string s)
|
internal void AddString(string s)
|
||||||
{
|
{
|
||||||
@@ -40,61 +39,61 @@ namespace Umbraco.Core
|
|||||||
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
|
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddCaseInsensitiveString(string s)
|
internal void AddCaseInsensitiveString(string s)
|
||||||
{
|
{
|
||||||
if (s != null)
|
if (s != null)
|
||||||
AddInt((StringComparer.InvariantCultureIgnoreCase).GetHashCode(s));
|
AddInt((StringComparer.InvariantCultureIgnoreCase).GetHashCode(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddFileSystemItem(FileSystemInfo f)
|
internal void AddFileSystemItem(FileSystemInfo f)
|
||||||
{
|
{
|
||||||
//if it doesn't exist, don't proceed.
|
//if it doesn't exist, don't proceed.
|
||||||
if (!f.Exists)
|
if (!f.Exists)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddCaseInsensitiveString(f.FullName);
|
AddCaseInsensitiveString(f.FullName);
|
||||||
AddDateTime(f.CreationTimeUtc);
|
AddDateTime(f.CreationTimeUtc);
|
||||||
AddDateTime(f.LastWriteTimeUtc);
|
AddDateTime(f.LastWriteTimeUtc);
|
||||||
|
|
||||||
//check if it is a file or folder
|
|
||||||
var fileInfo = f as FileInfo;
|
|
||||||
if (fileInfo != null)
|
|
||||||
{
|
|
||||||
AddInt(fileInfo.Length.GetHashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
var dirInfo = f as DirectoryInfo;
|
|
||||||
if (dirInfo != null)
|
|
||||||
{
|
|
||||||
foreach (var d in dirInfo.GetFiles())
|
|
||||||
{
|
|
||||||
AddFile(d);
|
|
||||||
}
|
|
||||||
foreach (var s in dirInfo.GetDirectories())
|
|
||||||
{
|
|
||||||
AddFolder(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddFile(FileInfo f)
|
//check if it is a file or folder
|
||||||
{
|
var fileInfo = f as FileInfo;
|
||||||
AddFileSystemItem(f);
|
if (fileInfo != null)
|
||||||
}
|
{
|
||||||
|
AddInt(fileInfo.Length.GetHashCode());
|
||||||
|
}
|
||||||
|
|
||||||
internal void AddFolder(DirectoryInfo d)
|
var dirInfo = f as DirectoryInfo;
|
||||||
{
|
if (dirInfo != null)
|
||||||
AddFileSystemItem(d);
|
{
|
||||||
}
|
foreach (var d in dirInfo.GetFiles())
|
||||||
|
{
|
||||||
|
AddFile(d);
|
||||||
|
}
|
||||||
|
foreach (var s in dirInfo.GetDirectories())
|
||||||
|
{
|
||||||
|
AddFolder(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
internal void AddFile(FileInfo f)
|
||||||
/// Returns the hex code of the combined hash code
|
{
|
||||||
/// </summary>
|
AddFileSystemItem(f);
|
||||||
/// <returns></returns>
|
}
|
||||||
internal string GetCombinedHashCode()
|
|
||||||
{
|
|
||||||
return _combinedHash.ToString("x", CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
internal void AddFolder(DirectoryInfo d)
|
||||||
|
{
|
||||||
|
AddFileSystemItem(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the hex code of the combined hash code
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal string GetCombinedHashCode()
|
||||||
|
{
|
||||||
|
return _combinedHash.ToString("x", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
104
src/Umbraco.Core/HashCodeHelper.cs
Normal file
104
src/Umbraco.Core/HashCodeHelper.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Umbraco.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Borrowed from http://stackoverflow.com/a/2575444/694494
|
||||||
|
/// </summary>
|
||||||
|
internal static class HashCodeHelper
|
||||||
|
{
|
||||||
|
public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
return 31 * arg1.GetHashCode() + arg2.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = arg1.GetHashCode();
|
||||||
|
hash = 31 * hash + arg2.GetHashCode();
|
||||||
|
return 31 * hash + arg3.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3,
|
||||||
|
T4 arg4)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = arg1.GetHashCode();
|
||||||
|
hash = 31 * hash + arg2.GetHashCode();
|
||||||
|
hash = 31 * hash + arg3.GetHashCode();
|
||||||
|
return 31 * hash + arg4.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetHashCode<T>(T[] list)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
hash = 31 * hash + item.GetHashCode();
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetHashCode<T>(IEnumerable<T> list)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
hash = 31 * hash + item.GetHashCode();
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a hashcode for a collection for that the order of items
|
||||||
|
/// does not matter.
|
||||||
|
/// So {1, 2, 3} and {3, 2, 1} will get same hash code.
|
||||||
|
/// </summary>
|
||||||
|
public static int GetHashCodeForOrderNoMatterCollection<T>(
|
||||||
|
IEnumerable<T> list)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
int count = 0;
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
if (item == null) continue;
|
||||||
|
hash += item.GetHashCode();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return 31 * hash + count.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Alternative way to get a hashcode is to use a fluent
|
||||||
|
/// interface like this:<br />
|
||||||
|
/// return 0.CombineHashCode(field1).CombineHashCode(field2).
|
||||||
|
/// CombineHashCode(field3);
|
||||||
|
/// </summary>
|
||||||
|
public static int CombineHashCode<T>(this int hashCode, T arg)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
return 31 * hashCode + arg.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ namespace Umbraco.Core.Models
|
|||||||
public Guid Key { get; set; }
|
public Guid Key { get; set; }
|
||||||
public DateTime CreateDate { get; set; }
|
public DateTime CreateDate { get; set; }
|
||||||
public DateTime UpdateDate { get; set; }
|
public DateTime UpdateDate { get; set; }
|
||||||
|
public DateTime? DeletedDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Special case, always return false, this will cause the repositories managing
|
/// Special case, always return false, this will cause the repositories managing
|
||||||
@@ -60,5 +61,7 @@ namespace Umbraco.Core.Models
|
|||||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,9 @@ namespace Umbraco.Core.Models.EntityBase
|
|||||||
set { SetPropertyValueAndDetectChanges(value, ref _updateDate, Ps.Value.UpdateDateSelector); }
|
set { SetPropertyValueAndDetectChanges(value, ref _updateDate, Ps.Value.UpdateDateSelector); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public DateTime? DeletedDate { get; set; }
|
||||||
|
|
||||||
internal virtual void ResetIdentity()
|
internal virtual void ResetIdentity()
|
||||||
{
|
{
|
||||||
_hasIdentity = false;
|
_hasIdentity = false;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marker interface for aggregate roots
|
/// Marker interface for aggregate roots
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAggregateRoot : IEntity
|
public interface IAggregateRoot : IDeletableEntity
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/Umbraco.Core/Models/EntityBase/IDeletableEntity.cs
Normal file
11
src/Umbraco.Core/Models/EntityBase/IDeletableEntity.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Models.EntityBase
|
||||||
|
{
|
||||||
|
public interface IDeletableEntity : IEntity
|
||||||
|
{
|
||||||
|
[DataMember]
|
||||||
|
DateTime? DeletedDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.EntityBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Guid based Id
|
/// Guid based Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>The key is currectly used to store the Unique Id from the
|
/// <remarks>The key is currectly used to store the Unique Id from the
|
||||||
/// umbracoNode table, which many of the entities are based on.</remarks>
|
/// umbracoNode table, which many of the entities are based on.</remarks>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
Guid Key { get; set; }
|
Guid Key { get; set; }
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core
|
|||||||
{
|
{
|
||||||
private readonly bool _keepOldest;
|
private readonly bool _keepOldest;
|
||||||
|
|
||||||
public OrderedHashSet(bool keepOldest = true)
|
public OrderedHashSet(bool keepOldest = true)
|
||||||
{
|
{
|
||||||
_keepOldest = keepOldest;
|
_keepOldest = keepOldest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
|||||||
{
|
{
|
||||||
//Remove 'published' xml from the cmsContentXml table for the unpublished content
|
//Remove 'published' xml from the cmsContentXml table for the unpublished content
|
||||||
Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
||||||
|
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PersistNewItem(ContentXmlEntity<TContent> entity)
|
protected override void PersistNewItem(ContentXmlEntity<TContent> entity)
|
||||||
|
|||||||
@@ -270,6 +270,8 @@ AND umbracoNode.id <> @id",
|
|||||||
|
|
||||||
//Delete (base) node data
|
//Delete (base) node data
|
||||||
Database.Delete<NodeDto>("WHERE uniqueID = @Id", new { Id = entity.Key });
|
Database.Delete<NodeDto>("WHERE uniqueID = @Id", new { Id = entity.Key });
|
||||||
|
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -539,6 +541,8 @@ AND umbracoNode.id <> @id",
|
|||||||
Database.Execute(
|
Database.Execute(
|
||||||
"DELETE FROM cmsDataTypePreValues WHERE id=@Id",
|
"DELETE FROM cmsDataTypePreValues WHERE id=@Id",
|
||||||
new { Id = entity.Id });
|
new { Id = entity.Id });
|
||||||
|
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PersistNewItem(PreValueEntity entity)
|
protected override void PersistNewItem(PreValueEntity entity)
|
||||||
|
|||||||
@@ -195,6 +195,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
|||||||
//Clear the cache entries that exist by uniqueid/item key
|
//Clear the cache entries that exist by uniqueid/item key
|
||||||
IsolatedCache.ClearCacheItem(GetCacheIdKey<IDictionaryItem>(entity.ItemKey));
|
IsolatedCache.ClearCacheItem(GetCacheIdKey<IDictionaryItem>(entity.ItemKey));
|
||||||
IsolatedCache.ClearCacheItem(GetCacheIdKey<IDictionaryItem>(entity.Key));
|
IsolatedCache.ClearCacheItem(GetCacheIdKey<IDictionaryItem>(entity.Key));
|
||||||
|
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RecursiveDelete(Guid parentId)
|
private void RecursiveDelete(Guid parentId)
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
|||||||
|
|
||||||
// delete
|
// delete
|
||||||
Database.Delete(nodeDto);
|
Database.Delete(nodeDto);
|
||||||
|
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PersistNewItem(EntityContainer entity)
|
protected override void PersistNewItem(EntityContainer entity)
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
|||||||
{
|
{
|
||||||
Database.Execute(delete, new { Id = GetEntityId(entity) });
|
Database.Execute(delete, new { Id = GetEntityId(entity) });
|
||||||
}
|
}
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +311,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
|||||||
var masterpageName = string.Concat(entity.Alias, ".master");
|
var masterpageName = string.Concat(entity.Alias, ".master");
|
||||||
_masterpagesFileSystem.DeleteFile(masterpageName);
|
_masterpagesFileSystem.DeleteFile(masterpageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entity.DeletedDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -166,8 +166,8 @@ namespace Umbraco.Core
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is of generic type] [the specified type].
|
/// Determines whether [is of generic type] [the specified type].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@@ -15,11 +16,51 @@ namespace Umbraco.Core
|
|||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache
|
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache
|
||||||
= new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
|
= new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
|
||||||
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache
|
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache
|
||||||
= new ConcurrentDictionary<Type, FieldInfo[]>();
|
= new ConcurrentDictionary<Type, FieldInfo[]>();
|
||||||
|
|
||||||
private static readonly Assembly[] EmptyAssemblies = new Assembly[0];
|
private static readonly Assembly[] EmptyAssemblies = new Assembly[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too.
|
||||||
|
/// If it cannot be done, null is returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static IList CreateGenericEnumerableFromObject(object obj)
|
||||||
|
{
|
||||||
|
var type = obj.GetType();
|
||||||
|
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
var genericTypeDef = type.GetGenericTypeDefinition();
|
||||||
|
|
||||||
|
if (genericTypeDef == typeof(IEnumerable<>)
|
||||||
|
|| genericTypeDef == typeof(ICollection<>)
|
||||||
|
|| genericTypeDef == typeof(Collection<>)
|
||||||
|
|| genericTypeDef == typeof(IList<>)
|
||||||
|
|| genericTypeDef == typeof(List<>)
|
||||||
|
//this will occur when Linq is used and we get the odd WhereIterator or DistinctIterators since those are special iterator types
|
||||||
|
|| obj is IEnumerable)
|
||||||
|
{
|
||||||
|
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
|
||||||
|
var genericType = typeof(List<>).MakeGenericType(type.GetGenericArguments());
|
||||||
|
//pass in obj to fill the list
|
||||||
|
return (IList)Activator.CreateInstance(genericType, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.IsArray)
|
||||||
|
{
|
||||||
|
//if its an array, we'll use a List<>
|
||||||
|
var genericType = typeof(List<>).MakeGenericType(type.GetElementType());
|
||||||
|
//pass in obj to fill the list
|
||||||
|
return (IList)Activator.CreateInstance(genericType, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the method is actually overriding a base method
|
/// Checks if the method is actually overriding a base method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -45,8 +86,8 @@ namespace Umbraco.Core
|
|||||||
if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly())
|
if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly())
|
||||||
return EmptyAssemblies;
|
return EmptyAssemblies;
|
||||||
|
|
||||||
|
|
||||||
// find all assembly references that are referencing the current type's assembly since we
|
// find all assembly references that are referencing the current type's assembly since we
|
||||||
// should only be scanning those assemblies because any other assembly will definitely not
|
// should only be scanning those assemblies because any other assembly will definitely not
|
||||||
// contain sub type's of the one we're currently looking for
|
// contain sub type's of the one we're currently looking for
|
||||||
var name = assembly.GetName().Name;
|
var name = assembly.GetName().Name;
|
||||||
|
|||||||
@@ -339,8 +339,11 @@
|
|||||||
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
|
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
|
||||||
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
|
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
|
||||||
<Compile Include="Events\ScopeLifespanMessagesFactory.cs" />
|
<Compile Include="Events\ScopeLifespanMessagesFactory.cs" />
|
||||||
|
<Compile Include="Events\SupersedeEventAttribute.cs" />
|
||||||
<Compile Include="Exceptions\ConnectionException.cs" />
|
<Compile Include="Exceptions\ConnectionException.cs" />
|
||||||
|
<Compile Include="HashCodeHelper.cs" />
|
||||||
<Compile Include="IHttpContextAccessor.cs" />
|
<Compile Include="IHttpContextAccessor.cs" />
|
||||||
|
<Compile Include="Models\EntityBase\IDeletableEntity.cs" />
|
||||||
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
|
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
|
||||||
<Compile Include="OrderedHashSet.cs" />
|
<Compile Include="OrderedHashSet.cs" />
|
||||||
<Compile Include="Events\UninstallPackageEventArgs.cs" />
|
<Compile Include="Events\UninstallPackageEventArgs.cs" />
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Models.Membership;
|
|||||||
using Umbraco.Core.Services;
|
using Umbraco.Core.Services;
|
||||||
using Umbraco.Tests.TestHelpers;
|
using Umbraco.Tests.TestHelpers;
|
||||||
using Umbraco.Web.Cache;
|
using Umbraco.Web.Cache;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Umbraco.Tests.Cache
|
namespace Umbraco.Tests.Cache
|
||||||
{
|
{
|
||||||
@@ -31,70 +32,70 @@ namespace Umbraco.Tests.Cache
|
|||||||
new EventDefinition<ISectionService, EventArgs>(null, ServiceContext.SectionService, new EventArgs(), "Deleted"),
|
new EventDefinition<ISectionService, EventArgs>(null, ServiceContext.SectionService, new EventArgs(), "Deleted"),
|
||||||
new EventDefinition<ISectionService, EventArgs>(null, ServiceContext.SectionService, new EventArgs(), "New"),
|
new EventDefinition<ISectionService, EventArgs>(null, ServiceContext.SectionService, new EventArgs(), "New"),
|
||||||
|
|
||||||
new EventDefinition<IUserService, SaveEventArgs<IUserType>>(null, ServiceContext.UserService, new SaveEventArgs<IUserType>((IUserType) null)),
|
new EventDefinition<IUserService, SaveEventArgs<IUserType>>(null, ServiceContext.UserService, new SaveEventArgs<IUserType>(Enumerable.Empty<IUserType>())),
|
||||||
new EventDefinition<IUserService, DeleteEventArgs<IUserType>>(null, ServiceContext.UserService, new DeleteEventArgs<IUserType>((IUserType) null)),
|
new EventDefinition<IUserService, DeleteEventArgs<IUserType>>(null, ServiceContext.UserService, new DeleteEventArgs<IUserType>(Enumerable.Empty<IUserType>())),
|
||||||
|
|
||||||
new EventDefinition<IUserService, SaveEventArgs<IUser>>(null, ServiceContext.UserService, new SaveEventArgs<IUser>((IUser) null)),
|
new EventDefinition<IUserService, SaveEventArgs<IUser>>(null, ServiceContext.UserService, new SaveEventArgs<IUser>(Enumerable.Empty<IUser>())),
|
||||||
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, ServiceContext.UserService, new DeleteEventArgs<IUser>((IUser) null)),
|
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, ServiceContext.UserService, new DeleteEventArgs<IUser>(Enumerable.Empty<IUser>())),
|
||||||
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, ServiceContext.UserService, new DeleteEventArgs<IUser>((IUser) null)),
|
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, ServiceContext.UserService, new DeleteEventArgs<IUser>(Enumerable.Empty<IUser>())),
|
||||||
|
|
||||||
new EventDefinition<ILocalizationService, SaveEventArgs<IDictionaryItem>>(null, ServiceContext.LocalizationService, new SaveEventArgs<IDictionaryItem>((IDictionaryItem) null)),
|
new EventDefinition<ILocalizationService, SaveEventArgs<IDictionaryItem>>(null, ServiceContext.LocalizationService, new SaveEventArgs<IDictionaryItem>(Enumerable.Empty<IDictionaryItem>())),
|
||||||
new EventDefinition<ILocalizationService, DeleteEventArgs<IDictionaryItem>>(null, ServiceContext.LocalizationService, new DeleteEventArgs<IDictionaryItem>((IDictionaryItem) null)),
|
new EventDefinition<ILocalizationService, DeleteEventArgs<IDictionaryItem>>(null, ServiceContext.LocalizationService, new DeleteEventArgs<IDictionaryItem>(Enumerable.Empty<IDictionaryItem>())),
|
||||||
|
|
||||||
new EventDefinition<IDataTypeService, SaveEventArgs<IDataTypeDefinition>>(null, ServiceContext.DataTypeService, new SaveEventArgs<IDataTypeDefinition>((IDataTypeDefinition) null)),
|
new EventDefinition<IDataTypeService, SaveEventArgs<IDataTypeDefinition>>(null, ServiceContext.DataTypeService, new SaveEventArgs<IDataTypeDefinition>(Enumerable.Empty<IDataTypeDefinition>())),
|
||||||
new EventDefinition<IDataTypeService, DeleteEventArgs<IDataTypeDefinition>>(null, ServiceContext.DataTypeService, new DeleteEventArgs<IDataTypeDefinition>((IDataTypeDefinition) null)),
|
new EventDefinition<IDataTypeService, DeleteEventArgs<IDataTypeDefinition>>(null, ServiceContext.DataTypeService, new DeleteEventArgs<IDataTypeDefinition>(Enumerable.Empty<IDataTypeDefinition>())),
|
||||||
|
|
||||||
new EventDefinition<IFileService, SaveEventArgs<Stylesheet>>(null, ServiceContext.FileService, new SaveEventArgs<Stylesheet>((Stylesheet) null)),
|
new EventDefinition<IFileService, SaveEventArgs<Stylesheet>>(null, ServiceContext.FileService, new SaveEventArgs<Stylesheet>(Enumerable.Empty<Stylesheet>())),
|
||||||
new EventDefinition<IFileService, DeleteEventArgs<Stylesheet>>(null, ServiceContext.FileService, new DeleteEventArgs<Stylesheet>((Stylesheet) null)),
|
new EventDefinition<IFileService, DeleteEventArgs<Stylesheet>>(null, ServiceContext.FileService, new DeleteEventArgs<Stylesheet>(Enumerable.Empty<Stylesheet>())),
|
||||||
|
|
||||||
new EventDefinition<IDomainService, SaveEventArgs<IDomain>>(null, ServiceContext.DomainService, new SaveEventArgs<IDomain>((IDomain) null)),
|
new EventDefinition<IDomainService, SaveEventArgs<IDomain>>(null, ServiceContext.DomainService, new SaveEventArgs<IDomain>(Enumerable.Empty<IDomain>())),
|
||||||
new EventDefinition<IDomainService, DeleteEventArgs<IDomain>>(null, ServiceContext.DomainService, new DeleteEventArgs<IDomain>((IDomain) null)),
|
new EventDefinition<IDomainService, DeleteEventArgs<IDomain>>(null, ServiceContext.DomainService, new DeleteEventArgs<IDomain>(Enumerable.Empty<IDomain>())),
|
||||||
|
|
||||||
new EventDefinition<ILocalizationService, SaveEventArgs<ILanguage>>(null, ServiceContext.LocalizationService, new SaveEventArgs<ILanguage>((ILanguage) null)),
|
new EventDefinition<ILocalizationService, SaveEventArgs<ILanguage>>(null, ServiceContext.LocalizationService, new SaveEventArgs<ILanguage>(Enumerable.Empty<ILanguage>())),
|
||||||
new EventDefinition<ILocalizationService, DeleteEventArgs<ILanguage>>(null, ServiceContext.LocalizationService, new DeleteEventArgs<ILanguage>((ILanguage) null)),
|
new EventDefinition<ILocalizationService, DeleteEventArgs<ILanguage>>(null, ServiceContext.LocalizationService, new DeleteEventArgs<ILanguage>(Enumerable.Empty<ILanguage>())),
|
||||||
|
|
||||||
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IContentType>((IContentType) null)),
|
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>())),
|
||||||
new EventDefinition<IContentTypeService, DeleteEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new DeleteEventArgs<IContentType>((IContentType) null)),
|
new EventDefinition<IContentTypeService, DeleteEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new DeleteEventArgs<IContentType>(Enumerable.Empty<IContentType>())),
|
||||||
new EventDefinition<IContentTypeService, SaveEventArgs<IMediaType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IMediaType>((IMediaType) null)),
|
new EventDefinition<IContentTypeService, SaveEventArgs<IMediaType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IMediaType>(Enumerable.Empty<IMediaType>())),
|
||||||
new EventDefinition<IContentTypeService, DeleteEventArgs<IMediaType>>(null, ServiceContext.ContentTypeService, new DeleteEventArgs<IMediaType>((IMediaType) null)),
|
new EventDefinition<IContentTypeService, DeleteEventArgs<IMediaType>>(null, ServiceContext.ContentTypeService, new DeleteEventArgs<IMediaType>(Enumerable.Empty<IMediaType>())),
|
||||||
|
|
||||||
new EventDefinition<IMemberTypeService, SaveEventArgs<IMemberType>>(null, ServiceContext.MemberTypeService, new SaveEventArgs<IMemberType>((IMemberType) null)),
|
new EventDefinition<IMemberTypeService, SaveEventArgs<IMemberType>>(null, ServiceContext.MemberTypeService, new SaveEventArgs<IMemberType>(Enumerable.Empty<IMemberType>())),
|
||||||
new EventDefinition<IMemberTypeService, DeleteEventArgs<IMemberType>>(null, ServiceContext.MemberTypeService, new DeleteEventArgs<IMemberType>((IMemberType) null)),
|
new EventDefinition<IMemberTypeService, DeleteEventArgs<IMemberType>>(null, ServiceContext.MemberTypeService, new DeleteEventArgs<IMemberType>(Enumerable.Empty<IMemberType>())),
|
||||||
|
|
||||||
new EventDefinition<IFileService, SaveEventArgs<ITemplate>>(null, ServiceContext.FileService, new SaveEventArgs<ITemplate>((ITemplate) null)),
|
new EventDefinition<IFileService, SaveEventArgs<ITemplate>>(null, ServiceContext.FileService, new SaveEventArgs<ITemplate>(Enumerable.Empty<ITemplate>())),
|
||||||
new EventDefinition<IFileService, DeleteEventArgs<ITemplate>>(null, ServiceContext.FileService, new DeleteEventArgs<ITemplate>((ITemplate) null)),
|
new EventDefinition<IFileService, DeleteEventArgs<ITemplate>>(null, ServiceContext.FileService, new DeleteEventArgs<ITemplate>(Enumerable.Empty<ITemplate>())),
|
||||||
|
|
||||||
new EventDefinition<IMacroService, SaveEventArgs<IMacro>>(null, ServiceContext.MacroService, new SaveEventArgs<IMacro>((IMacro) null)),
|
new EventDefinition<IMacroService, SaveEventArgs<IMacro>>(null, ServiceContext.MacroService, new SaveEventArgs<IMacro>(Enumerable.Empty<IMacro>())),
|
||||||
new EventDefinition<IMacroService, DeleteEventArgs<IMacro>>(null, ServiceContext.MacroService, new DeleteEventArgs<IMacro>((IMacro) null)),
|
new EventDefinition<IMacroService, DeleteEventArgs<IMacro>>(null, ServiceContext.MacroService, new DeleteEventArgs<IMacro>(Enumerable.Empty<IMacro>())),
|
||||||
|
|
||||||
new EventDefinition<IMemberService, SaveEventArgs<IMember>>(null, ServiceContext.MemberService, new SaveEventArgs<IMember>((IMember) null)),
|
new EventDefinition<IMemberService, SaveEventArgs<IMember>>(null, ServiceContext.MemberService, new SaveEventArgs<IMember>(Enumerable.Empty<IMember>())),
|
||||||
new EventDefinition<IMemberService, DeleteEventArgs<IMember>>(null, ServiceContext.MemberService, new DeleteEventArgs<IMember>((IMember) null)),
|
new EventDefinition<IMemberService, DeleteEventArgs<IMember>>(null, ServiceContext.MemberService, new DeleteEventArgs<IMember>(Enumerable.Empty<IMember>())),
|
||||||
|
|
||||||
new EventDefinition<IMemberGroupService, SaveEventArgs<IMemberGroup>>(null, ServiceContext.MemberGroupService, new SaveEventArgs<IMemberGroup>((IMemberGroup) null)),
|
new EventDefinition<IMemberGroupService, SaveEventArgs<IMemberGroup>>(null, ServiceContext.MemberGroupService, new SaveEventArgs<IMemberGroup>(Enumerable.Empty<IMemberGroup>())),
|
||||||
new EventDefinition<IMemberGroupService, DeleteEventArgs<IMemberGroup>>(null, ServiceContext.MemberGroupService, new DeleteEventArgs<IMemberGroup>((IMemberGroup) null)),
|
new EventDefinition<IMemberGroupService, DeleteEventArgs<IMemberGroup>>(null, ServiceContext.MemberGroupService, new DeleteEventArgs<IMemberGroup>(Enumerable.Empty<IMemberGroup>())),
|
||||||
|
|
||||||
new EventDefinition<IMediaService, SaveEventArgs<IMedia>>(null, ServiceContext.MediaService, new SaveEventArgs<IMedia>((IMedia) null)),
|
new EventDefinition<IMediaService, SaveEventArgs<IMedia>>(null, ServiceContext.MediaService, new SaveEventArgs<IMedia>(Enumerable.Empty<IMedia>())),
|
||||||
new EventDefinition<IMediaService, DeleteEventArgs<IMedia>>(null, ServiceContext.MediaService, new DeleteEventArgs<IMedia>((IMedia) null)),
|
new EventDefinition<IMediaService, DeleteEventArgs<IMedia>>(null, ServiceContext.MediaService, new DeleteEventArgs<IMedia>(Enumerable.Empty<IMedia>())),
|
||||||
new EventDefinition<IMediaService, MoveEventArgs<IMedia>>(null, ServiceContext.MediaService, new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(null, "", -1)), "Moved"),
|
new EventDefinition<IMediaService, MoveEventArgs<IMedia>>(null, ServiceContext.MediaService, new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(null, "", -1)), "Moved"),
|
||||||
new EventDefinition<IMediaService, MoveEventArgs<IMedia>>(null, ServiceContext.MediaService, new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(null, "", -1)), "Trashed"),
|
new EventDefinition<IMediaService, MoveEventArgs<IMedia>>(null, ServiceContext.MediaService, new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(null, "", -1)), "Trashed"),
|
||||||
new EventDefinition<IMediaService, RecycleBinEventArgs>(null, ServiceContext.MediaService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary<int, IEnumerable<Property>>(), true)),
|
new EventDefinition<IMediaService, RecycleBinEventArgs>(null, ServiceContext.MediaService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary<int, IEnumerable<Property>>(), true)),
|
||||||
|
|
||||||
new EventDefinition<IContentService, SaveEventArgs<IContent>>(null, ServiceContext.ContentService, new SaveEventArgs<IContent>((IContent) null)),
|
new EventDefinition<IContentService, SaveEventArgs<IContent>>(null, ServiceContext.ContentService, new SaveEventArgs<IContent>(Enumerable.Empty<IContent>())),
|
||||||
new EventDefinition<IContentService, DeleteEventArgs<IContent>>(null, ServiceContext.ContentService, new DeleteEventArgs<IContent>((IContent) null)),
|
new EventDefinition<IContentService, DeleteEventArgs<IContent>>(null, ServiceContext.ContentService, new DeleteEventArgs<IContent>(Enumerable.Empty<IContent>())),
|
||||||
new EventDefinition<IContentService, CopyEventArgs<IContent>>(null, ServiceContext.ContentService, new CopyEventArgs<IContent>(null, null, -1)),
|
new EventDefinition<IContentService, CopyEventArgs<IContent>>(null, ServiceContext.ContentService, new CopyEventArgs<IContent>(null, null, -1)),
|
||||||
new EventDefinition<IContentService, MoveEventArgs<IContent>>(null, ServiceContext.ContentService, new MoveEventArgs<IContent>(new MoveEventInfo<IContent>(null, "", -1)), "Trashed"),
|
new EventDefinition<IContentService, MoveEventArgs<IContent>>(null, ServiceContext.ContentService, new MoveEventArgs<IContent>(new MoveEventInfo<IContent>(null, "", -1)), "Trashed"),
|
||||||
new EventDefinition<IContentService, RecycleBinEventArgs>(null, ServiceContext.ContentService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary<int, IEnumerable<Property>>(), true)),
|
new EventDefinition<IContentService, RecycleBinEventArgs>(null, ServiceContext.ContentService, new RecycleBinEventArgs(Guid.NewGuid(), new Dictionary<int, IEnumerable<Property>>(), true)),
|
||||||
new EventDefinition<IContentService, PublishEventArgs<IContent>>(null, ServiceContext.ContentService, new PublishEventArgs<IContent>((IContent) null), "Published"),
|
new EventDefinition<IContentService, PublishEventArgs<IContent>>(null, ServiceContext.ContentService, new PublishEventArgs<IContent>(Enumerable.Empty<IContent>()), "Published"),
|
||||||
new EventDefinition<IContentService, PublishEventArgs<IContent>>(null, ServiceContext.ContentService, new PublishEventArgs<IContent>((IContent) null), "UnPublished"),
|
new EventDefinition<IContentService, PublishEventArgs<IContent>>(null, ServiceContext.ContentService, new PublishEventArgs<IContent>(Enumerable.Empty<IContent>()), "UnPublished"),
|
||||||
|
|
||||||
new EventDefinition<IPublicAccessService, SaveEventArgs<PublicAccessEntry>>(null, ServiceContext.PublicAccessService, new SaveEventArgs<PublicAccessEntry>((PublicAccessEntry) null)),
|
new EventDefinition<IPublicAccessService, SaveEventArgs<PublicAccessEntry>>(null, ServiceContext.PublicAccessService, new SaveEventArgs<PublicAccessEntry>(Enumerable.Empty<PublicAccessEntry>())),
|
||||||
new EventDefinition<IPublicAccessService, DeleteEventArgs<PublicAccessEntry>>(null, ServiceContext.PublicAccessService, new DeleteEventArgs<PublicAccessEntry>((PublicAccessEntry) null)),
|
new EventDefinition<IPublicAccessService, DeleteEventArgs<PublicAccessEntry>>(null, ServiceContext.PublicAccessService, new DeleteEventArgs<PublicAccessEntry>(Enumerable.Empty<PublicAccessEntry>())),
|
||||||
|
|
||||||
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, ServiceContext.RelationService, new SaveEventArgs<IRelationType>((IRelationType) null)),
|
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, ServiceContext.RelationService, new SaveEventArgs<IRelationType>(Enumerable.Empty<IRelationType>())),
|
||||||
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, ServiceContext.RelationService, new DeleteEventArgs<IRelationType>((IRelationType) null)),
|
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, ServiceContext.RelationService, new DeleteEventArgs<IRelationType>(Enumerable.Empty<IRelationType>())),
|
||||||
|
|
||||||
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, ServiceContext.RelationService, new SaveEventArgs<IRelationType>((IRelationType) null)),
|
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, ServiceContext.RelationService, new SaveEventArgs<IRelationType>(Enumerable.Empty<IRelationType>())),
|
||||||
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, ServiceContext.RelationService, new DeleteEventArgs<IRelationType>((IRelationType) null)),
|
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, ServiceContext.RelationService, new DeleteEventArgs<IRelationType>(Enumerable.Empty<IRelationType>())),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var definition in definitions)
|
foreach (var definition in definitions)
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ using System.Linq;
|
|||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Umbraco.Core.Events;
|
using Umbraco.Core.Events;
|
||||||
|
using Umbraco.Core.Models;
|
||||||
using Umbraco.Core.Persistence;
|
using Umbraco.Core.Persistence;
|
||||||
using Umbraco.Core.Scoping;
|
using Umbraco.Core.Scoping;
|
||||||
|
using Umbraco.Tests.TestHelpers;
|
||||||
|
using Umbraco.Tests.TestHelpers.Entities;
|
||||||
|
|
||||||
namespace Umbraco.Tests.Scoping
|
namespace Umbraco.Tests.Scoping
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ScopeEventDispatcherTests
|
public class ScopeEventDispatcherTests : BaseUmbracoConfigurationTest
|
||||||
{
|
{
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
@@ -79,7 +82,7 @@ namespace Umbraco.Tests.Scoping
|
|||||||
var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray();
|
var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray();
|
||||||
|
|
||||||
var knownNames = new[] { "DoThing1", "DoThing2", "DoThing3" };
|
var knownNames = new[] { "DoThing1", "DoThing2", "DoThing3" };
|
||||||
var knownArgTypes = new[] { typeof (SaveEventArgs<string>), typeof (SaveEventArgs<int>), typeof (SaveEventArgs<decimal>) };
|
var knownArgTypes = new[] { typeof(SaveEventArgs<string>), typeof(SaveEventArgs<int>), typeof(SaveEventArgs<decimal>) };
|
||||||
|
|
||||||
for (var i = 0; i < events.Length; i++)
|
for (var i = 0; i < events.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -89,6 +92,166 @@ namespace Umbraco.Tests.Scoping
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SupersededEvents()
|
||||||
|
{
|
||||||
|
DoSaveForContent += OnDoThingFail;
|
||||||
|
DoDeleteForContent += OnDoThingFail;
|
||||||
|
DoForTestArgs += OnDoThingFail;
|
||||||
|
DoForTestArgs2 += OnDoThingFail;
|
||||||
|
|
||||||
|
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||||
|
|
||||||
|
var content1 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content1.Id = 123;
|
||||||
|
|
||||||
|
var content2 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content2.Id = 456;
|
||||||
|
|
||||||
|
var content3 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content3.Id = 789;
|
||||||
|
|
||||||
|
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||||
|
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
|
||||||
|
{
|
||||||
|
|
||||||
|
//content1 will be filtered from the args
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(new[]{ content1 , content3}));
|
||||||
|
scope.Events.Dispatch(DoDeleteForContent, this, new DeleteEventArgs<IContent>(content1));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content2));
|
||||||
|
//this entire event will be filtered
|
||||||
|
scope.Events.Dispatch(DoForTestArgs, this, new TestEventArgs(content1));
|
||||||
|
scope.Events.Dispatch(DoForTestArgs2, this, new TestEventArgs2(content1));
|
||||||
|
|
||||||
|
// events have been queued
|
||||||
|
var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray();
|
||||||
|
Assert.AreEqual(4, events.Length);
|
||||||
|
|
||||||
|
Assert.AreEqual(typeof(SaveEventArgs<IContent>), events[0].Args.GetType());
|
||||||
|
Assert.AreEqual(1, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.Count());
|
||||||
|
Assert.AreEqual(content3.Id, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.First().Id);
|
||||||
|
|
||||||
|
Assert.AreEqual(typeof(DeleteEventArgs<IContent>), events[1].Args.GetType());
|
||||||
|
Assert.AreEqual(content1.Id, ((DeleteEventArgs<IContent>) events[1].Args).DeletedEntities.First().Id);
|
||||||
|
|
||||||
|
Assert.AreEqual(typeof(SaveEventArgs<IContent>), events[2].Args.GetType());
|
||||||
|
Assert.AreEqual(content2.Id, ((SaveEventArgs<IContent>)events[2].Args).SavedEntities.First().Id);
|
||||||
|
|
||||||
|
Assert.AreEqual(typeof(TestEventArgs2), events[3].Args.GetType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will test that when we track events that before we Get the events we normalize all of the
|
||||||
|
/// event entities to be the latest one (most current) found amongst the event so that there is
|
||||||
|
/// no 'stale' entities in any of the args
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void LatestEntities()
|
||||||
|
{
|
||||||
|
DoSaveForContent += OnDoThingFail;
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||||
|
var content1 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content1.Id = 123;
|
||||||
|
content1.UpdateDate = now.AddMinutes(1);
|
||||||
|
var content2 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content2.Id = 123;
|
||||||
|
content2.UpdateDate = now.AddMinutes(2);
|
||||||
|
var content3 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content3.Id = 123;
|
||||||
|
content3.UpdateDate = now.AddMinutes(3);
|
||||||
|
|
||||||
|
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||||
|
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
|
||||||
|
{
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content1));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content2));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content3));
|
||||||
|
|
||||||
|
// events have been queued
|
||||||
|
var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray();
|
||||||
|
Assert.AreEqual(3, events.Length);
|
||||||
|
|
||||||
|
foreach (var t in events)
|
||||||
|
{
|
||||||
|
var args = (SaveEventArgs<IContent>)t.Args;
|
||||||
|
foreach (var entity in args.SavedEntities)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(content3, entity);
|
||||||
|
Assert.IsTrue(object.ReferenceEquals(content3, entity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FirstIn()
|
||||||
|
{
|
||||||
|
DoSaveForContent += OnDoThingFail;
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||||
|
var content1 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content1.Id = 123;
|
||||||
|
content1.UpdateDate = now.AddMinutes(1);
|
||||||
|
var content2 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content2.Id = 123;
|
||||||
|
content1.UpdateDate = now.AddMinutes(2);
|
||||||
|
var content3 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content3.Id = 123;
|
||||||
|
content1.UpdateDate = now.AddMinutes(3);
|
||||||
|
|
||||||
|
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||||
|
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
|
||||||
|
{
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content1));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content2));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content3));
|
||||||
|
|
||||||
|
// events have been queued
|
||||||
|
var events = scope.Events.GetEvents(EventDefinitionFilter.FirstIn).ToArray();
|
||||||
|
Assert.AreEqual(1, events.Length);
|
||||||
|
Assert.AreEqual(content1, ((SaveEventArgs<IContent>) events[0].Args).SavedEntities.First());
|
||||||
|
Assert.IsTrue(object.ReferenceEquals(content1, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.First()));
|
||||||
|
Assert.AreEqual(content1.UpdateDate, ((SaveEventArgs<IContent>) events[0].Args).SavedEntities.First().UpdateDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void LastIn()
|
||||||
|
{
|
||||||
|
DoSaveForContent += OnDoThingFail;
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||||
|
var content1 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content1.Id = 123;
|
||||||
|
content1.UpdateDate = now.AddMinutes(1);
|
||||||
|
var content2 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content2.Id = 123;
|
||||||
|
content2.UpdateDate = now.AddMinutes(2);
|
||||||
|
var content3 = MockedContent.CreateBasicContent(contentType);
|
||||||
|
content3.Id = 123;
|
||||||
|
content3.UpdateDate = now.AddMinutes(3);
|
||||||
|
|
||||||
|
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||||
|
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
|
||||||
|
{
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content1));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content2));
|
||||||
|
scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs<IContent>(content3));
|
||||||
|
|
||||||
|
// events have been queued
|
||||||
|
var events = scope.Events.GetEvents(EventDefinitionFilter.LastIn).ToArray();
|
||||||
|
Assert.AreEqual(1, events.Length);
|
||||||
|
Assert.AreEqual(content3, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.First());
|
||||||
|
Assert.IsTrue(object.ReferenceEquals(content3, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.First()));
|
||||||
|
Assert.AreEqual(content3.UpdateDate, ((SaveEventArgs<IContent>)events[0].Args).SavedEntities.First().UpdateDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void EventsDispatching_Passive(bool complete)
|
public void EventsDispatching_Passive(bool complete)
|
||||||
@@ -177,12 +340,41 @@ namespace Umbraco.Tests.Scoping
|
|||||||
Assert.Fail();
|
Assert.Fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static event EventHandler<SaveEventArgs<IContent>> DoSaveForContent;
|
||||||
|
public static event EventHandler<DeleteEventArgs<IContent>> DoDeleteForContent;
|
||||||
|
public static event EventHandler<TestEventArgs> DoForTestArgs;
|
||||||
|
public static event EventHandler<TestEventArgs2> DoForTestArgs2;
|
||||||
public static event EventHandler<SaveEventArgs<string>> DoThing1;
|
public static event EventHandler<SaveEventArgs<string>> DoThing1;
|
||||||
|
|
||||||
public static event EventHandler<SaveEventArgs<int>> DoThing2;
|
public static event EventHandler<SaveEventArgs<int>> DoThing2;
|
||||||
|
|
||||||
public static event TypedEventHandler<ScopeEventDispatcherTests, SaveEventArgs<decimal>> DoThing3;
|
public static event TypedEventHandler<ScopeEventDispatcherTests, SaveEventArgs<decimal>> DoThing3;
|
||||||
|
|
||||||
|
public class TestEventArgs : CancellableObjectEventArgs
|
||||||
|
{
|
||||||
|
public TestEventArgs(object eventObject) : base(eventObject)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public object MyEventObject
|
||||||
|
{
|
||||||
|
get { return EventObject; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupersedeEvent(typeof(TestEventArgs))]
|
||||||
|
public class TestEventArgs2 : CancellableObjectEventArgs
|
||||||
|
{
|
||||||
|
public TestEventArgs2(object eventObject) : base(eventObject)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public object MyEventObject
|
||||||
|
{
|
||||||
|
get { return EventObject; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class PassiveEventDispatcher : ScopeEventDispatcherBase
|
public class PassiveEventDispatcher : ScopeEventDispatcherBase
|
||||||
{
|
{
|
||||||
public PassiveEventDispatcher()
|
public PassiveEventDispatcher()
|
||||||
|
|||||||
@@ -179,7 +179,9 @@ namespace Umbraco.Tests.Scoping
|
|||||||
scope.Complete();
|
scope.Complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.AreEqual(complete ? 2 : 0, evented);
|
//The reason why there is only 1 event occuring is because we are publishing twice for the same event for the same
|
||||||
|
//object and the scope deduplicates the events (uses the latest)
|
||||||
|
Assert.AreEqual(complete ? 1 : 0, evented);
|
||||||
|
|
||||||
// this should never change
|
// this should never change
|
||||||
Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml);
|
Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(x => x.Snippet, exp => exp.Ignore());
|
.ForMember(x => x.Snippet, exp => exp.Ignore());
|
||||||
|
|
||||||
config.CreateMap<CodeFileDisplay, IPartialView>()
|
config.CreateMap<CodeFileDisplay, IPartialView>()
|
||||||
|
.ForMember(x => x.DeletedDate, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Id, exp => exp.Ignore())
|
.ForMember(x => x.Id, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Key, exp => exp.Ignore())
|
.ForMember(x => x.Key, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||||
@@ -40,6 +41,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(x => x.HasIdentity, exp => exp.Ignore());
|
.ForMember(x => x.HasIdentity, exp => exp.Ignore());
|
||||||
|
|
||||||
config.CreateMap<CodeFileDisplay, Script>()
|
config.CreateMap<CodeFileDisplay, Script>()
|
||||||
|
.ForMember(x => x.DeletedDate, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Id, exp => exp.Ignore())
|
.ForMember(x => x.Id, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Key, exp => exp.Ignore())
|
.ForMember(x => x.Key, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(type => type.Key, expression => expression.Ignore())
|
.ForMember(type => type.Key, expression => expression.Ignore())
|
||||||
.ForMember(type => type.CreateDate, expression => expression.Ignore())
|
.ForMember(type => type.CreateDate, expression => expression.Ignore())
|
||||||
.ForMember(type => type.UpdateDate, expression => expression.Ignore())
|
.ForMember(type => type.UpdateDate, expression => expression.Ignore())
|
||||||
|
.ForMember(type => type.DeletedDate, expression => expression.Ignore())
|
||||||
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
|
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
|
||||||
|
|
||||||
config.CreateMap<DocumentTypeSave, IContentType>()
|
config.CreateMap<DocumentTypeSave, IContentType>()
|
||||||
@@ -72,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
config.CreateMap<MediaTypeSave, IMediaType>()
|
config.CreateMap<MediaTypeSave, IMediaType>()
|
||||||
//do the base mapping
|
//do the base mapping
|
||||||
.MapBaseContentTypeSaveToEntity<MediaTypeSave, PropertyTypeBasic, IMediaType>(applicationContext)
|
.MapBaseContentTypeSaveToEntity<MediaTypeSave, PropertyTypeBasic, IMediaType>(applicationContext)
|
||||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||||
.AfterMap((source, dest) =>
|
.AfterMap((source, dest) =>
|
||||||
{
|
{
|
||||||
ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, applicationContext);
|
ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, applicationContext);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(dest => dest.HasIdentity, map => map.Ignore())
|
.ForMember(dest => dest.HasIdentity, map => map.Ignore())
|
||||||
.ForMember(dest => dest.CreateDate, map => map.Ignore())
|
.ForMember(dest => dest.CreateDate, map => map.Ignore())
|
||||||
.ForMember(dest => dest.UpdateDate, map => map.Ignore())
|
.ForMember(dest => dest.UpdateDate, map => map.Ignore())
|
||||||
|
.ForMember(dest => dest.DeletedDate, map => map.Ignore())
|
||||||
.ForMember(dest => dest.PropertyTypes, map => map.Ignore());
|
.ForMember(dest => dest.PropertyTypes, map => map.Ignore());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +176,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
//These get persisted as part of the saving procedure, nothing to do with the display model
|
//These get persisted as part of the saving procedure, nothing to do with the display model
|
||||||
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())
|
||||||
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
.ForMember(dto => dto.UpdateDate, expression => expression.Ignore())
|
||||||
|
.ForMember(dto => dto.DeletedDate, expression => expression.Ignore())
|
||||||
|
|
||||||
.ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot))
|
.ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot))
|
||||||
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
|
.ForMember(dto => dto.CreatorId, expression => expression.Ignore())
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(x => x.Level, expression => expression.Ignore())
|
.ForMember(x => x.Level, expression => expression.Ignore())
|
||||||
.ForMember(x => x.SortOrder, expression => expression.Ignore())
|
.ForMember(x => x.SortOrder, expression => expression.Ignore())
|
||||||
.ForMember(x => x.CreateDate, expression => expression.Ignore())
|
.ForMember(x => x.CreateDate, expression => expression.Ignore())
|
||||||
|
.ForMember(x => x.DeletedDate, expression => expression.Ignore())
|
||||||
.ForMember(x => x.UpdateDate, expression => expression.Ignore());
|
.ForMember(x => x.UpdateDate, expression => expression.Ignore());
|
||||||
|
|
||||||
//Converts a property editor to a new list of pre-value fields - used when creating a new data type or changing a data type with new pre-vals
|
//Converts a property editor to a new list of pre-value fields - used when creating a new data type or changing a data type with new pre-vals
|
||||||
|
|||||||
@@ -55,8 +55,9 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(member => member.SortOrder, expression => expression.Ignore())
|
.ForMember(member => member.SortOrder, expression => expression.Ignore())
|
||||||
.ForMember(member => member.AdditionalData, expression => expression.Ignore())
|
.ForMember(member => member.AdditionalData, expression => expression.Ignore())
|
||||||
.ForMember(member => member.FailedPasswordAttempts, expression => expression.Ignore())
|
.ForMember(member => member.FailedPasswordAttempts, expression => expression.Ignore())
|
||||||
//TODO: Support these eventually
|
.ForMember(member => member.DeletedDate, expression => expression.Ignore())
|
||||||
.ForMember(member => member.PasswordQuestion, expression => expression.Ignore())
|
//TODO: Support these eventually
|
||||||
|
.ForMember(member => member.PasswordQuestion, expression => expression.Ignore())
|
||||||
.ForMember(member => member.RawPasswordAnswerValue, expression => expression.Ignore());
|
.ForMember(member => member.RawPasswordAnswerValue, expression => expression.Ignore());
|
||||||
|
|
||||||
//FROM IMember TO MediaItemDisplay
|
//FROM IMember TO MediaItemDisplay
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
.ForMember(x => x.Notifications, exp => exp.Ignore());
|
.ForMember(x => x.Notifications, exp => exp.Ignore());
|
||||||
|
|
||||||
config.CreateMap<TemplateDisplay, Template>()
|
config.CreateMap<TemplateDisplay, Template>()
|
||||||
|
.ForMember(x => x.DeletedDate, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Key, exp => exp.Ignore())
|
.ForMember(x => x.Key, exp => exp.Ignore())
|
||||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||||
.ForMember(x => x.CreateDate, exp => exp.Ignore())
|
.ForMember(x => x.CreateDate, exp => exp.Ignore())
|
||||||
|
|||||||
Reference in New Issue
Block a user