Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1853e8c51 | |||
| 31c965ae42 | |||
| d91fc6ce60 | |||
| d5124e9739 | |||
| d9b507c947 | |||
| d3fcdfb176 | |||
| 2479c5a272 | |||
| fee071db22 | |||
| 004e00599d | |||
| 2e2e8b43b0 | |||
| e77c5f8667 | |||
| 3509a2c40f | |||
| 378d0f7939 | |||
| d64fa7f92d | |||
| ea14170272 | |||
| 73209d7e2c | |||
| cf8ead62ac | |||
| 8461aea7d8 | |||
| 034de1651b | |||
| dcb8aa7e56 | |||
| e8a52a95da | |||
| 42c99e0f07 | |||
| a31b368b60 | |||
| 77f6d28a74 | |||
| 0b8d3ef214 | |||
| a4f83f9c00 | |||
| 85467976ad | |||
| d7e93fe466 | |||
| 660f048b4f | |||
| 9fa76a0133 | |||
| 444c444f0e | |||
| 383bbbe7e0 | |||
| 72eddca3c6 | |||
| 7c2ddde311 | |||
| c6229e4fcb | |||
| fbac0aba8b | |||
| f01511c592 | |||
| 187354f0bd | |||
| 2f540b1bfa | |||
| b1f104d1de | |||
| ba5fcad09a | |||
| d553eb2978 | |||
| d1e6c0e835 | |||
| 3242b31a8a | |||
| 1274fbaefd | |||
| 8140dfdc04 | |||
| a69eba61c8 | |||
| 46078a841c | |||
| 0eb5e5f6e4 | |||
| ec9020f63e | |||
| 0b61685fc8 | |||
| 3779b3c782 | |||
| 5b0e824301 | |||
| 9cd7e09986 | |||
| f737004544 | |||
| 850e354b11 | |||
| 0a2830aece | |||
| 23816665f3 | |||
| 13ccd86192 | |||
| 336266c0eb |
@@ -37,8 +37,8 @@
|
||||
<dependency id="Examine" version="[0.1.82, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.3, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.3, 5.0.0)" />
|
||||
<dependency id="semver" version="[2.0.4, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="semver" version="[1.1.2, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
|
||||
</dependencies>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.6.0
|
||||
RC3
|
||||
7.6.0
|
||||
+1
-1
@@ -12,4 +12,4 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.6.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-RC3")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0")]
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "RC3"; } }
|
||||
public static string CurrentComment { get { return ""; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -1,85 +1,177 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Permissions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <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> : CancellableEventArgs, IEquatable<CancellableObjectEventArgs<T>>
|
||||
{
|
||||
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||
/// <summary>
|
||||
/// Used as a base class for the generic type CancellableObjectEventArgs{T} so that we can get direct 'object' access to the underlying EventObject
|
||||
/// </summary>
|
||||
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
||||
public abstract class CancellableObjectEventArgs : CancellableEventArgs
|
||||
{
|
||||
protected CancellableObjectEventArgs(object eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||
: base(canCancel, messages, additionalData)
|
||||
{
|
||||
{
|
||||
EventObject = eventObject;
|
||||
}
|
||||
|
||||
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages)
|
||||
protected CancellableObjectEventArgs(object eventObject, bool canCancel, EventMessages eventMessages)
|
||||
: base(canCancel, eventMessages)
|
||||
{
|
||||
EventObject = eventObject;
|
||||
}
|
||||
|
||||
public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages)
|
||||
protected CancellableObjectEventArgs(object eventObject, EventMessages 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)
|
||||
: base(canCancel)
|
||||
{
|
||||
EventObject = eventObject;
|
||||
}
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public CancellableObjectEventArgs(T eventObject)
|
||||
: this(eventObject, true)
|
||||
{
|
||||
}
|
||||
public CancellableObjectEventArgs(T eventObject)
|
||||
: base(eventObject)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the object relating to the event
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is protected so that inheritors can expose it with their own name
|
||||
/// </remarks>
|
||||
protected T EventObject { get; set; }
|
||||
/// <summary>
|
||||
/// Returns the object relating to the event
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is protected so that inheritors can expose it with their own name
|
||||
/// </remarks>
|
||||
protected new T EventObject
|
||||
{
|
||||
get { return (T) base.EventObject; }
|
||||
set { base.EventObject = value; }
|
||||
}
|
||||
|
||||
public bool Equals(CancellableObjectEventArgs<T> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && EqualityComparer<T>.Default.Equals(EventObject, other.EventObject);
|
||||
}
|
||||
public bool Equals(CancellableObjectEventArgs<T> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && EqualityComparer<T>.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<T>) obj);
|
||||
}
|
||||
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<T>)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(EventObject);
|
||||
}
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(EventObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
public static bool operator ==(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
public static bool operator !=(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
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>
|
||||
/// 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(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)
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class ImportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportEventArgs<TEntity>>
|
||||
public class ImportEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportEventArgs<TEntity>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting an XElement with the xml being imported
|
||||
|
||||
@@ -4,7 +4,7 @@ using Umbraco.Core.Packaging.Models;
|
||||
|
||||
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;
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace Umbraco.Core.Events
|
||||
public bool Equals(ImportPackageEventArgs<TEntity> other)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<PublishEventArgs<TEntity>>
|
||||
public class PublishEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<PublishEventArgs<TEntity>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple entities that are used in the publish operation
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class SaveEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
public class SaveEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple entities that are used in the saving operation
|
||||
@@ -116,7 +117,5 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
@@ -15,6 +17,7 @@ namespace Umbraco.Core.Events
|
||||
/// </remarks>
|
||||
public abstract class ScopeEventDispatcherBase : IEventDispatcher
|
||||
{
|
||||
//events will be enlisted in the order they are raised
|
||||
private List<IEventDefinition> _events;
|
||||
private readonly bool _raiseCancelable;
|
||||
|
||||
@@ -73,29 +76,263 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
if (_events == null)
|
||||
return Enumerable.Empty<IEventDefinition>();
|
||||
|
||||
|
||||
switch (filter)
|
||||
{
|
||||
case EventDefinitionFilter.All:
|
||||
return _events;
|
||||
return FilterSupersededAndUpdateToLatestEntity(_events);
|
||||
case EventDefinitionFilter.FirstIn:
|
||||
var l1 = new OrderedHashSet<IEventDefinition>();
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l1.Add(e);
|
||||
}
|
||||
return l1;
|
||||
return FilterSupersededAndUpdateToLatestEntity(l1);
|
||||
case EventDefinitionFilter.LastIn:
|
||||
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l2.Add(e);
|
||||
}
|
||||
return l2;
|
||||
return FilterSupersededAndUpdateToLatestEntity(l2);
|
||||
default:
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -7,32 +6,32 @@ using System.Text;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to create a hash code from multiple objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// .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
|
||||
/// hash of directories/files to see if they have changed.
|
||||
/// </remarks>
|
||||
internal class HashCodeCombiner
|
||||
{
|
||||
private long _combinedHash = 5381L;
|
||||
/// <summary>
|
||||
/// Used to create a hash code from multiple objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// .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
|
||||
/// hash of directories/files to see if they have changed.
|
||||
/// </remarks>
|
||||
internal class HashCodeCombiner
|
||||
{
|
||||
private long _combinedHash = 5381L;
|
||||
|
||||
internal void AddInt(int i)
|
||||
{
|
||||
_combinedHash = ((_combinedHash << 5) + _combinedHash) ^ i;
|
||||
}
|
||||
internal void AddInt(int i)
|
||||
{
|
||||
_combinedHash = ((_combinedHash << 5) + _combinedHash) ^ i;
|
||||
}
|
||||
|
||||
internal void AddObject(object o)
|
||||
{
|
||||
AddInt(o.GetHashCode());
|
||||
}
|
||||
internal void AddObject(object o)
|
||||
{
|
||||
AddInt(o.GetHashCode());
|
||||
}
|
||||
|
||||
internal void AddDateTime(DateTime d)
|
||||
{
|
||||
AddInt(d.GetHashCode());
|
||||
}
|
||||
internal void AddDateTime(DateTime d)
|
||||
{
|
||||
AddInt(d.GetHashCode());
|
||||
}
|
||||
|
||||
internal void AddString(string s)
|
||||
{
|
||||
@@ -40,61 +39,61 @@ namespace Umbraco.Core
|
||||
AddInt((StringComparer.InvariantCulture).GetHashCode(s));
|
||||
}
|
||||
|
||||
internal void AddCaseInsensitiveString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
AddInt((StringComparer.InvariantCultureIgnoreCase).GetHashCode(s));
|
||||
}
|
||||
internal void AddCaseInsensitiveString(string s)
|
||||
{
|
||||
if (s != null)
|
||||
AddInt((StringComparer.InvariantCultureIgnoreCase).GetHashCode(s));
|
||||
}
|
||||
|
||||
internal void AddFileSystemItem(FileSystemInfo f)
|
||||
{
|
||||
//if it doesn't exist, don't proceed.
|
||||
if (!f.Exists)
|
||||
return;
|
||||
internal void AddFileSystemItem(FileSystemInfo f)
|
||||
{
|
||||
//if it doesn't exist, don't proceed.
|
||||
if (!f.Exists)
|
||||
return;
|
||||
|
||||
AddCaseInsensitiveString(f.FullName);
|
||||
AddDateTime(f.CreationTimeUtc);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
AddCaseInsensitiveString(f.FullName);
|
||||
AddDateTime(f.CreationTimeUtc);
|
||||
AddDateTime(f.LastWriteTimeUtc);
|
||||
|
||||
internal void AddFile(FileInfo f)
|
||||
{
|
||||
AddFileSystemItem(f);
|
||||
}
|
||||
//check if it is a file or folder
|
||||
var fileInfo = f as FileInfo;
|
||||
if (fileInfo != null)
|
||||
{
|
||||
AddInt(fileInfo.Length.GetHashCode());
|
||||
}
|
||||
|
||||
internal void AddFolder(DirectoryInfo d)
|
||||
{
|
||||
AddFileSystemItem(d);
|
||||
}
|
||||
var dirInfo = f as DirectoryInfo;
|
||||
if (dirInfo != null)
|
||||
{
|
||||
foreach (var d in dirInfo.GetFiles())
|
||||
{
|
||||
AddFile(d);
|
||||
}
|
||||
foreach (var s in dirInfo.GetDirectories())
|
||||
{
|
||||
AddFolder(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hex code of the combined hash code
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal string GetCombinedHashCode()
|
||||
{
|
||||
return _combinedHash.ToString("x", CultureInfo.InvariantCulture);
|
||||
}
|
||||
internal void AddFile(FileInfo f)
|
||||
{
|
||||
AddFileSystemItem(f);
|
||||
}
|
||||
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Special case, always return false, this will cause the repositories managing
|
||||
@@ -60,5 +61,7 @@ namespace Umbraco.Core.Models
|
||||
DeepCloneHelper.DeepCloneRefProperties(this, clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,9 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _updateDate, Ps.Value.UpdateDateSelector); }
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public DateTime? DeletedDate { get; set; }
|
||||
|
||||
internal virtual void ResetIdentity()
|
||||
{
|
||||
_hasIdentity = false;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Marker interface for aggregate roots
|
||||
/// </summary>
|
||||
public interface IAggregateRoot : IEntity
|
||||
public interface IAggregateRoot : IDeletableEntity
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
/// Guid based Id
|
||||
/// </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>
|
||||
[DataMember]
|
||||
Guid Key { get; set; }
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
private readonly bool _keepOldest;
|
||||
|
||||
public OrderedHashSet(bool keepOldest = true)
|
||||
public OrderedHashSet(bool keepOldest = true)
|
||||
{
|
||||
_keepOldest = keepOldest;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished content
|
||||
Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = entity.Id });
|
||||
|
||||
entity.DeletedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(ContentXmlEntity<TContent> entity)
|
||||
|
||||
@@ -270,6 +270,8 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
//Delete (base) node data
|
||||
Database.Delete<NodeDto>("WHERE uniqueID = @Id", new { Id = entity.Key });
|
||||
|
||||
entity.DeletedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -539,6 +541,8 @@ AND umbracoNode.id <> @id",
|
||||
Database.Execute(
|
||||
"DELETE FROM cmsDataTypePreValues WHERE id=@Id",
|
||||
new { Id = entity.Id });
|
||||
|
||||
entity.DeletedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
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
|
||||
IsolatedCache.ClearCacheItem(GetCacheIdKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.ClearCacheItem(GetCacheIdKey<IDictionaryItem>(entity.Key));
|
||||
|
||||
entity.DeletedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
private void RecursiveDelete(Guid parentId)
|
||||
|
||||
@@ -167,6 +167,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
// delete
|
||||
Database.Delete(nodeDto);
|
||||
|
||||
entity.DeletedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(EntityContainer entity)
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
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");
|
||||
_masterpagesFileSystem.DeleteFile(masterpageName);
|
||||
}
|
||||
|
||||
entity.DeletedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -191,6 +191,7 @@ namespace Umbraco.Core.Security
|
||||
|
||||
//track the last login date
|
||||
user.LastLoginDateUtc = DateTime.UtcNow;
|
||||
user.AccessFailedCount = 0;
|
||||
await UserManager.UpdateAsync(user);
|
||||
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
@@ -231,4 +232,4 @@ namespace Umbraco.Core.Security
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ namespace Umbraco.Core
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is of generic type] [the specified type].
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -15,11 +16,51 @@ namespace Umbraco.Core
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache
|
||||
= 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[]>();
|
||||
|
||||
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>
|
||||
/// Checks if the method is actually overriding a base method
|
||||
/// </summary>
|
||||
@@ -45,8 +86,8 @@ namespace Umbraco.Core
|
||||
if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly())
|
||||
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
|
||||
// contain sub type's of the one we're currently looking for
|
||||
var name = assembly.GetName().Name;
|
||||
|
||||
@@ -100,9 +100,8 @@
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Semver, Version=2.0.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Reference Include="Semver, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\semver.1.1.2\lib\net45\Semver.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
@@ -339,8 +338,11 @@
|
||||
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
|
||||
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
|
||||
<Compile Include="Events\ScopeLifespanMessagesFactory.cs" />
|
||||
<Compile Include="Events\SupersedeEventAttribute.cs" />
|
||||
<Compile Include="Exceptions\ConnectionException.cs" />
|
||||
<Compile Include="HashCodeHelper.cs" />
|
||||
<Compile Include="IHttpContextAccessor.cs" />
|
||||
<Compile Include="Models\EntityBase\IDeletableEntity.cs" />
|
||||
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
|
||||
<Compile Include="OrderedHashSet.cs" />
|
||||
<Compile Include="Events\UninstallPackageEventArgs.cs" />
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
<package id="MySql.Data" version="6.9.9" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
<package id="Semver" version="2.0.4" targetFramework="net45" />
|
||||
<package id="semver" version="1.1.2" targetFramework="net45" />
|
||||
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
|
||||
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web.Cache;
|
||||
using System.Linq;
|
||||
|
||||
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(), "New"),
|
||||
|
||||
new EventDefinition<IUserService, SaveEventArgs<IUserType>>(null, ServiceContext.UserService, new SaveEventArgs<IUserType>((IUserType) null)),
|
||||
new EventDefinition<IUserService, DeleteEventArgs<IUserType>>(null, ServiceContext.UserService, new DeleteEventArgs<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>(Enumerable.Empty<IUserType>())),
|
||||
|
||||
new EventDefinition<IUserService, SaveEventArgs<IUser>>(null, ServiceContext.UserService, new SaveEventArgs<IUser>((IUser) null)),
|
||||
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, ServiceContext.UserService, new DeleteEventArgs<IUser>((IUser) null)),
|
||||
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, ServiceContext.UserService, new DeleteEventArgs<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>(Enumerable.Empty<IUser>())),
|
||||
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, DeleteEventArgs<IDictionaryItem>>(null, ServiceContext.LocalizationService, new DeleteEventArgs<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>(Enumerable.Empty<IDictionaryItem>())),
|
||||
|
||||
new EventDefinition<IDataTypeService, SaveEventArgs<IDataTypeDefinition>>(null, ServiceContext.DataTypeService, new SaveEventArgs<IDataTypeDefinition>((IDataTypeDefinition) null)),
|
||||
new EventDefinition<IDataTypeService, DeleteEventArgs<IDataTypeDefinition>>(null, ServiceContext.DataTypeService, new DeleteEventArgs<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>(Enumerable.Empty<IDataTypeDefinition>())),
|
||||
|
||||
new EventDefinition<IFileService, SaveEventArgs<Stylesheet>>(null, ServiceContext.FileService, new SaveEventArgs<Stylesheet>((Stylesheet) null)),
|
||||
new EventDefinition<IFileService, DeleteEventArgs<Stylesheet>>(null, ServiceContext.FileService, new DeleteEventArgs<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>(Enumerable.Empty<Stylesheet>())),
|
||||
|
||||
new EventDefinition<IDomainService, SaveEventArgs<IDomain>>(null, ServiceContext.DomainService, new SaveEventArgs<IDomain>((IDomain) null)),
|
||||
new EventDefinition<IDomainService, DeleteEventArgs<IDomain>>(null, ServiceContext.DomainService, new DeleteEventArgs<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>(Enumerable.Empty<IDomain>())),
|
||||
|
||||
new EventDefinition<ILocalizationService, SaveEventArgs<ILanguage>>(null, ServiceContext.LocalizationService, new SaveEventArgs<ILanguage>((ILanguage) null)),
|
||||
new EventDefinition<ILocalizationService, DeleteEventArgs<ILanguage>>(null, ServiceContext.LocalizationService, new DeleteEventArgs<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>(Enumerable.Empty<ILanguage>())),
|
||||
|
||||
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IContentType>((IContentType) null)),
|
||||
new EventDefinition<IContentTypeService, DeleteEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new DeleteEventArgs<IContentType>((IContentType) null)),
|
||||
new EventDefinition<IContentTypeService, SaveEventArgs<IMediaType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IMediaType>((IMediaType) null)),
|
||||
new EventDefinition<IContentTypeService, DeleteEventArgs<IMediaType>>(null, ServiceContext.ContentTypeService, new DeleteEventArgs<IMediaType>((IMediaType) 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>(Enumerable.Empty<IContentType>())),
|
||||
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>(Enumerable.Empty<IMediaType>())),
|
||||
|
||||
new EventDefinition<IMemberTypeService, SaveEventArgs<IMemberType>>(null, ServiceContext.MemberTypeService, new SaveEventArgs<IMemberType>((IMemberType) null)),
|
||||
new EventDefinition<IMemberTypeService, DeleteEventArgs<IMemberType>>(null, ServiceContext.MemberTypeService, new DeleteEventArgs<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>(Enumerable.Empty<IMemberType>())),
|
||||
|
||||
new EventDefinition<IFileService, SaveEventArgs<ITemplate>>(null, ServiceContext.FileService, new SaveEventArgs<ITemplate>((ITemplate) null)),
|
||||
new EventDefinition<IFileService, DeleteEventArgs<ITemplate>>(null, ServiceContext.FileService, new DeleteEventArgs<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>(Enumerable.Empty<ITemplate>())),
|
||||
|
||||
new EventDefinition<IMacroService, SaveEventArgs<IMacro>>(null, ServiceContext.MacroService, new SaveEventArgs<IMacro>((IMacro) null)),
|
||||
new EventDefinition<IMacroService, DeleteEventArgs<IMacro>>(null, ServiceContext.MacroService, new DeleteEventArgs<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>(Enumerable.Empty<IMacro>())),
|
||||
|
||||
new EventDefinition<IMemberService, SaveEventArgs<IMember>>(null, ServiceContext.MemberService, new SaveEventArgs<IMember>((IMember) null)),
|
||||
new EventDefinition<IMemberService, DeleteEventArgs<IMember>>(null, ServiceContext.MemberService, new DeleteEventArgs<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>(Enumerable.Empty<IMember>())),
|
||||
|
||||
new EventDefinition<IMemberGroupService, SaveEventArgs<IMemberGroup>>(null, ServiceContext.MemberGroupService, new SaveEventArgs<IMemberGroup>((IMemberGroup) null)),
|
||||
new EventDefinition<IMemberGroupService, DeleteEventArgs<IMemberGroup>>(null, ServiceContext.MemberGroupService, new DeleteEventArgs<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>(Enumerable.Empty<IMemberGroup>())),
|
||||
|
||||
new EventDefinition<IMediaService, SaveEventArgs<IMedia>>(null, ServiceContext.MediaService, new SaveEventArgs<IMedia>((IMedia) null)),
|
||||
new EventDefinition<IMediaService, DeleteEventArgs<IMedia>>(null, ServiceContext.MediaService, new DeleteEventArgs<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>(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)), "Trashed"),
|
||||
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, DeleteEventArgs<IContent>>(null, ServiceContext.ContentService, new DeleteEventArgs<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>(Enumerable.Empty<IContent>())),
|
||||
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, 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>((IContent) null), "UnPublished"),
|
||||
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>(Enumerable.Empty<IContent>()), "UnPublished"),
|
||||
|
||||
new EventDefinition<IPublicAccessService, SaveEventArgs<PublicAccessEntry>>(null, ServiceContext.PublicAccessService, new SaveEventArgs<PublicAccessEntry>((PublicAccessEntry) null)),
|
||||
new EventDefinition<IPublicAccessService, DeleteEventArgs<PublicAccessEntry>>(null, ServiceContext.PublicAccessService, new DeleteEventArgs<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>(Enumerable.Empty<PublicAccessEntry>())),
|
||||
|
||||
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, ServiceContext.RelationService, new SaveEventArgs<IRelationType>((IRelationType) null)),
|
||||
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, ServiceContext.RelationService, new DeleteEventArgs<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>(Enumerable.Empty<IRelationType>())),
|
||||
|
||||
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, ServiceContext.RelationService, new SaveEventArgs<IRelationType>((IRelationType) null)),
|
||||
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, ServiceContext.RelationService, new DeleteEventArgs<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>(Enumerable.Empty<IRelationType>())),
|
||||
};
|
||||
|
||||
foreach (var definition in definitions)
|
||||
|
||||
@@ -4,13 +4,16 @@ using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
namespace Umbraco.Tests.Scoping
|
||||
{
|
||||
[TestFixture]
|
||||
public class ScopeEventDispatcherTests
|
||||
public class ScopeEventDispatcherTests : BaseUmbracoConfigurationTest
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
@@ -79,7 +82,7 @@ namespace Umbraco.Tests.Scoping
|
||||
var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray();
|
||||
|
||||
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++)
|
||||
{
|
||||
@@ -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(false)]
|
||||
public void EventsDispatching_Passive(bool complete)
|
||||
@@ -177,12 +340,41 @@ namespace Umbraco.Tests.Scoping
|
||||
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<int>> DoThing2;
|
||||
|
||||
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 PassiveEventDispatcher()
|
||||
|
||||
@@ -179,7 +179,9 @@ namespace Umbraco.Tests.Scoping
|
||||
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
|
||||
Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml);
|
||||
|
||||
@@ -97,9 +97,8 @@
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Semver, Version=2.0.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Reference Include="Semver, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\semver.1.1.2\lib\net45\Semver.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<package id="NUnit" version="2.6.2" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
<package id="Selenium.WebDriver" version="2.32.0" targetFramework="net45" />
|
||||
<package id="Semver" version="2.0.4" targetFramework="net45" />
|
||||
<package id="semver" version="1.1.2" targetFramework="net45" />
|
||||
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
|
||||
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net45" />
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
|
||||
<label><localize key="general_username">Username</localize></label>
|
||||
<input type="text" ng-model="login" name="username" class="-full-width-input" localize="placeholder" placeholder="@placeholders_username" />
|
||||
<input type="text" ng-model="login" name="username" class="-full-width-input" localize="placeholder" placeholder="@placeholders_usernameHint" />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
<key alias="releaseDate">Udgivelsesdato</key>
|
||||
<key alias="unpublishDate">Dato for Fortryd udgivelse</key>
|
||||
<key alias="removeDate">Fjern dato</key>
|
||||
<key alias="sortDone">Sorteringrækkefølgen er opdateret</key>
|
||||
<key alias="sortDone">Sorteringsrækkefølgen er opdateret</key>
|
||||
<key alias="sortHelp">For at sortere, træk siderne eller klik på en af kolonnehovederne. Du kan vælge flere sider ved at holde "shift" eller "control" nede mens du vælger.</key>
|
||||
<key alias="statistics">Statistik</key>
|
||||
<key alias="titleOptional">Titel (valgfri)</key>
|
||||
@@ -184,7 +184,7 @@
|
||||
<key alias="updateData">Vælg en type og skriv en titel</key>
|
||||
<key alias="noDocumentTypes" version="7.0"><![CDATA[Der kunne ikke findes nogen tilladte dokument typer. Du skal tillade disse i indstillinger under <strong>"dokument typer"</strong>.]]></key>
|
||||
<key alias="noMediaTypes" version="7.0"><![CDATA[Der kunne ikke findes nogen tilladte media typer. Du skal tillade disse i indstillinger under <strong>"media typer"</strong>.]]></key>
|
||||
<key alias="documentTypeWithoutTemplate">Dokument type uden skabelon</key>
|
||||
<key alias="documentTypeWithoutTemplate">Dokumenttype uden skabelon</key>
|
||||
<key alias="newFolder">Ny mappe</key>
|
||||
<key alias="newDataType">Ny datatype</key>
|
||||
</area>
|
||||
@@ -321,6 +321,7 @@
|
||||
<key alias="search">Søg...</key>
|
||||
<key alias="filter">Filtrer...</key>
|
||||
<key alias="enterTags">Indtast nøgleord (tryk på Enter efter hvert nøgleord)...</key>
|
||||
<key alias="usernameHint">Dit brugernavn er typisk din e-mail adresse</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="allowAtRoot" version="7.2">Tillad på rodniveau</key>
|
||||
@@ -579,13 +580,13 @@
|
||||
<key alias="permissionsAffectedFolders">Berørte filer og foldere</key>
|
||||
<key alias="permissionsAffectedFoldersMoreInfo">Flere informationer om at opsætte rettigheder for Umbraco her</key>
|
||||
<key alias="permissionsAffectedFoldersText">Du er nødt til at give ASP.NET 'modify' rettigheder på følgende filer/foldere</key>
|
||||
<key alias="permissionsAlmostPerfect"><![CDATA[<strong>Dine rettighedsinstillinger er næsten perfekte!</strong><br/><br/>Du kan køre Umbraco uden problemer, men du vil ikke være i stand til at installere pakker, som er anbefalet for at få fuldt udbytte af Umbraco.]]></key>
|
||||
<key alias="permissionsAlmostPerfect"><![CDATA[<strong>Dine rettighedsindstillinger er næsten perfekte!</strong><br/><br/>Du kan køre Umbraco uden problemer, men du vil ikke være i stand til at installere pakker, som er anbefalet for at få fuldt udbytte af Umbraco.]]></key>
|
||||
<key alias="permissionsHowtoResolve">Hvorledes besluttes</key>
|
||||
<key alias="permissionsHowtoResolveLink">Klik her for at læse tekstversionen</key>
|
||||
<key alias="permissionsHowtoResolveText"><![CDATA[Se vores <strong>video tutorials</strong> om at opsætte folderrettigheder for Umbraco eller læs tekstversionen.]]></key>
|
||||
<key alias="permissionsMaybeAnIssue"><![CDATA[<strong>Dine rettighedsinstillinger kan være et problem!</strong><br/><br/>Du kan afvikle Umbraco uden problemer, men du vil ikke være i stand til at oprette foldere eller installere pakker, hvilket er anbefalet for at få fuldt udbytte af Umbraco.]]></key>
|
||||
<key alias="permissionsNotReady"><![CDATA[<strong>Dine rettighedsinstillinger er ikke klar til Umbraco!</strong><br/><br/>For at afvikle Umbraco er du nødt til at opdatere dine rettighedsinstillinger.]]></key>
|
||||
<key alias="permissionsPerfect"><![CDATA[<strong>Dine rettighedsinstillinger er perfekte!</strong><br/><br/>Du er nu parat til at afvikle Umbraco og installere pakker!]]></key>
|
||||
<key alias="permissionsMaybeAnIssue"><![CDATA[<strong>Dine rettighedsindstillinger kan være et problem!</strong><br/><br/>Du kan afvikle Umbraco uden problemer, men du vil ikke være i stand til at oprette foldere eller installere pakker, hvilket er anbefalet for at få fuldt udbytte af Umbraco.]]></key>
|
||||
<key alias="permissionsNotReady"><![CDATA[<strong>Dine rettighedsindstillinger er ikke klar til Umbraco!</strong><br/><br/>For at afvikle Umbraco er du nødt til at opdatere dine rettighedsindstillinger.]]></key>
|
||||
<key alias="permissionsPerfect"><![CDATA[<strong>Dine rettighedsindstillinger er perfekte!</strong><br/><br/>Du er nu parat til at afvikle Umbraco og installere pakker!]]></key>
|
||||
<key alias="permissionsResolveFolderIssues">Løser folder problem</key>
|
||||
<key alias="permissionsResolveFolderIssuesLink">Følg dette link for mere information om udfordringer med ASP.NET og oprettelse af foldere</key>
|
||||
<key alias="permissionsSettingUpPermissions">Sætter folderrettigheder op</key>
|
||||
@@ -597,7 +598,7 @@
|
||||
<key alias="runwayInstalledText"><![CDATA[Du har fundamentet på plads. Vælg hvilke moduler du ønsker at installere ovenpå det.<br/>Dette er vores liste over anbefalede moduler. Kryds dem af du ønsker at installere eller se den <a href="#" onclick="toggleModules(); return false;" id="toggleModuleList">fulde liste af moduler</a> ]]></key>
|
||||
<key alias="runwayOnlyProUsers">Kun anbefalet for erfarne brugere</key>
|
||||
<key alias="runwaySimpleSite">Jeg ønsker at begynder med et simpelt website</key>
|
||||
<key alias="runwaySimpleSiteText"><![CDATA[<p>"Runway" er et simpelt website som stiller nogle basale dokumenttyper og skabeloner til rådighed. Instaleringsprogrammet kan automatisk opsætte Runway for dig, men du kan nemt redigere, udvide eller fjerne det. Det er ikke nødvendigt og du kan sagtens bruge Umbraco uden. Men Runway tilbyder et fundament, som er baseret på 'Best Practices', som får dig igang hurtigere end nogensinde før. Hvis du vælger at installere Runway, kan du efter eget valg vælge de grundlæggende byggesten kaldet 'Runway Modules' til at forbedre dine Runway-sider.</p><p><small><em>Inkluderet med Runway:</em>Home Page, Getting Started page, Installing Modules page.<br /> <em>Valgfri Moduler:</em> Top Navigation, Sitemap, Contact, Gallery. </small></p>]]></key>
|
||||
<key alias="runwaySimpleSiteText"><![CDATA[<p>"Runway" er et simpelt website som stiller nogle basale dokumenttyper og skabeloner til rådighed. Installeringsprogrammet kan automatisk opsætte Runway for dig, men du kan nemt redigere, udvide eller fjerne det. Det er ikke nødvendigt og du kan sagtens bruge Umbraco uden. Men Runway tilbyder et fundament, som er baseret på 'Best Practices', som får dig igang hurtigere end nogensinde før. Hvis du vælger at installere Runway, kan du efter eget valg vælge de grundlæggende byggesten kaldet 'Runway Modules' til at forbedre dine Runway-sider.</p><p><small><em>Inkluderet med Runway:</em>Home Page, Getting Started page, Installing Modules page.<br /> <em>Valgfri Moduler:</em> Top Navigation, Sitemap, Contact, Gallery. </small></p>]]></key>
|
||||
<key alias="runwayWhatIsRunway">Hvad er Runway</key>
|
||||
<key alias="step1">Skridt 1/5: Acceptér licens</key>
|
||||
<key alias="step2">Skridt 2/5: Database-konfiguration</key>
|
||||
@@ -714,7 +715,7 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="packageRepository">Pakke opbevaringsbase</key>
|
||||
<key alias="packageUninstallConfirm">Bekræft af-installering</key>
|
||||
<key alias="packageUninstalledHeader">Pakken blev fjernet</key>
|
||||
<key alias="packageUninstalledText">Pakken er på succefuld vis blevet fjernet</key>
|
||||
<key alias="packageUninstalledText">Pakken er på succesfuld vis blevet fjernet</key>
|
||||
<key alias="packageUninstallHeader">Afinstallér pakke</key>
|
||||
<key alias="packageUninstallText">
|
||||
<![CDATA[Du kan fjerne markeringen på elementer du ikke ønsker at fjerne, på dette tidspunkt, nedenfor. Når du klikker 'bekræft' vil alle afkrydsede elemenet blive fjernet <br/>
|
||||
@@ -795,7 +796,7 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="addRows">Tilføj række</key>
|
||||
<key alias="addElement">Tilføj indhold</key>
|
||||
<key alias="dropElement">Slip indhold</key>
|
||||
<key alias="settingsApplied">Instillinger tilføjet</key>
|
||||
<key alias="settingsApplied">Indstillinger tilføjet</key>
|
||||
|
||||
<key alias="contentNotAllowed">Indholdet er ikke tilladt her</key>
|
||||
<key alias="contentAllowed">Indholdet er tilladt her</key>
|
||||
@@ -940,7 +941,7 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="tab">Faneblad</key>
|
||||
<key alias="tabname">Titel på faneblad</key>
|
||||
<key alias="tabs">Faneblade</key>
|
||||
<key alias="masterDocumentType">Master Dokument Type</key>
|
||||
<key alias="masterDocumentType">Master Dokumenttype</key>
|
||||
<key alias="createMatchingTemplate">Opret matchende skabelon</key>
|
||||
</area>
|
||||
<area alias="sort">
|
||||
@@ -1024,7 +1025,7 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="insertContentAreaPlaceHolder">Indsæt pladsholder for indholdsområde</key>
|
||||
|
||||
<key alias="insert">Indsæt</key>
|
||||
<key alias="insertDesc">Hvad vil du indsætte ?</key>
|
||||
<key alias="insertDesc">Hvad vil du indsætte?</key>
|
||||
|
||||
<key alias="insertDictionaryItem">Oversættelse</key>
|
||||
<key alias="insertDictionaryItemDesc">Indsætter en oversætbar tekst, som skifter efter det sprog, som websitet vises i.</key>
|
||||
|
||||
@@ -278,6 +278,7 @@
|
||||
<key alias="search">Durchsuchen ...</key>
|
||||
<key alias="filter">Filtern ...</key>
|
||||
<key alias="enterTags">Tippen, um Tags hinzuzufügen (nach jedem Tag die Eingabetaste drücken) ...</key>
|
||||
<key alias="usernameHint">Der Benutzername ist normalerweise Ihre E-Mail-Adresse</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="allowAtRoot" version="7.2">Auf oberster Ebene erlauben</key>
|
||||
|
||||
@@ -348,6 +348,7 @@
|
||||
<key alias="filter">Type to filter...</key>
|
||||
<key alias="enterTags">Type to add tags (press enter after each tag)...</key>
|
||||
<key alias="email">Enter your email</key>
|
||||
<key alias="usernameHint">Your username is usually your email</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="allowAtRoot" version="7.2">Allow at root</key>
|
||||
|
||||
@@ -1547,4 +1547,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="enabledConfirm">URL tracker has now been enabled.</key>
|
||||
<key alias="enableError">Error enabling the URL tracker, more information can be found in your log file.</key>
|
||||
</area>
|
||||
<area alias="emptyStates">
|
||||
<key alias="emptyDictionaryTree">No Dictionary items to choose from</key>
|
||||
</area>
|
||||
</language>
|
||||
|
||||
@@ -277,6 +277,7 @@
|
||||
<key alias="entername">Escribe un nombre...</key>
|
||||
<key alias="search">Escribe tu búsqueda...</key>
|
||||
<key alias="filter">Escribe para filtrar resultados...</key>
|
||||
<key alias="usernameHint">Tu nombre de usuario normalmente es tu e-mail</key>
|
||||
</area>
|
||||
<area alias="editcontenttype">
|
||||
<key alias="allowAtRoot" version="7.2">Permitir en nodo raíz</key>
|
||||
|
||||
@@ -329,6 +329,7 @@
|
||||
<key alias="filter">Filtrer...</key>
|
||||
<key alias="enterTags">Ajouter des tags (appuyer sur enter entre chaque tag)...</key>
|
||||
<key alias="email">Entrez votre email</key>
|
||||
<key alias="usernameHint">Votre nom d'utilisateur est généralement votre adresse email</key>
|
||||
</area>
|
||||
|
||||
<area alias="editcontenttype">
|
||||
|
||||
@@ -339,6 +339,7 @@
|
||||
<key alias="filter">Typ om te filteren...</key>
|
||||
<key alias="enterTags">Typ om tags toe te voegen (druk op enter na elke tag)...</key>
|
||||
<key alias="email">Voer jouw email in</key>
|
||||
<key alias="usernameHint">Jouw gebruikersnaam is meestal jouw email</key>
|
||||
</area>
|
||||
|
||||
<area alias="editcontenttype">
|
||||
|
||||
@@ -416,6 +416,9 @@
|
||||
<key alias="rteShowLabel">Показать метку</key>
|
||||
<key alias="rteWidthAndHeight">Ширина и высота</key>
|
||||
</area>
|
||||
<area alias="emptyStates">
|
||||
<key alias="emptyDictionaryTree">Нет доступных элементов словаря</key>
|
||||
</area>
|
||||
<area alias="errorHandling">
|
||||
<key alias="errorButDataWasSaved">Ваши данные сохранены, но для того, чтобы опубликовать этот документ, Вы должны сначала исправить следующие ошибки:</key>
|
||||
<key alias="errorChangingProviderPassword">Текущий провайдер ролей пользователей не поддерживает изменение пароля (необходимо свойству EnablePasswordRetrieval в файле web.config присвоить значение true)</key>
|
||||
@@ -1019,6 +1022,7 @@
|
||||
<key alias="password">Укажите пароль</key>
|
||||
<key alias="search">Что искать...</key>
|
||||
<key alias="username">Укажите имя пользователя</key>
|
||||
<key alias="usernameHint">Имя пользователя (часто это Ваш email-адрес)</key>
|
||||
</area>
|
||||
<area alias="prompt">
|
||||
<key alias="stay">Остаться</key>
|
||||
|
||||
@@ -594,6 +594,7 @@
|
||||
<key alias="search">Skriv för att söka...</key>
|
||||
<key alias="username">Fyll i ditt lösenord</key>
|
||||
<key alias="enterTags">Skriv för att lägga till taggar (och tryck enter efter varje tagg)...</key>
|
||||
<key alias="usernamehint">Ditt användarnamn är vanligtvis din e-postadress</key>
|
||||
</area>
|
||||
<area alias="publicAccess">
|
||||
<key alias="paAdvanced">Rollbaserat lösenordsskydd</key>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -11,9 +12,14 @@ using umbraco.BusinessLogic;
|
||||
using umbraco.cms.businesslogic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Publishing;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
using Content = Umbraco.Core.Models.Content;
|
||||
using ApplicationTree = Umbraco.Core.Models.ApplicationTree;
|
||||
using DeleteEventArgs = umbraco.cms.businesslogic.DeleteEventArgs;
|
||||
@@ -787,14 +793,39 @@ namespace Umbraco.Web.Cache
|
||||
/// </summary>
|
||||
/// <param name="events"></param>
|
||||
internal static void HandleEvents(IEnumerable<IEventDefinition> events)
|
||||
{
|
||||
foreach (var e in events)
|
||||
{
|
||||
//TODO: We should remove this in v8, this is a backwards compat hack and is needed because when we are using Deploy, the events will be raised on a background
|
||||
//thread which means that cache refreshers will also execute on a background thread and in many cases developers may be using UmbracoContext.Current in their
|
||||
//cache refresher handlers, so before we execute all of the events, we'll ensure a context
|
||||
UmbracoContext tempContext = null;
|
||||
if (UmbracoContext.Current == null)
|
||||
{
|
||||
var handler = FindHandler(e);
|
||||
if (handler == null) continue;
|
||||
|
||||
handler.Invoke(null, new[] { e.Sender, e.Args });
|
||||
var httpContext = new HttpContextWrapper(HttpContext.Current ?? new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter())));
|
||||
tempContext = UmbracoContext.EnsureContext(
|
||||
httpContext,
|
||||
ApplicationContext.Current,
|
||||
new WebSecurity(httpContext, ApplicationContext.Current),
|
||||
UmbracoConfig.For.UmbracoSettings(),
|
||||
UrlProviderResolver.Current.Providers,
|
||||
true);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var e in events)
|
||||
{
|
||||
var handler = FindHandler(e);
|
||||
if (handler == null) continue;
|
||||
|
||||
handler.Invoke(null, new[] { e.Sender, e.Args });
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (tempContext != null)
|
||||
tempContext.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(x => x.Snippet, exp => exp.Ignore());
|
||||
|
||||
config.CreateMap<CodeFileDisplay, IPartialView>()
|
||||
.ForMember(x => x.DeletedDate, exp => exp.Ignore())
|
||||
.ForMember(x => x.Id, exp => exp.Ignore())
|
||||
.ForMember(x => x.Key, 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());
|
||||
|
||||
config.CreateMap<CodeFileDisplay, Script>()
|
||||
.ForMember(x => x.DeletedDate, exp => exp.Ignore())
|
||||
.ForMember(x => x.Id, exp => exp.Ignore())
|
||||
.ForMember(x => x.Key, 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.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.UpdateDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.DeletedDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<DocumentTypeSave, IContentType>()
|
||||
@@ -72,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
config.CreateMap<MediaTypeSave, IMediaType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<MediaTypeSave, PropertyTypeBasic, IMediaType>(applicationContext)
|
||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, applicationContext);
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(dest => dest.HasIdentity, map => map.Ignore())
|
||||
.ForMember(dest => dest.CreateDate, map => map.Ignore())
|
||||
.ForMember(dest => dest.UpdateDate, map => map.Ignore())
|
||||
.ForMember(dest => dest.DeletedDate, 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
|
||||
.ForMember(dto => dto.CreateDate, 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.CreatorId, expression => expression.Ignore())
|
||||
|
||||
@@ -108,6 +108,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(x => x.Level, expression => expression.Ignore())
|
||||
.ForMember(x => x.SortOrder, expression => expression.Ignore())
|
||||
.ForMember(x => x.CreateDate, expression => expression.Ignore())
|
||||
.ForMember(x => x.DeletedDate, 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
|
||||
|
||||
@@ -55,8 +55,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(member => member.SortOrder, expression => expression.Ignore())
|
||||
.ForMember(member => member.AdditionalData, expression => expression.Ignore())
|
||||
.ForMember(member => member.FailedPasswordAttempts, expression => expression.Ignore())
|
||||
//TODO: Support these eventually
|
||||
.ForMember(member => member.PasswordQuestion, expression => expression.Ignore())
|
||||
.ForMember(member => member.DeletedDate, expression => expression.Ignore())
|
||||
//TODO: Support these eventually
|
||||
.ForMember(member => member.PasswordQuestion, expression => expression.Ignore())
|
||||
.ForMember(member => member.RawPasswordAnswerValue, expression => expression.Ignore());
|
||||
|
||||
//FROM IMember TO MediaItemDisplay
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(x => x.Notifications, exp => exp.Ignore());
|
||||
|
||||
config.CreateMap<TemplateDisplay, Template>()
|
||||
.ForMember(x => x.DeletedDate, exp => exp.Ignore())
|
||||
.ForMember(x => x.Key, exp => exp.Ignore())
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.CreateDate, exp => exp.Ignore())
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
.GetPreValuesByDataTypeId(property.PropertyType.DataTypeDefinitionId).FirstOrDefault();
|
||||
var crops = string.IsNullOrWhiteSpace(config) ? "[]" : config;
|
||||
src = svalue;
|
||||
property.Value = "{src: '" + svalue + "', crops: " + crops + "}";
|
||||
property.Value = "{\"src\": \"" + svalue + "\", \"crops\": " + crops + "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using Examine;
|
||||
using Examine.LuceneEngine.SearchCriteria;
|
||||
using Examine.Providers;
|
||||
using Lucene.Net.Documents;
|
||||
using Lucene.Net.Store;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Dynamics;
|
||||
@@ -173,6 +174,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
// the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null
|
||||
// reference error will occur because the examine settings are null.
|
||||
}
|
||||
catch (AlreadyClosedException)
|
||||
{
|
||||
//If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot
|
||||
//be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -211,13 +217,23 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
var result = searchProvider.Search(filter.Compile()).FirstOrDefault();
|
||||
if (result != null) return ConvertFromSearchResult(result);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
|
||||
//See this thread: http://examine.cdodeplex.com/discussions/264341
|
||||
//Catch the exception here for the time being, and just fallback to GetMedia
|
||||
//TODO: Need to fix examine in LB scenarios!
|
||||
LogHelper.Error<PublishedMediaCache>("Could not load data from Examine index for media", ex);
|
||||
if (ex is FileNotFoundException)
|
||||
{
|
||||
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
|
||||
//See this thread: http://examine.cdodeplex.com/discussions/264341
|
||||
//Catch the exception here for the time being, and just fallback to GetMedia
|
||||
//TODO: Need to fix examine in LB scenarios!
|
||||
LogHelper.Error<PublishedMediaCache>("Could not load data from Examine index for media", ex);
|
||||
}
|
||||
else if (ex is AlreadyClosedException)
|
||||
{
|
||||
//If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot
|
||||
//be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db.
|
||||
LogHelper.Error<PublishedMediaCache>("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex);
|
||||
}
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,9 +184,8 @@
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Semver, Version=2.0.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Reference Include="Semver, Version=1.1.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\semver.1.1.2\lib\net45\Semver.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System">
|
||||
<Name>System</Name>
|
||||
@@ -442,7 +441,9 @@
|
||||
<Compile Include="Security\WebAuthExtensions.cs" />
|
||||
<Compile Include="umbraco.presentation\SafeXmlReaderWriter.cs" />
|
||||
<Compile Include="Trees\ScriptTreeController.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\installer.aspx.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\installer.aspx.cs">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UmbracoDefaultOwinStartup.cs" />
|
||||
<Compile Include="IUmbracoContextAccessor.cs" />
|
||||
<Compile Include="Models\ContentEditing\Relation.cs" />
|
||||
|
||||
@@ -26,12 +26,8 @@
|
||||
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net45" />
|
||||
<package id="Semver" version="2.0.4" targetFramework="net45" />
|
||||
<package id="semver" version="1.1.2" targetFramework="net45" />
|
||||
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
|
||||
<package id="xmlrpcnet" version="2.5.0" targetFramework="net45" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net45" />
|
||||
<package id="System.Threading.Tasks.Dataflow" version="4.7.0" targetFramework="net45" />
|
||||
<package id="xmlrpcnet" version="2.5.0" targetFramework="net45" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user