Compare commits

...

22 Commits

Author SHA1 Message Date
Shannon 856c345905 Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 18:01:56 +01:00
Shannon b2cd5dfb85 adds null check to session id - might not exist in old cookies 2016-02-02 18:01:36 +01:00
Sebastiaan Janssen 462f7a59b5 Merge pull request #1078 from dawoe/U4-7872
Use product name in migration runner instead of Umbraco productname
2016-02-02 17:06:56 +01:00
Shannon 22689d24ec changes port 2016-02-02 17:05:21 +01:00
Shannon 4055afdf05 Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 16:52:08 +01:00
Shannon a1c70a219a ensures correct sort order for content type queries + test 2016-02-02 16:51:57 +01:00
Sebastiaan Janssen 3151515855 U4-7072 Grid TinyMce with style_formats throws "Cannot assign to read only property 'name' 2016-02-02 16:09:02 +01:00
Shannon 25df9da715 bumps Version 2016-02-02 16:00:28 +01:00
Shannon 9c2f438476 fixes tests 2016-02-02 15:43:44 +01:00
Shannon 0de3be42cf U4-7869 Examine & SQL Azure - Timeouts - ensure the other reader is executed with retry. 2016-02-02 15:33:22 +01:00
Shannon 1545e0eed7 Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 15:14:56 +01:00
Shannon 42a7ed6877 U4-7821 KeepUserLoggedIn with a long umbracoTimeOutInMinutes has logout issues 2016-02-02 15:14:47 +01:00
Sebastiaan Janssen 98b8aedc4d U4-7849 Unclear error message when applying public access 2016-02-02 15:13:57 +01:00
Shannon c7a2b54d1c Merge branch '7.3.7' of https://github.com/umbraco/Umbraco-CMS into 7.3.7 2016-02-02 12:13:03 +01:00
Shannon 6583ff4439 U4-7821 KeepUserLoggedIn with a long umbracoTimeOutInMinutes has logout issues 2016-02-02 12:12:51 +01:00
Sebastiaan Janssen 7b08b3e936 Merged branch 7.3.7 into 7.3.7 2016-02-02 11:55:40 +01:00
Sebastiaan Janssen bd2fc71dc5 U4-7276 When creating folders in the "Partials" view folder a YSOD appears (Umbraco 7.3 and 7.4) 2016-02-02 11:55:24 +01:00
Shannon 0af97f63e2 U4-7857 Flexible Load Balancing does not sync with the correct timeout threshold 2016-02-02 11:11:47 +01:00
Shannon 1db635f24c Updates DeepCloneableList to support behaviors, for the FullDataSetCachePolicy we only want to clone when writing to cache, not when reading, the cloning will then be done on individual items after filtering by the FullDataSetRepositoryCachePolicy 2016-02-02 01:32:36 +01:00
Shannon 6e27b3d6d4 Fixes up the FullDataSetRepositoryCachePolicy to handle individual items, updates and removes correctly, splits up it's logic so it's not overriding the DefaultRepositoryCachePolicy since that is just different. Adds tests. 2016-02-02 00:47:18 +01:00
Shannon 1dea0edcf1 re-includes the static cache for published property types, this cache is much more than a simple cache of content type infos, it is also the cache for associated converters which is required for all front-end rendering. Fixes the issue of not setting the xpath cache level corectly. 2016-02-01 22:50:38 +01:00
Shannon 1abab41955 Fixes perf issue with DeepCloneHelper - so we cache the actual property types instead of re-reflecting each time 2016-02-01 21:45:34 +01:00
44 changed files with 1035 additions and 352 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.3.6
7.3.7
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.3.6")]
[assembly: AssemblyInformationalVersion("7.3.6")]
[assembly: AssemblyFileVersion("7.3.7")]
[assembly: AssemblyInformationalVersion("7.3.7")]
@@ -15,35 +15,31 @@ namespace Umbraco.Core.Cache
/// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the
/// default policy with no expiry.
/// </remarks>
internal class DefaultRepositoryCachePolicy<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly RepositoryCachePolicyOptions _options;
protected IRuntimeCacheProvider Cache { get; private set; }
private Action _action;
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
{
if (cache == null) throw new ArgumentNullException("cache");
: base(cache)
{
if (options == null) throw new ArgumentNullException("options");
_options = options;
Cache = cache;
_options = options;
}
public string GetCacheIdKey(object id)
protected string GetCacheIdKey(object id)
{
if (id == null) throw new ArgumentNullException("id");
return string.Format("{0}{1}", GetCacheTypeKey(), id);
}
public string GetCacheTypeKey()
protected string GetCacheTypeKey()
{
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
}
public void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
@@ -85,24 +81,29 @@ namespace Umbraco.Core.Cache
}
}
public void Remove(TEntity entity, Action<TEntity> persistMethod)
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
persistMethod(entity);
//set the disposal action
var cacheKey = GetCacheIdKey(entity.Id);
SetCacheAction(() =>
try
{
Cache.ClearCacheItem(cacheKey);
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetCacheTypeKey());
});
persistMethod(entity);
}
finally
{
//set the disposal action
var cacheKey = GetCacheIdKey(entity.Id);
SetCacheAction(() =>
{
Cache.ClearCacheItem(cacheKey);
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.ClearCacheItem(GetCacheTypeKey());
});
}
}
public TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
{
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
@@ -119,13 +120,13 @@ namespace Umbraco.Core.Cache
return entity;
}
public TEntity Get(TId id)
public override TEntity Get(TId id)
{
var cacheKey = GetCacheIdKey(id);
return Cache.GetCacheItem<TEntity>(cacheKey);
}
public bool Exists(TId id, Func<TId, bool> getFromRepo)
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
{
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
@@ -134,7 +135,7 @@ namespace Umbraco.Core.Cache
return fromCache != null || getFromRepo(id);
}
public virtual TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
{
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
@@ -188,7 +189,7 @@ namespace Umbraco.Core.Cache
/// Looks up the zero count cache, must return null if it doesn't exist
/// </summary>
/// <returns></returns>
protected virtual bool HasZeroCountCache()
protected bool HasZeroCountCache()
{
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
return (zeroCount != null && zeroCount.Any() == false);
@@ -198,24 +199,13 @@ namespace Umbraco.Core.Cache
/// Performs the lookup for all entities of this type from the cache
/// </summary>
/// <returns></returns>
protected virtual TEntity[] GetAllFromCache()
protected TEntity[] GetAllFromCache()
{
var allEntities = Cache.GetCacheItemsByKeySearch<TEntity>(GetCacheTypeKey())
.WhereNotNull()
.ToArray();
return allEntities.Any() ? allEntities : new TEntity[] {};
}
/// <summary>
/// The disposal performs the caching
/// </summary>
protected override void DisposeResources()
{
if (_action != null)
{
_action();
}
}
}
/// <summary>
/// Sets the action to execute on disposal for a single entity
@@ -273,14 +263,6 @@ namespace Umbraco.Core.Cache
}
});
}
/// <summary>
/// Sets the action to execute on disposal
/// </summary>
/// <param name="action"></param>
protected void SetCacheAction(Action action)
{
_action = action;
}
}
}
@@ -11,40 +11,157 @@ namespace Umbraco.Core.Cache
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
/// <remarks>
/// This caching policy has no sliding expiration but uses the default ObjectCache.InfiniteAbsoluteExpiration as it's timeout, so it
/// should not leave the cache unless the cache memory is exceeded and it gets thrown out.
/// </remarks>
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private readonly Func<TEntity, TId> _getEntityId;
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> getEntityId) : base(cache,
new RepositoryCachePolicyOptions
{
//Definitely allow zero'd cache entires since this is a full set, in many cases there will be none,
// and we must cache this!
GetAllCacheAllowZeroCount = true
})
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
: base(cache)
{
_getEntityId = getEntityId;
_getAllFromRepo = getAllFromRepo;
_expires = expires;
}
private bool? _hasZeroCountCache;
protected string GetCacheTypeKey()
{
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
}
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
try
{
persistMethod(entity);
//set the disposal action
SetCacheAction(() =>
{
//Clear all
Cache.ClearCacheItem(GetCacheTypeKey());
});
}
catch
{
//set the disposal action
SetCacheAction(() =>
{
//Clear all
Cache.ClearCacheItem(GetCacheTypeKey());
});
throw;
}
}
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
{
if (entity == null) throw new ArgumentNullException("entity");
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
try
{
persistMethod(entity);
}
finally
{
//set the disposal action
SetCacheAction(() =>
{
//Clear all
Cache.ClearCacheItem(GetCacheTypeKey());
});
}
}
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
{
//Force get all with cache
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
//we don't have anything in cache (this should never happen), just return from the repo
if (found == null) return getFromRepo(id);
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
if (entity == null) return null;
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
return (TEntity)entity.DeepClone();
}
public override TEntity Get(TId id)
{
//Force get all with cache
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
//we don't have anything in cache (this should never happen), just return null
if (found == null) return null;
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
if (entity == null) return null;
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
return (TEntity)entity.DeepClone();
}
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
{
//Force get all with cache
var found = GetAll(new TId[] {}, ids => _getAllFromRepo().WhereNotNull());
//we don't have anything in cache (this should never happen), just return from the repo
return found == null
? getFromRepo(id)
: found.Any(x => _getEntityId(x).Equals(id));
}
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
{
//process the base logic without any Ids - we want to cache them all!
var result = base.GetAll(new TId[] { }, getFromRepo);
//process getting all including setting the cache callback
var result = PerformGetAll(getFromRepo);
//now that the base result has been calculated, they will all be cached.
// Now we can just filter by ids if they have been supplied
return ids.Any()
return (ids.Any()
? result.Where(x => ids.Contains(_getEntityId(x))).ToArray()
: result;
: result)
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
.Select(x => (TEntity)x.DeepClone())
.ToArray();
}
private TEntity[] PerformGetAll(Func<TId[], IEnumerable<TEntity>> getFromRepo)
{
var allEntities = GetAllFromCache();
if (allEntities.Any())
{
return allEntities;
}
//check the zero count cache
if (HasZeroCountCache())
{
//there is a zero count cache so return an empty list
return new TEntity[] { };
}
//we need to do the lookup from the repo
var entityCollection = getFromRepo(new TId[] {})
//ensure we don't include any null refs in the returned collection!
.WhereNotNull()
.ToArray();
//set the disposal action
SetCacheAction(entityCollection);
return entityCollection;
}
/// <summary>
@@ -52,26 +169,32 @@ namespace Umbraco.Core.Cache
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="entity"></param>
protected override void SetCacheAction(string cacheKey, TEntity entity)
protected void SetCacheAction(string cacheKey, TEntity entity)
{
//do nothing
//No-op
}
/// <summary>
/// Sets the action to execute on disposal for an entity collection
/// </summary>
/// <param name="ids"></param>
/// <param name="entityCollection"></param>
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
protected void SetCacheAction(TEntity[] entityCollection)
{
//for this type of caching policy, we don't want to cache any GetAll request containing specific Ids
if (ids.Any()) return;
//set the disposal action
SetCacheAction(() =>
{
//We want to cache the result as a single collection
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection));
if (_expires)
{
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection),
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
else
{
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection));
}
});
}
@@ -79,7 +202,7 @@ namespace Umbraco.Core.Cache
/// Looks up the zero count cache, must return null if it doesn't exist
/// </summary>
/// <returns></returns>
protected override bool HasZeroCountCache()
protected bool HasZeroCountCache()
{
if (_hasZeroCountCache.HasValue)
return _hasZeroCountCache.Value;
@@ -92,7 +215,7 @@ namespace Umbraco.Core.Cache
/// This policy will cache the full data set as a single collection
/// </summary>
/// <returns></returns>
protected override TEntity[] GetAllFromCache()
protected TEntity[] GetAllFromCache()
{
var found = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey());
@@ -101,5 +224,6 @@ namespace Umbraco.Core.Cache
return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray();
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
@@ -13,16 +14,20 @@ namespace Umbraco.Core.Cache
{
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly Func<TEntity, TId> _getEntityId;
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func<TEntity, TId> getEntityId)
public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
{
_runtimeCache = runtimeCache;
_getEntityId = getEntityId;
_getAllFromRepo = getAllFromRepo;
_expires = expires;
}
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
{
return new FullDataSetRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _getEntityId);
return new FullDataSetRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _getEntityId, _getAllFromRepo, _expires);
}
}
}
@@ -10,9 +10,7 @@ namespace Umbraco.Core.Cache
TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
TEntity Get(TId id);
bool Exists(TId id, Func<TId, bool> getFromRepo);
string GetCacheIdKey(object id);
string GetCacheTypeKey();
void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
void Remove(TEntity entity, Action<TEntity> persistMethod);
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Cache
{
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
private Action _action;
protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache)
{
if (cache == null) throw new ArgumentNullException("cache");
Cache = cache;
}
protected IRuntimeCacheProvider Cache { get; private set; }
/// <summary>
/// The disposal performs the caching
/// </summary>
protected override void DisposeResources()
{
if (_action != null)
{
_action();
}
}
/// <summary>
/// Sets the action to execute on disposal
/// </summary>
/// <param name="action"></param>
protected void SetCacheAction(Action action)
{
_action = action;
}
public abstract TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
public abstract TEntity Get(TId id);
public abstract bool Exists(TId id, Func<TId, bool> getFromRepo);
public abstract void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
public abstract void Remove(TEntity entity, Action<TEntity> persistMethod);
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
}
}
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Cache
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
{
//do nothing
//no-op
}
}
}
@@ -14,18 +14,24 @@ namespace Umbraco.Core.Collections
/// <typeparam name="T"></typeparam>
internal class DeepCloneableList<T> : List<T>, IDeepCloneable, IRememberBeingDirty
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Collections.Generic.List`1"/> class that is empty and has the default initial capacity.
/// </summary>
public DeepCloneableList()
private readonly ListCloneBehavior _listCloneBehavior;
public DeepCloneableList(ListCloneBehavior listCloneBehavior)
{
_listCloneBehavior = listCloneBehavior;
}
public DeepCloneableList(IEnumerable<T> collection, ListCloneBehavior listCloneBehavior) : base(collection)
{
_listCloneBehavior = listCloneBehavior;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Collections.Generic.List`1"/> class that contains elements copied from the specified collection and has sufficient capacity to accommodate the number of elements copied.
/// Default behavior is CloneOnce
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param><exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is null.</exception>
public DeepCloneableList(IEnumerable<T> collection) : base(collection)
/// <param name="collection"></param>
public DeepCloneableList(IEnumerable<T> collection)
: this(collection, ListCloneBehavior.CloneOnce)
{
}
@@ -35,20 +41,47 @@ namespace Umbraco.Core.Collections
/// <returns></returns>
public object DeepClone()
{
var newList = new DeepCloneableList<T>();
foreach (var item in this)
switch (_listCloneBehavior)
{
var dc = item as IDeepCloneable;
if (dc != null)
{
newList.Add((T) dc.DeepClone());
}
else
{
newList.Add(item);
}
case ListCloneBehavior.CloneOnce:
//we are cloning once, so create a new list in none mode
// and deep clone all items into it
var newList = new DeepCloneableList<T>(ListCloneBehavior.None);
foreach (var item in this)
{
var dc = item as IDeepCloneable;
if (dc != null)
{
newList.Add((T)dc.DeepClone());
}
else
{
newList.Add(item);
}
}
return newList;
case ListCloneBehavior.None:
//we are in none mode, so just return a new list with the same items
return new DeepCloneableList<T>(this, ListCloneBehavior.None);
case ListCloneBehavior.Always:
//always clone to new list
var newList2 = new DeepCloneableList<T>(ListCloneBehavior.Always);
foreach (var item in this)
{
var dc = item as IDeepCloneable;
if (dc != null)
{
newList2.Add((T)dc.DeepClone());
}
else
{
newList2.Add(item);
}
}
return newList2;
default:
throw new ArgumentOutOfRangeException();
}
return newList;
}
public bool IsDirty()
@@ -0,0 +1,20 @@
namespace Umbraco.Core.Collections
{
internal enum ListCloneBehavior
{
/// <summary>
/// When set, DeepClone will clone the items one time and the result list behavior will be None
/// </summary>
CloneOnce,
/// <summary>
/// When set, DeepClone will not clone any items
/// </summary>
None,
/// <summary>
/// When set, DeepClone will always clone all items
/// </summary>
Always
}
}
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.3.6");
private static readonly Version Version = new Version("7.3.7");
/// <summary>
/// Gets the current version of Umbraco.
+99 -61
View File
@@ -9,10 +9,30 @@ namespace Umbraco.Core.Models
{
public static class DeepCloneHelper
{
/// <summary>
/// Stores the metadata for the properties for a given type so we know how to create them
/// </summary>
private struct ClonePropertyInfo
{
public ClonePropertyInfo(PropertyInfo propertyInfo) : this()
{
if (propertyInfo == null) throw new ArgumentNullException("propertyInfo");
PropertyInfo = propertyInfo;
}
public PropertyInfo PropertyInfo { get; private set; }
public bool IsDeepCloneable { get; set; }
public Type GenericListType { get; set; }
public bool IsList
{
get { return GenericListType != null; }
}
}
/// <summary>
/// Used to avoid constant reflection (perf)
/// </summary>
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
private static readonly ConcurrentDictionary<Type, ClonePropertyInfo[]> PropCache = new ConcurrentDictionary<Type, ClonePropertyInfo[]>();
/// <summary>
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
@@ -30,81 +50,99 @@ namespace Umbraco.Core.Models
throw new InvalidOperationException("Both the input and output types must be the same");
}
//get the property metadata from cache so we only have to figure this out once per type
var refProperties = PropCache.GetOrAdd(inputType, type =>
inputType.GetProperties()
.Where(x =>
//is not attributed with the ignore clone attribute
x.GetCustomAttribute<DoNotCloneAttribute>() == null
.Select<PropertyInfo, ClonePropertyInfo?>(propertyInfo =>
{
if (
//is not attributed with the ignore clone attribute
propertyInfo.GetCustomAttribute<DoNotCloneAttribute>() != null
//reference type but not string
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|| propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType == typeof (string)
//settable
&& x.CanWrite
|| propertyInfo.CanWrite == false
//non-indexed
&& x.GetIndexParameters().Any() == false)
|| propertyInfo.GetIndexParameters().Any())
{
return null;
}
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
{
return new ClonePropertyInfo(propertyInfo) { IsDeepCloneable = true };
}
if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
{
if (propertyInfo.PropertyType.IsGenericType
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
return new ClonePropertyInfo(propertyInfo) { GenericListType = genericType };
}
if (propertyInfo.PropertyType.IsArray
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
{
//if its an array, we'll create a list to work with first and then convert to array later
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
return new ClonePropertyInfo(propertyInfo) { GenericListType = typeof(List<object>) };
}
//skip instead of trying to create instance of abstract or interface
if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
{
return null;
}
//its a custom IEnumerable, we'll try to create it
try
{
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
//if it's an IList we can work with it, otherwise we cannot
var newList = custom as IList;
if (newList == null)
{
return null;
}
return new ClonePropertyInfo(propertyInfo) {GenericListType = propertyInfo.PropertyType};
}
catch (Exception)
{
//could not create this type so we'll skip it
return null;
}
}
return new ClonePropertyInfo(propertyInfo);
})
.Where(x => x.HasValue)
.Select(x => x.Value)
.ToArray());
foreach (var propertyInfo in refProperties)
foreach (var clonePropertyInfo in refProperties)
{
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
if (clonePropertyInfo.IsDeepCloneable)
{
//this ref property is also deep cloneable so clone it
var result = (IDeepCloneable)propertyInfo.GetValue(input, null);
var result = (IDeepCloneable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
if (result != null)
{
//set the cloned value to the property
propertyInfo.SetValue(output, result.DeepClone(), null);
clonePropertyInfo.PropertyInfo.SetValue(output, result.DeepClone(), null);
}
}
else if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
else if (clonePropertyInfo.IsList)
{
IList newList;
if (propertyInfo.PropertyType.IsGenericType
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
newList = (IList)Activator.CreateInstance(genericType);
}
else if (propertyInfo.PropertyType.IsArray
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
{
//if its an array, we'll create a list to work with first and then convert to array later
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
newList = new List<object>();
}
else
{
//skip instead of trying to create instance of abstract or interface
if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
{
continue;
}
//its a custom IEnumerable, we'll try to create it
try
{
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
//if it's an IList we can work with it, otherwise we cannot
newList = custom as IList;
if (newList == null)
{
continue;
}
}
catch (Exception)
{
//could not create this type so we'll skip it
continue;
}
}
var enumerable = (IEnumerable)propertyInfo.GetValue(input, null);
var enumerable = (IEnumerable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
if (enumerable == null) continue;
var newList = (IList)Activator.CreateInstance(clonePropertyInfo.GenericListType);
var isUsableType = true;
//now clone each item
@@ -136,21 +174,21 @@ namespace Umbraco.Core.Models
continue;
}
if (propertyInfo.PropertyType.IsArray)
if (clonePropertyInfo.PropertyInfo.PropertyType.IsArray)
{
//need to convert to array
var arr = (object[])Activator.CreateInstance(propertyInfo.PropertyType, newList.Count);
var arr = (object[])Activator.CreateInstance(clonePropertyInfo.PropertyInfo.PropertyType, newList.Count);
for (int i = 0; i < newList.Count; i++)
{
arr[i] = newList[i];
}
//set the cloned collection
propertyInfo.SetValue(output, arr, null);
clonePropertyInfo.PropertyInfo.SetValue(output, arr, null);
}
else
{
//set the cloned collection
propertyInfo.SetValue(output, newList, null);
clonePropertyInfo.PropertyInfo.SetValue(output, newList, null);
}
}
@@ -98,10 +98,45 @@ namespace Umbraco.Core.Models.PublishedContent
#endregion
#region Cache
// these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher
internal static void ClearAll()
{
Logging.LogHelper.Debug<PublishedContentType>("Clear all.");
// ok and faster to do it by types, assuming noone else caches PublishedContentType instances
//ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_");
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>();
}
internal static void ClearContentType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear content type w/id {0}.", () => id);
// requires a predicate because the key does not contain the ID
// faster than key strings comparisons anyway
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
(key, value) => value.Id == id);
}
internal static void ClearDataType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear data type w/id {0}.", () => id);
// there is no recursion to handle here because a PublishedContentType contains *all* its
// properties ie both its own properties and those that were inherited (it's based upon an
// IContentTypeComposition) and so every PublishedContentType having a property based upon
// the cleared data type, be it local or inherited, will be cleared.
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
(key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id));
}
public static PublishedContentType Get(PublishedItemType itemType, string alias)
{
var type = CreatePublishedContentType(itemType, alias);
var key = string.Format("PublishedContentType_{0}_{1}",
itemType.ToString().ToLowerInvariant(), alias.ToLowerInvariant());
var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem<PublishedContentType>(key,
() => CreatePublishedContentType(itemType, alias));
return type;
}
@@ -134,8 +169,21 @@ namespace Umbraco.Core.Models.PublishedContent
return new PublishedContentType(contentType);
}
// for unit tests
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback { get; set; }
// for unit tests - changing the callback must reset the cache obviously
private static Func<string, PublishedContentType> _getPublishedContentTypeCallBack;
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback
{
get { return _getPublishedContentTypeCallBack; }
set
{
// see note above
//ClearAll();
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_");
_getPublishedContentTypeCallBack = value;
}
}
#endregion
}
}
@@ -230,13 +230,13 @@ namespace Umbraco.Core.Models.PublishedContent
{
_sourceCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Source);
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Object);
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
_xpathCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
}
else
{
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
_xpathCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
}
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
@@ -289,7 +289,7 @@ namespace Umbraco.Core.Persistence.Migrations
//NOTE: We CANNOT do this as part of the transaction!!! This is because when upgrading to 7.3, we cannot
// create the migrations table and then add data to it in the same transaction without issuing things like GO
// commands and since we need to support all Dbs, we need to just do this after the fact.
var exists = _migrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, _targetVersion);
var exists = _migrationEntryService.FindEntry(_productName, _targetVersion);
if (exists == null)
{
_migrationEntryService.CreateEntry(_productName, _targetVersion);
+1 -1
View File
@@ -834,7 +834,7 @@ namespace Umbraco.Core.Persistence
var pd = PocoData.ForType(typeof(T));
try
{
r = cmd.ExecuteReader();
r = cmd.ExecuteReaderWithRetry();
OnExecutedCommand(cmd);
}
catch (Exception x)
@@ -34,7 +34,10 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IContentType, int>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IContentType, int>(
RuntimeCache, GetEntityId, () => PerformGetAll(),
//allow this cache to expire
expires:true));
}
}
@@ -60,13 +63,17 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContentType>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.Text);
var sql = translator.Translate();
var dtos = Database.Fetch<DocumentTypeDto, ContentTypeDto, NodeDto>(sql);
return dtos.Any()
? GetAll(dtos.DistinctBy(x => x.ContentTypeDto.NodeId).Select(x => x.ContentTypeDto.NodeId).ToArray())
: Enumerable.Empty<IContentType>();
return
//This returns a lookup from the GetAll cached looup
(dtos.Any()
? GetAll(dtos.DistinctBy(x => x.ContentTypeDto.NodeId).Select(x => x.ContentTypeDto.NodeId).ToArray())
: Enumerable.Empty<IContentType>())
//order the result by name
.OrderBy(x => x.Name);
}
/// <summary>
@@ -29,7 +29,8 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IDomain, int>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IDomain, int>(
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
}
}
@@ -30,7 +30,8 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ILanguage, int>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ILanguage, int>(
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
}
}
@@ -31,7 +31,10 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMediaType, int>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMediaType, int>(
RuntimeCache, GetEntityId, () => PerformGetAll(),
//allow this cache to expire
expires: true));
}
}
@@ -57,13 +60,17 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IMediaType>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<NodeDto>(x => x.Text);
var sql = translator.Translate();
var dtos = Database.Fetch<ContentTypeDto, NodeDto>(sql);
return dtos.Any()
? GetAll(dtos.DistinctBy(x => x.NodeId).Select(x => x.NodeId).ToArray())
: Enumerable.Empty<IMediaType>();
return
//This returns a lookup from the GetAll cached looup
(dtos.Any()
? GetAll(dtos.DistinctBy(x => x.NodeId).Select(x => x.NodeId).ToArray())
: Enumerable.Empty<IMediaType>())
//order the result by name
.OrderBy(x => x.Name);
}
@@ -33,7 +33,10 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMemberType, int>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMemberType, int>(
RuntimeCache, GetEntityId, () => PerformGetAll(),
//allow this cache to expire
expires: true));
}
}
@@ -26,7 +26,8 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid>(
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
}
}
@@ -51,7 +51,8 @@ namespace Umbraco.Core.Persistence.Repositories
get
{
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ITemplate, int>(RuntimeCache, GetEntityId));
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ITemplate, int>(
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
}
}
@@ -9,6 +9,12 @@ namespace Umbraco.Core.Security
{
public class BackOfficeClaimsIdentityFactory : ClaimsIdentityFactory<BackOfficeIdentityUser, int>
{
public BackOfficeClaimsIdentityFactory()
{
SecurityStampClaimType = Constants.Security.SessionIdClaimType;
UserNameClaimType = ClaimTypes.Name;
}
/// <summary>
/// Create a ClaimsIdentity from a user
/// </summary>
@@ -20,7 +26,7 @@ namespace Umbraco.Core.Security
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity,
//set a new session id
new UserData(Guid.NewGuid().ToString("N"))
new UserData
{
Id = user.Id,
Username = user.UserName,
@@ -29,7 +35,8 @@ namespace Umbraco.Core.Security
Culture = user.Culture,
Roles = user.Roles.Select(x => x.RoleId).ToArray(),
StartContentNode = user.StartContentId,
StartMediaNode = user.StartMediaId
StartMediaNode = user.StartMediaId,
SessionId = user.SecurityStamp
});
return umbracoIdentity;
@@ -209,17 +209,19 @@ namespace Umbraco.Core.Security
if (HasClaim(x => x.Type == ClaimTypes.Locality) == false)
AddClaim(new Claim(ClaimTypes.Locality, Culture, ClaimValueTypes.String, Issuer, Issuer, this));
////TODO: Not sure why this is null sometimes, it shouldn't be. Somewhere it's not being set
/// I think it's due to some bug I had in chrome, we'll see
//if (UserData.SessionId.IsNullOrWhiteSpace())
//{
// UserData.SessionId = Guid.NewGuid().ToString();
//}
if (HasClaim(x => x.Type == Constants.Security.SessionIdClaimType) == false)
if (HasClaim(x => x.Type == Constants.Security.SessionIdClaimType) == false && SessionId.IsNullOrWhiteSpace() == false)
{
AddClaim(new Claim(Constants.Security.SessionIdClaimType, SessionId, ClaimValueTypes.String, Issuer, Issuer, this));
//The security stamp claim is also required... this is because this claim type is hard coded
// by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444
if (HasClaim(x => x.Type == Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType) == false)
{
AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SessionId, ClaimValueTypes.String, Issuer, Issuer, this));
}
}
//Add each app as a separate claim
if (HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false)
{
+2 -5
View File
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Security
/// Use this constructor to create/assign new UserData to the ticket
/// </summary>
/// <param name="sessionId">
/// A unique id that is assigned to this ticket
/// The security stamp for the user
/// </param>
public UserData(string sessionId)
{
@@ -30,8 +30,7 @@ namespace Umbraco.Core.Security
}
/// <summary>
/// This is used to Id the current ticket which we can then use to mitigate csrf attacks
/// and other things that require request validation.
/// This is the 'security stamp' for validation
/// </summary>
[DataMember(Name = "sessionId")]
public string SessionId { get; set; }
@@ -42,8 +41,6 @@ namespace Umbraco.Core.Security
[DataMember(Name = "roles")]
public string[] Roles { get; set; }
//public int SessionTimeout { get; set; }
[DataMember(Name = "username")]
public string Username { get; set; }
@@ -198,7 +198,7 @@ namespace Umbraco.Core.Sync
if (_released)
return;
if ((DateTime.UtcNow - _lastSync).Seconds <= _options.ThrottleSeconds)
if ((DateTime.UtcNow - _lastSync).TotalSeconds <= _options.ThrottleSeconds)
return;
_syncing = true;
+2
View File
@@ -175,6 +175,7 @@
<Compile Include="Cache\IJsonCacheRefresher.cs" />
<Compile Include="Cache\JsonCacheRefresherBase.cs" />
<Compile Include="Cache\NullCacheProvider.cs" />
<Compile Include="Cache\RepositoryCachePolicyBase.cs" />
<Compile Include="Cache\SingleItemsOnlyRepositoryCachePolicy.cs" />
<Compile Include="Cache\OnlySingleItemsRepositoryCachePolicyFactory.cs" />
<Compile Include="Cache\PayloadCacheRefresherBase.cs" />
@@ -187,6 +188,7 @@
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
<Compile Include="Collections\DeepCloneableList.cs" />
<Compile Include="Collections\ListCloneBehavior.cs" />
<Compile Include="ConcurrentHashSet.cs" />
<Compile Include="Configuration\BaseRest\IBaseRestSection.cs" />
<Compile Include="Configuration\BaseRest\IExtension.cs" />
@@ -41,7 +41,7 @@ namespace Umbraco.Tests.Cache
[Test]
public void Clones_List()
{
var original = new DeepCloneableList<DeepCloneableListTests.TestClone>();
var original = new DeepCloneableList<DeepCloneableListTests.TestClone>(ListCloneBehavior.Always);
original.Add(new DeepCloneableListTests.TestClone());
original.Add(new DeepCloneableListTests.TestClone());
original.Add(new DeepCloneableListTests.TestClone());
@@ -120,5 +120,37 @@ namespace Umbraco.Tests.Cache
Assert.IsTrue(cacheCleared);
}
}
[Test]
public void If_Removes_Throws_Cache_Is_Removed()
{
var cacheCleared = false;
var cache = new Mock<IRuntimeCacheProvider>();
cache.Setup(x => x.ClearCacheItem(It.IsAny<string>()))
.Callback(() =>
{
cacheCleared = true;
});
var defaultPolicy = new DefaultRepositoryCachePolicy<AuditItem, object>(cache.Object, new RepositoryCachePolicyOptions());
try
{
using (defaultPolicy)
{
defaultPolicy.Remove(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
{
throw new Exception("blah!");
});
}
}
catch
{
//we need this catch or nunit throw up
}
finally
{
Assert.IsTrue(cacheCleared);
}
}
}
}
@@ -14,9 +14,57 @@ namespace Umbraco.Tests.Cache
[TestFixture]
public class FullDataSetCachePolicyTests
{
[Test]
public void Caches_Single()
{
var getAll = new[]
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
};
var isCached = false;
var cache = new Mock<IRuntimeCacheProvider>();
cache.Setup(x => x.InsertCacheItem(It.IsAny<string>(), It.IsAny<Func<object>>(), It.IsAny<TimeSpan?>(), It.IsAny<bool>(),
It.IsAny<CacheItemPriority>(), It.IsAny<CacheItemRemovedCallback>(), It.IsAny<string[]>()))
.Callback(() =>
{
isCached = true;
});
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
using (defaultPolicy)
{
var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123));
}
Assert.IsTrue(isCached);
}
[Test]
public void Get_Single_From_Cache()
{
var getAll = new[]
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
};
var cache = new Mock<IRuntimeCacheProvider>();
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem(1, "blah", AuditType.Copy, 123));
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
using (defaultPolicy)
{
var found = defaultPolicy.Get(1, o => (AuditItem)null);
Assert.IsNotNull(found);
}
}
[Test]
public void Get_All_Caches_Empty_List()
{
var getAll = new AuditItem[] {};
var cached = new List<string>();
IList list = null;
@@ -33,23 +81,23 @@ namespace Umbraco.Tests.Cache
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(() =>
{
//return null if this is the first pass
return cached.Any() ? new DeepCloneableList<AuditItem>() : null;
return cached.Any() ? new DeepCloneableList<AuditItem>(ListCloneBehavior.CloneOnce) : null;
});
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id);
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
using (defaultPolicy)
{
var found = defaultPolicy.GetAll(new object[] {}, o => new AuditItem[] {});
var found = defaultPolicy.GetAll(new object[] {}, o => getAll);
}
Assert.AreEqual(1, cached.Count);
Assert.IsNotNull(list);
//Do it again, ensure that its coming from the cache!
defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id);
defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
using (defaultPolicy)
{
var found = defaultPolicy.GetAll(new object[] { }, o => new AuditItem[] { });
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
}
Assert.AreEqual(1, cached.Count);
@@ -59,6 +107,12 @@ namespace Umbraco.Tests.Cache
[Test]
public void Get_All_Caches_As_Single_List()
{
var getAll = new[]
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
};
var cached = new List<string>();
IList list = null;
@@ -73,14 +127,10 @@ namespace Umbraco.Tests.Cache
});
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(new AuditItem[] { });
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id);
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
using (defaultPolicy)
{
var found = defaultPolicy.GetAll(new object[] { }, o => new[]
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
});
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
}
Assert.AreEqual(1, cached.Count);
@@ -89,21 +139,99 @@ namespace Umbraco.Tests.Cache
[Test]
public void Get_All_Without_Ids_From_Cache()
{
{
var getAll = new[] { (AuditItem)null };
var cache = new Mock<IRuntimeCacheProvider>();
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(() => new DeepCloneableList<AuditItem>
cache.Setup(x => x.GetCacheItem(It.IsAny<string>())).Returns(() => new DeepCloneableList<AuditItem>(ListCloneBehavior.CloneOnce)
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
});
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id);
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
using (defaultPolicy)
{
var found = defaultPolicy.GetAll(new object[] { }, o => new[] { (AuditItem)null });
var found = defaultPolicy.GetAll(new object[] { }, o => getAll);
Assert.AreEqual(2, found.Length);
}
}
[Test]
public void If_CreateOrUpdate_Throws_Cache_Is_Removed()
{
var getAll = new[]
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
};
var cacheCleared = false;
var cache = new Mock<IRuntimeCacheProvider>();
cache.Setup(x => x.ClearCacheItem(It.IsAny<string>()))
.Callback(() =>
{
cacheCleared = true;
});
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
try
{
using (defaultPolicy)
{
defaultPolicy.CreateOrUpdate(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
{
throw new Exception("blah!");
});
}
}
catch
{
//we need this catch or nunit throw up
}
finally
{
Assert.IsTrue(cacheCleared);
}
}
[Test]
public void If_Removes_Throws_Cache_Is_Removed()
{
var getAll = new[]
{
new AuditItem(1, "blah", AuditType.Copy, 123),
new AuditItem(2, "blah2", AuditType.Copy, 123)
};
var cacheCleared = false;
var cache = new Mock<IRuntimeCacheProvider>();
cache.Setup(x => x.ClearCacheItem(It.IsAny<string>()))
.Callback(() =>
{
cacheCleared = true;
});
var defaultPolicy = new FullDataSetRepositoryCachePolicy<AuditItem, object>(cache.Object, item => item.Id, () => getAll, false);
try
{
using (defaultPolicy)
{
defaultPolicy.Remove(new AuditItem(1, "blah", AuditType.Copy, 123), item =>
{
throw new Exception("blah!");
});
}
}
catch
{
//we need this catch or nunit throw up
}
finally
{
Assert.IsTrue(cacheCleared);
}
}
}
}
@@ -12,10 +12,44 @@ namespace Umbraco.Tests.Collections
[TestFixture]
public class DeepCloneableListTests
{
[Test]
public void Deep_Clones_Each_Item_Once()
{
var list = new DeepCloneableList<TestClone>(ListCloneBehavior.CloneOnce);
list.Add(new TestClone());
list.Add(new TestClone());
list.Add(new TestClone());
var cloned = list.DeepClone() as DeepCloneableList<TestClone>;
//Test that each item in the sequence is equal - based on the equality comparer of TestClone (i.e. it's ID)
Assert.IsTrue(list.SequenceEqual(cloned));
//Test that each instance in the list is not the same one
foreach (var item in list)
{
var clone = cloned.Single(x => x.Id == item.Id);
Assert.AreNotSame(item, clone);
}
//clone again from the clone - since it's clone once the items should be the same
var cloned2 = cloned.DeepClone() as DeepCloneableList<TestClone>;
//Test that each item in the sequence is equal - based on the equality comparer of TestClone (i.e. it's ID)
Assert.IsTrue(cloned.SequenceEqual(cloned2));
//Test that each instance in the list is the same one
foreach (var item in cloned)
{
var clone = cloned2.Single(x => x.Id == item.Id);
Assert.AreSame(item, clone);
}
}
[Test]
public void Deep_Clones_All_Elements()
{
var list = new DeepCloneableList<TestClone>();
var list = new DeepCloneableList<TestClone>(ListCloneBehavior.Always);
list.Add(new TestClone());
list.Add(new TestClone());
list.Add(new TestClone());
@@ -30,7 +64,7 @@ namespace Umbraco.Tests.Collections
[Test]
public void Clones_Each_Item()
{
var list = new DeepCloneableList<TestClone>();
var list = new DeepCloneableList<TestClone>(ListCloneBehavior.Always);
list.Add(new TestClone());
list.Add(new TestClone());
list.Add(new TestClone());
@@ -46,7 +80,7 @@ namespace Umbraco.Tests.Collections
[Test]
public void Cloned_Sequence_Equals()
{
var list = new DeepCloneableList<TestClone>();
var list = new DeepCloneableList<TestClone>(ListCloneBehavior.Always);
list.Add(new TestClone());
list.Add(new TestClone());
list.Add(new TestClone());
@@ -11,7 +11,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
@@ -208,6 +208,35 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Perform_Query_On_ContentTypeRepository_Sort_By_Name()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repository = CreateRepository(unitOfWork))
{
var contentType = repository.Get(NodeDto.NodeIdSeed + 1);
var child1 = MockedContentTypes.CreateSimpleContentType("aabc", "aabc", contentType, randomizeAliases: true);
repository.AddOrUpdate(child1);
var child3 = MockedContentTypes.CreateSimpleContentType("zyx", "zyx", contentType, randomizeAliases: true);
repository.AddOrUpdate(child3);
var child2 = MockedContentTypes.CreateSimpleContentType("a123", "a123", contentType, randomizeAliases: true);
repository.AddOrUpdate(child2);
unitOfWork.Commit();
// Act
var contentTypes = repository.GetByQuery(new Query<IContentType>().Where(x => x.ParentId == contentType.Id));
// Assert
Assert.That(contentTypes.Count(), Is.EqualTo(3));
Assert.AreEqual("a123", contentTypes.ElementAt(0).Name);
Assert.AreEqual("aabc", contentTypes.ElementAt(1).Name);
Assert.AreEqual("zyx", contentTypes.ElementAt(2).Name);
}
}
[Test]
public void Can_Perform_Get_On_ContentTypeRepository()
{
@@ -105,7 +105,7 @@ namespace Umbraco.Tests.Security
var identity = new UmbracoBackOfficeIdentity(userData);
Assert.AreEqual(10, identity.Claims.Count());
Assert.AreEqual(11, identity.Claims.Count());
}
[Test]
@@ -132,7 +132,7 @@ namespace Umbraco.Tests.Security
var backofficeIdentity = new UmbracoBackOfficeIdentity(claimsIdentity, userData);
Assert.AreEqual(12, backofficeIdentity.Claims.Count());
Assert.AreEqual(13, backofficeIdentity.Claims.Count());
}
[Test]
@@ -156,7 +156,7 @@ namespace Umbraco.Tests.Security
var identity = new UmbracoBackOfficeIdentity(ticket);
Assert.AreEqual(11, identity.Claims.Count());
Assert.AreEqual(12, identity.Claims.Count());
}
[Test]
@@ -182,7 +182,7 @@ namespace Umbraco.Tests.Security
var cloned = identity.Clone();
Assert.AreEqual(11, cloned.Claims.Count());
Assert.AreEqual(12, cloned.Claims.Count());
}
}
@@ -106,6 +106,31 @@ angular.module("umbraco.directives")
if (tinyMceConfig.customConfig) {
//if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to
// convert it to json instead of having it as a string since this is what tinymce requires
for (var i in tinyMceConfig.customConfig) {
var val = tinyMceConfig.customConfig[i];
if (val) {
val = val.toString().trim();
if (val.detectIsJson()) {
try {
tinyMceConfig.customConfig[i] = JSON.parse(val);
//now we need to check if this custom config key is defined in our baseline, if it is we don't want to
//overwrite the baseline config item if it is an array, we want to concat the items in the array, otherwise
//if it's an object it will overwrite the baseline
if (angular.isArray(baseLineConfigObj[i]) && angular.isArray(tinyMceConfig.customConfig[i])) {
//concat it and below this concat'd array will overwrite the baseline in angular.extend
tinyMceConfig.customConfig[i] = baseLineConfigObj[i].concat(tinyMceConfig.customConfig[i]);
}
}
catch (e) {
//cannot parse, we'll just leave it
}
}
}
}
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
}
+92 -1
View File
@@ -67,4 +67,95 @@ iframe, .content-column-body {
}
.icon-chevron-down:before {
content: "\e0c9";
}
}
/* Styling for validation in Public Access */
.pa-umb-overlay {
-webkit-font-smoothing: antialiased;
font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.pa-umb-overlay + .pa-umb-overlay {
padding-top: 30px;
border-top: 1px solid @grayLight;
}
.pa-select-type {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: center;
align-items: flex-start;
margin-top: 15px;
}
.pa-select-type label {
padding: 0 20px;
}
.pa-access-header {
font-weight: bold;
margin: 0 0 3px 0;
padding-bottom: 0;
}
.pa-access-description {
color: #b3b3b3;
margin: 0;
}
.pa-validation-message {
padding: 6px 12px !important;
margin: 5px 0 0 0 !important;
display: inline-block;
}
.pa-select-pages label {
margin: 0;
font-size: 15px;
}
.pa-select-pages label + .controls-row {
padding-top: 0;
}
.pa-select-pages .umb-detail {
font-size: 13px;
margin: 2px 0 5px;
}
.pa-choose-page a {
color: @blue;
font-size: 15px;
}
.pa-choose-page a:hover, .pa-choose-page a:active, .pa-choose-page a:focus {
color: @blueDark;
text-decoration: none;
}
.pa-choose-page a:before {
content:"+";
margin-right: 3px;
font-weight: bold;
}
.pa-choose-page .treePickerTitle {
font-weight: bold;
font-size: 13px;
font-style: italic;
background: whitesmoke;
padding: 3px 5px;
color: grey;
border-bottom: none;
}
.pa-form + .pa-form {
margin-top: 10px;
}
+1 -1
View File
@@ -2416,7 +2416,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7360</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7360</IISUrl>
<IISUrl>http://localhost:7370</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -5,30 +5,30 @@
<asp:Content ContentPlaceHolderID="head" runat="server">
<script type="text/javascript">
function updateLoginId() {
var treePicker = window.showModalDialog('<%=umbraco.cms.presentation.Trees.TreeService.GetPickerUrl(true,"content","content")%>', 'treePicker', 'dialogWidth=350px;dialogHeight=300px;scrollbars=no;center=yes;border=thin;help=no;status=no')
var treePicker = window.showModalDialog('<%=umbraco.cms.presentation.Trees.TreeService.GetPickerUrl(true,"content","content")%>', 'treePicker', 'dialogWidth=350px;dialogHeight=300px;scrollbars=no;center=yes;border=thin;help=no;status=no')
if (treePicker != undefined) {
document.getElementById("loginId").value = treePicker;
if (treePicker > 0) {
umbraco.presentation.webservices.CMSNode.GetNodeName('<%=umbraco.BasePages.BasePage.umbracoUserContextID%>', treePicker, updateLoginTitle);
} else
} else
document.getElementById("loginTitle").innerHTML = "<strong><%=umbraco.ui.Text("content", base.getUser())%></strong>";
}
}
function updateLoginTitle(result) {
document.getElementById("loginTitle").innerHTML = "<strong>" + result + "</strong> &nbsp;";
}
function updateErrorId() {
var treePicker = window.showModalDialog('<%=umbraco.cms.presentation.Trees.TreeService.GetPickerUrl(true,"content","content")%>', 'treePicker', 'dialogWidth=350px;dialogHeight=300px;scrollbars=no;center=yes;border=thin;help=no;status=no')
var treePicker = window.showModalDialog('<%=umbraco.cms.presentation.Trees.TreeService.GetPickerUrl(true,"content","content")%>', 'treePicker', 'dialogWidth=350px;dialogHeight=300px;scrollbars=no;center=yes;border=thin;help=no;status=no')
if (treePicker != undefined) {
document.getElementById("errorId").value = treePicker;
if (treePicker > 0) {
umbraco.presentation.webservices.CMSNode.GetNodeName('<%=umbraco.BasePages.BasePage.umbracoUserContextID%>', treePicker, updateErrorTitle);
} else
} else
document.getElementById("errorTitle").innerHTML = "<strong><%=umbraco.ui.Text("content", base.getUser())%></strong>";
}
}
}
function updateErrorTitle(result) {
document.getElementById("errorTitle").innerHTML = "<strong>" + result + "</strong> &nbsp;";
}
@@ -49,7 +49,7 @@
document.getElementById("pagesForm").style.display = "none";
}
}
function togglePages() {
document.getElementById("pagesForm").style.display = "block";
}
@@ -81,34 +81,37 @@
<cc1:Feedback ID="feedback" runat="server" />
<asp:Panel ID="p_mode" runat="server">
<asp:Panel ID="p_mode" runat="server" CssClass="pa-umb-overlay">
<div class="umg-dialog-body">
<cc1:Pane ID="pane_chooseMode" runat="server" Text="Choose how to restict access to this page">
<asp:RadioButton GroupName="mode" ID="rb_simple" runat="server" Style="float: left; margin: 10px;" Checked="true" />
<div class="pa-select-type">
<asp:RadioButton GroupName="mode" ID="rb_simple" runat="server" Checked="true" />
<div style="float: right;">
<h4 style="padding-top: 0px;"><%= umbraco.ui.Text("publicAccess", "paSimple", base.getUser())%></h4>
<p><%= umbraco.ui.Text("publicAccess", "paSimpleHelp", base.getUser())%></p>
</div>
<label for="body_rb_simple">
<h4 class="pa-access-header"><%= umbraco.ui.Text("publicAccess", "paSimple", base.getUser())%></h4>
<p class="pa-access-description"><%= umbraco.ui.Text("publicAccess", "paSimpleHelp", base.getUser())%></p>
</label>
</div>
<br style="clear: both;" />
<asp:RadioButton GroupName="mode" ID="rb_advanced" runat="server" Style="float: left; margin: 10px;" />
<div class="pa-select-type">
<asp:RadioButton GroupName="mode" ID="rb_advanced" runat="server"/>
<div style="float: left; padding-left: 10px;">
<h4 style="padding-top: 0px;"><%= umbraco.ui.Text("publicAccess", "paAdvanced", base.getUser())%></h4>
<p><%= umbraco.ui.Text("publicAccess", "paAdvancedHelp", base.getUser())%></p>
<label for="body_rb_advanced">
<h4 class="pa-access-header"><%= umbraco.ui.Text("publicAccess", "paAdvanced", base.getUser())%></h4>
<p class="pa-access-description"><%= umbraco.ui.Text("publicAccess", "paAdvancedHelp", base.getUser())%></p>
<asp:Panel runat="server" Visible="false" ID="p_noGroupsFound" CssClass="error">
<p>
<%= umbraco.ui.Text("publicAccess", "paAdvancedNoGroups", UmbracoUser)%>
</p>
</asp:Panel>
</label>
</div>
</div>
</cc1:Pane>
</div>
@@ -119,20 +122,24 @@
</asp:Panel>
<cc1:Pane ID="pane_simple" runat="server" Visible="false" Text="Single user protection">
<cc1:PropertyPanel ID="PropertyPanel1" runat="server">
<p><%= umbraco.ui.Text("publicAccess", "paSetLogin", UmbracoUser)%></p>
<asp:CustomValidator runat="server" ID="SimpleLoginNameValidator" Display="Dynamic" EnableViewState="False">
<p class="alert">Member name already exists, click <asp:LinkButton runat="server" OnClick="ChangeOnClick" CssClass="btn btn-mini btn-warning">Change</asp:LinkButton> to use a different name or Update to continue</p>
</asp:CustomValidator>
</cc1:PropertyPanel>
<cc1:PropertyPanel Text="Login" ID="pp_login" runat="server">
<asp:TextBox ID="simpleLogin" runat="server" Width="150px"></asp:TextBox>
<asp:Label runat="server" ID="SimpleLoginLabel" Visible="False"></asp:Label>
</cc1:PropertyPanel>
<cc1:PropertyPanel Text="Password" ID="pp_pass" runat="server">
<asp:TextBox ID="simplePassword" runat="server" Width="150px"></asp:TextBox>
</cc1:PropertyPanel>
<cc1:Pane ID="pane_simple" runat="server" Visible="false" Text="Set the login and password for this page" CssClass="pa-umb-overlay">
<div class="pa-form">
<cc1:PropertyPanel Text="Login" ID="pp_login" runat="server">
<asp:TextBox ID="simpleLogin" runat="server" Width="250px"></asp:TextBox>
<asp:Label runat="server" ID="SimpleLoginLabel" Visible="False"></asp:Label>
</cc1:PropertyPanel>
</div>
<div class="pa-form">
<cc1:PropertyPanel Text="Password" ID="pp_pass" runat="server">
<asp:TextBox ID="simplePassword" runat="server" Width="250px"></asp:TextBox>
</cc1:PropertyPanel>
</div>
<asp:CustomValidator CssClass="pa-validation-message error" runat="server" ID="SimpleLoginNameValidator" Display="Dynamic" EnableViewState="False">
<p class="alert">Member name already exists, click <asp:LinkButton runat="server" OnClick="ChangeOnClick" CssClass="btn btn-mini btn-warning">Change</asp:LinkButton> to use a different name or Update to continue</p>
</asp:CustomValidator>
</cc1:Pane>
<cc1:Pane ID="pane_advanced" runat="server" Visible="false" Text="Role based protection">
@@ -144,27 +151,29 @@
</cc1:PropertyPanel>
</cc1:Pane>
<asp:Panel ID="p_buttons" runat="server" Visible="false">
<asp:Panel ID="p_buttons" runat="server" Visible="false" CssClass="pa-umb-overlay">
<cc1:Pane runat="server" ID="pane_pages" Text="Select the pages that contain login form and error messages">
<cc1:PropertyPanel runat="server" ID="pp_loginPage">
<asp:PlaceHolder ID="ph_loginpage" runat="server" />
<asp:CustomValidator ErrorMessage="*" runat="server" ID="cv_loginPage" ForeColor="Red" />
<br />
<small>
<cc1:PropertyPanel runat="server" ID="pp_loginPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=umbraco.ui.Text("paLoginPageHelp")%>
</small>
<br />
<br />
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_loginpage" runat="server" />
</div>
<asp:CustomValidator ErrorMessage="Please pick a login page" runat="server" ID="cv_loginPage" ForeColor="Red" />
</cc1:PropertyPanel>
<cc1:PropertyPanel runat="server" ID="pp_errorPage">
<asp:PlaceHolder ID="ph_errorpage" runat="server" />
<asp:CustomValidator ErrorMessage="*" runat="server" ID="cv_errorPage" ForeColor="Red" />
<br />
<small>
<cc1:PropertyPanel runat="server" ID="pp_errorPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=umbraco.ui.Text("paErrorPageHelp")%>
</small>
<br />
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_errorpage" runat="server" />
</div>
<asp:CustomValidator ErrorMessage="Please pick an error page" runat="server" ID="cv_errorPage" ForeColor="Red" />
</cc1:PropertyPanel>
</cc1:Pane>
@@ -141,7 +141,9 @@ namespace Umbraco.Web.Cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
//clear static object cache
global::umbraco.cms.businesslogic.ContentType.RemoveAllDataTypeCache();
PublishedContentType.ClearAll();
base.RefreshAll();
}
@@ -278,7 +280,9 @@ namespace Umbraco.Web.Cache
//clears the dictionary object cache of the legacy ContentType
global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(payload.Alias);
PublishedContentType.ClearContentType(payload.Id);
//need to recursively clear the cache for each child content type
foreach (var descendant in payload.DescendantPayloads)
{
@@ -108,6 +108,7 @@ namespace Umbraco.Web.Cache
if (dataTypeCache)
dataTypeCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id));
PublishedContentType.ClearDataType(payload.Id);
});
base.Refresh(jsonPayload);
@@ -42,7 +42,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id)));
config.CreateMap<IUser, UserData>()
.ConstructUsing((IUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
.ConstructUsing((IUser user) => new UserData())
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
@@ -44,7 +44,7 @@ namespace Umbraco.Web.Security.Identity
CookieName = securitySection.AuthCookieName;
CookieHttpOnly = true;
CookieSecure = forceSsl ? CookieSecureOption.Always : CookieSecureOption.SameAsRequest;
CookiePath = "/";
CookiePath = "/";
//Custom cookie manager so we can filter requests
CookieManager = new BackOfficeCookieManager(new SingletonUmbracoContextAccessor(), explicitPaths);
@@ -84,7 +84,7 @@ namespace Umbraco.Web.Security.Identity
if (ticket.Properties.IsPersistent)
{
cookieOptions.Expires = expiresUtc.ToUniversalTime().DateTime;
cookieOptions.Expires = expiresUtc.UtcDateTime;
}
return cookieOptions;
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Web;
using Umbraco.Core.CodeAnnotations;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
@@ -52,7 +53,7 @@ namespace umbraco
if (IsPartialViewMacro == false)
{
var attempt = fileService.CreatePartialView(model, snippetName, User.Id);
_returnUrl = string.Format("settings/views/EditView.aspx?treeType=partialViews&file={0}", model.Path.TrimStart('/'));
_returnUrl = string.Format("settings/views/EditView.aspx?treeType=partialViews&file={0}", HttpUtility.UrlEncode(model.Path.TrimStart('/').Replace("\\", "/")));
return attempt.Success;
}
else
@@ -68,7 +69,7 @@ namespace umbraco
macroService.Save(new Macro(attempt.Result.Alias, attempt.Result.Alias) { ScriptPath = virtualPath });
}
_returnUrl = string.Format("settings/views/EditView.aspx?treeType=partialViewMacros&file={0}", model.Path.TrimStart('/'));
_returnUrl = string.Format("settings/views/EditView.aspx?treeType=partialViewMacros&file={0}", HttpUtility.UrlEncode(model.Path.TrimStart('/').Replace("\\", "/")));
return attempt.Success;
}
@@ -183,71 +183,71 @@ namespace umbraco.presentation.umbraco.dialogs
SimpleLoginNameValidator.IsValid = true;
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
int pageId = int.Parse(helper.Request("nodeId"));
if (Page.IsValid)
if (e.CommandName == "simple")
{
int pageId = int.Parse(helper.Request("nodeId"));
var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text;
if (e.CommandName == "simple")
var member = provider.GetUser(memberLogin, false);
if (member == null)
{
var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text;
var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com";
var member = provider.GetUser(memberLogin, false);
if (member == null)
// this needs to work differently depending on umbraco members or external membership provider
if (provider.IsUmbracoMembershipProvider() == false)
{
var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com";
// this needs to work differently depending on umbraco members or external membership provider
if (provider.IsUmbracoMembershipProvider() == false)
member = provider.CreateUser(memberLogin, simplePassword.Text, tempEmail);
}
else
{
//if it's the umbraco membership provider, then we need to tell it what member type to create it with
if (MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null)
{
member = provider.CreateUser(memberLogin, simplePassword.Text, tempEmail);
MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType);
}
else
var castedProvider = provider.AsUmbracoMembershipProvider();
MembershipCreateStatus status;
member = castedProvider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType,
memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status);
if (status != MembershipCreateStatus.Success)
{
//if it's the umbraco membership provider, then we need to tell it what member type to create it with
if (MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null)
{
MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType);
}
var castedProvider = provider.AsUmbracoMembershipProvider();
MembershipCreateStatus status;
member = castedProvider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType,
memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status);
if (status != MembershipCreateStatus.Success)
{
SimpleLoginNameValidator.IsValid = false;
SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status;
SimpleLoginNameValidator.Text = "Could not create user: " + status;
return;
}
SimpleLoginNameValidator.IsValid = false;
SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status;
SimpleLoginNameValidator.Text = "Could not create user: " + status;
return;
}
}
else if (pp_pass.Visible)
{
SimpleLoginNameValidator.IsValid = false;
SimpleLoginLabel.Visible = true;
SimpleLoginLabel.Text = memberLogin;
simpleLogin.Visible = false;
pp_pass.Visible = false;
return;
}
// Create or find a memberGroup
var simpleRoleName = "__umbracoRole_" + member.UserName;
if (Roles.RoleExists(simpleRoleName) == false)
{
Roles.CreateRole(simpleRoleName);
}
if (Roles.IsUserInRole(member.UserName, simpleRoleName) == false)
{
Roles.AddUserToRole(member.UserName, simpleRoleName);
}
Access.ProtectPage(true, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value));
Access.AddMembershipRoleToDocument(pageId, simpleRoleName);
Access.AddMembershipUserToDocument(pageId, member.UserName);
}
else if (e.CommandName == "advanced")
else if (pp_pass.Visible)
{
SimpleLoginNameValidator.IsValid = false;
SimpleLoginLabel.Visible = true;
SimpleLoginLabel.Text = memberLogin;
simpleLogin.Visible = false;
pp_pass.Visible = false;
return;
}
// Create or find a memberGroup
var simpleRoleName = "__umbracoRole_" + member.UserName;
if (Roles.RoleExists(simpleRoleName) == false)
{
Roles.CreateRole(simpleRoleName);
}
if (Roles.IsUserInRole(member.UserName, simpleRoleName) == false)
{
Roles.AddUserToRole(member.UserName, simpleRoleName);
}
Access.ProtectPage(true, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value));
Access.AddMembershipRoleToDocument(pageId, simpleRoleName);
Access.AddMembershipUserToDocument(pageId, member.UserName);
}
else if (e.CommandName == "advanced")
{
if (cv_errorPage.IsValid && cv_loginPage.IsValid)
{
Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value));
@@ -257,19 +257,23 @@ namespace umbraco.presentation.umbraco.dialogs
else
Access.RemoveMembershipRoleFromDocument(pageId, li.Value);
}
feedback.Text = ui.Text("publicAccess", "paIsProtected", new cms.businesslogic.CMSNode(pageId).Text) + "</p><p><a href='#' onclick='" + ClientTools.Scripts.CloseModalWindow() + "'>" + ui.Text("closeThisWindow") + "</a>";
p_buttons.Visible = false;
pane_advanced.Visible = false;
pane_simple.Visible = false;
var content = ApplicationContext.Current.Services.ContentService.GetById(pageId);
//reloads the current node in the tree
ClientTools.SyncTree(content.Path, true);
//reloads the current node's children in the tree
ClientTools.ReloadActionNode(false, true);
feedback.type = global::umbraco.uicontrols.Feedback.feedbacktype.success;
else
{
return;
}
}
feedback.Text = ui.Text("publicAccess", "paIsProtected", new cms.businesslogic.CMSNode(pageId).Text) + "</p><p><a href='#' onclick='" + ClientTools.Scripts.CloseModalWindow() + "'>" + ui.Text("closeThisWindow") + "</a>";
p_buttons.Visible = false;
pane_advanced.Visible = false;
pane_simple.Visible = false;
var content = ApplicationContext.Current.Services.ContentService.GetById(pageId);
//reloads the current node in the tree
ClientTools.SyncTree(content.Path, true);
//reloads the current node's children in the tree
ClientTools.ReloadActionNode(false, true);
feedback.type = global::umbraco.uicontrols.Feedback.feedbacktype.success;
}